Skip to content

Commit 346ca4a

Browse files
committed
ci: let the ruleset decide whether the merge is unattended
The approval gate added in b1670c3 was the wrong shape. It read reviewDecision and refused unless APPROVED, which is correct only while main's ruleset lists no bypass actors — the moment this workflow's identity becomes one, the gate would keep refusing merges the ruleset would have allowed, and the fix would be to remember to delete it. The merge is now attempted unconditionally and the outcome classified, so one script is right in both worlds: unattended where a bypass actor exists, and "blocked awaiting a code-owner approval (@aws/aws-lambda-tooling), or a ruleset bypass actor for this workflow" where none does. Nothing to keep in sync with a repository setting the script cannot see. BLOCKED is still separated from DIRTY/BEHIND/DRAFT/UNKNOWN, and now splits by reviewDecision so the three cases read differently: awaiting approval, approved but some other rule unsatisfied, and changes requested. It is no longer reported as "a sibling update landed first", which was never true for it. Seven paths exercised: unapproved without bypass, approved without bypass, unapproved with bypass, approved-but-blocked, changes-requested, a sibling conflict, and an unexplained failure still exiting 1.
1 parent b1670c3 commit 346ca4a

5 files changed

Lines changed: 210 additions & 30 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
#
3+
# One-time setup for a regional PipelineExecutionRole before enabling
4+
# publish-layer-parameter.sh in CI. Run this with administrator credentials in the
5+
# role's account; AWS_PROFILE and the standard AWS CLI credential variables are
6+
# honored.
7+
#
8+
# Usage:
9+
# configure-layer-parameter-iam.sh <pipeline-execution-role-arn> <region>
10+
set -euo pipefail
11+
export AWS_PAGER=""
12+
13+
ROLE_ARN="${1:?pipeline execution role ARN required}"
14+
REGION="${2:?region required}"
15+
16+
IFS=: read -r ARN_PREFIX PARTITION SERVICE ARN_REGION ACCOUNT_ID RESOURCE <<<"$ROLE_ARN"
17+
if [[ "$ARN_PREFIX" != "arn" || "$SERVICE" != "iam" || -n "$ARN_REGION" ||
18+
! "$ACCOUNT_ID" =~ ^[0-9]{12}$ || "$RESOURCE" != role/* ]]; then
19+
echo "Invalid IAM role ARN: $ROLE_ARN" >&2
20+
exit 2
21+
fi
22+
23+
ROLE_PATH="${RESOURCE#role/}"
24+
ROLE_NAME="${ROLE_PATH##*/}"
25+
CALLER_ACCOUNT="$(
26+
aws sts get-caller-identity \
27+
--region "$REGION" \
28+
--query Account \
29+
--output text
30+
)"
31+
32+
if [[ "$CALLER_ACCOUNT" != "$ACCOUNT_ID" ]]; then
33+
echo "AWS credentials are for account $CALLER_ACCOUNT, but $ROLE_ARN is in $ACCOUNT_ID" >&2
34+
exit 1
35+
fi
36+
37+
POLICY_DOCUMENT="$(
38+
jq -cn \
39+
--arg x86 "arn:${PARTITION}:ssm:${REGION}:${ACCOUNT_ID}:parameter/lambda-web-adapter/layer/x86_64/*" \
40+
--arg arm "arn:${PARTITION}:ssm:${REGION}:${ACCOUNT_ID}:parameter/lambda-web-adapter/layer/arm64/*" \
41+
'{
42+
Version: "2012-10-17",
43+
Statement: [{
44+
Sid: "PublishLambdaWebAdapterVersionedLayerParameters",
45+
Effect: "Allow",
46+
Action: "ssm:PutParameter",
47+
Resource: [$x86, $arm]
48+
}]
49+
}'
50+
)"
51+
52+
aws iam put-role-policy \
53+
--role-name "$ROLE_NAME" \
54+
--policy-name LambdaWebAdapterVersionedLayerParameters \
55+
--policy-document "$POLICY_DOCUMENT" \
56+
--region "$REGION"
57+
58+
echo "Granted ssm:PutParameter in $REGION to $ROLE_ARN"

