fix(ci): satisfy PR policy and authorizer trust constraints

Move weekly-menu step expressions into env blocks, terraform-fmt SES
alignment, and pin API Gateway authorizer invoke role assume conditions.
This commit is contained in:
Adam Moussa 2026-08-07 19:33:49 -04:00
parent 3906ad1885
commit 1f67543f16
No known key found for this signature in database
2 changed files with 44 additions and 17 deletions

View file

@ -28,8 +28,9 @@ jobs:
steps:
- name: Timezone guard
if: github.event_name == 'schedule'
env:
CRON: ${{ github.event.schedule }}
run: |
CRON="${{ github.event.schedule }}"
OFFSET=$(TZ='America/New_York' date +%z)
echo "Cron: $CRON | Eastern offset: $OFFSET"
if { [ "$OFFSET" = "-0400" ] && [ "$CRON" = "30 12 * * 1" ]; } || \
@ -92,9 +93,10 @@ jobs:
- name: Get discount settings
if: env.SKIP_RUN != 'true'
id: discount
env:
API_URL: ${{ steps.stack.outputs.api_url }}
run: |
SETTINGS=$(python3 scripts/upload_menu.py settings \
--api-url "${{ steps.stack.outputs.api_url }}")
SETTINGS=$(python3 scripts/upload_menu.py settings --api-url "$API_URL")
BULK=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["bulk_discount_percent"])' "$SETTINGS")
SUBSIDY=$(python3 -c 'import json,sys; print(json.loads(sys.argv[1])["company_subsidy_percent"])' "$SETTINGS")
echo "bulk_discount=$BULK" >> "$GITHUB_OUTPUT"
@ -112,42 +114,53 @@ jobs:
echo "Google client ID is required for cloud form generation" >&2
exit 1
fi
echo "client_id=$GOOGLE_CLIENT_ID" >> $GITHUB_OUTPUT
echo "client_id=$GOOGLE_CLIENT_ID" >> "$GITHUB_OUTPUT"
- name: Generate order form
if: env.SKIP_RUN != 'true'
env:
API_URL: ${{ steps.stack.outputs.api_url }}
BULK_DISCOUNT: ${{ steps.discount.outputs.bulk_discount }}
COMPANY_SUBSIDY: ${{ steps.discount.outputs.company_subsidy }}
GOOGLE_CLIENT_ID: ${{ steps.google.outputs.client_id }}
run: |
python3 src/server/generate_form.py \
--api-url "${{ steps.stack.outputs.api_url }}" \
--bulk-discount "${{ steps.discount.outputs.bulk_discount }}" \
--company-subsidy "${{ steps.discount.outputs.company_subsidy }}" \
--google-client-id "${{ steps.google.outputs.client_id }}"
--api-url "$API_URL" \
--bulk-discount "$BULK_DISCOUNT" \
--company-subsidy "$COMPANY_SUBSIDY" \
--google-client-id "$GOOGLE_CLIENT_ID"
- name: Publish menu through API
if: env.SKIP_RUN != 'true'
run: |
python3 scripts/upload_menu.py publish \
--api-url "${{ steps.stack.outputs.api_url }}"
env:
API_URL: ${{ steps.stack.outputs.api_url }}
run: python3 scripts/upload_menu.py publish --api-url "$API_URL"
- name: Upload form to S3
if: env.SKIP_RUN != 'true'
env:
FORM_BUCKET: ${{ steps.stack.outputs.form_bucket }}
run: |
WEEK=$(date +%Y-W%U)
aws s3 cp "output/order-form-$WEEK.html" \
"s3://${{ steps.stack.outputs.form_bucket }}/index.html" \
"s3://${FORM_BUCKET}/index.html" \
--content-type "text/html" \
--cache-control "no-cache"
aws s3 cp "output/order-form-$WEEK.html" \
"s3://${{ steps.stack.outputs.form_bucket }}/archive/$WEEK.html" \
"s3://${FORM_BUCKET}/archive/$WEEK.html" \
--content-type "text/html"
- name: Invalidate CloudFront cache
if: env.SKIP_RUN != 'true'
env:
DIST_ID: ${{ steps.stack.outputs.dist_id }}
run: |
aws cloudfront create-invalidation \
--distribution-id "${{ steps.stack.outputs.dist_id }}" \
--distribution-id "$DIST_ID" \
--paths "/index.html"
- name: Notify Slack
if: env.SKIP_RUN != 'true'
run: python3 scripts/notify_slack.py "${{ steps.stack.outputs.form_url }}"
env:
FORM_URL: ${{ steps.stack.outputs.form_url }}
run: python3 scripts/notify_slack.py "$FORM_URL"

View file

@ -174,6 +174,8 @@ resource "aws_iam_role_policy" "admin_authorizer" {
# Role API Gateway assumes to invoke the authorizer Lambda. The authorizer has
# no resource policy of its own; this identity-based grant is the only path.
# SourceAccount + execute-api ArnLike close the confused-deputy window without
# pinning the authorizer id (that would cycle: authorizer needs this role).
data "aws_iam_policy_document" "apigateway_assume" {
statement {
effect = "Allow"
@ -183,6 +185,18 @@ data "aws_iam_policy_document" "apigateway_assume" {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "aws:SourceAccount"
values = [local.account_id]
}
condition {
test = "ArnLike"
variable = "aws:SourceArn"
values = ["arn:aws:execute-api:${var.aws_region}:${local.account_id}:${aws_apigatewayv2_api.order_api.id}/*"]
}
}
}
@ -391,8 +405,8 @@ data "aws_iam_policy_document" "email_report" {
# Scope SendRawEmail to the verified sender domain/identity rather than "*".
# SES still enforces verification; IAM pins the From identity ARNs.
statement {
sid = "SendPayrollReport"
effect = "Allow"
sid = "SendPayrollReport"
effect = "Allow"
actions = ["ses:SendRawEmail"]
resources = [
"arn:aws:ses:${var.aws_region}:${local.account_id}:identity/${var.sender_email}",