main.
How It Works
- Merge to
main—continuous-delivery-stg.ymlbuilds aversion.sha.betaimage, deploys it to staging (stg.activepieces.com), and runs the E2E suite against staging (e2e-staging). - By hand —
continuous-delivery-cloud.yml(the Cloud gate) picks the newestmaincommit whosee2e-stagingpassed, runs the E2E matrix and the upgrade test on the image staging built from it, and waits for one approval. - On approval — the gate deploys that same image to the canary, then to the whole production fleet.
- Self-hosted —
release-self-hosted.yml, by hand. The weeklycontinuous-delivery-release.ymlis disabled.
Environments
Hotfix Workflow
- Merge the fix into
main. - Run
continuous-delivery-cloud.ymlby hand withhotfix_prsset to the PR numbers, for example17001,17005. The gate cherry-picks only those PRs, oldest first, onto the commit Cloud runs (branchhotfix/cloud/<run id>-<attempt>), builds aversion.sha.pickimage, and runs the E2E matrix and the upgrade test on it. The rest ofmainwaits for the next full gate run. - The run summary names what Cloud runs and what this run adds. The approval and the deploy are the same as any other Cloud deploy.
main, is already on Cloud, does not apply cleanly, or changes a migration (set allow_migrations only when the migration needs nothing Cloud lacks). A hotfix deploy records no Linear release; the next full gate run does. That run deploys main only if main contains every picked PR.
To ship all of main at once instead, leave hotfix_prs empty: the gate takes the newest commit that passed e2e-staging, or the commit named in sha.
If Cloud is down and the gate is too slow, use emergency-cloud-deploy.yml. To go back to an earlier image, use continuous-delivery-rollback.yml.
Self-Hosted Release
Userelease-self-hosted.yml (see below). The weekly continuous-delivery-release.yml still reads the old release-candidate tag and is disabled.
GitHub Actions Workflows
To run any workflow manually, go to the repo’s Actions tab, select the workflow, and click Run workflow.continuous-delivery-stg.yml — Staging
Manual dispatch choices
deploy-staging— Re-deploy the latestmainto staging. Use this after an infra change.
continuous-delivery-cloud.yml — Cloud Gate
The gate has no schedule yet. A daily run (Sunday to Thursday, 8 AM UTC) comes back when the E2E suite covers more of the product; the
cut job already stops a scheduled run when every Cloud host runs the candidate.
Then, for the candidate:
e2e-matrix.ymlandupgrade-test.ymlrun on the candidate’sversion.sha.betaimage. Nothing is built again.- The
reportjob posts thee2e-matrixandupgrade-testcommit statuses on the candidate SHA. A red upgrade test says whether the candidate broke or the setup failed before the upgrade. - The run summary lists the tickets between the commit Cloud runs and the candidate.
- The
approvejob waits for approval in theproductionGitHub Environment. It starts only when every check passed. A newer candidate cancels an older one that still waits. - Under the
promote-productionlock, thedeployjob reads the Cloud version again and refuses to deploy when the canary and the fleet run different images, when Cloud changed after the cut, or when the candidate lacks a PR that Cloud runs (a downgrade, or a fix shipped through another path). Then the breaking-migration check, the canary, the fleet, and the Linear “Cloud” release.
emergency-cloud-deploy.yml, which deploys only the fleet), deploy the fleet’s tag to the canary from mrsk/prod: kamal deploy --version <tag> --config-file=config/app-canary.yml --skip-push, then the same with config/worker-canary.yml.
The canary has no workflow of its own since ENG-1021: the gate’s deploy job runs the breaking-migration check, deploys the canary, then the fleet, under the promote-production lock that continuous-delivery-rollback.yml also uses. See the Canary Deployment playbook.
release-self-hosted.yml
Manual dispatch only. Prompts for a release tag (e.g., 0.79.2 or 0.79.2-hotfix.1). Builds multi-platform images (amd64 + arm64), pushes to Docker Hub and GHCR, and creates a git tag. Can be run from any branch — useful for patching older versions.
This is the only release path that takes a free-text version — the scheduled continuous-delivery-release.yml and the Cloud images both derive the version from package.json (node --print "require('./package.json').version") and never ask a human. The free-text input exists precisely because off-cycle patches carry deploy-only suffixes (-hotfix.N, -rc.N) that don’t live in package.json. Because package.json is the single runtime source of truth (apVersionUtil.getCurrentRelease reads it, and it’s baked into the image from source), a mistyped tag would publish an image whose real version disagrees with its tag — invisible until it’s mixed with a correctly-built peer and the worker↔app version gate silently withholds jobs. The Verify tag matches package.json step fails the release when the tag’s base X.Y.Z (suffix stripped) doesn’t equal package.json, so bump package.json (or fix the tag) before releasing.
Because the bump commit for an off-cycle release lives on its release branch, main’s package.json would otherwise stay stale, and the next weekly release then reads the old version at the release-candidate ref and refuses to overwrite the already-existing tag (this is how 0.85.4/0.85.5 came to report 0.85.2). To close that loop, a sync-version-to-main job runs after every successful release: if main’s version is behind the released base version, it opens an automated PR (branch automated/sync-main-<tag>, one per tag so a hotfix never overwrites a pending base-release PR, pushed and created with the automated-PR token so CI runs on it) setting package.json to the base version and the docker-compose.yml image pins to the full released tag. Merge that PR before the next release. The same job runs tools/scripts/breaking-changes-rollover.ts: every entry on the hidden docs/install/reference/breaking-changes-unreleased.mdx page that already existed at the released tag is moved under ## <tag> on the public Breaking Changes page, so the PR opens whenever the version or the breaking-changes pages need syncing, not only when the version is behind. If neither needs a change, the job no-ops. The weekly continuous-delivery-release.yml runs the same rollover in its sync-main job, so both release paths file entries under the tag that shipped them. The job lives in the workflow definition at the dispatched ref, so releases run from branches cut before it existed won’t open a sync PR. Check main’s version manually in that case.
publish-embed-sdk.yml
Publishes the embed SDK bundle (packages/ee/embed-sdk) to https://cdn.activepieces.com/sdk/embed/<version>.js. Runs on pushes to main that change packages/ee/embed-sdk/package.json, on a daily schedule, and on manual dispatch (main only). The check job compares the package version against the CDN and publishes only when that version is missing: a 200 skips, a 404 publishes, anything else fails the run. Bumping version in packages/ee/embed-sdk/package.json is the entire release procedure for the SDK; the daily schedule retries failed publishes, and already-published versions are never overwritten. Failures notify the on-call Discord channel. Requires the CDN_S3_ENDPOINT, CDN_S3_BUCKET, CDN_S3_ACCESS_KEY_ID, CDN_S3_SECRET_ACCESS_KEY, and DISCORD_ON_CALL_WEBHOOK secrets.