Docs

CI/CD

Current GitHub Actions and deployment behavior.

Pipelines

ci.yml runs one CI Quality job when a PR targeting main opens, reopens, or receives a commit. The checkout explicitly uses the PR head SHA. One installation runs prepare once, then lint, TypeScript, documentation, Better Auth, security, and the existing isolated suites share that checkout. Older quality runs on the same PR are canceled. There is no automatic image build or deployment on a PR push, and no second quality run after merge.

Request a build or preview from the PR

After CI Quality succeeds, add one label in the PR sidebar:

  • build: build immutable app and worker images only.

  • preview: build those images and deploy the shared development environment.

Remove and re-add the label to request a new run. A new commit does not build again automatically. The resulting PR Build or PR Preview commit status links to the run. This is GitHub's label-based entrypoint on the PR; GitHub has no native GitLab-style manual-job play button. These labels take effect after pr-preview.yml exists on main.

Only repository writers can request same-repository PRs targeting main. The control plane is checked out from the immutable trusted base workflow SHA; PR scripts are not executed by the deployment runner. The current PR head must have a successful latest quality run. Closed, draft, or changed PR requests are skipped before build or environment mutation. Builds can execute application code with development build secrets, so this remains a trusted-maintainer path.

The preview uses the configured DEVELOPMENT_URL and a single shared Coolify Compose resource. It updates the durable coolify/development branch to the requested head and pins the returned Coolify deployment UUID. Preview deployment is serialized and does not cancel a migration in progress. Reader/freeze rules remain enforced; a preview never activates exact writers automatically.

Production delivery

cms-production-promotion.yml starts directly on a push to main:

  1. Find the PR merged at that commit, require its latest successful CI Quality run on its head SHA, and compare the complete head and merged Git trees.

  2. Build and push production images before freezing CMS.

  3. Recheck that the automatic target is current and prove the live fleet.

  4. Freeze an exact fleet, or preserve an already live-frozen fleet.

  5. Apply migrations and roll out the prebuilt app/worker images.

  6. Verify readiness, immutable SHA, authentication, CMS evidence, then activate exact writers through the existing receipt protocol.

TypeScript, lint, and security audits are not repeated. A direct push, missing validation, or different merged tree blocks delivery. Update the PR from main and wait for its checks before merging; resolve blocked delivery through a new validated PR. Main-history membership alone is not quality evidence.

The production lock spans the whole chain and never cancels a running release. Obsolete automatic requests are skipped before production mutations. A build failure leaves the serving version and CMS write state untouched. A rolling or exact-activation failure keeps the existing rollback/freeze safeguards. SQL migrations remain applied; changes must use expand/contract. A coherent exact or frozen production fleet is required; reader bootstrap uses the explicit runtime rollout first.

Manual recovery uses CMS Production Promotion, selected from main, with current_sha, target_sha, and promote-exact:<current-sha>:<target-sha>. The target must match the workflow's immutable SHA; it stays pinned throughout an authorized manual operation even if main advances. force bypasses both Docker layer and mount caches. Receipt resume/supersession remains in CMS Prototype Runtime Rollout. coolify-rolling-deploy.yml is now reusable only and consumes the images built by its parent.

Caches and operator settings

build-images.yml uses persistent BuildKit builders named yayaw-development-arm64 and yayaw-production-arm64. Builds remain serial with one Next worker. Bun and Next compiler caches stay local to each builder; GHCR stores an additional layer cache. Environment image prefixes are separate. Build secrets use mounts, and effective input fingerprints invalidate compiled output when configuration changes. No workflow prunes Docker images globally.

The detailed operational runbook is .github/CI-OPERATIONS.md: cache budgets, public variable migration, failure diagnostics, host inventory, and repeatable cold/warm measurements. Bun remains 1.3.14 and Next.js remains 16.3.3. Vercel is not a deployment target.

Branch and Pull Request Flow

Use one branch per task:

git checkout main
git pull --ff-only origin main
git checkout -b codex/<task-name>

Before opening a PR:

  1. Keep the diff scoped to the task.

  2. Stage only intended files.

  3. Commit with a terse summary.

  4. Push the branch.

  5. Open a draft PR unless the user explicitly asks for ready review.

Generated files are expected when their source changes:

  • AGENTS.md

  • GEMINI.md

  • .github/copilot-instructions.md

  • generated Drizzle server actions when schemas change

  • Fumadocs source artifacts when docs content changes

Local Parity Commands

bun run check
bun run docs:generate
bun run docs:check-links
bun run docs:check-translations
bun run docs:llm:check
bunx tsc --noEmit
bun run build

For billing, webhook, or code-access work, also run:

bun run test

For generated action changes, run:

bun run generate:actions

For assistant-doc changes, run:

bun run docs:llm:generate

Deployment Environments

Docker/Coolify is the supported deployment target. Runtime variables live in the orchestrator secret store or in the untracked .env.self-host file used by docker-compose.self-host.yml. Do not link this checkout to Vercel or add a Vercel deployment workflow. See Deployment Environment Setup and Self-Hosting for provider retrieval steps and deployment checks.

The self-host compose migrate service owns the non-interactive versioned Drizzle migration, seed, and deployed dynamic-data storage repair for Coolify deployments. Migration 0050 on an existing database additionally requires the documented external /api/auth write pause and the temporary BETTER_AUTH_17_WRITES_PAUSED=true operator attestation. Keep the GitHub production environment DATABASE_URL available for build-time server route evaluation, but do not run a separate production migration workflow alongside the compose deployment.

The Coolify prebuilt-image flow requires these environment-scoped GitHub secrets or variables:

EnvironmentNamePurpose
development, productionBETTER_AUTH_SECRETRequired by the Docker build so Better Auth route evaluation is stable.
development, productionSCIM_CREDENTIAL_HASH_SECRETRequired BuildKit secret for managed SCIM configuration validation during next build; also required at runtime.
development, productionDATABASE_URLBuild-time database URL fallback for server route evaluation.
development, productionNEXT_PUBLIC_* build valuesPublic client bundle configuration baked into Next.js.
development, productionNEXT_BUILD_WORKERS variableBuild worker count. Defaults to 1 for stable self-hosted Docker builds.
development, productionNEXT_STATIC_PAGE_GENERATION_TIMEOUT variableStatic generation timeout. Defaults to 180.
development, productionCOOLIFY_SERVER_SSH_* variables or COOLIFY_SERVER_SSH_KEY secretOptional overrides for loading images onto the Coolify VM. The Mac mini runner defaults match the local VM SSH setup.
productionBETTER_AUTH_17_WRITES_PAUSED variableOne-cutover or beta-retry attestation. When true, the workflow requires 503 for an auth write plus GET/POST/PUT/PATCH/DELETE SCIM probes, then smokes auth inside the new container without weakening the public block. Remove it after cutover.
productionBETTER_AUTH_CUTOVER_BLOCK_STATUS variableOptional ingress maintenance status for the cutover probes; it must remain 503 so an application response cannot satisfy the proof.

The workflows grant packages: write to the image-build job and packages: read to the Coolify deploy job so the repository GITHUB_TOKEN can push immutable images to GHCR and the self-hosted runner can pull them onto the VM.

After changing a self-host secret, restart the affected app or worker containers and verify /dashboard/admin deployment status.

Merge Safety

Auto-merge only runs when required checks have succeeded.

Do not bypass failed docs integrity checks. They usually mean either:

  • an internal docs link is broken

  • content/llm/llm-source.md changed without regenerating assistant files

  • generated docs artifacts are stale