Skip to content

Commit 26cfeea

Browse files
ci: auto-update version references in the release pipeline
Automate the manual version-string sweep that PR aws#746 did by hand, per the maintainer's note on that PR ("I will automate this update via the release pipeline"). Two hard-coded references across docs/examples are kept in sync: - Docker image tag public.ecr.aws/awsguru/aws-lambda-adapter:<X.Y.Z> (= the crate semver) - Lambda layer version LambdaAdapterLayer{X86,Arm64}:<N> (an integer AWS assigns at publish time) How: - scripts/update-versions.sh <image_version> <layer_version>: a reusable, idempotent, anchored updater (skips CHANGELOG.md history and the obsolete commented example ARN, account 753240598076). Runnable by a human too. - release-plz.yml: after release-plz opens/updates the Release PR, an inject step predicts the next layer version (current + 1) and the new image tag and amends them into the same release commit. The +1 prediction is reliable because the SAM layer's Description embeds the crate version, so every release publishes exactly one new immutable layer version. - release.yaml: a post-deploy verify-layer-versions job reads the ACTUAL published layer version and opens a self-healing correction PR if the prediction was ever wrong. (Requires lambda:ListLayerVersions on the prod pipeline role; the prediction stands on its own without it.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a1fd5c2 commit 26cfeea

3 files changed

Lines changed: 224 additions & 0 deletions

File tree

.github/workflows/release-plz.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,75 @@ jobs:
3737
- name: Install Rust toolchain
3838
uses: dtolnay/rust-toolchain@stable
3939
- name: Run release-plz (release-pr)
40+
id: release-plz
4041
uses: release-plz/action@v0.5
4142
with:
4243
command: release-pr
4344
env:
4445
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
4546

47+
# Fold the version-string updates (Docker image tag + predicted Lambda
48+
# layer version) into the same commit release-plz just pushed, so the
49+
# Release PR bumps Cargo.toml, the CHANGELOG, AND the docs/examples in one
50+
# commit. The layer version is predicted as (current + 1): the SAM layer's
51+
# Description embeds the crate version, so every release publishes exactly
52+
# one new immutable layer version. release.yaml verifies the prediction
53+
# after deploy and self-heals if it was ever wrong.
54+
# Runs on PR create AND update (`pr` is populated for both; `prs_created`
55+
# is only true on create).
56+
- name: Inject version-string updates into the Release PR commit
57+
if: steps.release-plz.outputs.pr != ''
58+
env:
59+
TOKEN: ${{ steps.app-token.outputs.token }}
60+
PR_JSON: ${{ steps.release-plz.outputs.pr }}
61+
run: |
62+
set -euo pipefail
63+
BRANCH="$(jq -r '.head_branch' <<< "$PR_JSON")"
64+
VERSION="$(jq -r '.releases[0].version' <<< "$PR_JSON")"
65+
if [ -z "$BRANCH" ] || [ "$BRANCH" = null ] || [ -z "$VERSION" ] || [ "$VERSION" = null ]; then
66+
echo "No pending release PR (no head_branch/version); skipping."
67+
exit 0
68+
fi
69+
echo "Release PR branch=$BRANCH version=$VERSION"
70+
71+
# release-plz leaves the runner checkout on main; fetch + check out its branch.
72+
git fetch --force origin "$BRANCH":"refs/remotes/origin/$BRANCH"
73+
git checkout -B "$BRANCH" "refs/remotes/origin/$BRANCH"
74+
75+
# Predict next layer version = current repo value + 1. Anchor on the prod
76+
# account (753240598075) so the commented obsolete ARN (…598076) is excluded.
77+
# More than one distinct value means drift -> fail loudly.
78+
mapfile -t cur < <(
79+
grep -rhoE '753240598075:layer:LambdaAdapterLayer(X86|Arm64):[0-9]+' . \
80+
--include='*.md' --include='*.yaml' --include='*.yml' | grep -oE '[0-9]+$' | sort -u
81+
)
82+
if [ "${#cur[@]}" -ne 1 ]; then
83+
echo "::error::Lambda layer version drift; expected one value, found: ${cur[*]:-<none>}"
84+
exit 1
85+
fi
86+
NEXT=$(( cur[0] + 1 ))
87+
echo "current layer=${cur[0]} predicted next=$NEXT"
88+
89+
scripts/update-versions.sh "$VERSION" "$NEXT"
90+
if git diff --quiet; then
91+
echo "Docs already up to date; leaving release-plz commit untouched."
92+
exit 0
93+
fi
94+
95+
# Amend into release-plz's single commit, preserving the bot identity.
96+
AN="$(git log -1 --format='%an')"
97+
AE="$(git log -1 --format='%ae')"
98+
git config user.name "$AN"
99+
git config user.email "$AE"
100+
git add -A
101+
GIT_COMMITTER_NAME="$AN" GIT_COMMITTER_EMAIL="$AE" \
102+
git commit --amend --no-edit --reset-author
103+
104+
# Checkout used persist-credentials:false, so authenticate the push explicitly.
105+
git push --force \
106+
"https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
107+
"HEAD:${BRANCH}"
108+
46109
release-plz-release:
47110
name: Release-plz release
48111
if: github.repository == 'aws/aws-lambda-web-adapter'

.github/workflows/release.yaml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,3 +757,107 @@ jobs:
757757
public.ecr.aws/awsguru/aws-lambda-adapter:${CARGO_PKG_VERSION}-x86_64 \
758758
public.ecr.aws/awsguru/aws-lambda-adapter:${CARGO_PKG_VERSION}-aarch64
759759
docker manifest push public.ecr.aws/awsguru/aws-lambda-adapter:${CARGO_PKG_VERSION}
760+
761+
# Safety net for the predicted Lambda layer version that the release-plz PR
762+
# baked into the docs/examples. The prediction (previous + 1) is reliable
763+
# because the SAM layer's Description embeds the crate version, so every
764+
# release publishes exactly one new immutable layer version. This job reads
765+
# the ACTUAL published version and, if the prediction was ever wrong, opens a
766+
# self-healing PR to correct the docs.
767+
#
768+
# Requires the prod pipeline role (account 753240598075, where the public
769+
# layer ARNs live) to allow `lambda:ListLayerVersions`. If that permission is
770+
# not yet granted, this job will fail at the read step -- the prediction in
771+
# release-plz.yml still stands on its own.
772+
verify-layer-versions:
773+
needs: [deploy-prod, publish-to-public-ecr]
774+
runs-on: ubuntu-24.04
775+
environment: prod
776+
permissions:
777+
id-token: write
778+
contents: write
779+
pull-requests: write
780+
steps:
781+
- uses: actions/checkout@v4
782+
with:
783+
ref: main
784+
fetch-depth: 0
785+
786+
- name: Assume the github runner role
787+
uses: aws-actions/configure-aws-credentials@v4
788+
with:
789+
aws-region: ${{ env.PROD_ECR_REGION }}
790+
role-to-assume: ${{ env.GITHUB_RUNNER_ROLE }}
791+
792+
# us-east-1 prod pipeline role from prod.json -> account 753240598075,
793+
# the account whose layer ARNs appear in the public docs/examples.
794+
- name: Assume the prod pipeline user role
795+
uses: aws-actions/configure-aws-credentials@v4
796+
with:
797+
aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
798+
aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
799+
aws-session-token: ${{ env.AWS_SESSION_TOKEN }}
800+
role-skip-session-tagging: true
801+
aws-region: ${{ env.PROD_ECR_REGION }}
802+
role-to-assume: arn:aws:iam::753240598075:role/aws-sam-cli-managed-prod-pipe-PipelineExecutionRole-7Hangwn17nEz
803+
804+
- name: Compare published layer version to the repo and self-heal if needed
805+
id: cmp
806+
run: |
807+
set -euo pipefail
808+
X86=$(aws lambda list-layer-versions --layer-name LambdaAdapterLayerX86 \
809+
--region us-east-1 --query 'LayerVersions[0].Version' --output text)
810+
ARM=$(aws lambda list-layer-versions --layer-name LambdaAdapterLayerArm64 \
811+
--region us-east-1 --query 'LayerVersions[0].Version' --output text)
812+
echo "published x86=$X86 arm64=$ARM"
813+
if [ "$X86" != "$ARM" ]; then
814+
echo "::error::x86 ($X86) and arm64 ($ARM) layer versions differ; docs use one integer"
815+
exit 1
816+
fi
817+
818+
mapfile -t repo < <(
819+
grep -rhoE '753240598075:layer:LambdaAdapterLayer(X86|Arm64):[0-9]+' . \
820+
--include='*.md' --include='*.yaml' --include='*.yml' | grep -oE '[0-9]+$' | sort -u
821+
)
822+
if [ "${#repo[@]}" -ne 1 ]; then
823+
echo "::error::repo layer version drift on main: ${repo[*]:-<none>}"
824+
exit 1
825+
fi
826+
echo "repo layer=${repo[0]} published=$X86"
827+
828+
if [ "${repo[0]}" = "$X86" ]; then
829+
echo "Prediction was correct; nothing to do."
830+
echo "needs_fix=false" >> "$GITHUB_OUTPUT"
831+
exit 0
832+
fi
833+
834+
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
835+
scripts/update-versions.sh "$VERSION" "$X86"
836+
echo "needs_fix=true" >> "$GITHUB_OUTPUT"
837+
echo "layer_version=$X86" >> "$GITHUB_OUTPUT"
838+
839+
- name: Generate a GitHub App token
840+
if: steps.cmp.outputs.needs_fix == 'true'
841+
uses: actions/create-github-app-token@v3
842+
id: app-token
843+
with:
844+
app-id: ${{ secrets.RELEASE_PLZ_APP_ID }}
845+
private-key: ${{ secrets.RELEASE_PLZ_APP_PRIVATE_KEY }}
846+
847+
# App token so the correction PR triggers CI (a PR opened with the default
848+
# GITHUB_TOKEN would not).
849+
- name: Open self-healing correction PR
850+
if: steps.cmp.outputs.needs_fix == 'true'
851+
uses: peter-evans/create-pull-request@v8
852+
with:
853+
token: ${{ steps.app-token.outputs.token }}
854+
base: main
855+
branch: fix/layer-version-${{ steps.cmp.outputs.layer_version }}
856+
delete-branch: true
857+
commit-message: "fix: correct Lambda layer version to ${{ steps.cmp.outputs.layer_version }}"
858+
title: "fix: correct Lambda layer version to ${{ steps.cmp.outputs.layer_version }}"
859+
body: |
860+
Automated correction: the published Lambda layer version is
861+
`${{ steps.cmp.outputs.layer_version }}`, but the docs/examples on
862+
`main` recorded a different value (the predicted version was wrong).
863+
Updating the version strings to match what was actually published.