.github/scripts/dependabot-automerge.sh

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#!/usr/bin/env bash
22
#
3-
# Merges one Dependabot pull request, if it is an example-only update that a code owner
4-
# has approved and that Verify Examples has verified at the pull request's current head.
3+
# Merges one Dependabot pull request, if it is an example-only update that Verify Examples
4+
# has verified at the pull request's current head.
55
#
6-
# Not auto-merge-on-green: main is governed by a ruleset requiring one code-owner approval
7-
# with zero bypass actors, so nothing can merge without a human. What this removes is the
8-
# second trip — approve once and the merge happens within the hour, but only if the
9-
# verification covers the exact commit being merged, so a stale approval cannot land an
10-
# unverified head.
6+
# Whether that merge is unattended depends on main's ruleset, not on this script. The
7+
# ruleset currently requires one code-owner approval and lists no bypass actors, so the
8+
# merge is refused until a human approves and the refusal is reported as exactly that.
9+
# Making this workflow's identity a bypass actor turns the same code into unattended
10+
# auto-merge, with the guards below as the only thing standing between a bump and main —
11+
# which is why they are what they are: example-only, verified at this exact head, every
12+
# check green.
1113
#
1214
# Usage: REPO=<owner/repo> dependabot-automerge.sh <pr-number>
1315
#
@@ -164,28 +166,13 @@ if [[ -n "$unverified" ]]; then
164166
skip "in the matrix but not verified by run $run_id: $(join_list "$unverified")"
165167
fi
166168

167-
# The approval is the last gate, and it is checked here rather than earlier on purpose:
168-
# reaching this line means the pull request is example-only, verified at its current head,
169-
# and green. Reporting it now makes the job summary a worklist of "verified, waiting only
170-
# on you" rather than a list of things that may also be unverified.
171-
#
172-
# main is governed by a ruleset (not classic branch protection, which is why
173-
# `branches/main/protection` returns 404): one approving review, `require_code_owner_review`,
174-
# and zero bypass actors, with .github/CODEOWNERS assigning `*` to @aws/aws-lambda-tooling.
175-
# No token can merge past that and no bot approval can satisfy it, so this workflow merges
176-
# after a human approves — it does not approve on anyone's behalf.
177-
review=$(jq -r '.reviewDecision // ""' <<<"$pr_json")
178-
case "$review" in
179-
APPROVED) ;;
180-
CHANGES_REQUESTED)
181-
skip "verified at ${head_sha:0:8} by run $run_id, but a reviewer requested changes."
182-
;;
183-
*)
184-
skip "verified at ${head_sha:0:8} by run $run_id — waiting for a code-owner approval (@aws/aws-lambda-tooling)."
185-
;;
186-
esac
187-
188-
echo "PR #$PR is example-only, verified at $head_sha by run $run_id, and approved. Merging."
169+
# Deliberately no approval gate of its own: the merge is attempted and the outcome
170+
# classified below. That way this one script behaves correctly whichever way main's
171+
# ruleset is configured — it merges unattended where the workflow is a bypass actor, and
172+
# reports "waiting for a code-owner approval" where it is not, with no toggle to keep in
173+
# sync with a repository setting it cannot see.
174+
review=$(jq -r '.reviewDecision // "NONE"' <<<"$pr_json")
175+
echo "PR #$PR is example-only and verified at $head_sha by run $run_id (reviewDecision=$review). Merging."
189176

190177
# --match-head-commit closes the remaining window: if the branch moves between the
191178
# lookups above and this call, the API rejects the merge rather than applying it to an
@@ -221,7 +208,21 @@ case "$state" in
221208
# reported as "a sibling update landed first", which was simply the wrong diagnosis:
222209
# main's ruleset blocks a merge until the required review is satisfied.
223210
BLOCKED)
224-
skip "merge rejected, blocked by main's ruleset (review or a required check) — reviewDecision was $review."
211+
# main's ruleset requires one code-owner approval (.github/CODEOWNERS assigns `*` to
212+
# @aws/aws-lambda-tooling) and lists no bypass actors, so this is the expected
213+
# outcome until either a human approves or this workflow's identity is made a bypass
214+
# actor. Named precisely, because it used to be reported as a sibling conflict.
215+
case "$review" in
216+
APPROVED)
217+
skip "merge rejected, blocked by main's ruleset even though it is approved — a required rule is unsatisfied."
218+
;;
219+
CHANGES_REQUESTED)
220+
skip "merge rejected, a reviewer requested changes."
221+
;;
222+
*)
223+
skip "verified at ${head_sha:0:8} by run $run_id — blocked awaiting a code-owner approval (@aws/aws-lambda-tooling), or a ruleset bypass actor for this workflow."
224+
;;
225+
esac
225226
;;
226227
DIRTY | BEHIND | DRAFT | UNKNOWN)
227228
skip "merge rejected, not mergeable (mergeStateStatus=$state) — most likely a sibling update landed first."
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Publishes the version-named SSM pointer after CloudFormation has deployed a layer.
4+
# The parameter deliberately lives outside the stack: retaining a
5+
# parameter whose Name contains the package version made CloudFormation collide with
6+
# the retained resource whenever a version was re-used.
7+
#
8+
# Usage:
9+
# publish-layer-parameter.sh <stack-name> <x86_64|arm64> <version> <region>
10+
set -euo pipefail
11+
export AWS_PAGER=""
12+
13+
STACK_NAME="${1:?stack name required}"
14+
ARCHITECTURE="${2:?architecture required}"
15+
VERSION="${3:?version required}"
16+
REGION="${4:?region required}"
17+
18+
case "$ARCHITECTURE" in
19+
x86_64)
20+
OUTPUT_KEY="LambdaAdapterLayerX86Arn"
21+
DESCRIPTION_ARCH="X86_64"
22+
;;
23+
arm64)
24+
OUTPUT_KEY="LambdaAdapterLayerArm64Arn"
25+
DESCRIPTION_ARCH="Arm64"
26+
;;
27+
*)
28+
echo "Unsupported architecture: $ARCHITECTURE" >&2
29+
exit 2
30+
;;
31+
esac
32+
33+
LAYER_ARN="$(
34+
aws cloudformation describe-stacks \
35+
--stack-name "$STACK_NAME" \
36+
--region "$REGION" \
37+
--query "Stacks[0].Outputs[?OutputKey=='${OUTPUT_KEY}'].OutputValue | [0]" \
38+
--output text
39+
)"
40+
41+
if [[ -z "$LAYER_ARN" || "$LAYER_ARN" == "None" ]]; then
42+
echo "Stack $STACK_NAME has no $OUTPUT_KEY output in $REGION" >&2
43+
exit 1
44+
fi
45+
46+
PARAMETER_NAME="/lambda-web-adapter/layer/${ARCHITECTURE}/${VERSION}"
47+
PARAMETER_VERSION="$(
48+
aws ssm put-parameter \
49+
--name "$PARAMETER_NAME" \
50+
--description "Layer ARN for the Lambda Web Adapter ${DESCRIPTION_ARCH} Layer: ${VERSION}" \
51+
--type String \
52+
--value "$LAYER_ARN" \
53+
--overwrite \
54+
--region "$REGION" \
55+
--query Version \
56+
--output text
57+
)"
58+
59+
echo "Published $PARAMETER_NAME -> $LAYER_ARN (parameter version $PARAMETER_VERSION)"

.github/workflows/merge.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ jobs:
244244
--no-fail-on-empty-changeset \
245245
--role-arn ${BETA_CLOUDFORMATION_EXECUTION_ROLE}
246246
247+
- name: Publish versioned x86_64 layer parameter
248+
run: |
249+
.github/scripts/publish-layer-parameter.sh \
250+
"${BETA_STACK_NAME}-x86" x86_64 "${CARGO_PKG_VERSION}" "${BETA_REGION}"
251+
247252
- uses: actions/download-artifact@v4
248253
with:
249254
name: packaged-beta-arm64.yaml
@@ -260,6 +265,11 @@ jobs:
260265
--no-fail-on-empty-changeset \
261266
--role-arn ${BETA_CLOUDFORMATION_EXECUTION_ROLE}
262267
268+
- name: Publish versioned arm64 layer parameter
269+
run: |
270+
.github/scripts/publish-layer-parameter.sh \
271+
"${BETA_STACK_NAME}-arm64" arm64 "${CARGO_PKG_VERSION}" "${BETA_REGION}"
272+
263273
e2e-test-zip:
264274
needs: [deploy-beta]
265275
runs-on: ubuntu-24.04

