mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 08:03:13 +00:00
ci(deploy): make dev content deploy workflow_dispatch only
Remove the push-to-dev trigger and the org cd-cdk.yaml caller so CI no longer runs cdk deploy during the adoption. The workflow assumes the pinned dev role and runs the simple scripts/deploy-web.sh against a pinned bucket and distribution, which keeps content deploys working after CloudFormation relinquishes the stack outputs. Staging is untouched.
This commit is contained in:
parent
82361e14b5
commit
87e79072ad
2 changed files with 108 additions and 48 deletions
104
.github/workflows/deploy.yml
vendored
104
.github/workflows/deploy.yml
vendored
|
|
@ -1,22 +1,22 @@
|
|||
name: Deploy
|
||||
name: Deploy dev content
|
||||
|
||||
# Continuous deployment to AWS (S3 + CloudFront) on push to `dev`.
|
||||
# Manual dev content deployment during the Terraform adoption (SH-300).
|
||||
#
|
||||
# This is a thin caller of the org's reusable CD workflow. `cd-cdk.yaml` runs
|
||||
# `cdk deploy` (provisioning the infra in infra/cdk) and then the
|
||||
# post-deploy-script, which builds the SPA and syncs it to S3 + invalidates
|
||||
# CloudFront. Both run as the OIDC deploy role created by the stack.
|
||||
# The push-to-`dev` trigger and the org reusable `cd-cdk.yaml` caller are
|
||||
# retired: `cdk deploy` no longer runs from CI. Infrastructure changes are
|
||||
# administrator-run (`infra/cdk/README.md`) while CloudFormation still owns the
|
||||
# resources, and move to HCP Terraform (`terraform/README.md`) as adoption
|
||||
# completes. Automatic push-to-`dev` releases return with the Terraform
|
||||
# content-CD change, gated on a repository variable.
|
||||
#
|
||||
# When staging/prod accounts exist, add jobs keyed to their branches and their
|
||||
# own AWS_DEPLOY_ROLE_ARN, reusing this same reusable workflow.
|
||||
# This workflow publishes only content: verify, build, `aws s3 sync`, and a
|
||||
# CloudFront invalidation through `scripts/deploy-web.sh`, as the pinned OIDC
|
||||
# deploy role. The bucket and distribution are pinned here so a content deploy
|
||||
# keeps working after CloudFormation relinquishes the stack outputs.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
workflow_dispatch: {}
|
||||
|
||||
# OIDC needs id-token: write — it is never in the default token set and cannot
|
||||
# be granted to the reusable workflow unless the caller has it.
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
|
@ -27,22 +27,13 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
deploy:
|
||||
uses: Sea-Haven-Industries/.github/.github/workflows/cd-cdk.yaml@af0f002e14a08cdbfd879c1183bfe7eb2604bce9 # v1.0.8
|
||||
with:
|
||||
node-version: "24"
|
||||
region: us-east-1
|
||||
cdk-dir: infra/cdk
|
||||
stack-name: shoc-frontend-dev
|
||||
post-deploy-script: scripts/deploy-web.sh
|
||||
secrets:
|
||||
deploy-role-arn: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
|
||||
|
||||
upload-sourcemaps:
|
||||
name: Upload private source maps
|
||||
needs: deploy
|
||||
name: Publish content to dev
|
||||
# Deploy only the exact dev branch ref: workflow_dispatch can be invoked
|
||||
# from arbitrary refs, and the deploy role trusts only refs/heads/dev.
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
AWS_REGION: us-east-1
|
||||
VITE_APP_COMMIT_SHA: ${{ github.sha }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
|
@ -52,9 +43,66 @@ jobs:
|
|||
with:
|
||||
node-version: "24"
|
||||
cache: npm
|
||||
- name: Build exact deployed release
|
||||
run: npm ci && npm run build
|
||||
- name: Upload source maps to Sentry
|
||||
- name: Set up Terraform
|
||||
# Required by `npm run verify` (governance runs terraform fmt/validate).
|
||||
uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
|
||||
with:
|
||||
terraform_version: "1.16.0"
|
||||
terraform_wrapper: false
|
||||
- name: Quality gates (full verify before any deploy)
|
||||
run: npm ci && npm run verify
|
||||
env:
|
||||
GOVERNANCE_BASE: origin/dev
|
||||
|
||||
- name: Assume dev deploy role (OIDC)
|
||||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::396287094661:role/githubdeploy-shoc-frontend-new-dev
|
||||
aws-region: us-east-1
|
||||
|
||||
# Builds with the dev values committed in .env.production (VITE_API_URL,
|
||||
# Sentry DSN), syncs to the pinned bucket, and invalidates CloudFront.
|
||||
- name: Build and publish SPA
|
||||
run: bash scripts/deploy-web.sh
|
||||
env:
|
||||
SITE_BUCKET: seahaven-shoc-frontend-dev
|
||||
CLOUDFRONT_DISTRIBUTION_ID: E2CWLM1AFB964P
|
||||
WAIT_FOR_INVALIDATION: "true"
|
||||
|
||||
- name: Upload private source maps
|
||||
run: bash scripts/upload-sourcemaps.sh
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
|
||||
- name: Verify deployment
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SITE_URL="https://dev.seahaven.com"
|
||||
if grep -Rq "api.staging.seahaven.com" dist/; then
|
||||
echo "::error::Built assets contain the staging API URL." >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Rq "api.dev.seahaven.com" dist/
|
||||
echo "Built assets reference the dev API URL."
|
||||
|
||||
# The invalidation has completed, but give edges a short window to
|
||||
# converge before calling the served index.html wrong.
|
||||
remote_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "${remote_dir}"' EXIT
|
||||
matched=false
|
||||
for i in 1 2 3 4 5 6; do
|
||||
if curl -fsS --max-time 30 "${SITE_URL}" -o "${remote_dir}/index.html" \
|
||||
&& cmp -s dist/index.html "${remote_dir}/index.html"; then
|
||||
matched=true
|
||||
break
|
||||
fi
|
||||
echo "Served index.html does not yet match the published build (attempt ${i}); retrying in 20s..."
|
||||
sleep 20
|
||||
done
|
||||
if [[ "${matched}" != "true" ]]; then
|
||||
echo "::error::Served index.html does not match the build just published." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Served index.html matches the published build."
|
||||
curl -fsS --max-time 30 -o /dev/null "${SITE_URL}/login"
|
||||
echo "Extensionless SPA route serves."
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Post-deploy step for the org reusable workflow `cd-cdk.yaml`
|
||||
# (wired in via `.github/workflows/deploy.yml` -> `post-deploy-script`).
|
||||
# Content publish step for the environment deploy workflows
|
||||
# (`.github/workflows/deploy.yml`, `.github/workflows/deploy-staging.yml`).
|
||||
#
|
||||
# Runs AFTER `cdk deploy` has provisioned/updated the infra, as the GitHub
|
||||
# OIDC deploy role. Builds the SPA, uploads it to the stack's S3 bucket with
|
||||
# the right cache headers, and invalidates CloudFront.
|
||||
# Runs as the GitHub OIDC deploy role. Builds the SPA, uploads it to the
|
||||
# environment's S3 bucket with the right cache headers, and invalidates
|
||||
# CloudFront. It never touches infrastructure.
|
||||
#
|
||||
# Runs from the repo root. Reads the bucket + distribution from stack outputs,
|
||||
# so it has no hardcoded resource IDs.
|
||||
# Runs from the repo root. The target is resolved from, in order:
|
||||
# 1. SITE_BUCKET + CLOUDFRONT_DISTRIBUTION_ID (pinned by the workflow; used by
|
||||
# dev, whose CloudFormation outputs disappear during Terraform adoption)
|
||||
# 2. the BucketName/DistributionId outputs of STACK_NAME (staging)
|
||||
set -euo pipefail
|
||||
|
||||
STACK_NAME="${STACK_NAME:-shoc-frontend-dev}"
|
||||
|
|
@ -20,21 +22,31 @@ export VITE_APP_COMMIT_SHA="${VITE_APP_COMMIT_SHA:-${GITHUB_SHA:-}}"
|
|||
npm ci
|
||||
npm run build
|
||||
|
||||
echo "Reading stack outputs from ${STACK_NAME}..."
|
||||
stack_output() {
|
||||
aws cloudformation describe-stacks \
|
||||
--stack-name "${STACK_NAME}" \
|
||||
--region "${REGION}" \
|
||||
--query "Stacks[0].Outputs[?OutputKey=='$1'].OutputValue" \
|
||||
--output text
|
||||
}
|
||||
BUCKET="${SITE_BUCKET:-}"
|
||||
DIST_ID="${CLOUDFRONT_DISTRIBUTION_ID:-}"
|
||||
|
||||
BUCKET="$(stack_output BucketName)"
|
||||
DIST_ID="$(stack_output DistributionId)"
|
||||
|
||||
if [[ -z "${BUCKET}" || "${BUCKET}" == "None" || -z "${DIST_ID}" || "${DIST_ID}" == "None" ]]; then
|
||||
echo "::error::Could not resolve BucketName/DistributionId from stack ${STACK_NAME}." >&2
|
||||
if [[ -n "${BUCKET}" && -n "${DIST_ID}" ]]; then
|
||||
echo "Using pinned target: bucket ${BUCKET}, distribution ${DIST_ID}."
|
||||
elif [[ -n "${BUCKET}" || -n "${DIST_ID}" ]]; then
|
||||
echo "::error::Set both SITE_BUCKET and CLOUDFRONT_DISTRIBUTION_ID, or neither." >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Reading stack outputs from ${STACK_NAME}..."
|
||||
stack_output() {
|
||||
aws cloudformation describe-stacks \
|
||||
--stack-name "${STACK_NAME}" \
|
||||
--region "${REGION}" \
|
||||
--query "Stacks[0].Outputs[?OutputKey=='$1'].OutputValue" \
|
||||
--output text
|
||||
}
|
||||
|
||||
BUCKET="$(stack_output BucketName)"
|
||||
DIST_ID="$(stack_output DistributionId)"
|
||||
|
||||
if [[ -z "${BUCKET}" || "${BUCKET}" == "None" || -z "${DIST_ID}" || "${DIST_ID}" == "None" ]]; then
|
||||
echo "::error::Could not resolve BucketName/DistributionId from stack ${STACK_NAME}." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Uploading hashed assets (immutable) to s3://${BUCKET}..."
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue