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:
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_KEYon the central API Worker.OPERATIONS_API_TOKENon the central API Worker. This is required for protected Commerce and Logistics operational endpoints. Configure withnpm run configure:operations:token.ADMIN_BOOTSTRAP_TOKENon 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/shipmentsGET/POST /api/commerce/ordersPOST /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.