Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
99f2549
fix: update gradle version (#568)
r0ssing Mar 24, 2026
c92b91b
feat(formulus): move fastlane to parent and add images (#569)
r0ssing Mar 24, 2026
1d64a01
fix: correct api URL handling in CLI (#570)
r0ssing Mar 24, 2026
db08552
feat(formulus): bump version (#571)
r0ssing Mar 24, 2026
2ddff93
fix: add changelog for F-Droid and remove binary test fixtures (#572)
najuna-brian Mar 24, 2026
8f8924c
fix: move fastlane metadata to repo root for F-Droid compatibility (#…
najuna-brian Mar 24, 2026
913a858
fix(tokens): commit dist/ and guard prepare script for F-Droid compat…
najuna-brian Mar 26, 2026
6032a80
feat: add context files (#576)
r0ssing Mar 27, 2026
a7e3425
refactor(QRScanner): update to use react-native-camera-kit-no-google …
r0ssing Mar 28, 2026
48007c1
feat: improve with proper semver comparison (#577)
r0ssing Mar 28, 2026
c795759
fix(all): Aligning http headers, autogenerate API clients (#583)
r0ssing Mar 28, 2026
6c3c9e0
feat(docs): Added some roadmap notes, plus text for review (#584)
r0ssing Mar 31, 2026
9d82fa7
chore: package updates (#589)
r0ssing Apr 4, 2026
0855cfe
fix: wrap FormplayerModal in KeyboardAvoidingView to fix whitespace b…
Jexsie Apr 5, 2026
498b2b3
ci(synkronus): Support FOSS multi-arch build in CI (#586)
bokchan Apr 5, 2026
3b74c2f
feat(formulus): WebView file URL bridge API (v1.2.0)
Mishael-2584 Apr 9, 2026
d6c517f
feat(synkronus): Last seen list (#592)
r0ssing Apr 9, 2026
23adf8e
Merge pull request #593 from OpenDataEnsemble/feature/formulus-webvie…
Mishael-2584 Apr 9, 2026
104e109
feat(synkronus): add repository generation and admin hard reset (#594)
r0ssing Apr 9, 2026
78e6147
chore(ci): update Docker workflow for multi-arch builds and dependenc…
r0ssing Apr 10, 2026
48ab3b0
Add Dockerfile.slim for CI slim runtime image builds
r0ssing Apr 10, 2026
76aed2a
Merge pull request #595 from HelloSapiens/fix/docker-build
Ndacyayisenga-droid Apr 10, 2026
9cb5e7a
chore(ci): update Docker workflow to enforce lowercase image names an…
r0ssing Apr 10, 2026
3b1f7ed
fix(synkronus-cli): Fix install script and Build & Release CI workflo…
bokchan Apr 11, 2026
aef46f2
Fix: Linked table nested form lifecycle - preserve parent form state …
najuna-brian Apr 8, 2026
4116a54
feat: Add returnOnly support for embedded child forms in Formulus
najuna-brian Apr 10, 2026
db15d86
fix(formplayer): remove observation metadata and uuid fallback for re…
najuna-brian Apr 11, 2026
c7a432c
fix prettier
Ndacyayisenga-droid Apr 11, 2026
b7a4ac7
fix(formulus): forward openFormplayer options so returnOnly child for…
Ndacyayisenga-droid Apr 11, 2026
c6daba2
fix prettier
Ndacyayisenga-droid Apr 11, 2026
3723bfb
feat(formulus): Making Formulus UI a Banger (#598)
IamLRBA Apr 12, 2026
d8a3351
doc: add notifee comment
r0ssing Apr 12, 2026
13add4e
feat(formulus): Redesign Sync App bundle section (#599)
IamLRBA Apr 12, 2026
ef817f9
fix(formulus): better reset handling (#601)
r0ssing Apr 12, 2026
e32baa6
fix: resolve merge conflict in FormulusInjectionScript - keep options…
najuna-brian Apr 13, 2026
0d723eb
fix(portal): avoid double v in server version on login (#602)
IamLRBA Apr 14, 2026
9f22f39
make sure child-form is not saved
Mishael-2584 Apr 14, 2026
ca58848
Merge branch 'dev' into linked-table-support
IamLRBA Apr 14, 2026
3d3ed6c
Merge pull request #591 from najuna-brian/linked-table-support
IamLRBA Apr 14, 2026
d51ef84
Merge pull request #600 from HelloSapiens/doc/update-notifee-readme
bahati308 Apr 14, 2026
d5afa0c
refactor: rename returnOnly to subObservationMode for clarity in form…
Mishael-2584 Apr 14, 2026
b0abd67
Fix image name formatting in Docker workflow
r0ssing Apr 15, 2026
2e25512
fix: replace regex with just removing the v
r0ssing Apr 15, 2026
2c63e37
Merge pull request #606 from HelloSapiens/fix/optimize-semver-display
IamLRBA Apr 15, 2026
b7dbf41
feat: Add ODE Desktop Application (Tauri + React) (#610)
najuna-brian Apr 16, 2026
fa50d80
fix(portal): Synkronus portal UI Mods (#611)
IamLRBA Apr 17, 2026
56974be
fix(formulus): attachment synchronization refactor (#612)
r0ssing Apr 17, 2026
fd13599
fix: add missing header param
r0ssing Apr 17, 2026
6b848fd
Merge pull request #613 from HelloSapiens/fix/add-missing-header
IamLRBA Apr 17, 2026
52d4c5b
chore: update README and CICD documentation for improved clarity and …
r0ssing Apr 17, 2026
579df79
Merge pull request #614 from HelloSapiens/feat/improve-docker-image-t…
IamLRBA Apr 17, 2026
6ff7ad2
feat: hardcode image name in Docker workflow to ensure lowercase comp…
r0ssing Apr 17, 2026
e149fad
fix: add import to fix darkmode issue (#616)
r0ssing Apr 17, 2026
a4d1ffd
feat: refactor attachment handling (#617)
r0ssing Apr 17, 2026
08c626d
wip (migration help)
r0ssing Apr 20, 2026
4be970d
desktop: sync/import progress UI
najuna-brian Apr 24, 2026
4eba241
fix(desktop): resolve lint violations in attachment extraction and im…
najuna-brian Apr 25, 2026
7ad26e1
chore(desktop): apply prettier formatting
najuna-brian Apr 25, 2026
b437ebd
chore(desktop): apply rustfmt in src-tauri
najuna-brian Apr 25, 2026
fce563f
fix(desktop): satisfy clippy collapsible_if and needless_borrow
najuna-brian Apr 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
28 changes: 28 additions & 0 deletions .cursor/skills/ode-custom-app-form-validation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: ode-custom-app-form-validation
description: >-
Helps validate ODE custom app form definitions (JSON Schema draft-07 + UI schema)
and CI integration. Use when the user asks to validate forms, add validate-forms
scripts, or check schema/ui consistency for Synkronus bundles. Prefer official docs
at opendataensemble.org and the custom-app template repo on GitHub.
---

# ODE custom app — form validation

## When to use

- Adding or maintaining **`schema.json`** / **`ui.json`** for ODE custom apps.
- Setting up **CI** to fail on invalid forms.
- Explaining **draft-07**, `scope` references, or ODE-specific **`format`** rules.

## What to do

1. Point to **[Form specifications](https://opendataensemble.org/docs/reference/form-specifications)** as the normative reference for question types and UI structure.
2. Ensure **UI `scope`** paths match **JSON Schema** properties; **`required`** arrays align with product intent.
3. For automation, describe a **Node** (or other) script pattern: load each form folder, `JSON.parse`, validate schema with a draft-07 validator, and check structural rules (both files present, cross-references). Link **[custom_app](https://github.com/OpenDataEnsemble/custom_app)** for AI context, not as a code dependency.
4. Do **not** invent Synkronus-specific validation rules beyond public docs.

## Official links

- [Form specifications](https://opendataensemble.org/docs/reference/form-specifications)
- [Custom applications](https://opendataensemble.org/docs/guides/custom-applications)
26 changes: 26 additions & 0 deletions .cursor/skills/ode-custom-jsonforms-extension/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: ode-custom-jsonforms-extension
description: >-
Guides adding ODE custom JSON Forms extensions (custom renderers, testers, ext.json,
dynamic functions). Use when extending forms with non-standard controls or wiring
extension manifests. Official behavior is documented on opendataensemble.org.
---

# ODE custom JSON Forms extensions

## When to use

- Custom **control** rendering (tester + renderer pattern).
- **`ext.json`** (or app-level extension manifest) and module paths.
- **Dynamic choice lists** or helper **functions** referenced from the UI schema.

## What to do

1. Read **[Custom extensions](https://opendataensemble.org/docs/guides/custom-extensions)** and **[Dynamic choice lists](https://opendataensemble.org/docs/guides/dynamic-choice-lists)** on the docs site.
2. Keep **bundled JS** compatible with the Formulus WebView (no Node-only APIs in runtime code).
3. Align **`format`** in schema with the renderer contract described in docs.
4. For AI-only context summaries, **[CONTEXT_ODE_FORMS.md](https://github.com/OpenDataEnsemble/custom_app/blob/main/CONTEXT_ODE_FORMS.md)** in **custom_app** may help — still verify against **opendataensemble.org**.

## Do not

- Assume vanilla **jsonforms.io** examples work without checking ODE’s registered renderers and extension loading rules.
27 changes: 27 additions & 0 deletions .cursor/skills/ode-synk-bundle-publish/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: ode-synk-bundle-publish
description: >-
Guides publishing and managing ODE app bundles with the Synkronus CLI (synk):
config, auth, upload, versions. Use when deploying custom app zips or automating
bundle uploads. Official reference is opendataensemble.org and OpenDataEnsemble/ode docs.
---

# Synkronus CLI — app bundle publish

## When to use

- Uploading a **custom app** ZIP to Synkronus.
- Managing **bundle versions** or scripting **CI** upload steps.
- Configuring **`synk`** (`~/.synkronus.yaml`, `synk config use`, etc.).

## What to do

1. Use **[Synkronus CLI](https://opendataensemble.org/docs/reference/synkronus-cli)** as the primary command reference.
2. Ensure the ZIP matches **[App bundle format](https://opendataensemble.org/docs/reference/app-bundle-format)** before upload.
3. Store **API URLs and tokens** in CI secrets; never embed production credentials in source.
4. For bundle structure reminders, see **[CONTEXT_BUNDLE_AND_CI.md](https://github.com/OpenDataEnsemble/custom_app/blob/main/CONTEXT_BUNDLE_AND_CI.md)** in **custom_app** (summary only).

## Related

- [App bundles (using)](https://opendataensemble.org/docs/using/app-bundles)
- [Deployment](https://opendataensemble.org/docs/guides/deployment)
25 changes: 25 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Normalize line endings: LF in repo and working tree (avoids CRLF churn on Windows).
* text=auto eol=lf

# Shell / Docker (explicit LF)
*.sh text eol=lf
*.bash text eol=lf
Dockerfile text eol=lf
*.dockerfile text eol=lf

# Windows scripts: CRLF so cmd.exe treats them correctly
*.bat text eol=crlf
*.cmd text eol=crlf

# Binary assets
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.pdf binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
114 changes: 88 additions & 26 deletions .github/CICD.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,40 @@ Images are published to **GitHub Container Registry (GHCR)**:

#### Tagging Strategy

| Branch/Event | Tags Generated | Description |
|--------------|----------------|-------------|
| `main` | `latest`, `main-{sha}` | Latest stable release |
| `dev` | `dev`, `dev-{sha}` | Development pre-release |
| Feature branches | `{branch-name}`, `{branch-name}-{sha}` | Feature-specific builds |
| Pull Requests | `pr-{number}` | PR validation builds (not pushed) |
| Manual with version | `v{version}`, `v{major}.{minor}`, `latest` | Versioned release |
Image tags are computed by `docker/metadata-action`. The highest-priority tag per event is also used as the primary tag in manifest verification.

| Event | Tags produced | Published? |
|-------|--------------|------------|
| Release published, **not** prerelease | `v{X.Y.Z}`, `v{X.Y}`, `v{X}`, `latest` | Yes |
| Release published, **is** prerelease | `v{X.Y.Z}-{pre}`, `latest-pre-release` | Yes |
| Push → `main` | `main`, `sha-{short}` | Yes |
| Push → `dev` | `dev`, `sha-{short}` | Yes |
| Push → other branch | `{branch-name}`, `sha-{short}` | Yes |
| `workflow_dispatch` | `sha-{short}` | Yes |
| Pull request | `pr-{number}` | No (build only) |

Moving pointer tags:

- **`latest`** — always points to the most recent **non-prerelease** GitHub Release. Never moved by branch pushes.
- **`latest-pre-release`** — always points to the most recent **prerelease** GitHub Release (e.g. `-alpha.N`, `-rc.N`).
- **`main`** / **`dev`** — track the tip of the respective branch.

`workflow_dispatch` intentionally produces only `sha-{short}` so that manual runs cannot accidentally reassign `latest`, `main`, `dev`, or any other pointer tag.

#### Build Features

- **Multi-platform**: Builds for `linux/amd64` and `linux/arm64`
- **Build Cache**: Uses GitHub Actions cache for faster builds
- **Attestation**: Generates build provenance for security
- **Metadata**: Includes OCI-compliant labels and annotations
- **Multi-platform**: Builds for `linux/amd64` and `linux/arm64` using Buildah
- **Attestation**: Generates SLSA build provenance and pushes it to GHCR
- **Metadata**: Includes OCI-compliant labels (title, description, vendor, source, revision)
- **Verification**: After push, the manifest list and per-arch layers are pulled to confirm correctness

#### Permissions Required

The workflow requires these permissions:
- `contents: read` - To checkout the repository
- `packages: write` - To publish to GHCR
- `id-token: write` - For OIDC-based build provenance attestation
- `attestations: write` - To push attestation records to GHCR

#### Secrets Used

Expand Down Expand Up @@ -139,12 +153,22 @@ node scripts/sbom/generate-sboms.mjs --out sbom-dist

## Using Published Images

### Pull Latest Release
### Pull Latest Stable Release

Points to the most recent non-prerelease GitHub Release.

```bash
docker pull ghcr.io/opendataensemble/synkronus:latest
```

### Pull Latest Pre-release

Points to the most recent prerelease GitHub Release (e.g. `-alpha.N`, `-rc.N`).

```bash
docker pull ghcr.io/opendataensemble/synkronus:latest-pre-release
```

### Pull Specific Version

```bash
Expand All @@ -153,30 +177,46 @@ docker pull ghcr.io/opendataensemble/synkronus:v1.0.0

### Pull Development Build

Tracks the tip of the `dev` branch (rebuilt on every push to `dev`).

```bash
docker pull ghcr.io/opendataensemble/synkronus:dev
```

### Pull Main Branch Build

Tracks the tip of the `main` branch between releases.

```bash
docker pull ghcr.io/opendataensemble/synkronus:main
```

### Pull Feature Branch Build

```bash
docker pull ghcr.io/opendataensemble/synkronus:feature-xyz
```

## Manual Release Process
## Release Process

To create a versioned release:
Versioned images are produced by publishing a **GitHub Release** (the workflow listens for `release: [published]`).

1. Go to **Actions** → **Synkronus Docker Build & Publish**
2. Click **Run workflow**
3. Select the `main` branch
4. Enter version (e.g., `v1.0.0`)
5. Click **Run workflow**
1. Go to **Releases** → **Draft a new release**
2. Create a new tag following semver, prefixed with `v`:
- Stable release: `v1.0.0`
- Pre-release: `v1.0.0-rc.1`, `v1.0.1-alpha.7`, etc.
3. Select the target commit (typically tip of `main` for stable, tip of `dev` for pre-release)
4. Tick **Set as a pre-release** for alpha/beta/rc tags
5. Click **Publish release**

This will create:
- `ghcr.io/opendataensemble/synkronus:latest`
- `ghcr.io/opendataensemble/synkronus:v1.0.0`
- `ghcr.io/opendataensemble/synkronus:v1.0`

| Release kind | Tags produced |
|-------|--------------|
| Stable (`v1.0.0`) | `v1.0.0`, `v1.0`, `v1`, `latest` |
| Pre-release (`v1.0.0-rc.1`) | `v1.0.0-rc.1`, `latest-pre-release` |

> Note: `workflow_dispatch` is available for manual runs but intentionally **does not** create any pointer tags (only `sha-{short}`) — it is for debugging the build pipeline, not for publishing releases.

## Image Visibility

Expand Down Expand Up @@ -240,10 +280,11 @@ echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

### For Deployments

1. **Pin versions in production**: Use specific version tags, not `latest`
2. **Test pre-releases**: Use `dev` tag for staging environments
3. **Monitor image sizes**: Keep images lean for faster deployments
4. **Use health checks**: Always configure health checks in deployments
1. **Pin versions in production**: Use specific version tags (`v{X.Y.Z}`), not `latest`
2. **Staging/QA upcoming releases**: Use `latest-pre-release` to track the most recent prerelease (alpha/rc)
3. **Bleeding-edge integration**: Use `dev` for builds from the tip of the `dev` branch
4. **Monitor image sizes**: Keep images lean for faster deployments
5. **Use health checks**: Always configure health checks in deployments

## Formplayer Asset Synchronization

Expand Down Expand Up @@ -279,6 +320,27 @@ This will:

The `build:rn` script automatically handles cleaning, so no need to run `clean-rn-assets` separately.

## ODE Desktop (Tauri)

**Workflow file**: `.github/workflows/ode-desktop.yml`

### Triggers

- **Pull requests** and **pushes** to `main` / `dev` when files under `desktop/**` change (or the workflow file itself)
- **Manual dispatch**

### Path filters

Runs only when `desktop/**` or `.github/workflows/ode-desktop.yml` changes.

### What it runs

From `desktop/`: `pnpm lint`, `pnpm format:check`, `pnpm test`, `pnpm typecheck`, `pnpm codegen:synk-client`, then **fails** if `desktop/src/generated` drifts from the regenerated OpenAPI client. From `desktop/src-tauri/`: `cargo fmt --check`, `cargo clippy -D warnings`, `cargo test`.

### Formplayer embed

After building formplayer (`npm run build` in `formulus-formplayer`), copy its `build/` into the desktop app with `pnpm copy:formplayer` from `desktop/` (see `desktop/README.md`). Copied assets are gitignored under `desktop/public/formplayer_dist/`.

## Future Enhancements

Potential improvements to the CI/CD pipeline:
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/e2e-attachments.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# E2E attachment-sync harness (skeleton).
#
# Intentionally runs on `workflow_dispatch` ONLY. The jobs below stand
# up the Compose rig and the language runtimes the eventual tests will
# need, then exit 0. They pin the shape of the CI integration so that
# wiring in real tests is a drop-in change.
#
# See e2e/README.md for the three-layer test design.

name: E2E — attachments

on:
workflow_dispatch:
# When the layer 1 contract tests land, enable this:
# pull_request:
# paths:
# - 'synkronus/**'
# - 'synkronus-cli/**'
# - 'e2e/**'
# - '.github/workflows/e2e-attachments.yml'

permissions:
contents: read

concurrency:
group: e2e-attachments-${{ github.ref }}
cancel-in-progress: true

jobs:
contract:
name: Layer 1 — Synkronus contract
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: synkronus/go.mod
cache: true

- name: Start Compose (postgres + synkronus)
run: |
docker compose \
-f docker-compose.yml \
-f e2e/docker-compose.e2e.yml \
--profile e2e up --build --wait

- name: Smoke — /health responds
env:
SYNKRONUS_HOST_PORT: 18080
run: |
curl --fail --retry 10 --retry-delay 2 --retry-connrefused \
"http://127.0.0.1:${SYNKRONUS_HOST_PORT}/health"

# TODO: replace with `go test ./e2e/contracts/... -tags=e2e` once
# the contract package lands.
- name: Contract tests (placeholder)
run: echo "No contract tests wired yet — see e2e/README.md"

- name: Tear down Compose
if: always()
run: |
docker compose \
-f docker-compose.yml \
-f e2e/docker-compose.e2e.yml \
--profile e2e down -v

headless:
name: Layer 2 — Formulus headless
runs-on: ubuntu-latest
timeout-minutes: 20
needs: contract
steps:
- uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: formulus/package-lock.json

- name: Start Compose (postgres + synkronus)
run: |
docker compose \
-f docker-compose.yml \
-f e2e/docker-compose.e2e.yml \
--profile e2e up --build --wait

# TODO: replace with `npm --prefix e2e/headless test` once the
# headless harness lands.
- name: Headless tests (placeholder)
run: echo "No headless tests wired yet — see e2e/README.md"

- name: Tear down Compose
if: always()
run: |
docker compose \
-f docker-compose.yml \
-f e2e/docker-compose.e2e.yml \
--profile e2e down -v
Loading
Loading