Shipping and operations
Backend source control with Git
Commit schemas, properties, workflows, functions, libraries, and document templates alongside application source.
GSB backend resources can live alongside application source in the same repository and be reviewed in the same commits and pull requests. The CLI serializes tenant resources into ordinary files; Git versions those files. This is a Git-compatible source workflow, not automatic Git synchronization: the CLI does not create commits, merge branches, or push to a Git remote.
What belongs in a commit
Keep one .gsb/ directory at the repository root, with one directory per checked-out
tenant. Commit deployable content and its identity/sync metadata together, not a
separate .gsb/ directory inside every app.
| Resource | Checked-in representation |
|---|---|
| Schema definition | schema/<name>/<name>.json |
| Columns/properties | schema/<name>/properties/<property>.json, one file per property |
| Workflow | workflows/<name>/workflow.meta.json and design.json |
| Workflow logic | Ordered JSON files under activities/ and transitions/ |
| Standalone function | serverless/functions-std/<name>/ |
| Workflow-only function | serverless/functions-wf/<name>/ |
| Function implementation | <name>.ts, <name>.meta.json, and operation files such as op-1.json and op-1.ts |
| Shared library | serverless/libraries/<name>.ts, metadata, and declarations when emitted |
| Document template | docTemplates/<name>/meta.json plus per-language HTML or content.html |
| Test input | Optional function <name>.test.json and workflow.test.json, containing only reviewed nonsensitive fixtures |
A feature can commit a column, workflow transition, shared function change, and email template together. This illustrative tree uses the current folder format:
.gsb/dev1/
schema/Order/
Order.json
properties/approvalStatus.json
workflows/OrderApproval/
workflow.meta.json
workflow.test.json
design.json
activities/010-Start.json
activities/020-Approve.json
transitions/010-StartToApprove.json
serverless/
functions-std/SendApprovalEmail/
SendApprovalEmail.ts
SendApprovalEmail.meta.json
SendApprovalEmail.test.json
functions-wf/ValidateOrder/
ValidateOrder.meta.json
op-1.json
op-1.ts
libraries/OrderRules.ts
libraries/OrderRules.meta.json
docTemplates/OrderApproved/
meta.json
en_us.html
tr_tr.html
Use actual paths emitted by your tenant checkout. Current schema folders embed
identity and serverSync in definition and property documents; they do not require
a separate definition metadata sidecar. Pull migrates legacy flat schemas to folders.
Workflow activities reference shared functions by name; function bodies live separately.
Changing a workflow does not implicitly deploy its referenced functions. Commit and
deploy related function/library changes explicitly. Preserve immutable IDs and review
renames rather than generating new identities to resolve merge conflicts. Designer
layout stays in design.json, separate from logic. Generated serverless declarations
and editor configuration can be committed as generated support, not hand-authored code.
Exclude secrets and runtime data
Never commit .gsb/<tenant>/credentials.json, real environment values, private keys,
tokens, or production session data. The repository ignores tenant credentials, build
output, logs, and environment files; do not force-add them. Review fixture payloads,
code, template HTML, and metadata too: source files can still contain secrets or PII.
Runtime entity rows, workflow instances, logs, and uploaded documents are not all captured by a source checkout. Promote explicitly selected data using resource packs or reviewed seeds, not a blanket export of a tenant database into Git.
Git state versus GSB sync state
| Check | What it compares |
|---|---|
git status / git diff |
Working files, index, and Git commits |
gsb status --json |
Current local payloads versus their last successful sync baseline |
gsb push --all-changed --dry-run |
Resources selected for upload and applicable push preflight checks |
gsb verify <artifact> --json |
An immutable plan's current preconditions or an apply result's persisted writes |
gsb status reports clean, modified, or legacy-untracked. Its
remoteComparison: "not-requested" means it does not compare current remote resource
content. It is not a three-way merge or a server-drift check. Target authentication
can still require a backend call even though resource comparison is local.
Successful pulls and pushes record serverSync.contentHash, with lastPull and
lastPush timestamps where applicable. Hashes normalize JSON key order, ignore
embedded serverSync, and normalize text line endings. Older resources without
a hash use timestamp fallback until the next successful sync. Fresh Git checkouts
change filesystem times; review legacy-untracked resources before bulk upload.
Successful synchronization also records the cloud row's lastUpdateDate under
serverSync. Before every update, the CLI requires that committed baseline to equal
the live row and atomically claims that exact (id, lastUpdateDate) revision. A Git
rollback therefore cannot silently overwrite a newer tenant row, and two CLI clients
cannot both win the same revision. Pull before pushing when the baseline is missing or
stale; the successful write read-back records the next cloud timestamp. The CLI keeps
the backend revision string unchanged, including timezone-less values, because
normalizing it would break the compare-and-set contract.
An apply result with revision-claim-failed means the resource payload write did not
start, but a lost response can leave the timestamp claim applied in the cloud. Pull
that resource, review the resulting Git diff, and create a new plan. Do not retry the
old plan or infer payload success from the changed timestamp.
Use gsb push <path> --force only for an intentional overwrite after reviewing
the cloud change. The resulting operation records approval: "explicit-force" and
still claims the current cloud timestamp atomically; --force does not bypass tenant,
identity, create, rename, or concurrent-write checks.
Identity and metadata files belong in Git even where excluded from payload hashing.
Metadata-only edits to function, workflow, library, or document-template sidecars may
not select that resource for --all-changed; explicitly preview and push it when
changing those fields. Use both Git diff and GSB status.
A resource can be committed but not deployed, or deployed but not yet committed. Committing does not reset its GSB sync baseline. A clean working tree does not prove the tenant matches that commit, and clean GSB status does not prove files are committed. Changed-resource selection is not a Git commit-range deployment mechanism.
Daily branch and pull-request workflow
- Start from reviewed source. Use a normal Git branch and explicitly select the intended tenant. Preserve local work in a commit before refreshing resources.
- Pull intentionally. Prefer named resources for small refreshes. Pull can overwrite local files and reconcile/remove files inside resource folders; it is not a merge with uncommitted edits. Review the resulting Git diff.
- Edit related resources together. Include definitions/properties, activities, transitions, functions, libraries, and document templates belonging to the feature.
- Validate and test. Run static checks and isolated tenant tests.
gsb testruns code on the backend without saving the function; execution can still have side effects. - Review both diffs. Inspect Git changes and GSB status, then preview uploads. Reject unexpected creates, renames, permissions, seed data, or tenant identities.
- Commit explicit paths. Use Git's normal add/commit/PR process; avoid blindly staging a whole tenant checkout containing unrelated work or sensitive fixtures.
- Deploy after approval. Create an immutable plan from the approved source, apply that exact artifact, and verify its apply result. Schema changes also require the matching ordered migration artifact. Review refreshed IDs, hashes, and timestamps.
- Retain sync updates deliberately. Commit legitimate refreshed source metadata separately when needed. Link deployment to the original reviewed commit; a follow-up metadata commit is not a second release implementation.
These examples assume dev1 is the intended tenant. Pull writes local files; use it
only after protecting and reviewing existing work:
pnpm exec gsb --tenant dev1 pull --schema --workflow --function --library --doc-template
pnpm exec gsb --tenant dev1 status --json
pnpm exec gsb --tenant dev1 diff --remote --json
pnpm exec gsb --tenant dev1 push --all-changed --dry-run
pnpm exec gsb --tenant dev1 plan --output change-plan.json
pnpm exec gsb --tenant dev1 apply change-plan.json --output apply-result.json --json
pnpm exec gsb --tenant dev1 verify apply-result.json --json
Preview individual resources using their actual checked-out paths:
pnpm exec gsb --tenant dev1 push .gsb/dev1/schema/Order --schema --dry-run
pnpm exec gsb --tenant dev1 push .gsb/dev1/docTemplates/OrderApproved --doc-template --dry-run
New assets require current push creation approval; inspect gsb push --help.
Do not bypass identity checks to make a branch deploy. Commit history helps manual
reconciliation; remote stale-write rejection comes from the committed
lastUpdateDate baseline and the atomic cloud-revision claim, not from Git history.
For a schema edit, pull immediately before editing and record its migration before creating the deployment plan:
pnpm exec gsb --tenant dev1 pull Order --schema
pnpm exec gsb --tenant dev1 migration plan .gsb/dev1/schema/Order \
--recovery forward-repair \
--recovery-instructions "Revert with a reviewed follow-up migration"
pnpm exec gsb --tenant dev1 migration check --json
diff --remote compares committed immutable IDs and cloud lastUpdateDate baselines
with the selected tenant. It reports remote-only updates, missing rows, and name
conflicts without mutating either side. It does not claim a remote payload-level diff;
pull and review the resulting Git changes before replacing a stale local baseline.
Migration artifacts form a continuous hash chain for each schema. A teammate's
parallel migration from the same prior revision is rejected; pull their revision and
create the next migration instead. Migrations requiring data transformation remain
blocked from deployment until the platform has a typed transformation runtime.
Existing-schema chains include stable schema/property IDs in their fingerprints.
Create-origin chains use authored-content fingerprints for their entire lifetime so
IDs assigned by the server cannot create false drift or break post-apply verification.
The apply result records the exact migration sequence, checksum, and hashes; verify
rechecks that binding and the current authoritative schema fingerprint after read-back.
Recording a migration also takes an exclusive tenant-local lock so parallel CLI
processes cannot both claim the next sequence.
For a compatible metadata change that must be reversed, prepare a compensating migration and deploy it through the same controls as any other schema change:
pnpm exec gsb --tenant dev1 migration rollback <sequence> \
--recovery-instructions "Reapply the reviewed change if needed"
pnpm exec gsb --tenant dev1 status --json
pnpm exec gsb --tenant dev1 plan --output rollback-plan.json --json
pnpm exec gsb --tenant dev1 verify rollback-plan.json --json
pnpm exec gsb --tenant dev1 apply rollback-plan.json \
--output rollback-result.json --json
pnpm exec gsb --tenant dev1 verify rollback-result.json --json
pnpm exec gsb --tenant dev1 migration check --json
The rollback command stages source and records a new migration; it does not mutate the
tenant. It requires the target to be the latest migration for that schema, an embedded
pre-change snapshot, and exact local/live agreement with the target toHash. It
rejects schema creation reversal, property deletion, type/data reversal, stale state,
and every destructive or transformation-requiring reverse. In particular, a forward
property addition cannot currently be reversed automatically because its reverse is a
property deletion. Schema definitions do not support entity-version restore.
Merges, deletions, and rollback
- Merge JSON, TypeScript, and HTML through normal Git review. Preserve IDs and valid references; do not resolve every metadata conflict by choosing one side wholesale.
- Coordinate shared development-tenant edits. Branches do not isolate the live tenant; a developer's pull/push can observe another developer's backend changes.
- Removing a local file is not a general remote-delete instruction. Some resource serializers replace collections, while schema/property removals require explicit contracts. Review resource-specific deletion impact before applying a change.
- Reverting a Git commit restores source, not live schema or data. Check out or revert the reviewed source, then preview, approve, and push it again as a new forward deployment. Data and external side effects may need a snapshot restore or forward repair.
- Tenant backup restore is destructive disaster recovery, not routine schema rollback. Restore only a verified completed backup under explicit approval, then reconcile the local migration chain with the restored authoritative schema before another deploy.
- A dry run and later push do not reuse one immutable artifact; the later push builds a
fresh plan. Use explicit
plan -> apply -> verifywhen review must bind to exact content and preconditions. Per-resource revision claims prevent stale writes, but they are not a tenant-wide deployment lock or atomic multi-resource rollback.
Connect commits to releases
Record the reviewed source commit, exact built pack/file identity, build selection, target, installation policy, approval, job status, and verification evidence. Packs can contain assets and query-selected data beyond the CLI checkout; a commit alone does not prove complete pack contents. Confirm live packing inputs match reviewed source, then promote the same exact pack from staging to production.
Do not copy dev credentials, IDs indiscriminately, or sync baselines into production folders. A branch or copied folder is not a tenant identity-mapping mechanism. See CI/CD, deployment, and seeds for selective data promotion, installation policy, and release controls.
docs/guides/cli-source-control.md