.github/workflows/release.yaml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,12 @@ jobs:
474474
--no-fail-on-empty-changeset \
475475
--role-arn ${{ matrix.cloudformation_execution_role }}
476476
477+
- name: Publish versioned x86_64 layer parameter
478+
run: |
479+
.github/scripts/publish-layer-parameter.sh \
480+
"lambda-adapter-gamma-x86-${{ matrix.region }}" \
481+
x86_64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
482+
477483
- uses: actions/download-artifact@v4
478484
with:
479485
name: packaged-gamma-arm64-${{ matrix.region }}.yaml
@@ -491,6 +497,13 @@ jobs:
491497
--no-fail-on-empty-changeset \
492498
--role-arn ${{ matrix.cloudformation_execution_role }}
493499
500+
- name: Publish versioned arm64 layer parameter
501+
if: ${{ matrix.arm64_supported }}
502+
run: |
503+
.github/scripts/publish-layer-parameter.sh \
504+
"lambda-adapter-gamma-arm64-${{ matrix.region }}" \
505+
arm64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
506+
494507
deploy-prod:
495508
needs: [load-matrices, deploy-gamma, package-prod]
496509
runs-on: ubuntu-24.04
@@ -543,6 +556,12 @@ jobs:
543556
--no-fail-on-empty-changeset \
544557
--role-arn ${{ matrix.cloudformation_execution_role }}
545558
559+
- name: Publish versioned x86_64 layer parameter
560+
run: |
561+
.github/scripts/publish-layer-parameter.sh \
562+
"lambda-adapter-prod-x86-${{ matrix.region }}" \
563+
x86_64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
564+
546565
- uses: actions/download-artifact@v4
547566
with:
548567
name: packaged-prod-arm64-${{ matrix.region }}.yaml
@@ -560,6 +579,13 @@ jobs:
560579
--no-fail-on-empty-changeset \
561580
--role-arn ${{ matrix.cloudformation_execution_role }}
562581
582+
- name: Publish versioned arm64 layer parameter
583+
if: ${{ matrix.arm64_supported }}
584+
run: |
585+
.github/scripts/publish-layer-parameter.sh \
586+
"lambda-adapter-prod-arm64-${{ matrix.region }}" \
587+
arm64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
588+
563589
deploy-china-gamma:
564590
needs: [load-matrices, package-china-gamma]
565591
runs-on: ubuntu-24.04
@@ -613,6 +639,12 @@ jobs:
613639
--no-fail-on-empty-changeset \
614640
--role-arn ${{ matrix.cloudformation_execution_role }}
615641
642+
- name: Publish versioned x86_64 layer parameter
643+
run: |
644+
.github/scripts/publish-layer-parameter.sh \
645+
"lambda-adapter-gamma-x86-${{ matrix.region }}" \
646+
x86_64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
647+
616648
- uses: actions/download-artifact@v4
617649
with:
618650
name: packaged-china-gamma-arm64-${{ matrix.region }}.yaml
@@ -630,6 +662,13 @@ jobs:
630662
--no-fail-on-empty-changeset \
631663
--role-arn ${{ matrix.cloudformation_execution_role }}
632664
665+
- name: Publish versioned arm64 layer parameter
666+
if: ${{ matrix.arm64_supported }}
667+
run: |
668+
.github/scripts/publish-layer-parameter.sh \
669+
"lambda-adapter-gamma-arm64-${{ matrix.region }}" \
670+
arm64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
671+
633672
deploy-china-prod:
634673
needs: [load-matrices, deploy-china-gamma, package-china-prod]
635674
runs-on: ubuntu-24.04
@@ -684,6 +723,12 @@ jobs:
684723
--no-fail-on-empty-changeset \
685724
--role-arn ${{ matrix.cloudformation_execution_role }}
686725
726+
- name: Publish versioned x86_64 layer parameter
727+
run: |
728+
.github/scripts/publish-layer-parameter.sh \
729+
"lambda-adapter-prod-x86-${{ matrix.region }}" \
730+
x86_64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
731+
687732
- uses: actions/download-artifact@v4
688733
with:
689734
name: packaged-china-prod-arm64-${{ matrix.region }}.yaml
@@ -701,6 +746,13 @@ jobs:
701746
--no-fail-on-empty-changeset \
702747
--role-arn ${{ matrix.cloudformation_execution_role }}
703748
749+
- name: Publish versioned arm64 layer parameter
750+
if: ${{ matrix.arm64_supported }}
751+
run: |
752+
.github/scripts/publish-layer-parameter.sh \
753+
"lambda-adapter-prod-arm64-${{ matrix.region }}" \
754+
arm64 "${CARGO_PKG_VERSION}" "${{ matrix.region }}"
755+
704756
publish-to-public-ecr:
705757
needs: [deploy-prod]
706758
runs-on: ubuntu-24.04

0 commit comments

Comments
 (0)