mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-09-30 15:13:12 +00:00
Compare commits
No commits in common. "main" and "v1.0.18" have entirely different histories.
5 changed files with 232 additions and 653 deletions
274
.github/workflows/cd-hcp-lambda.yaml
vendored
274
.github/workflows/cd-hcp-lambda.yaml
vendored
|
|
@ -1,274 +0,0 @@
|
||||||
name: CD — HCP Lambda
|
|
||||||
|
|
||||||
# Reusable Lambda zip CD for HCP app repos. The caller owns triggers and
|
|
||||||
# passes `environment` as a `with:` input. This job owns `environment:`,
|
|
||||||
# concurrency, OIDC, and `vars.DEPLOY_ROLE_ARN`. GitHub rejects `environment:`
|
|
||||||
# beside `uses:`.
|
|
||||||
#
|
|
||||||
# Caller example (one job per GitHub Environment):
|
|
||||||
# jobs:
|
|
||||||
# deploy-prod:
|
|
||||||
# uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-lambda.yaml@<sha> # vX.Y.Z
|
|
||||||
# permissions: { contents: read, id-token: write }
|
|
||||||
# secrets: inherit
|
|
||||||
# with:
|
|
||||||
# environment: prod
|
|
||||||
# ref: ${{ github.event.release.tag_name || inputs.ref }}
|
|
||||||
# ssm-prefix: /payments-dashboard/deploy
|
|
||||||
# function-keys: process_csv,slack_app_home
|
|
||||||
# ship-gate: true
|
|
||||||
#
|
|
||||||
# The caller repo must provide scripts/package_lambdas.mjs, which writes
|
|
||||||
# build/packages/<key>.zip and embeds the commit in src/buildInfo.js.
|
|
||||||
# Terraform owns the functions and ignores code attributes. SSM under
|
|
||||||
# ssm-prefix supplies artifacts-bucket and <key>-function-name.
|
|
||||||
#
|
|
||||||
# Nothing here creates an HCP run.
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
description: "GitHub Environment to deploy to (dev, staging, prod)"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
ref:
|
|
||||||
description: "Git ref to build. Empty means github.sha."
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
ssm-prefix:
|
|
||||||
description: "SSM prefix for deploy parameters (e.g. /payments-dashboard/deploy)"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
function-keys:
|
|
||||||
description: "Comma-separated package keys. Each maps to SSM <prefix>/<key>-function-name."
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
node-version:
|
|
||||||
description: "Node.js version for setup-node and the packager"
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: "24"
|
|
||||||
ship-gate:
|
|
||||||
description: "Require the ref to be on main or a legal hotfix/release tag"
|
|
||||||
type: boolean
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: Deploy Lambda to ${{ inputs.environment }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 30
|
|
||||||
environment: ${{ inputs.environment }}
|
|
||||||
concurrency:
|
|
||||||
group: deploy-${{ inputs.ssm-prefix }}-${{ inputs.environment }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
env:
|
|
||||||
AWS_REGION: us-east-1
|
|
||||||
DEPLOY_ROLE_ARN: ${{ vars.DEPLOY_ROLE_ARN }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
ref: ${{ inputs.ref != '' && inputs.ref || github.sha }}
|
|
||||||
persist-credentials: false
|
|
||||||
fetch-tags: true
|
|
||||||
|
|
||||||
- name: Resolve commit
|
|
||||||
id: commit
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
sha="$(git rev-parse HEAD)"
|
|
||||||
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "Building ${sha}"
|
|
||||||
|
|
||||||
- name: Ship-gate
|
|
||||||
if: ${{ inputs.ship-gate }}
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
INPUT_REF: ${{ inputs.ref != '' && inputs.ref || github.sha }}
|
|
||||||
ENVIRONMENT: ${{ inputs.environment }}
|
|
||||||
HEAD_SHA: ${{ steps.commit.outputs.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
status="$(gh api "repos/${REPO}/compare/main...${INPUT_REF}" --jq .status)"
|
|
||||||
if [ "${status}" = "behind" ] || [ "${status}" = "identical" ]; then
|
|
||||||
echo "ship-gate: ${INPUT_REF} is ${status} relative to main"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "ship-gate: ${INPUT_REF} is not on main (compare status: ${status}); checking hotfix/release path"
|
|
||||||
|
|
||||||
TAG="${INPUT_REF}"
|
|
||||||
if [[ ! "${TAG}" =~ ^v ]]; then
|
|
||||||
TAG="$(git describe --tags --exact-match "${HEAD_SHA}" 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${ENVIRONMENT}" = "staging" ]; then
|
|
||||||
PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+-staging$'
|
|
||||||
else
|
|
||||||
PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+$'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${TAG}" ]] || [[ ! "${TAG}" =~ ${PATTERN} ]]; then
|
|
||||||
echo "ship-gate: ref ${INPUT_REF} (tag ${TAG:-none}) does not match ${PATTERN} for environment ${ENVIRONMENT} and is not on main" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
export PATTERN TAG
|
|
||||||
# Newest matching release that is an ancestor of TAG. The highest
|
|
||||||
# release overall is not that ancestor when a hotfix is cut from an
|
|
||||||
# older line (v2.0.0 exists, v1.2.1 is cut from v1.2.0).
|
|
||||||
CANDIDATES="$(
|
|
||||||
gh api "repos/${REPO}/releases" --paginate --jq '.[].tag_name' | python3 -c '
|
|
||||||
import os, re, sys
|
|
||||||
pattern = re.compile(os.environ["PATTERN"])
|
|
||||||
current = os.environ["TAG"]
|
|
||||||
tags = [
|
|
||||||
line.strip()
|
|
||||||
for line in sys.stdin
|
|
||||||
if pattern.fullmatch(line.strip()) and line.strip() != current
|
|
||||||
]
|
|
||||||
def key(tag):
|
|
||||||
body = tag[1:]
|
|
||||||
core = body.split("-", 1)[0]
|
|
||||||
return tuple(int(part) for part in core.split("."))
|
|
||||||
tags.sort(key=key, reverse=True)
|
|
||||||
print("\n".join(tags))
|
|
||||||
'
|
|
||||||
)"
|
|
||||||
|
|
||||||
PREV=""
|
|
||||||
if [ -n "${CANDIDATES}" ]; then
|
|
||||||
while IFS= read -r candidate; do
|
|
||||||
if [ -z "${candidate}" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
candidate_status="$(gh api "repos/${REPO}/compare/${candidate}...${TAG}" --jq .status)"
|
|
||||||
if [ "${candidate_status}" = "ahead" ]; then
|
|
||||||
PREV="${candidate}"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done <<< "${CANDIDATES}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "${PREV}" ]; then
|
|
||||||
echo "ship-gate: no prior ${PATTERN} release is an ancestor of ${TAG}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "ship-gate: ${TAG} is ahead of ${PREV}"
|
|
||||||
|
|
||||||
from_train=false
|
|
||||||
TARGET_COMMITISH="$(gh api "repos/${REPO}/releases/tags/${TAG}" --jq .target_commitish 2>/dev/null || true)"
|
|
||||||
if [[ "${TARGET_COMMITISH}" == hotfix/* || "${TARGET_COMMITISH}" == release/* ]]; then
|
|
||||||
from_train=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${from_train}" = false ]; then
|
|
||||||
git fetch origin '+refs/heads/hotfix/*:refs/remotes/origin/hotfix/*' '+refs/heads/release/*:refs/remotes/origin/release/*' || true
|
|
||||||
if git branch -r --contains "${HEAD_SHA}" | grep -Eq 'origin/(hotfix|release)/'; then
|
|
||||||
from_train=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${from_train}" = false ]; then
|
|
||||||
echo "ship-gate: ${TAG} was not created from hotfix/* or release/* (target_commitish=${TARGET_COMMITISH:-none})" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "ship-gate: hotfix/release path accepted (${PREV} -> ${TAG} from ${TARGET_COMMITISH:-branch})"
|
|
||||||
|
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
||||||
with:
|
|
||||||
node-version: ${{ inputs.node-version }}
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: Build function zips
|
|
||||||
env:
|
|
||||||
GIT_SHA: ${{ steps.commit.outputs.sha }}
|
|
||||||
FUNCTION_KEYS: ${{ inputs.function-keys }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
if [ -z "${FUNCTION_KEYS}" ]; then
|
|
||||||
echo "function-keys is required" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
keys=()
|
|
||||||
IFS=',' read -r -a raw_keys <<< "${FUNCTION_KEYS}"
|
|
||||||
for raw in "${raw_keys[@]}"; do
|
|
||||||
key="${raw#"${raw%%[![:space:]]*}"}"
|
|
||||||
key="${key%"${key##*[![:space:]]}"}"
|
|
||||||
if [ -z "${key}" ]; then
|
|
||||||
echo "function-keys contains an empty key" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ ! "${key}" =~ ^[A-Za-z0-9_]+$ ]]; then
|
|
||||||
echo "invalid function key: ${key}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
keys+=("${key}")
|
|
||||||
done
|
|
||||||
if [ "${#keys[@]}" -eq 0 ]; then
|
|
||||||
echo "function-keys is empty" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
clean="$(IFS=,; echo "${keys[*]}")"
|
|
||||||
echo "keys=${clean}" >> "${GITHUB_ENV}"
|
|
||||||
cmd=(node scripts/package_lambdas.mjs --git-sha "${GIT_SHA}" --out-dir build/packages)
|
|
||||||
for key in "${keys[@]}"; do
|
|
||||||
cmd+=(--only "${key}")
|
|
||||||
done
|
|
||||||
"${cmd[@]}"
|
|
||||||
FUNCTION_KEYS_CLEAN="${clean}" python3 - <<'PY'
|
|
||||||
import os, zipfile
|
|
||||||
from pathlib import Path
|
|
||||||
sha = os.environ["GIT_SHA"]
|
|
||||||
keys = [part for part in os.environ["FUNCTION_KEYS_CLEAN"].split(",") if part]
|
|
||||||
for name in keys:
|
|
||||||
path = Path("build/packages") / f"{name}.zip"
|
|
||||||
if not path.is_file():
|
|
||||||
raise SystemExit(f"missing {path}")
|
|
||||||
with zipfile.ZipFile(path) as zf:
|
|
||||||
info = zf.read("src/buildInfo.js").decode()
|
|
||||||
if sha not in info:
|
|
||||||
raise SystemExit(f"{path} missing GIT_SHA {sha}")
|
|
||||||
print("zips ok")
|
|
||||||
PY
|
|
||||||
|
|
||||||
- name: Configure AWS credentials using OIDC
|
|
||||||
uses: aws-actions/configure-aws-credentials@cbe3b392738ccf3f987d68400dafcf4b0624a56c # v6.2.4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ env.DEPLOY_ROLE_ARN }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
audience: sts.amazonaws.com
|
|
||||||
|
|
||||||
- name: Upload zips and update function code
|
|
||||||
env:
|
|
||||||
SSM_PREFIX: ${{ inputs.ssm-prefix }}
|
|
||||||
GIT_SHA: ${{ steps.commit.outputs.sha }}
|
|
||||||
FUNCTION_KEYS_CLEAN: ${{ env.keys }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
prefix="${SSM_PREFIX%/}"
|
|
||||||
bucket="$(aws ssm get-parameter --name "${prefix}/artifacts-bucket" --query Parameter.Value --output text)"
|
|
||||||
IFS=',' read -r -a keys <<< "${FUNCTION_KEYS_CLEAN}"
|
|
||||||
for key in "${keys[@]}"; do
|
|
||||||
fn="$(aws ssm get-parameter --name "${prefix}/${key}-function-name" --query Parameter.Value --output text)"
|
|
||||||
s3_key="functions/${key}/${GIT_SHA}.zip"
|
|
||||||
aws s3 cp "build/packages/${key}.zip" "s3://${bucket}/${s3_key}"
|
|
||||||
aws lambda update-function-code \
|
|
||||||
--function-name "${fn}" \
|
|
||||||
--s3-bucket "${bucket}" \
|
|
||||||
--s3-key "${s3_key}" \
|
|
||||||
--query '{Function:FunctionName,Sha256:CodeSha256,Updated:LastModified}' \
|
|
||||||
--output table
|
|
||||||
aws lambda wait function-updated-v2 --function-name "${fn}"
|
|
||||||
done
|
|
||||||
312
.github/workflows/cd-hcp-static.yaml
vendored
312
.github/workflows/cd-hcp-static.yaml
vendored
|
|
@ -1,312 +0,0 @@
|
||||||
name: CD — HCP static site
|
|
||||||
|
|
||||||
# Reusable CloudFront/S3 CD for unfingerprinted static sites. The caller owns
|
|
||||||
# triggers and passes `environment` as a `with:` input. This job owns
|
|
||||||
# `environment:`, concurrency, OIDC, and `vars.DEPLOY_ROLE_ARN`. GitHub
|
|
||||||
# rejects `environment:` beside `uses:`.
|
|
||||||
#
|
|
||||||
# Assets are not content-hashed, so they get a one-day cache. HTML, XML, and
|
|
||||||
# text get no-cache. Do not point a hashed SPA at this workflow.
|
|
||||||
#
|
|
||||||
# Caller example:
|
|
||||||
# jobs:
|
|
||||||
# deploy-prod:
|
|
||||||
# uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-static.yaml@<sha> # vX.Y.Z
|
|
||||||
# permissions: { contents: read, id-token: write }
|
|
||||||
# secrets: inherit
|
|
||||||
# with:
|
|
||||||
# environment: prod
|
|
||||||
# ref: ${{ inputs.ref }}
|
|
||||||
# ssm-prefix: /seahaven-site/deploy
|
|
||||||
# required-paths: _site/index.html,_site/contact/index.html,_site/404.html
|
|
||||||
# min-file-count: 40
|
|
||||||
# ship-gate: true
|
|
||||||
#
|
|
||||||
# Nothing here creates an HCP run. Terraform owns the bucket and distribution.
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
description: "GitHub Environment to deploy to (dev, staging, prod)"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
ref:
|
|
||||||
description: "Git ref to build. Empty means github.sha."
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
ssm-prefix:
|
|
||||||
description: "SSM prefix for deploy parameters (e.g. /seahaven-site/deploy)"
|
|
||||||
type: string
|
|
||||||
required: true
|
|
||||||
ship-gate:
|
|
||||||
description: "Require the ref to be on main or a legal hotfix/release tag"
|
|
||||||
type: boolean
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
output-dir:
|
|
||||||
description: "Build output directory"
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: "_site"
|
|
||||||
required-paths:
|
|
||||||
description: "Comma-separated repo-relative files that must exist after the build"
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
min-file-count:
|
|
||||||
description: "Minimum file count under output-dir. Zero skips the count check."
|
|
||||||
type: number
|
|
||||||
required: false
|
|
||||||
default: 1
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
name: Deploy static site to ${{ inputs.environment }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 45
|
|
||||||
environment: ${{ inputs.environment }}
|
|
||||||
concurrency:
|
|
||||||
group: deploy-${{ inputs.ssm-prefix }}-${{ inputs.environment }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
env:
|
|
||||||
AWS_REGION: us-east-1
|
|
||||||
DEPLOY_ROLE_ARN: ${{ vars.DEPLOY_ROLE_ARN }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
||||||
with:
|
|
||||||
ref: ${{ inputs.ref != '' && inputs.ref || github.sha }}
|
|
||||||
persist-credentials: false
|
|
||||||
fetch-tags: true
|
|
||||||
|
|
||||||
- name: Resolve commit
|
|
||||||
id: commit
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
sha="$(git rev-parse HEAD)"
|
|
||||||
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
|
|
||||||
echo "Building ${sha}"
|
|
||||||
|
|
||||||
- name: Ship-gate
|
|
||||||
if: ${{ inputs.ship-gate }}
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
INPUT_REF: ${{ inputs.ref != '' && inputs.ref || github.sha }}
|
|
||||||
ENVIRONMENT: ${{ inputs.environment }}
|
|
||||||
HEAD_SHA: ${{ steps.commit.outputs.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
status="$(gh api "repos/${REPO}/compare/main...${INPUT_REF}" --jq .status)"
|
|
||||||
if [ "${status}" = "behind" ] || [ "${status}" = "identical" ]; then
|
|
||||||
echo "ship-gate: ${INPUT_REF} is ${status} relative to main"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "ship-gate: ${INPUT_REF} is not on main (compare status: ${status}); checking hotfix/release path"
|
|
||||||
|
|
||||||
TAG="${INPUT_REF}"
|
|
||||||
if [[ ! "${TAG}" =~ ^v ]]; then
|
|
||||||
TAG="$(git describe --tags --exact-match "${HEAD_SHA}" 2>/dev/null || true)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${ENVIRONMENT}" = "staging" ]; then
|
|
||||||
PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+-staging$'
|
|
||||||
else
|
|
||||||
PATTERN='^v[0-9]+\.[0-9]+\.[0-9]+$'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${TAG}" ]] || [[ ! "${TAG}" =~ ${PATTERN} ]]; then
|
|
||||||
echo "ship-gate: ref ${INPUT_REF} (tag ${TAG:-none}) does not match ${PATTERN} for environment ${ENVIRONMENT} and is not on main" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
export PATTERN TAG
|
|
||||||
PREV="$(
|
|
||||||
gh api "repos/${REPO}/releases" --paginate --jq '.[].tag_name' | python3 -c '
|
|
||||||
import os, re, sys
|
|
||||||
pattern = re.compile(os.environ["PATTERN"])
|
|
||||||
current = os.environ["TAG"]
|
|
||||||
tags = [
|
|
||||||
line.strip()
|
|
||||||
for line in sys.stdin
|
|
||||||
if pattern.fullmatch(line.strip()) and line.strip() != current
|
|
||||||
]
|
|
||||||
def key(tag):
|
|
||||||
body = tag[1:]
|
|
||||||
core = body.split("-", 1)[0]
|
|
||||||
return tuple(int(part) for part in core.split("."))
|
|
||||||
tags.sort(key=key)
|
|
||||||
print(tags[-1] if tags else "")
|
|
||||||
'
|
|
||||||
)"
|
|
||||||
|
|
||||||
if [ -z "${PREV}" ]; then
|
|
||||||
echo "ship-gate: no previous matching release tag; hotfix path requires a prior ${PATTERN} release" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ff_status="$(gh api "repos/${REPO}/compare/${PREV}...${TAG}" --jq .status)"
|
|
||||||
if [ "${ff_status}" != "ahead" ]; then
|
|
||||||
echo "ship-gate: ${TAG} is not a fast-forward of ${PREV} (status: ${ff_status})" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
from_train=false
|
|
||||||
TARGET_COMMITISH="$(gh api "repos/${REPO}/releases/tags/${TAG}" --jq .target_commitish 2>/dev/null || true)"
|
|
||||||
if [[ "${TARGET_COMMITISH}" == hotfix/* || "${TARGET_COMMITISH}" == release/* ]]; then
|
|
||||||
from_train=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${from_train}" = false ]; then
|
|
||||||
git fetch origin '+refs/heads/hotfix/*:refs/remotes/origin/hotfix/*' '+refs/heads/release/*:refs/remotes/origin/release/*' || true
|
|
||||||
if git branch -r --contains "${HEAD_SHA}" | grep -Eq 'origin/(hotfix|release)/'; then
|
|
||||||
from_train=true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${from_train}" = false ]; then
|
|
||||||
echo "ship-gate: ${TAG} was not created from hotfix/* or release/* (target_commitish=${TARGET_COMMITISH:-none})" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "ship-gate: hotfix/release path accepted (${PREV} -> ${TAG} from ${TARGET_COMMITISH:-branch})"
|
|
||||||
|
|
||||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
||||||
with:
|
|
||||||
node-version: "24"
|
|
||||||
cache: npm
|
|
||||||
|
|
||||||
- name: Build site
|
|
||||||
env:
|
|
||||||
OUTPUT_DIR: ${{ inputs.output-dir }}
|
|
||||||
REQUIRED_PATHS: ${{ inputs.required-paths }}
|
|
||||||
MIN_FILE_COUNT: ${{ inputs.min-file-count }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
npm ci --ignore-scripts
|
|
||||||
npm run build
|
|
||||||
python3 - <<'PY'
|
|
||||||
import os, sys
|
|
||||||
output_dir = os.environ["OUTPUT_DIR"]
|
|
||||||
if not os.path.isdir(output_dir):
|
|
||||||
print(f"build did not produce {output_dir}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
missing = []
|
|
||||||
for raw in os.environ.get("REQUIRED_PATHS", "").split(","):
|
|
||||||
path = raw.strip()
|
|
||||||
if path and not os.path.isfile(path):
|
|
||||||
missing.append(path)
|
|
||||||
if missing:
|
|
||||||
print("missing required build files: " + ", ".join(missing), file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
count = 0
|
|
||||||
for _root, _dirs, files in os.walk(output_dir):
|
|
||||||
count += len(files)
|
|
||||||
minimum = int(os.environ["MIN_FILE_COUNT"])
|
|
||||||
if minimum > 0 and count < minimum:
|
|
||||||
print(f"build produced only {count} files (expected >= {minimum})", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
index = os.path.join(output_dir, "index.html")
|
|
||||||
if not os.path.isfile(index):
|
|
||||||
print(f"missing {index}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
print(f"Build OK: {count} files.")
|
|
||||||
PY
|
|
||||||
index_sha="$(python3 -c 'import hashlib,os,pathlib; print(hashlib.sha256(pathlib.Path(os.environ["OUTPUT_DIR"], "index.html").read_bytes()).hexdigest())')"
|
|
||||||
echo "INDEX_SHA256=${index_sha}" >> "${GITHUB_ENV}"
|
|
||||||
echo "${OUTPUT_DIR}/index.html sha256=${index_sha}"
|
|
||||||
|
|
||||||
- name: Configure AWS credentials using OIDC
|
|
||||||
uses: aws-actions/configure-aws-credentials@cbe3b392738ccf3f987d68400dafcf4b0624a56c # v6.2.4
|
|
||||||
with:
|
|
||||||
role-to-assume: ${{ env.DEPLOY_ROLE_ARN }}
|
|
||||||
aws-region: us-east-1
|
|
||||||
audience: sts.amazonaws.com
|
|
||||||
|
|
||||||
- name: Get deploy parameters
|
|
||||||
id: deploy
|
|
||||||
env:
|
|
||||||
SSM_PREFIX: ${{ inputs.ssm-prefix }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
prefix="${SSM_PREFIX%/}"
|
|
||||||
BUCKET=$(aws ssm get-parameter --name "${prefix}/bucket" --query Parameter.Value --output text)
|
|
||||||
DIST_ID=$(aws ssm get-parameter --name "${prefix}/distribution-id" --query Parameter.Value --output text)
|
|
||||||
DOMAIN=$(aws cloudfront get-distribution --id "${DIST_ID}" --query Distribution.DomainName --output text)
|
|
||||||
origin_paths="$(aws cloudfront get-distribution --id "${DIST_ID}" \
|
|
||||||
--query 'Distribution.DistributionConfig.Origins.Items[].OriginPath' --output text | tr -d '[:space:]')"
|
|
||||||
if [ -n "${origin_paths}" ]; then
|
|
||||||
echo "Distribution ${DIST_ID} still has a non-empty origin path (${origin_paths})." >&2
|
|
||||||
echo "Wait for the HCP apply that moves the origin to the bucket root, then re-run." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
{
|
|
||||||
echo "bucket=${BUCKET}"
|
|
||||||
echo "distribution_id=${DIST_ID}"
|
|
||||||
echo "site_url=https://${DOMAIN}"
|
|
||||||
} >> "${GITHUB_OUTPUT}"
|
|
||||||
|
|
||||||
- name: Sync build output to the bucket root
|
|
||||||
env:
|
|
||||||
SITE_BUCKET: ${{ steps.deploy.outputs.bucket }}
|
|
||||||
OUTPUT_DIR: ${{ inputs.output-dir }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
aws s3 sync "${OUTPUT_DIR}/" "s3://${SITE_BUCKET}/" --no-progress \
|
|
||||||
--exclude "*.html" --exclude "*.xml" --exclude "*.txt" \
|
|
||||||
--cache-control "public, max-age=86400"
|
|
||||||
aws s3 sync "${OUTPUT_DIR}/" "s3://${SITE_BUCKET}/" --no-progress \
|
|
||||||
--exclude "*" --include "*.html" --include "*.xml" --include "*.txt" \
|
|
||||||
--cache-control "no-cache"
|
|
||||||
aws s3 sync "${OUTPUT_DIR}/" "s3://${SITE_BUCKET}/" --no-progress --delete
|
|
||||||
aws s3api head-object --bucket "${SITE_BUCKET}" --key index.html
|
|
||||||
|
|
||||||
- name: Invalidate CloudFront
|
|
||||||
env:
|
|
||||||
DISTRIBUTION_ID: ${{ steps.deploy.outputs.distribution_id }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
invalidation_id="$(aws cloudfront create-invalidation \
|
|
||||||
--distribution-id "${DISTRIBUTION_ID}" \
|
|
||||||
--paths "/*" \
|
|
||||||
--query Invalidation.Id --output text)"
|
|
||||||
echo "Invalidation ${invalidation_id} created; waiting"
|
|
||||||
aws cloudfront wait invalidation-completed \
|
|
||||||
--distribution-id "${DISTRIBUTION_ID}" \
|
|
||||||
--id "${invalidation_id}"
|
|
||||||
|
|
||||||
- name: Verify served release
|
|
||||||
env:
|
|
||||||
DISTRIBUTION_ID: ${{ steps.deploy.outputs.distribution_id }}
|
|
||||||
SITE_URL: ${{ steps.deploy.outputs.site_url }}
|
|
||||||
EXPECTED_INDEX_SHA256: ${{ env.INDEX_SHA256 }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
SITE_URL="${SITE_URL%/}"
|
|
||||||
sha256_of() {
|
|
||||||
python3 -c "import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())"
|
|
||||||
}
|
|
||||||
last_status="Unknown"
|
|
||||||
last_hash="Unknown"
|
|
||||||
for attempt in $(seq 1 40); do
|
|
||||||
last_status="$(aws cloudfront get-distribution --id "${DISTRIBUTION_ID}" --query Distribution.Status --output text)"
|
|
||||||
if last_hash="$(curl -fsS --max-time 30 "${SITE_URL}/" | sha256_of)" && [ -n "${last_hash}" ]; then
|
|
||||||
:
|
|
||||||
else
|
|
||||||
last_hash="unreachable"
|
|
||||||
fi
|
|
||||||
echo "poll ${attempt}/40: status=${last_status} served_sha256=${last_hash}"
|
|
||||||
if [ "${last_status}" = "Deployed" ] && [ "${last_hash}" = "${EXPECTED_INDEX_SHA256}" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
sleep 15
|
|
||||||
done
|
|
||||||
echo "release did not converge: status=${last_status} served_sha256=${last_hash} expected=${EXPECTED_INDEX_SHA256}" >&2
|
|
||||||
exit 1
|
|
||||||
|
|
@ -39,10 +39,6 @@ The formatter GitHub App is not on the main-branch bypass list.
|
||||||
|
|
||||||
## What's in here
|
## What's in here
|
||||||
|
|
||||||
### Renovate preset
|
|
||||||
|
|
||||||
`default.json` is the file loaded by `local>Sea-Haven-Industries/.github`. It is intentionally empty of policy. Org Renovate rules live in `Sea-Haven-Industries/renovate-config` as `org-inherited-config.json`.
|
|
||||||
|
|
||||||
### Reusable Workflows
|
### Reusable Workflows
|
||||||
|
|
||||||
**`.github/workflows/ci-python-sam.yaml`** — Reusable CI workflow for Python / SAM repos. Runs `ruff check` + `ruff format --check`, optional `pytest`, and optional `sam validate --lint`. Also usable for Python CDK repos by disabling SAM validate.
|
**`.github/workflows/ci-python-sam.yaml`** — Reusable CI workflow for Python / SAM repos. Runs `ruff check` + `ruff format --check`, optional `pytest`, and optional `sam validate --lint`. Also usable for Python CDK repos by disabling SAM validate.
|
||||||
|
|
@ -61,8 +57,6 @@ The formatter GitHub App is not on the main-branch bypass list.
|
||||||
|
|
||||||
**`.github/workflows/cd-hcp-spa.yaml`** — HCP SPA CD. Checkout at `ref`, Node 24, `npm ci` + `npm run build`, origin-path guard, hashed s3 sync then `index.html` last then prune, invalidate, verify. Build env comes from the GitHub Environment (`vars.VITE_*`, `secrets.SENTRY_AUTH_TOKEN`).
|
**`.github/workflows/cd-hcp-spa.yaml`** — HCP SPA CD. Checkout at `ref`, Node 24, `npm ci` + `npm run build`, origin-path guard, hashed s3 sync then `index.html` last then prune, invalidate, verify. Build env comes from the GitHub Environment (`vars.VITE_*`, `secrets.SENTRY_AUTH_TOKEN`).
|
||||||
|
|
||||||
**`.github/workflows/cd-hcp-static.yaml`** — HCP static-site CD for unfingerprinted builds such as Eleventy. Checkout at `ref`, Node 24, `npm ci --ignore-scripts` + `npm run build`, one-day cache on assets, `no-cache` on HTML/XML/text, prune, invalidate, verify the served index hash. Reads `/<prefix>/bucket` and `/<prefix>/distribution-id`. Do not use this for a hashed SPA.
|
|
||||||
|
|
||||||
**`.github/workflows/cd-sam.yaml`** — Reusable CD workflow for SAM repos. Runs `sam build` + `sam deploy` with OIDC credentials and a CloudFormation execution role. Triggers via `workflow_call` from per-repo `deploy.yaml` on push to `main`.
|
**`.github/workflows/cd-sam.yaml`** — Reusable CD workflow for SAM repos. Runs `sam build` + `sam deploy` with OIDC credentials and a CloudFormation execution role. Triggers via `workflow_call` from per-repo `deploy.yaml` on push to `main`.
|
||||||
|
|
||||||
**`.github/workflows/cd-cdk.yaml`** — Reusable CD workflow for CDK repos (TypeScript and Python). Runs `cdk deploy --all` with OIDC credentials. Supports optional Python setup for Python CDK repos and QEMU emulation for cross-platform Docker builds.
|
**`.github/workflows/cd-cdk.yaml`** — Reusable CD workflow for CDK repos (TypeScript and Python). Runs `cdk deploy --all` with OIDC credentials. Supports optional Python setup for Python CDK repos and QEMU emulation for cross-platform Docker builds.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
|
||||||
}
|
|
||||||
|
|
@ -1051,6 +1051,146 @@ Resources:
|
||||||
Resource:
|
Resource:
|
||||||
- !GetAtt SamCfnExecutionRole.Arn
|
- !GetAtt SamCfnExecutionRole.Arn
|
||||||
|
|
||||||
|
FrontIntegrationsDeployRole:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: githubdeploy-front-integrations
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/front-integrations:ref:refs/heads/main
|
||||||
|
Policies:
|
||||||
|
- PolicyName: sam-deploy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:CreateChangeSet
|
||||||
|
- cloudformation:DeleteChangeSet
|
||||||
|
- cloudformation:DescribeChangeSet
|
||||||
|
- cloudformation:DescribeStackEvents
|
||||||
|
- cloudformation:DescribeStacks
|
||||||
|
- cloudformation:ExecuteChangeSet
|
||||||
|
- cloudformation:GetTemplate
|
||||||
|
- cloudformation:ListStackResources
|
||||||
|
- cloudformation:UpdateStack
|
||||||
|
- cloudformation:CreateStack
|
||||||
|
- cloudformation:TagResource
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:cloudformation:us-east-1:${AWS::AccountId}:stack/front-integrations/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:GetTemplateSummary
|
||||||
|
Resource: "*"
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:DescribeStacks
|
||||||
|
- cloudformation:CreateChangeSet
|
||||||
|
- cloudformation:DescribeChangeSet
|
||||||
|
- cloudformation:ExecuteChangeSet
|
||||||
|
- cloudformation:CreateStack
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:cloudformation:us-east-1:${AWS::AccountId}:stack/aws-sam-cli-managed-default/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- s3:PutObject
|
||||||
|
- s3:GetObject
|
||||||
|
- s3:ListBucket
|
||||||
|
- s3:GetBucketLocation
|
||||||
|
- s3:CreateBucket
|
||||||
|
- s3:PutBucketPolicy
|
||||||
|
- s3:GetBucketPolicy
|
||||||
|
- s3:PutLifecycleConfiguration
|
||||||
|
- s3:PutBucketVersioning
|
||||||
|
- s3:DeleteObject
|
||||||
|
Resource:
|
||||||
|
- arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*
|
||||||
|
- arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- iam:PassRole
|
||||||
|
Resource:
|
||||||
|
- !GetAtt SamCfnExecutionRole.Arn
|
||||||
|
|
||||||
|
AfiBackupMonitorDeployRole:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: githubdeploy-afi-backup-monitor
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/afi-backup-monitor:ref:refs/heads/main
|
||||||
|
Policies:
|
||||||
|
- PolicyName: sam-deploy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:CreateChangeSet
|
||||||
|
- cloudformation:DeleteChangeSet
|
||||||
|
- cloudformation:DescribeChangeSet
|
||||||
|
- cloudformation:DescribeStackEvents
|
||||||
|
- cloudformation:DescribeStacks
|
||||||
|
- cloudformation:ExecuteChangeSet
|
||||||
|
- cloudformation:GetTemplate
|
||||||
|
- cloudformation:ListStackResources
|
||||||
|
- cloudformation:UpdateStack
|
||||||
|
- cloudformation:CreateStack
|
||||||
|
- cloudformation:TagResource
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:cloudformation:us-east-1:${AWS::AccountId}:stack/afi-backup-monitor/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:GetTemplateSummary
|
||||||
|
Resource: "*"
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- cloudformation:DescribeStacks
|
||||||
|
- cloudformation:CreateChangeSet
|
||||||
|
- cloudformation:DescribeChangeSet
|
||||||
|
- cloudformation:ExecuteChangeSet
|
||||||
|
- cloudformation:CreateStack
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:cloudformation:us-east-1:${AWS::AccountId}:stack/aws-sam-cli-managed-default/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- s3:PutObject
|
||||||
|
- s3:GetObject
|
||||||
|
- s3:ListBucket
|
||||||
|
- s3:GetBucketLocation
|
||||||
|
- s3:CreateBucket
|
||||||
|
- s3:PutBucketPolicy
|
||||||
|
- s3:GetBucketPolicy
|
||||||
|
- s3:PutLifecycleConfiguration
|
||||||
|
- s3:PutBucketVersioning
|
||||||
|
- s3:DeleteObject
|
||||||
|
Resource:
|
||||||
|
- arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*
|
||||||
|
- arn:aws:s3:::aws-sam-cli-managed-default-samclisourcebucket-*/*
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- iam:PassRole
|
||||||
|
Resource:
|
||||||
|
- !GetAtt SamCfnExecutionRole.Arn
|
||||||
|
|
||||||
PaymentsDashboardDeployRole:
|
PaymentsDashboardDeployRole:
|
||||||
Type: AWS::IAM::Role
|
Type: AWS::IAM::Role
|
||||||
Properties:
|
Properties:
|
||||||
|
|
@ -1122,12 +1262,90 @@ Resources:
|
||||||
- !GetAtt SamCfnExecutionRole.Arn
|
- !GetAtt SamCfnExecutionRole.Arn
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# CDK deploy role for seahaven-org-baseline.
|
# CDK deploy roles (4 repos)
|
||||||
# Soaked githubdeploy roles for front-integrations, afi-backup-monitor,
|
|
||||||
# exec-aide, seahaven-door-unlock-api, and apm-wo-analysis are removed
|
|
||||||
# here (PLAT-232). Deploying this stack deletes those roles.
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ExecAideDeployRole:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: githubdeploy-exec-aide
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/exec-aide:ref:refs/heads/main
|
||||||
|
Policies:
|
||||||
|
- PolicyName: cdk-deploy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- sts:AssumeRole
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-*
|
||||||
|
|
||||||
|
SeahavenDoorUnlockApiDeployRole:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: githubdeploy-seahaven-door-unlock-api
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/seahaven-door-unlock-api:ref:refs/heads/main
|
||||||
|
Policies:
|
||||||
|
- PolicyName: cdk-deploy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- sts:AssumeRole
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-*
|
||||||
|
|
||||||
|
ApmWoAnalysisDeployRole:
|
||||||
|
Type: AWS::IAM::Role
|
||||||
|
Properties:
|
||||||
|
RoleName: githubdeploy-apm-wo-analysis
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
||||||
|
Action: sts:AssumeRoleWithWebIdentity
|
||||||
|
Condition:
|
||||||
|
StringEquals:
|
||||||
|
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
||||||
|
StringLike:
|
||||||
|
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/apm-wo-analysis:ref:refs/heads/main
|
||||||
|
Policies:
|
||||||
|
- PolicyName: cdk-deploy
|
||||||
|
PolicyDocument:
|
||||||
|
Version: "2012-10-17"
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- sts:AssumeRole
|
||||||
|
Resource:
|
||||||
|
- !Sub arn:aws:iam::${AWS::AccountId}:role/cdk-hnb659fds-*
|
||||||
|
|
||||||
SeahavenAccountBaselineDeployRole:
|
SeahavenAccountBaselineDeployRole:
|
||||||
Type: AWS::IAM::Role
|
Type: AWS::IAM::Role
|
||||||
Properties:
|
Properties:
|
||||||
|
|
@ -1162,54 +1380,6 @@ Resources:
|
||||||
# Mgmt role github-meal-order-manager-weekly-menu deleted with this stack update.
|
# Mgmt role github-meal-order-manager-weekly-menu deleted with this stack update.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PLAT-234 principal only. The checks are seahaven-org-baseline pull request
|
|
||||||
# 160: .github/workflows/ci.yaml job iam-policy-check and
|
|
||||||
# scripts/check_iam_policies.py. That job assumes this role. It asserts
|
|
||||||
# StringEquals on the bootstrap trust templates, no lambda write on the
|
|
||||||
# plan template, then ValidatePolicy and CheckNoNewAccess when this role
|
|
||||||
# can be assumed.
|
|
||||||
SeahavenOrgBaselinePolicyCheckRole:
|
|
||||||
Type: AWS::IAM::Role
|
|
||||||
Properties:
|
|
||||||
RoleName: githubdeploy-seahaven-org-baseline-policy-check
|
|
||||||
Description: Access Analyzer policy checks for seahaven-org-baseline CI. No deploy permissions.
|
|
||||||
AssumeRolePolicyDocument:
|
|
||||||
Version: "2012-10-17"
|
|
||||||
Statement:
|
|
||||||
- Effect: Allow
|
|
||||||
Principal:
|
|
||||||
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
|
||||||
Action: sts:AssumeRoleWithWebIdentity
|
|
||||||
# pull_request jobs with no environment use sub
|
|
||||||
# repo:ORG/seahaven-org-baseline:pull_request. refs/pull/N/merge is
|
|
||||||
# the ref claim, not sub. A second statement is required: StringEquals
|
|
||||||
# and StringLike in one condition are AND.
|
|
||||||
Condition:
|
|
||||||
StringEquals:
|
|
||||||
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
|
||||||
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/seahaven-org-baseline:pull_request
|
|
||||||
- Effect: Allow
|
|
||||||
Principal:
|
|
||||||
Federated: !Sub arn:aws:iam::${AWS::AccountId}:oidc-provider/token.actions.githubusercontent.com
|
|
||||||
Action: sts:AssumeRoleWithWebIdentity
|
|
||||||
Condition:
|
|
||||||
StringEquals:
|
|
||||||
token.actions.githubusercontent.com:aud: sts.amazonaws.com
|
|
||||||
StringLike:
|
|
||||||
token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/seahaven-org-baseline:ref:refs/heads/gh-readonly-queue/main/*
|
|
||||||
Policies:
|
|
||||||
- PolicyName: access-analyzer-policy-check
|
|
||||||
PolicyDocument:
|
|
||||||
Version: "2012-10-17"
|
|
||||||
Statement:
|
|
||||||
- Sid: AccessAnalyzerPolicyCheck
|
|
||||||
Effect: Allow
|
|
||||||
Action:
|
|
||||||
- access-analyzer:ValidatePolicy
|
|
||||||
- access-analyzer:CheckNoNewAccess
|
|
||||||
Resource: "*"
|
|
||||||
|
|
||||||
Outputs:
|
Outputs:
|
||||||
LambdaExecutionBoundaryArn:
|
LambdaExecutionBoundaryArn:
|
||||||
Value: !Ref LambdaExecutionBoundary
|
Value: !Ref LambdaExecutionBoundary
|
||||||
|
|
@ -1224,20 +1394,24 @@ Outputs:
|
||||||
Name: github-cfn-execution-role-arn
|
Name: github-cfn-execution-role-arn
|
||||||
AfterhoursShiftManagerDeployRoleArn:
|
AfterhoursShiftManagerDeployRoleArn:
|
||||||
Value: !GetAtt AfterhoursShiftManagerDeployRole.Arn
|
Value: !GetAtt AfterhoursShiftManagerDeployRole.Arn
|
||||||
|
FrontIntegrationsDeployRoleArn:
|
||||||
|
Value: !GetAtt FrontIntegrationsDeployRole.Arn
|
||||||
|
AfiBackupMonitorDeployRoleArn:
|
||||||
|
Value: !GetAtt AfiBackupMonitorDeployRole.Arn
|
||||||
PaymentsDashboardDeployRoleArn:
|
PaymentsDashboardDeployRoleArn:
|
||||||
Value: !GetAtt PaymentsDashboardDeployRole.Arn
|
Value: !GetAtt PaymentsDashboardDeployRole.Arn
|
||||||
# SeahavenSlackBotDeployRoleArn removed 2026-07-27: the role was deleted
|
# SeahavenSlackBotDeployRoleArn removed 2026-07-27: the role was deleted
|
||||||
# out-of-band on 2026-07-23, so this !GetAtt failed as a live IAM read and
|
# out-of-band on 2026-07-23, so this !GetAtt failed as a live IAM read and
|
||||||
# broke every stack update. Nothing imported it (the Output had no
|
# broke every stack update. Nothing imported it (the Output had no
|
||||||
# ExportName, and no stack imports any export from this stack).
|
# ExportName, and no stack imports any export from this stack).
|
||||||
|
ExecAideDeployRoleArn:
|
||||||
|
Value: !GetAtt ExecAideDeployRole.Arn
|
||||||
|
SeahavenDoorUnlockApiDeployRoleArn:
|
||||||
|
Value: !GetAtt SeahavenDoorUnlockApiDeployRole.Arn
|
||||||
# ProcurementIngestDeployRoleArn removed 2026-08-07 (PLAT-88): HCP sole
|
# ProcurementIngestDeployRoleArn removed 2026-08-07 (PLAT-88): HCP sole
|
||||||
# mutate path; prod githubdeploy role deleted; mgmt twin already gone.
|
# mutate path; prod githubdeploy role deleted; mgmt twin already gone.
|
||||||
# FrontIntegrations, AfiBackupMonitor, ExecAide, SeahavenDoorUnlockApi,
|
ApmWoAnalysisDeployRoleArn:
|
||||||
# and ApmWoAnalysis deploy role outputs removed 2026-09-28 (PLAT-232).
|
Value: !GetAtt ApmWoAnalysisDeployRole.Arn
|
||||||
# CloudTrail showed no successful mutation for 14 days. The roles are
|
|
||||||
# deleted only when this stack is deployed. That deploy is not this change.
|
|
||||||
SeahavenAccountBaselineDeployRoleArn:
|
SeahavenAccountBaselineDeployRoleArn:
|
||||||
Value: !GetAtt SeahavenAccountBaselineDeployRole.Arn
|
Value: !GetAtt SeahavenAccountBaselineDeployRole.Arn
|
||||||
SeahavenOrgBaselinePolicyCheckRoleArn:
|
|
||||||
Value: !GetAtt SeahavenOrgBaselinePolicyCheckRole.Arn
|
|
||||||
# MealOrderManagerWeeklyMenuRoleArn removed 2026-08-20 (PLAT-70): role deleted with resource.
|
# MealOrderManagerWeeklyMenuRoleArn removed 2026-08-20 (PLAT-70): role deleted with resource.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue