mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-10-07 16:18:54 +00:00
Compare commits
9 commits
7ed6297e38
...
feb5f2655f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
feb5f2655f | ||
|
|
fdf595ea19 | ||
|
|
f7957436bd | ||
|
|
f632020b20 | ||
|
|
7574fc471a | ||
|
|
63b31fcdc3 | ||
|
|
449cc10b9f | ||
|
|
78f6d1dbe4 | ||
|
|
77780899c2 |
18 changed files with 150 additions and 274 deletions
32
.github/workflows/ci-terraform.yaml
vendored
32
.github/workflows/ci-terraform.yaml
vendored
|
|
@ -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
|
||||
56
.github/workflows/ci.yml
vendored
56
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
225
.github/workflows/deploy-api.yaml
vendored
225
.github/workflows/deploy-api.yaml
vendored
|
|
@ -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"}'
|
||||
|
|
|
|||
2
.github/workflows/weekly-menu.yml
vendored
2
.github/workflows/weekly-menu.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
extends: .github
|
||||
|
|
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
16
package-lock.json
generated
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
playwright==1.63.0
|
||||
flask==3.1.3
|
||||
boto3==1.43.97
|
||||
boto3==1.43.98
|
||||
jinja2==3.1.6
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
boto3==1.43.97
|
||||
boto3==1.43.98
|
||||
fpdf2==2.8.8
|
||||
PyJWT[crypto]==2.14.0
|
||||
|
|
|
|||
42
terraform/.terraform.lock.hcl
generated
42
terraform/.terraform.lock.hcl
generated
|
|
@ -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",
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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@*",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ terraform {
|
|||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "3.8.1"
|
||||
version = "3.9.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
18
tests/test_terraform_github_deploy.py
Normal file
18
tests/test_terraform_github_deploy.py
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue