Skip to main content
This page is the complete pipeline: every change is checked, every pull request gets a preview on its own ephemeral, and every tag becomes a release that lands on your workspace or your tenants. Shipping workflow explains the flow itself; this page is how to run it unattended.

Credentials for a headless run

CI signs in with three environment variables instead of xanosdk login, which waits for a browser: Store them as repository secrets. Set all three or none: setting some but not all is a hard error that names the missing ones, so a misspelled secret never falls back to another credential. Together they outrank every other credential, read no file and never rotate, so the same three secrets work on every run. Avoid XANO_REFRESH_TOKEN in CI. Xano rotates a refresh token each time it is used, so a stored one works once and the next run fails.

More than one workspace

One credential addresses one instance and one workspace. To reach a second one from the same job, store a profile for each and pick it per command with --profile. Do not set the three variables in that job: an environment credential beside an explicit --profile is a hard error.
Across workspaces covers release transfer between them.

Checks that need no credentials

A scaffolded project has npm run xano:check. It typechecks the project, then runs xanosdk export ./xano/index.ts --check --strict: every check an export runs, writing nothing, so it needs no secrets and no xano/.env.
  • --strict turns every export warning into a failure.
  • --check implies --frozen-lock: the run fails rather than change xano/xano.lock. That catches a change whose lock update was not committed, and stops CI minting an identity or a public URL token that the committed lock does not hold. It also fails on a lock entry no object matches, which is what an unfinished rename looks like. See Git, locks & merges.
--frozen-lock is also a flag on deploy and export, for a job that builds without running the full check.

Env values in CI

CI has no xano/.env. A deploy to an ephemeral replaces the ephemeral’s env set, so it refuses when a name your workspaceConfig declares has no value. Store the file’s contents as a secret (XANO_BACKEND_ENV below) and write it to xano/.env in the job. The file is gitignored, so the job never commits it. A project that declares no env names can drop that step. See Env and secrets.

The workflow

Drop the npm run build, --static and publish lines if your project has no frontend.

What each job does

  • check runs on every push and pull request with no secrets. git diff --exit-code is the scaffold’s own backstop: it fails when the check left anything changed in the working tree.
  • preview deploys the pull request to an ephemeral and runs its tests there. --test exits 5 when a test fails and 6 when the tests cannot be reached, which fails the job. The job log prints the backend and frontend URLs for reviewers.
  • release runs on a v* tag. It deploys the tagged commit to an ephemeral, runs the tests, and cuts the release from that ephemeral, so the release is exactly what passed. With the workspace as production it then promotes to a new Xano branch named after the release and switches live to it. With tenants it deploys the release to dev, and ship takes the same release to stage and prod from the Actions tab.
  • The last step commits xano/xano.lock back. promote and tenant deploy record what they landed (and promote records a new sync baseline) in CI’s checkout, which is lost when the job ends. The step opens a pull request with the change; merge it like any other. It needs “Allow GitHub Actions to create and approve pull requests” turned on in the repository’s Actions settings.

Concurrency

Each job that writes to Xano sits in a GitHub concurrency group:
  • xano-release with cancel-in-progress: false. Release jobs queue, one at a time, and a running landing is never cancelled halfway. Cancelling a release write mid-flight is how a run ends at exit 9.
  • xano-preview-<pull request number> with cancel-in-progress: true. A new push to a pull request replaces the preview run still going for the previous push.
Each preview run starts from a fresh checkout, with no record of an earlier ephemeral, and deploys a new one, which expires on its own (after an hour, by default). Runs for different pull requests do not collide: two checkouts deploying at once each get their own ephemeral, even with the same display name.

Going live safely

The promote step reads the live Xano branch, then passes it to --expect-live. Promote checks it before landing and again immediately before switching, so if someone switched live by hand in between, the new Xano branch stays landed but is not served, and the job fails with exit 2 (SDK_LIVE_BRANCH_MISMATCH). --branch "$RELEASE" makes a re-run of the same tag safe: the label already exists, so the second promote is refused before it lands anything. The promote prints the rollback command in the job log, as Roll back with xanosdk workspace branch set-live <previous label>. See Rollback and recovery.

A release that changes a table

The workflow does not pass --allow-shared-schema-changes. A release that drops, retypes or constrains a column is refused at the promote step (exit 2, SDK_SHARED_SCHEMA_CHANGE) after the release was cut. That is the point where a person takes over: read the change, back up, then land the release that already exists from a terminal:
Commit xano/xano.lock afterwards. With tenants, tenant deploy --yes does not stop on a table change, so the review has to happen before the tag. See Schema changes.

Exit codes in a pipeline

Commands print their result as JSON when stdout is not a terminal, so the job log holds each run’s document. Branch on the exit code: Never retry a step that exited 9, and do not use “Re-run failed jobs” on it. The write was sent and the answer was lost, so it may have landed, and a retried release create can leave a second release under a suffixed name. Read the resolveWith command in the step’s JSON output, run it, and continue from what it says. Rollback and recovery covers each command. --yes is required off a terminal. promote, tenant deploy, publish to a workspace or tenant, workspace branch set-live and pull ask before they write, and with no terminal to ask on they refuse rather than guess. --yes answers the question. It never waives a refusal.

Watch for SDK upgrades

xanosdk upgrade --check exits 7 when a newer @xano/sdk is published and 0 when you are current. A pipeline that only wants to report it must not treat 7 as a failure:
Let 7 fail the job only when you mean “fail until we upgrade”. Upgrade in a pull request, so the check job runs against the new version before anything ships.

Next

Git, locks & merges: what to commit, and what a reviewer checks.