Credentials for a headless run
CI signs in with three environment variables instead ofxanosdk 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.
release transfer between them.
Checks that need no credentials
A scaffolded project hasnpm 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.
--strictturns every export warning into a failure.--checkimplies--frozen-lock: the run fails rather than changexano/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 noxano/.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
- Workspace as production
- With tenants
npm run build, --static and publish lines if your project has no frontend.
What each job does
checkruns on every push and pull request with no secrets.git diff --exit-codeis the scaffold’s own backstop: it fails when the check left anything changed in the working tree.previewdeploys the pull request to an ephemeral and runs its tests there.--testexits5when a test fails and6when the tests cannot be reached, which fails the job. The job log prints the backend and frontend URLs for reviewers.releaseruns on av*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 todev, andshiptakes the same release tostageandprodfrom the Actions tab.- The last step commits
xano/xano.lockback.promoteandtenant deployrecord what they landed (andpromoterecords 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 GitHubconcurrency group:
xano-releasewithcancel-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 exit9.xano-preview-<pull request number>withcancel-in-progress: true. A new push to a pull request replaces the preview run still going for the previous push.
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:
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:
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.