How app-starter interacts with GitHub. Everything here ships dormant and
gracefully gated: a fresh fork with no dev branch and no environments
configured still passes CI, and the deploy/policy workflows simply don't fire
until you complete the one-time setup below.
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
PR + push to main |
typecheck → lint → build → migrate → test against a throwaway Postgres; plus docs-sync (fails a PR that edits only one of FEATURE_WORKFLOW.md / .html). |
aws-deploy.yml |
push to main/dev, or manual |
Branch-gated deploy. main→prod, dev→dev. Runs a destructive-DDL gate, applies migrations to the env database, then deploys CDK (Lambda + API Gateway) and the SPA (S3 + CloudFront). |
pr-policy.yml |
PR to main |
Enforces feature → dev → main: a PR to main must come from dev. Skips itself until a dev branch exists. |
Two more workflows ship dormant under optional-workflows/
(GitHub doesn't run files outside workflows/): label-deployed-environments
and refresh-dev-db. They're opinionated / infra-dependent — see that folder's
README for what they do and how to activate.
feature/<slug> → dev → main
(dev env) (prod env)
- Open feature PRs against
dev. Merging todevdeploys to the dev environment. - Promote with a PR from
dev→mainusing the Promote Dev to Main issue template. Merge it as a merge commit (not squash/rebase) — see the template for why. Merging tomaindeploys to prod.
The deploy and policy workflows depend on infrastructure a fresh fork won't have. To activate them:
-
Create the
devbranch (git switch -c dev && git push -u origin dev). This activatespr-policyand thedevdeploy trigger. -
Configure GitHub OIDC → AWS: create an IAM role trusted by GitHub's OIDC provider with permission to deploy the CDK stack and write to the frontend S3 bucket + CloudFront. (See
packages/infra/README.mdfor the stack it deploys.) -
Create two GitHub Environments (Settings → Environments):
prodanddev. Onprod, add a required reviewer as a deployment protection rule — that's the production approval gate. Set per environment:Kind Name Notes secret AWS_DEPLOY_ROLE_ARNOIDC role ARN secret DATABASE_URLPostgres connection string (Lambda env + migrate target). For RDS, append ?sslmode=require— RDS refuses non-SSL connections, and the client only enables TLS when the URL says so.var APP_NAMElogical app name (default app-starter)var CDK_STACK_NAMEdistinct per environment (e.g. app-starter-web-prod/-dev)var AWS_REGIONoptional; defaults to us-east-1Because these are environment-scoped, the workflow references the same names (
secrets.DATABASE_URL,vars.CDK_STACK_NAME, …) and gets the right value per branch automatically. -
Branch protection on
main: require status checksciandpr-policy / enforce-source-branch. Optionally require Code Owner review (seeCODEOWNERS— replace the placeholder owner first).
Migrations are the single source of truth (packages/db/src/migrations/*.sql,
tracked in the app_migrations ledger); packages/db/schema/ is a review
snapshot, not a deploy source. aws-deploy runs pnpm migrate against the
environment's DATABASE_URL before deploying app code, so the schema is never
behind the running Lambda.
Runner must reach the database. GitHub-hosted runners can only reach a publicly-resolvable RDS endpoint. If your RDS is VPC-private, use a self-hosted runner inside the VPC (or move the migrate step to a path that can reach it) — otherwise
pnpm migrateand the gate's ledger query will time out.
The destructive-DDL gate scans pending up-migrations for
DROP TABLE/COLUMN/SCHEMA, ALTER … DROP, and ALTER COLUMN … TYPE. A match
halts the deploy: apply it manually, record the migration name in
app_migrations, then re-run. .down.sql files are never auto-applied and are
not scanned.
pull_request_template.md— checklist tied to CLAUDE.md's critical rules.ISSUE_TEMPLATE/— bug, feature, and promote-dev-to-main templates.CODEOWNERS— review routing (replace the placeholder owner).dependabot.yml— weekly npm + GitHub Actions updates.