Skip to content

CI/CD Deployment

Purpose

Document the two production deployment paths and the safety controls around them.

Path 1: automatic (ci.yml)

A push to main runs validate. Production deployment then starts only when the pushed commit is associated with a merged pull request into main. This is a workflow safety net against an ordinary accidental direct push; it does not replace GitHub branch protection/rulesets, which should still require PRs and required checks on main.

PR -> merge to main -> validate -> merged-PR gate -> D1 migrations -> API ->
Corporate -> Software -> Logistics -> Commerce -> Embroidery -> Admin -> smoke test

The deploy job uses concurrency.group: akshaya-production with cancel-in-progress: false, so a later production run cannot race an existing one.

Investors is not deployed.

Path 2: manual (deploy-cloudflare.yml)

The manual workflow is workflow_dispatch only and uses the same workflow-level akshaya-production concurrency group as the automatic deploy.

Execution is deliberately ordered:

D1 migrations -> API -> active web apps (max-parallel: 1) -> production smoke test

The active web-app matrix contains Corporate, Software, Logistics, Commerce, Embroidery and Admin. strategy.max-parallel: 1 serializes those matrix entries. Do not put the same job-level concurrency group on every matrix child: GitHub keeps only one running and one pending member for a concurrency group, so sibling matrix jobs can replace/cancel pending siblings rather than all queueing safely.

Investors is inactive and is not present in the manual matrix.

Required GitHub Actions secrets

Secret Used by
CLOUDFLARE_API_TOKEN production migration/deploy commands
CLOUDFLARE_ACCOUNT_ID production migration/deploy commands

The old CLOUDFLARE_D1_DATABASE_ID injection is no longer used. The real D1 resource id is already committed in workers/api/wrangler.jsonc; it is a resource identifier, not a credential.

Required Cloudflare Worker secrets

These are Worker secrets, not GitHub Actions secrets:

  • TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY on the central API Worker.
  • OPERATIONS_API_TOKEN on the central API Worker. This is required for protected Commerce and Logistics operational endpoints. Configure with npm run configure:operations:token.
  • ADMIN_BOOTSTRAP_TOKEN on the Admin Worker for first-admin bootstrap.

OPERATIONS_API_TOKEN must be a strong token of at least 32 characters. It is server-to-server only and must never be embedded in React/Vite/browser JavaScript. Future Commerce/Logistics Workers should store the matching credential as a Worker secret and call the central API from the server side.

API deployment safety

workers/api/wrangler.jsonc points at src/entry.ts. That entry point protects:

  • GET/POST /api/logistics/shipments
  • GET/POST /api/commerce/orders
  • POST /api/commerce/orders/:id/fulfil

with Authorization: Bearer <OPERATIONS_API_TOKEN>. Requests carrying a browser Origin header are rejected even with the token, making accidental client-side use fail closed.

A trusted server caller may additionally send X-Akshaya-User-Id; new orders/shipments persist that user_id, and Commerce fulfilment propagates the order owner onto the generated shipment. The inactive /api/investors/* surface is blocked at the same entry point.

Database / R2 ordering

Both deployment paths apply D1 migrations before deploying code that may depend on the new schema. Migrations must remain backward-compatible/additive. R2 is provisioned separately and is assumed to exist before deploy.

Smoke testing

Both production deployment paths finish with npm run smoke:production. The smoke suite is an availability check; it is not a substitute for authenticated end-to-end tests. Playwright coverage for Customer, Freelancer, Commerce, Logistics and Admin journeys remains a separate workstream.

Rollback

There is no automatic database rollback. Identify the last known-good production commit, determine which component failed, and redeploy only the affected application when practical. Never reverse a production D1 migration destructively; ship forward-compatible corrective migrations instead.

See Operations → Rollback for the detailed procedure.