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:
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.
Build and push production images before freezing CMS.
Recheck that the automatic target is current and prove the live fleet.
Freeze an exact fleet, or preserve an already live-frozen fleet.
Apply migrations and roll out the prebuilt app/worker images.
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:
Keep the diff scoped to the task.
Stage only intended files.
Commit with a terse summary.
Push the branch.
Open a draft PR unless the user explicitly asks for ready review.
Generated files are expected when their source changes:
AGENTS.mdGEMINI.md.github/copilot-instructions.mdgenerated 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 buildFor billing, webhook, or code-access work, also run:
bun run testFor generated action changes, run:
bun run generate:actionsFor assistant-doc changes, run:
bun run docs:llm:generateDeployment 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:
| Environment | Name | Purpose |
|---|---|---|
development, production | BETTER_AUTH_SECRET | Required by the Docker build so Better Auth route evaluation is stable. |
development, production | SCIM_CREDENTIAL_HASH_SECRET | Required BuildKit secret for managed SCIM configuration validation during next build; also required at runtime. |
development, production | DATABASE_URL | Build-time database URL fallback for server route evaluation. |
development, production | NEXT_PUBLIC_* build values | Public client bundle configuration baked into Next.js. |
development, production | NEXT_BUILD_WORKERS variable | Build worker count. Defaults to 1 for stable self-hosted Docker builds. |
development, production | NEXT_STATIC_PAGE_GENERATION_TIMEOUT variable | Static generation timeout. Defaults to 180. |
development, production | COOLIFY_SERVER_SSH_* variables or COOLIFY_SERVER_SSH_KEY secret | Optional overrides for loading images onto the Coolify VM. The Mac mini runner defaults match the local VM SSH setup. |
production | BETTER_AUTH_17_WRITES_PAUSED variable | One-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. |
production | BETTER_AUTH_CUTOVER_BLOCK_STATUS variable | Optional 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.mdchanged without regenerating assistant filesgenerated docs artifacts are stale