Production frontend hosting for townofwiley.gov is S3 (townofwiley-static-site) + CloudFront (E1NZ3XCY5CYR1J). After Site CI passes on a push to main with deployable app changes, GitHub Actions syncs the CI-built artifact and invalidates CloudFront.
This is the only supported production frontend deploy path. Ansible was removed; Terraform is IaC only (not the static site publisher). See DEPLOYMENT_SSOT.md.
Amplify Hosting is not the deploy authority (app d331voxr1fhoir was deleted June 2026).
push main (app_changed) → Site CI (lint, build, e2e smoke)
→ deploy-production job (OIDC → S3 sync → CF invalidation)
→ post-deploy curl + CSP probe
Manual break-glass: Actions → Deploy production (manual) or local npm run deploy:site.
Apply branch protection after the ci-gate job has run on at least one PR so the required check name exists:
site-ci / CI gate (merge required)
Runbook: docs/github-branch-protection.md
The GitHub production environment is used for deployment tracking only — no required reviewers (auto-deploy after merge; PR + CI is the gate). Restrict deployment branches to main.
GitHub Actions uses OIDC — no long-lived AWS_ACCESS_KEY_ID secrets for deploy.
| File | Purpose |
|---|---|
infrastructure/iam/github-actions-static-site-deploy-trust.json |
Trust: repo Bigessfour/Townofwiley, branch main |
infrastructure/iam/github-actions-static-site-deploy-policy.json |
Least privilege: S3 bucket + CloudFront invalidation |
scripts/setup-github-actions-deploy-role.sh |
Creates OIDC provider (if missing) + role |
source scripts/agent-aws-env.sh
bash scripts/setup-github-actions-deploy-role.shRole ARN (used in workflows):
arn:aws:iam::570912405222:role/GitHubActions-TownOfWiley-StaticSiteDeploy
aws iam list-open-id-connect-providers
aws iam get-role --role-name GitHubActions-TownOfWiley-StaticSiteDeployDeploy consumes the artifact from frontend-lint-build, which runs npm run build:ci with strict runtime config when repository secrets are present (STRICT_RUNTIME_CONFIG=1). These secrets must exist on staff PRs and on main deploy paths (see infrastructure/amplify-branch-env.manifest.json requiredForProduction):
APPSYNC_CMS_ENDPOINT,APPSYNC_CMS_API_KEY,APPSYNC_CMS_REGIONNWS_PROXY_ENDPOINTSEVERE_WEATHER_SIGNUP_API_ENDPOINT,SEVERE_WEATHER_SIGNUP_ENABLEDEASYPEASY_API_ENDPOINT,EASYPEASY_CHAT_URLCONTACT_UPDATE_REVIEW_API_URL
No new secrets are required for S3/CloudFront when using OIDC.
Refresh all requiredForProduction values from live site + AWS SSOT (full workflow: docs/pipeline-workflow.md):
source scripts/agent-aws-env.sh
npm run secrets:sync-runtime # local user-secrets.json
npm run secrets:sync-runtime -- --github # mirror to GitHub Actions
npm run secrets:lock # update encrypted lockbox (safe to commit)| Workflow | Trigger | Deploy |
|---|---|---|
.github/workflows/ci.yml |
PR / push main |
Calls reusable Site CI |
.github/workflows/git-workflow.yml |
workflow_call |
deploy-production after CI green |
.github/workflows/deploy-production.yml |
workflow_dispatch |
Manual build + deploy from main |
All of the following must be true:
- Event is
pushtorefs/heads/main detect-changesreportsapp_changed=truefrontend-lint-buildsucceededfrontend-smokesucceeded or was skipped
Docs-only merges skip deploy.
scripts/deploy-static-site.sh reads bucket and distribution ID from infrastructure/aws-infrastructure.manifest.json.
source scripts/agent-aws-env.sh
npm run deploy:site # build + tiered s3 sync + invalidation
npm run deploy:site:dry # dry-run sync onlyCI passes --skip-build because the artifact is already at dist/townofwiley-app/browser.
Critical: Sync to the S3 bucket root — not a browser/ prefix. CloudFront has no OriginPath.
- Revert + merge — push a revert commit to
main; auto-deploy runs ifapp_changed. - Manual redeploy — Actions → Deploy production (manual) on
main(rebuilds from currentmaintip). - Local break-glass — checkout known-good commit,
npm run deploy:sitewithtownofwileyprofile.
- Docs-only change → expected.
- PR build → deploy only runs on push to main, not PRs.
- Confirm IAM role exists (
bash scripts/setup-github-actions-deploy-role.sh). - Trust policy must allow both OIDC subject forms used by deploy jobs:
repo:Bigessfour/Townofwiley:ref:refs/heads/main(branch context)repo:Bigessfour/Townofwiley:environment:production(jobs withenvironment: production)
- Confirm workflow job has
permissions: id-token: write. - Manual deploy workflow must run from
mainbranch.
- Wrong AWS profile or credentials in local deploy.
frontend-lint-buildfailed strict prebuild — fix missing GitHub secrets.
- CloudFront invalidation may still be in progress (usually seconds). Re-run job or wait and curl manually:
curl -sfI https://townofwiley.gov/ curl -sf https://townofwiley.gov/runtime-config.js | head
npm run verify:live-csp-probe
npm run verify:live-csp-vs-repoSee docs/third-party-csp-registry.md.
README.md— hosting overviewdocs/git-workflow.md— CD modeldocs/AWS_INFRASTRUCTURE_SOT.md— resource IDs.github/instructions/aws-hosting.instructions.md