main.
How It Works
Five separate workflows cover the full delivery lifecycle:- Merge to
main(Mon–Thu, 9 AM–5 PM UTC) —continuous-delivery-stg.ymlautomatically builds a Docker image and deploys to staging (stg.activepieces.com). - Staging freeze (5 PM UTC) — merges to
mainafter 5 PM UTC are accepted but not deployed to staging. The content team uses the frozen staging environment overnight. - Daily 9 AM UTC —
continuous-delivery-canary.ymlbuilds aversion.sha.canaryimage from the latestmainand deploys it to the canary environment. Breaking migrations block the deployment. See the Canary Deployment playbook for details. - Thursday 5 PM UTC —
tag-release-candidatejob tags the current staging image and commit asrelease-candidate. - Sunday 9 AM UTC —
continuous-delivery-cloud.ymlruns a fresh canary build frommain(by callingcontinuous-delivery-canary.ymlas a reusable workflow), then promotesrelease-candidateto production and creates adeploy/cloud/YYYY-MM-DDbranch. - Monday 9 AM UTC —
continuous-delivery-release.ymlre-tags therelease-candidateimage and publishes the self-hosted release.
Environments
Hotfix Workflow
- Checkout from the current
deploy/cloud/YYYY-MM-DDbranch (created by the last Sunday promotion). - Push your fix commit(s) to that branch.
- Manually trigger
continuous-delivery-cloud.ymlwith actioncloud-hotfixon the hotfix branch (notmain). This builds the image and deploys directly to production — staging is not involved. If the next scheduled Sunday promotion is within 1 hour, the workflow will refuse to run — just wait for the scheduled run instead. - Merge the hotfix branch into
main. This automatically triggerstag-release-candidate, which SSHes to staging and retrags whatever image is running there asrelease-candidate. Sunday’s scheduled run will re-deploy — a safe no-op.
Self-Hosted Release
The weekly self-hosted release runs automatically on Mondays at 9 AM UTC. It re-tags therelease-candidate cloud image as the versioned self-hosted image — no separate build required.
For off-cycle or hotfix releases from any branch, use release-self-hosted.yml (see below).
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, or to force a deploy that was skipped due to the freeze window.deploy-staging-skip-freeze— Same as above but explicitly bypasses the freeze window check.
continuous-delivery-cloud.yml — Production Promotion
continuous-delivery-release.yml — Weekly Release
continuous-delivery-canary.yml — Canary
See the Canary Deployment playbook for full details.
continuous-delivery-rollback-canary.yml — Canary Rollback
Manual dispatch only. Rolls back the canary environment to a previous image tag. Reverses DB migrations not present in the target image’s manifest, then redeploys the canary app and workers to that tag.
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 continuous-delivery-canary.yml 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-version-X.Y.Z, 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-candidate cut (Thursday 5 PM UTC). The weekly release reads the version at the release-candidate ref, so merging after the cut is too late. If main is already at or ahead of the released version, the job no-ops. 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.