Compare commits

...

9 commits

Author SHA1 Message Date
renovate[bot]
feb5f2655f
chore(deps): update npm minor and patch 2026-09-24 15:47:36 +00:00
Adam Moussa
fdf595ea19
ci(workflows): use autofix formatter presets (PLAT-222) (#216)
* ci(workflows): use autofix formatter presets (PLAT-222)

Point autofix at the presets commit so ruff and Terraform fmt use the
org presets, and write the template Prettier and ESLint fixes CI checks.

* style: add an unused import for autofix

Deliberate F401 in week_keys so the ruff preset can remove it.

* ci(workflows): pin autofix to v1.0.15 (PLAT-222)

The previous pin was the pre-squash commit, which became unreachable
when that branch was deleted, so CI failed before autofix started.

* style: apply formatter

---------

Co-authored-by: sea-haven-auto-fix[bot] <5037331+sea-haven-auto-fix[bot]@users.noreply.github.com>
2026-09-24 00:05:36 +00:00
Adam Moussa
f7957436bd
fix(iam): drop githubdeploy workflow_ref OIDC condition (PLAT-222) (#215)
Some checks failed
Deploy API / Deploy API to dev (push) Has been cancelled
Deploy API / Deploy API to prod (push) Has been cancelled
* fix(iam): drop githubdeploy workflow_ref OIDC condition

AWS STS does not evaluate GitHub workflow_ref, so that trust condition fail-closes AssumeRoleWithWebIdentity.

* chore(security): retarget githubdeploy Checkov suppression

Dropping the workflow_ref trust condition shifted CKV_AWS_111 from line 49 to 42. Permissions are unchanged.

* style: apply formatter

---------

Co-authored-by: sea-haven-auto-fix[bot] <5037331+sea-haven-auto-fix[bot]@users.noreply.github.com>
2026-09-22 21:30:25 +00:00
Adam Moussa
f632020b20
ci: convert onto org HCP reusables (PLAT-222) (#214)
Some checks are pending
Deploy API / Deploy API to dev (push) Waiting to run
Deploy API / Deploy API to prod (push) Waiting to run
* ci: convert onto org HCP reusables

Switch Fargate CD and CI to the v1.0.13 org workflows, emit ci-complete, and retarget githubdeploy OIDC to the reusable plus the thin caller.

* chore(security): retarget githubdeploy Checkov suppression

The OIDC dual-claim edit shifted CKV_AWS_111 from line 40 to 49. Permissions are unchanged.

* style: apply formatter

* ci: pin org reusables to v1.0.14

Drop collect-only and requirements from the python lint caller now that ci-python-app is lint-only.

* test(ci): probe autofix with a ruff format violation

* style: apply formatter

---------

Co-authored-by: sea-haven-auto-fix[bot] <5037331+sea-haven-auto-fix[bot]@users.noreply.github.com>
2026-09-22 20:01:17 +00:00
Adam Moussa
7574fc471a
chore(ci): remove unused Mergify stub (#213)
Some checks are pending
Deploy API / Resolve target (push) Waiting to run
Deploy API / Deploy API to (push) Blocked by required conditions
2026-09-22 18:41:55 +00:00
renovate[bot]
63b31fcdc3
chore(deps): update terraform random to v3.9.1 (#211)
Some checks are pending
Deploy API / Resolve target (push) Waiting to run
Deploy API / Deploy API to (push) Blocked by required conditions
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-22 14:33:14 +00:00
renovate[bot]
449cc10b9f
chore(deps): update dependency boto3 to v1.43.98 (#208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-22 14:31:22 +00:00
renovate[bot]
78f6d1dbe4
chore(deps): update aws-actions/configure-aws-credentials action to v6.3.0 (#209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-22 14:31:10 +00:00
renovate[bot]
77780899c2
chore(deps): pin python docker tag to 2f17fc0 (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-22 14:24:50 +00:00
18 changed files with 150 additions and 274 deletions

View file

@ -1,32 +0,0 @@
name: Terraform CI
on:
pull_request:
branches: [main]
paths:
- "terraform/**"
- ".github/workflows/ci-terraform.yaml"
permissions:
contents: read
jobs:
terraform:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1
with:
terraform_version: "1.9.8"
- name: Terraform fmt
run: terraform fmt -check -recursive
- name: Terraform init
run: terraform init -backend=false
- name: Terraform validate
run: terraform validate

View file

@ -1,21 +1,38 @@
name: CI
on:
pull_request:
branches: [main]
branches: [main, hotfix/**, release/**]
merge_group:
push:
branches: [hotfix/**, release/**]
permissions:
contents: read
jobs:
ci:
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-app.yaml@9781774f04b824b1182ff41638687f1c01c04361 # v1.0.11
autofix:
if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
uses: Sea-Haven-Industries/.github/.github/workflows/ci-autofix.yaml@61f15d78b53e7f3f13dc21316c24bb27db472fd2 # v1.0.15
permissions:
contents: write
secrets: inherit
with:
presets: ruff,terraform
terraform-version: "1.9.8"
node-version: "24.19.0"
format-command: npm run format:templates:write
lint-fix-command: npx eslint "src/server/templates/*.js" --fix
lint:
needs: autofix
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
uses: Sea-Haven-Industries/.github/.github/workflows/ci-python-app.yaml@fd4113241058d4709f80d5c29f7d5c43030ae6fa # v1.0.14
with:
python-version: "3.12.14"
requirements: "requirements-api.txt"
collect-only: false
template-js:
needs: autofix
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
@ -38,6 +55,8 @@ jobs:
run: npm run openapi:lint
test:
needs: autofix
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
@ -63,3 +82,30 @@ jobs:
- name: Run tests
run: pytest
terraform:
needs: autofix
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
uses: Sea-Haven-Industries/.github/.github/workflows/ci-terraform.yaml@fd4113241058d4709f80d5c29f7d5c43030ae6fa # v1.0.14
with:
terraform-version: "1.9.8"
ci-complete:
name: ci-complete
needs: [autofix, lint, template-js, test, terraform]
if: always() && !cancelled() && (needs.autofix.result == 'skipped' || needs.autofix.outputs.committed != 'true')
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Require portions
env:
LINT: ${{ needs.lint.result }}
TEMPLATE_JS: ${{ needs.template-js.result }}
TEST: ${{ needs.test.result }}
TERRAFORM: ${{ needs.terraform.result }}
run: |
set -euo pipefail
test "${LINT}" = success
test "${TEMPLATE_JS}" = success
test "${TEST}" = success
test "${TERRAFORM}" = success

View file

@ -1,8 +1,8 @@
name: Deploy API
# Fargate image CD (PLAT-215). GitHub Actions builds the Flask image, pushes
# to ECR, and registers a new task definition. Terraform owns the cluster,
# service, ALB, and ignores container_definitions / task_definition.
# Fargate image CD. GitHub Actions builds the Flask image, pushes to ECR,
# and registers a new task definition. Terraform owns the cluster, service,
# ALB, and ignores container_definitions / task_definition.
#
# push to main -> dev, at github.sha
# release: published -> prod, at the release tag
@ -20,7 +20,6 @@ on:
- "*.md"
- ".github/workflows/weekly-menu.yml"
- ".github/workflows/ci.yml"
- ".github/workflows/ci-terraform.yaml"
release:
types: [published]
workflow_dispatch:
@ -40,199 +39,33 @@ permissions:
contents: read
jobs:
target:
name: Resolve target
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
environment: ${{ steps.resolve.outputs.environment }}
ref: ${{ steps.resolve.outputs.ref }}
steps:
- id: resolve
env:
EVENT_NAME: ${{ github.event_name }}
GITHUB_REF_NAME_IN: ${{ github.ref }}
GITHUB_SHA_IN: ${{ github.sha }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
INPUT_ENVIRONMENT: ${{ inputs.environment }}
INPUT_REF: ${{ inputs.ref }}
run: |
set -euo pipefail
case "${EVENT_NAME}" in
push)
if [ "${GITHUB_REF_NAME_IN}" != "refs/heads/main" ]; then
echo "push deploys only run from main" >&2
exit 1
fi
environment=dev
ref="${GITHUB_SHA_IN}"
;;
release)
environment=prod
ref="${RELEASE_TAG}"
status="$(gh api "repos/${REPO}/compare/main...${RELEASE_TAG}" --jq .status)"
if [ "${status}" != "behind" ] && [ "${status}" != "identical" ]; then
echo "release tag ${RELEASE_TAG} is not on main (compare status: ${status})" >&2
exit 1
fi
;;
workflow_dispatch)
environment="${INPUT_ENVIRONMENT}"
ref="${INPUT_REF:-${GITHUB_SHA_IN}}"
;;
*)
echo "unsupported event ${EVENT_NAME}" >&2
exit 1
;;
esac
{
echo "environment=${environment}"
echo "ref=${ref}"
} >> "${GITHUB_OUTPUT}"
echo "Deploying ${ref} to ${environment}"
deploy:
name: Deploy API to ${{ needs.target.outputs.environment }}
needs: target
runs-on: ubuntu-latest
timeout-minutes: 30
environment: ${{ needs.target.outputs.environment }}
concurrency:
group: deploy-api-${{ needs.target.outputs.environment }}
cancel-in-progress: false
deploy-dev:
name: Deploy API to dev
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'dev')
uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@fd4113241058d4709f80d5c29f7d5c43030ae6fa # v1.0.14
permissions:
contents: read
id-token: write
env:
AWS_REGION: us-east-1
DEPLOY_ROLE_ARN: ${{ vars.DEPLOY_ROLE_ARN }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.target.outputs.ref }}
persist-credentials: false
secrets: inherit
with:
environment: dev
ref: ${{ inputs.ref }}
ssm-prefix: /meal-order-manager/deploy
docker-platform: linux/amd64
extra-task-env: '{"SENTRY_DSN_PARAM":"/meal-order-manager/sentry-dsn"}'
- name: Resolve commit
id: commit
run: |
set -euo pipefail
sha="$(git rev-parse HEAD)"
echo "sha=${sha}" >> "${GITHUB_OUTPUT}"
echo "Building ${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
run: |
set -euo pipefail
get_param() {
aws ssm get-parameter --name "$1" --query Parameter.Value --output text
}
CLUSTER=$(get_param /meal-order-manager/deploy/cluster)
SERVICE=$(get_param /meal-order-manager/deploy/service)
FAMILY=$(get_param /meal-order-manager/deploy/task-family)
ECR=$(get_param /meal-order-manager/deploy/ecr-repository)
CONTAINER=$(get_param /meal-order-manager/deploy/container-name)
API_URL=$(get_param /meal-order-manager/deploy/api-url)
{
echo "cluster=${CLUSTER}"
echo "service=${SERVICE}"
echo "family=${FAMILY}"
echo "ecr=${ECR}"
echo "container=${CONTAINER}"
echo "api_url=${API_URL}"
} >> "${GITHUB_OUTPUT}"
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@03f1aad4c6c7ffd436567f42f9384779290529bd # v2.1.7
- name: Build and push image
env:
ECR: ${{ steps.deploy.outputs.ecr }}
GIT_SHA: ${{ steps.commit.outputs.sha }}
ENVIRONMENT: ${{ needs.target.outputs.environment }}
run: |
set -euo pipefail
docker build \
--build-arg "GIT_SHA=${GIT_SHA}" \
-t "${ECR}:${GIT_SHA}" \
-t "${ECR}:${ENVIRONMENT}" \
.
docker push "${ECR}:${GIT_SHA}"
docker push "${ECR}:${ENVIRONMENT}"
- name: Register task definition and update service
env:
CLUSTER: ${{ steps.deploy.outputs.cluster }}
SERVICE: ${{ steps.deploy.outputs.service }}
FAMILY: ${{ steps.deploy.outputs.family }}
CONTAINER: ${{ steps.deploy.outputs.container }}
IMAGE: ${{ steps.deploy.outputs.ecr }}:${{ steps.commit.outputs.sha }}
GIT_SHA: ${{ steps.commit.outputs.sha }}
run: |
set -euo pipefail
aws ecs describe-task-definition \
--task-definition "${FAMILY}" \
--query taskDefinition \
--output json \
| python3 -c '
import json, os, sys
td = json.load(sys.stdin)
for key in (
"taskDefinitionArn",
"revision",
"status",
"requiresAttributes",
"compatibilities",
"registeredAt",
"registeredBy",
"deregisteredAt",
):
td.pop(key, None)
image = os.environ["IMAGE"]
sha = os.environ["GIT_SHA"]
name = os.environ["CONTAINER"]
for container in td["containerDefinitions"]:
if container["name"] != name:
continue
container["image"] = image
env = {item["name"]: item["value"] for item in container.get("environment", [])}
env["GIT_SHA"] = sha
env["SENTRY_DSN_PARAM"] = "/meal-order-manager/sentry-dsn"
container["environment"] = [{"name": key, "value": value} for key, value in env.items()]
container.pop("command", None)
json.dump(td, sys.stdout)
' > /tmp/task-def.json
REV="$(aws ecs register-task-definition --cli-input-json file:///tmp/task-def.json --query taskDefinition.revision --output text)"
aws ecs update-service \
--cluster "${CLUSTER}" \
--service "${SERVICE}" \
--task-definition "${FAMILY}:${REV}" \
--force-new-deployment \
>/dev/null
aws ecs wait services-stable --cluster "${CLUSTER}" --services "${SERVICE}"
- name: Verify health SHA
env:
API_URL: ${{ steps.deploy.outputs.api_url }}
EXPECTED_SHA: ${{ steps.commit.outputs.sha }}
run: |
set -euo pipefail
for _ in 1 2 3 4 5 6; do
BODY="$(curl -fsS "${API_URL}/api/health" || true)"
echo "${BODY}"
if echo "${BODY}" | python3 -c 'import json,os,sys; d=json.load(sys.stdin); sys.exit(0 if d.get("sha")==os.environ["EXPECTED_SHA"] else 1)'; then
exit 0
fi
sleep 10
done
echo "health SHA did not match ${EXPECTED_SHA}" >&2
exit 1
deploy-prod:
name: Deploy API to prod
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'prod')
uses: Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@fd4113241058d4709f80d5c29f7d5c43030ae6fa # v1.0.14
permissions:
contents: read
id-token: write
secrets: inherit
with:
environment: prod
ref: ${{ github.event.release.tag_name || inputs.ref }}
ssm-prefix: /meal-order-manager/deploy
docker-platform: linux/amd64
ship-gate: true
extra-task-env: '{"SENTRY_DSN_PARAM":"/meal-order-manager/sentry-dsn"}'

View file

@ -55,7 +55,7 @@ jobs:
- name: Configure AWS credentials
if: env.SKIP_RUN != 'true'
uses: aws-actions/configure-aws-credentials@cbe3b392738ccf3f987d68400dafcf4b0624a56c # v6.2.4
uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
with:
role-to-assume: ${{ secrets.AWS_WEEKLY_MENU_ROLE_ARN }}
aws-region: us-east-1

View file

@ -1 +0,0 @@
extends: .github

View file

@ -9,8 +9,8 @@
"justification": "The meals API ALB is the CloudFront HTTP origin for orders.seahaven.com. TLS and WAF terminate at CloudFront. Restricting the security group to the CloudFront managed prefix list would block GitHub-hosted weekly-menu HMAC publish, which must call the origin with X-Meals-Publish-Key. Application gates are HMAC on /api/publish, Cognito or Google Bearer on admin, and public submit only. Accepted as the HTTP-origin design for PLAT-215; TLS on the ALB is a follow-up."
},
{
"id": "checkov-CKV_AWS_111-40",
"justification": "githubdeploy-meal-order-manager needs ecr:GetAuthorizationToken and ecs:RegisterTaskDefinition, both of which AWS documents as Resource *. Trust is pinned to environment:dev or environment:prod plus job_workflow_ref on deploy-api.yaml at refs/heads/main or refs/tags/v*. iam:PassRole is limited to the ECS task and execution roles. ECR push and SSM parameter reads are already resource-scoped."
"id": "checkov-CKV_AWS_111-42",
"justification": "LINE SHIFT ONLY: dropping the workflow_ref trust condition shifts github_deploy from 49 to 42. The permission document is unchanged. Original justification: githubdeploy-meal-order-manager needs ecr:GetAuthorizationToken and ecs:RegisterTaskDefinition, both of which AWS documents as Resource *. Trust is pinned to environment:dev or environment:prod and job_workflow_ref on org cd-hcp-fargate.yaml@*. iam:PassRole is limited to the ECS task and execution roles. ECR push and SSM parameter reads are already resource-scoped."
}
]
}

View file

@ -1,4 +1,4 @@
FROM python:3.12-slim
FROM python:3.12-slim@sha256:2f17fc044b579bab302c2e8054d3a686e2cb9a83de48e70534b94cd8ebbe06a9
WORKDIR /app
COPY src/shared/requirements.txt /tmp/shared-requirements.txt

View file

@ -188,8 +188,7 @@ meal-order-manager/
├── .github/workflows/
│ ├── weekly-menu.yml # Monday cron: scrape + HMAC publish + notify
│ ├── deploy-api.yaml # Image CD to Fargate
│ ├── ci.yml # PR checks (pytest, template JS, OpenAPI lint)
│ └── ci-terraform.yaml # terraform fmt / validate
│ └── ci.yml # PR checks (lint, pytest, template JS, terraform, ci-complete)
├── terraform/ # HCP Terraform (cluster, ALB, ECR, jobs queue)
├── openapi.yaml # Employee HTTP contract (Redocly recommended)
├── .redocly.yaml

16
package-lock.json generated
View file

@ -9,8 +9,8 @@
"version": "1.0.0",
"devDependencies": {
"@eslint/js": "10.0.1",
"@redocly/cli": "2.52.1",
"eslint": "10.10.0",
"@redocly/cli": "2.53.3",
"eslint": "10.11.0",
"globals": "17.12.0",
"prettier": "3.9.8"
}
@ -258,9 +258,9 @@
"license": "MIT"
},
"node_modules/@redocly/cli": {
"version": "2.52.1",
"resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.52.1.tgz",
"integrity": "sha512-gwfx1WelVDHU/cw+GkpLV9+xksOI3obJGgL1nUVrAFUyXNDxM+aifXDUZk7BfbOIpF69Sea2ant0cOqbQfg/KQ==",
"version": "2.53.3",
"resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-2.53.3.tgz",
"integrity": "sha512-hzNAWzHCOZ05vwRx0ehTxNeJaxxizjGV505eKtfs9MR/8ieD/8lYhAK4GF5FaqRRwOEpLO0PvzQwBSKYXmxhRA==",
"dev": true,
"license": "MIT",
"bin": {
@ -424,9 +424,9 @@
}
},
"node_modules/eslint": {
"version": "10.10.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.10.0.tgz",
"integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==",
"version": "10.11.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz",
"integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==",
"dev": true,
"license": "MIT",
"workspaces": [

View file

@ -11,8 +11,8 @@
},
"devDependencies": {
"@eslint/js": "10.0.1",
"@redocly/cli": "2.52.1",
"eslint": "10.10.0",
"@redocly/cli": "2.53.3",
"eslint": "10.11.0",
"globals": "17.12.0",
"prettier": "3.9.8"
}

View file

@ -1,6 +1,6 @@
flask==3.1.3
gunicorn==23.0.0
boto3==1.43.97
boto3==1.43.98
sentry-sdk==2.69.2
jinja2==3.1.6
fpdf2==2.8.8

View file

@ -1,4 +1,4 @@
playwright==1.63.0
flask==3.1.3
boto3==1.43.97
boto3==1.43.98
jinja2==3.1.6

View file

@ -1,3 +1,3 @@
boto3==1.43.97
boto3==1.43.98
fpdf2==2.8.8
PyJWT[crypto]==2.14.0

View file

@ -40,23 +40,33 @@ provider "registry.terraform.io/hashicorp/aws" {
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.8.1"
constraints = "3.8.1"
version = "3.9.1"
constraints = "3.9.1"
hashes = [
"h1:Eexl06+6J+s75uD46+WnZtpJZYRVUMB0AiuPBifK6Jc=",
"h1:fdfOl1HabDT42XLH8qjmfTbVZpgQZ5lyOyOa+GQhm0w=",
"h1:u8AKlWVDTH5r9YLSeswoVEjiY72Rt4/ch7U+61ZDkiQ=",
"zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4",
"zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae",
"zh:229665ddf060aa0ed315597908483eee5b818a17d09b6417a0f52fd9405c4f57",
"zh:2469d2e48f28076254a2a3fc327f184914566d9e40c5780b8d96ebf7205f8bc0",
"zh:37d7eb334d9561f335e748280f5535a384a88675af9a9eac439d4cfd663bcb66",
"zh:741101426a2f2c52dee37122f0f4a2f2d6af6d852cb1db634480a86398fa3511",
"h1:0rmWNCsP90oLS9KtLiqcNmkRzsaOZD95THXyuGYUfOM=",
"h1:4AN630toDK+4Is2MQcbGA37yR56nIEDPO0qv8ivZD2M=",
"h1:7uiStw0Rl9KOdX5UNMG/sp9nyadoD4LZekQTiYlYPhE=",
"h1:9RiO3l/4iDguiVMryazTvf8ZAOAwbS0LPWl/XRo2El4=",
"h1:AjDEYpTXfyc3CupsrJYjgxCYnqxdpCLPkaLKPiw9WTA=",
"h1:PYbnOqRuGn4c0/Ae1f7yOS/0zvmXNHFJRZjuF4KECnM=",
"h1:PlW+UZ4EElQF3NQwf41KQwavFujab3Czc51zu9dyVM8=",
"h1:VGeIpAn+nL6i8a6Y1W7XIq+Z6XFIWu5al4f2PNageoo=",
"h1:g40qr7yDmIpaur4SsK5BcOda3HSo1RJ6zHVMqN4EJ+0=",
"h1:nozfr4CZq73d4HjubKJundX/8A+Mj282oS/hyNfjUPU=",
"h1:uFgaD8lhFrHzFm88V3/+wypq5AzCUdzXR60vLSlZ2cI=",
"h1:xxRd4yd9LvPiDqeiGQj7FhZHwD08tDcIToAAePfoahE=",
"zh:05f4734c1f0be840b711b3eff259ebc5fca436784c728955b1678078466f48d7",
"zh:0b91bf19371d012434eba1deeb6aab77158def9b39601dcbd94450b3974a2a26",
"zh:0ee6eacd47ec00183d55d726a4b6c4ce951a199f944bf22f1aa58392ebdfa7a2",
"zh:19388a4074b76a89a43a6c8328d7ae8ee2e7de3d346af51e80d3e6d3d12925f1",
"zh:23e74d48c5e2ac2e823fd527f49fee9db37d32a1990c9e3bf126ead697b843eb",
"zh:3cabf7fbd096c520064aae3aba61aba670af83ab91291a71fa1b1332929c2b7f",
"zh:5c0a3b8af0be60be4eca12ddee385cfa8babc1ec8e98cdf9de2f2274c73eabfa",
"zh:60b4f8a8ef18f52bf8e19215229dae408bee732825964092db7c989fd2de4097",
"zh:7359015acfedcbd6366f2329c854cf8d3c8ca5cd0faa89d2d37db358d6eba6c5",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:a902473f08ef8df62cfe6116bd6c157070a93f66622384300de235a533e9d4a9",
"zh:b85c511a23e57a2147355932b3b6dce2a11e856b941165793a0c3d7578d94d05",
"zh:c5172226d18eaac95b1daac80172287b69d4ce32750c82ad77fa0768be4ea4b8",
"zh:dab4434dba34aad569b0bc243c2d3f3ff86dd7740def373f2a49816bd2ff819b",
"zh:f49fd62aa8c5525a5c17abd51e27ca5e213881d58882fd42fec4a545b53c9699",
"zh:7b38758402f0e13a1071162da28994023cd2ac676e54af350c9ffd8dfa73fa7b",
"zh:7c7fbb8895eb75bb4de1f933e98553bd99c8d048c89a925ddba490aa5a67f7dc",
"zh:8c2b8c6a7ccdec16b73e2fb9f3700ea097f58c592571e4c5de60c93d2301732c",
]
}

View file

@ -1,4 +1,5 @@
# GitHub Actions OIDC role for .github/workflows/deploy-api.yaml.
# GitHub Actions OIDC role for the thin deploy-api.yaml caller of
# org reusable cd-hcp-fargate.yaml.
data "aws_iam_policy_document" "github_deploy_assume" {
statement {
@ -26,12 +27,13 @@ data "aws_iam_policy_document" "github_deploy_assume" {
]
}
# AWS STS GitHub condition keys include job_workflow_ref, not workflow_ref.
# A workflow_ref condition fail-closes AssumeRoleWithWebIdentity.
condition {
test = "StringLike"
variable = "token.actions.githubusercontent.com:job_workflow_ref"
values = [
"Sea-Haven-Industries/meal-order-manager/.github/workflows/deploy-api.yaml@refs/heads/main",
"Sea-Haven-Industries/meal-order-manager/.github/workflows/deploy-api.yaml@refs/tags/v*",
"Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@*",
]
}
}

View file

@ -8,7 +8,7 @@ terraform {
}
random = {
source = "hashicorp/random"
version = "3.8.1"
version = "3.9.1"
}
}

View file

@ -0,0 +1,18 @@
"""githubdeploy OIDC trust pins the org reusable with AWS-supported claims."""
from pathlib import Path
IAM = Path(__file__).resolve().parents[1] / "terraform" / "iam_github_deploy.tf"
def test_github_deploy_trust_uses_org_reusable_and_caller():
text = IAM.read_text()
assert "token.actions.githubusercontent.com:sub" in text
assert "repo:Sea-Haven-Industries/meal-order-manager:environment:dev" in text
assert "repo:Sea-Haven-Industries/meal-order-manager:environment:prod" in text
assert (
"Sea-Haven-Industries/.github/.github/workflows/cd-hcp-fargate.yaml@*" in text
)
assert "token.actions.githubusercontent.com:job_workflow_ref" in text
assert "token.actions.githubusercontent.com:workflow_ref" not in text
assert "cd-hcp-spa.yaml" not in text

View file

@ -36,4 +36,5 @@ def test_terraform_does_not_embed_a_sentry_dsn():
def test_deploy_api_injects_sentry_dsn_param():
workflow = (ROOT / ".github/workflows/deploy-api.yaml").read_text()
assert 'env["SENTRY_DSN_PARAM"] = "/meal-order-manager/sentry-dsn"' in workflow
assert "extra-task-env:" in workflow
assert '"SENTRY_DSN_PARAM":"/meal-order-manager/sentry-dsn"' in workflow