scripts/update-versions.sh

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Update the hard-coded AWS Lambda Web Adapter version references across the
4+
# docs and examples. There are two kinds:
5+
#
6+
# KIND 1 Docker image tag public.ecr.aws/awsguru/aws-lambda-adapter:<X.Y.Z>
7+
# (equals the crate semver)
8+
# KIND 2 Lambda layer ARN ...:layer:LambdaAdapterLayerX86:<N>
9+
# ...:layer:LambdaAdapterLayerArm64:<N>
10+
# (an integer AWS assigns when the layer is published)
11+
#
12+
# Usage:
13+
# scripts/update-versions.sh <image_version> <layer_version>
14+
# <image_version> Docker image tag == crate semver, e.g. 1.0.2
15+
# <layer_version> Lambda layer version integer, e.g. 29
16+
#
17+
# The substitutions are anchored and idempotent (re-running with the same
18+
# arguments produces no diff). CHANGELOG.md is never modified, and the obsolete
19+
# commented example ARN (account 753240598076) is intentionally left untouched.
20+
# Can be run by a human or from the release pipeline.
21+
set -euo pipefail
22+
23+
IMAGE_VERSION="${1:?usage: update-versions.sh <image_version> <layer_version>}"
24+
LAYER_VERSION="${2:?usage: update-versions.sh <image_version> <layer_version>}"
25+
26+
[[ "$IMAGE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
27+
echo "error: image_version must be semver X.Y.Z, got '$IMAGE_VERSION'" >&2; exit 1; }
28+
[[ "$LAYER_VERSION" =~ ^[0-9]+$ ]] || {
29+
echo "error: layer_version must be an integer, got '$LAYER_VERSION'" >&2; exit 1; }
30+
31+
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
32+
33+
# KIND 1 -- Docker image tag. Anchored on the full image name so only a bare
34+
# X.Y.Z right after the colon is replaced (leaves -x86_64 / -aarch64 suffixed
35+
# internal build tags and the aws/aws-lambda-adapter repo URL alone).
36+
mapfile -t kind1_files < <(
37+
grep -rlE 'aws-lambda-adapter:[0-9]+\.[0-9]+\.[0-9]+' . \
38+
--exclude=CHANGELOG.md --exclude-dir=.git --exclude-dir=.sl || true
39+
)
40+
for f in "${kind1_files[@]}"; do
41+
sed -i -E "s#(public\.ecr\.aws/awsguru/aws-lambda-adapter:)[0-9]+\.[0-9]+\.[0-9]+#\1${IMAGE_VERSION}#g" "$f"
42+
done
43+
44+
# KIND 2 -- Lambda layer version integer. The layer-name token is unique to these
45+
# ARNs, so one pattern covers !Sub ARNs, markdown tables, JSON, and the China
46+
# ARNs across every file type. Skip the obsolete commented example (account
47+
# 753240598076) so that historical sample is preserved.
48+
mapfile -t kind2_files < <(
49+
grep -rlE 'LambdaAdapterLayer(X86|Arm64):[0-9]+' . \
50+
--exclude=CHANGELOG.md --exclude-dir=.git --exclude-dir=.sl || true
51+
)
52+
for f in "${kind2_files[@]}"; do
53+
sed -i -E "/753240598076/!s#(LambdaAdapterLayer(X86|Arm64)):[0-9]+#\1:${LAYER_VERSION}#g" "$f"
54+
done
55+
56+
echo "KIND1 image tag -> ${IMAGE_VERSION} (${#kind1_files[@]} files)"
57+
echo "KIND2 layer version -> ${LAYER_VERSION} (${#kind2_files[@]} files)"

0 commit comments

Comments
 (0)