This document describes the CI/CD pipelines for the Open Data Ensemble (ODE) monorepo.
The ODE monorepo uses GitHub Actions for continuous integration and deployment. Each project has its own pipeline that triggers only when relevant files change.
Workflow File: .github/workflows/synkronus-docker.yml
- Push to
main: Builds and publishes release images - Push to
dev: Builds and publishes pre-release images - Push to feature branches: Builds and publishes branch-specific images
- Pull Requests: Builds but does not publish (validation only)
- Manual Dispatch: Allows manual triggering with optional version tag
The workflow only runs when files in these paths change:
synkronus/**- Any file in the Synkronus project.github/workflows/synkronus-docker.yml- The workflow itself
Images are published to GitHub Container Registry (GHCR):
- Registry:
ghcr.io - Image:
ghcr.io/opendataensemble/synkronus
| 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 |
- Multi-platform: Builds for
linux/amd64andlinux/arm64 - Build Cache: Uses GitHub Actions cache for faster builds
- Attestation: Generates build provenance for security
- Metadata: Includes OCI-compliant labels and annotations
The workflow requires these permissions:
contents: read- To checkout the repositorypackages: write- To publish to GHCR
GITHUB_TOKEN- Automatically provided by GitHub Actions
Workflow File: .github/workflows/formulus-android.yml
Builds Android APK for the Formulus React Native application, and builds/consumes Formplayer assets in a single, two‑job workflow.
- Push to
main/dev(formulus or formulus-formplayer changes): Builds Formplayer assets and then a release APK using those assets - Pull Requests (formulus or formulus-formplayer changes): Builds Formplayer assets and then a debug APK for validation
- Release: Publishes APK to GitHub Release
The workflow runs when files in these paths change:
formulus/**- Any file in the formulus projectformulus-formplayer/**- Any file in the formulus-formplayer projectpackages/tokens/**- Shared design tokens and build inputs.github/workflows/formulus-android.yml- The workflow itself
The workflow intelligently handles formplayer assets using two jobs:
-
build-formplayer-assetsjob:- Builds
@ode/tokens - Builds Formplayer assets using
npm run build:rninformulus-formplayer - Uploads the built assets from
formulus/android/app/src/main/assets/formplayer_dist/as a GitHub Actions artifact
- Builds
-
build-androidjob (depends on assets job):- Downloads the Formplayer assets artifact into
formulus/android/app/src/main/assets/formplayer_dist/ - Builds the Android APK (debug for PRs, release for main/dev/release events)
- Downloads the Formplayer assets artifact into
Formplayer assets are not committed to git and are ignored via .gitignore. CI builds always use the assets artifact produced in the same workflow run, ensuring a single, consistent source of truth for each build.
- Pull Requests: Debug APK (unsigned)
- Push to main/dev: Release APK (signed with secrets)
- Release: Release APK published to GitHub Release
FORMULUS_RELEASE_KEYSTORE_B64- Base64 encoded keystore fileFORMULUS_RELEASE_STORE_PASSWORD- Keystore passwordFORMULUS_RELEASE_KEY_ALIAS- Key aliasFORMULUS_RELEASE_KEY_PASSWORD- Key password
Formplayer asset building and Android APK building are now handled within the same workflow:
Formplayer or Formulus Changes → build-formplayer-assets job → build-android job (consumes artifact) → APK artifact / Release upload
This ensures:
- No duplicate cross-workflow wiring
- A single workflow owns both asset and APK builds
- Each APK is built against the exact assets produced in the same run
- Formplayer build outputs do not pollute git history
docker pull ghcr.io/opendataensemble/synkronus:latestdocker pull ghcr.io/opendataensemble/synkronus:v1.0.0docker pull ghcr.io/opendataensemble/synkronus:devdocker pull ghcr.io/opendataensemble/synkronus:feature-xyzTo create a versioned release:
- Go to Actions → Synkronus Docker Build & Publish
- Click Run workflow
- Select the
mainbranch - Enter version (e.g.,
v1.0.0) - Click Run workflow
This will create:
ghcr.io/opendataensemble/synkronus:latestghcr.io/opendataensemble/synkronus:v1.0.0ghcr.io/opendataensemble/synkronus:v1.0
By default, GHCR packages inherit the repository's visibility:
- Public repositories → Public images (no authentication needed)
- Private repositories → Private images (authentication required)
For private images:
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin- Go to the Actions tab in GitHub
- Select Synkronus Docker Build & Publish
- View recent runs and their status
- Go to the repository main page
- Click Packages in the right sidebar
- Select synkronus
- View all published tags and their details
- Check the Actions tab for error logs
- Common issues:
- Dockerfile syntax errors
- Missing dependencies in build context
- Network issues during dependency download
- Verify the branch name matches the workflow triggers
- Check that the workflow has
packages: writepermission - Ensure the push event (not PR) triggered the workflow
- Verify the image tag exists in GHCR
- For private repos, ensure you're authenticated
- Check image name spelling:
ghcr.io/opendataensemble/synkronus
- Test locally first: Build and test Docker images locally before pushing
- Use feature branches: Create feature branches for experimental changes
- Review build logs: Check Actions logs even for successful builds
- Tag releases properly: Use semantic versioning for releases
- Pin versions in production: Use specific version tags, not
latest - Test pre-releases: Use
devtag for staging environments - Monitor image sizes: Keep images lean for faster deployments
- Use health checks: Always configure health checks in deployments
The automated asset build process ensures Formulus Android builds always have matching Formplayer assets:
- Developer makes changes to
formulus-formplayer - Opens/updates PR (or pushes to
main/dev) → the Formulus Android workflow:- Runs the
build-formplayer-assetsjob to build Formplayer assets and upload them as an artifact - Runs the
build-androidjob, which downloads the artifact and builds the APK
- Runs the
- No manual work: Assets are automatically built and passed between jobs via artifacts
- No conflicts: Assets are not committed to git, avoiding noisy diffs and merge issues
- Always consistent: Each Android build uses the assets built in that same workflow run
- Clean repository: Built assets live only in CI artifacts and local workspaces, not in version control
For local development, you can manually build and copy assets:
cd formulus-formplayer
npm run build:rnThis will:
- Build the formplayer web app
- Clean existing assets in formulus
- Copy new assets to
formulus/android/app/src/main/assets/formplayer_dist/
The build:rn script automatically handles cleaning, so no need to run clean-rn-assets separately.
Potential improvements to the CI/CD pipeline:
- Automated formplayer asset synchronization
- Add automated testing before build
- Implement security scanning (Trivy, Snyk)
- Add deployment to staging environment
- Create release notes automation
- Add Slack/Discord notifications
- Implement rollback mechanisms
- Add performance benchmarking
- Root README - Monorepo overview
- Synkronus DOCKER.md - Docker quick start
- Synkronus DEPLOYMENT.md - Comprehensive deployment guide