Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cf830ff
feat(release): add changelog entry, GitHub Releases, lastPublished, a…
fabisev Aug 24, 2026
3bde5cb
Merge branch 'main' into fabisev/release-changelog-and-github-releases
fabisev Aug 25, 2026
555b3f5
build: centralize module versions in a parent POM
fabisev Aug 25, 2026
cff1683
Merge branch 'main' into fabisev/release-changelog-and-github-releases
fabisev Aug 26, 2026
45420b7
Merge branch 'main' into fabisev/release-changelog-and-github-releases
fabisev Aug 27, 2026
5d51046
refactor(release): address PR review on parent POM and workflow
fabisev Aug 31, 2026
ce9f80b
docs(release): shorten version-map comment
fabisev Aug 31, 2026
5a6a528
chore(release): set meaningful Central deploymentName (GAV)
fabisev Aug 31, 2026
f980a36
feat(release): attach signed jars + verify notes to GitHub Release
fabisev Sep 1, 2026
0d41205
Merge branch 'main' into fabisev/release-changelog-and-github-releases
fabisev Sep 1, 2026
fb9da27
refactor(release): split shared parent POM into a follow-up PR
fabisev Sep 1, 2026
9bc7a4a
Merge remote-tracking branch 'origin/fabisev/release-changelog-and-gi…
fabisev Sep 1, 2026
91ebbbc
feat(release): attach signed jars to GitHub Releases
fabisev Sep 1, 2026
e9d9111
Merge branch 'main' into fabisev/release-changelog-and-github-releases
fabisev Sep 1, 2026
7363371
fix(release): validate release version and keep GPG passphrase off argv
fabisev Sep 2, 2026
649ce7e
chore(release): squash post-release bump into one commit
fabisev Sep 2, 2026
b3d7b5b
Merge remote-tracking branch 'origin/fabisev/release-changelog-and-gi…
fabisev Sep 2, 2026
baa4cd5
refactor(release): centralize release tag in TAG_NAME
fabisev Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/actions/create-github-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: "Create GitHub Release with signed assets"
description: >
Creates a GitHub Release on an already-pushed tag. Builds the release notes
from the changelog entry, a Maven Central link, and GPG verification
instructions, then attaches the signed jars and their detached .asc
signatures (byte-for-byte the same signatures uploaded to Maven Central). The
attachment set is derived from the .asc files, so only signed, published
artifacts are attached (unsigned intermediates like shade's original-*.jar are
skipped). No re-signing happens here.

inputs:
tag:
description: "Git tag the release is created on (already pushed)."
required: true
title:
description: "Release title."
required: true
module:
description: "Module artifactId (used for the Central link and verify example)."
required: true
version:
description: "Released version."
required: true
changelog-entry:
description: "Markdown changelog entry used as the lead of the release notes."
required: true
fingerprint:
description: "Public GPG key fingerprint, shown in the verification instructions."
required: true
artifact-dir:
description: >
Directory (searched non-recursively) holding the signed jars and their
.asc siblings. For release:perform this is <module>/target/checkout/target;
for an in-place deploy it is <module>/target.
required: true
extra-notes:
description: "Optional Markdown inserted between the changelog and the Central link (e.g. an artifact inventory)."
required: false
default: ""
github-token:
description: "Token for the gh CLI (typically github.token)."
required: true

runs:
using: composite
steps:
- name: Create GitHub Release
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
TAG: ${{ inputs.tag }}
RELEASE_TITLE: ${{ inputs.title }}
REL_MODULE: ${{ inputs.module }}
REL_VERSION: ${{ inputs.version }}
CHANGELOG_ENTRY: ${{ inputs.changelog-entry }}
GPG_FINGERPRINT: ${{ inputs.fingerprint }}
ARTIFACT_DIR: ${{ inputs.artifact-dir }}
EXTRA_NOTES: ${{ inputs.extra-notes }}
run: |
NOTES="$(mktemp)"
{
printf '%s\n\n' "$CHANGELOG_ENTRY"
if [ -n "$EXTRA_NOTES" ]; then
printf '%s\n\n' "$EXTRA_NOTES"
fi
echo "Published to Maven Central: https://central.sonatype.com/artifact/com.amazonaws/${REL_MODULE}/${REL_VERSION}"
echo
echo "## Verifying the signatures"
echo
echo "The attached \`.jar.asc\` files are the same GPG signatures published to Maven Central, made with the AWS Lambda Java release signing key."
echo
echo '```sh'
echo "# Import the public signing key"
echo "gpg --keyserver keys.openpgp.org --recv-keys ${GPG_FINGERPRINT}"
echo "# Verify a downloaded jar against its signature"
echo "gpg --verify ${REL_MODULE}-${REL_VERSION}.jar.asc ${REL_MODULE}-${REL_VERSION}.jar"
echo '```'
} > "$NOTES"

# Attach the signed jars + their detached GPG signatures. Deriving the
# list from the .asc set means only signed, published artifacts are
# attached (skips unsigned intermediates like shade's original-*.jar).
ASSETS=()
while IFS= read -r sig; do
ASSETS+=("${sig%.asc}" "$sig")
done < <(find "$ARTIFACT_DIR" -maxdepth 1 -type f -name '*.jar.asc' 2>/dev/null | sort)
if [ ${#ASSETS[@]} -eq 0 ]; then
echo "::warning::No signed jars found under $ARTIFACT_DIR; creating the release without attachments"
fi

gh release create "$TAG" \
--title "$RELEASE_TITLE" \
--notes-file "$NOTES" \
"${ASSETS[@]}"
18 changes: 18 additions & 0 deletions .github/actions/resolve-release-version/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,23 @@ runs:
# Optional override; default strips -SNAPSHOT.
EFFECTIVE_RELEASE_VERSION="${RELEASE_VERSION_OVERRIDE:-${CURRENT_VERSION%-SNAPSHOT}}"

# Constrain the version to a strict MAJOR.MINOR.PATCH(-prerelease) shape.
# This is a security control: EFFECTIVE_RELEASE_VERSION
# (which can come verbatim from the releaseVersion input) is later spliced
# into a `sed` program, a git tag/branch name, and written to $GITHUB_ENV.
# The character class here forbids sed delimiters/metacharacters (| & \ /),
# whitespace, and newlines, so none of those sinks can be injected.
if [[ ! "$EFFECTIVE_RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then
echo "::error::Release version '$EFFECTIVE_RELEASE_VERSION' is not a valid MAJOR.MINOR.PATCH[-prerelease] version"
exit 1
fi
# A release version must not itself be a SNAPSHOT (the default path strips
# it; only an override could reintroduce it). Mirrors the developmentVersion
# must-be-SNAPSHOT rule.
if [[ "$EFFECTIVE_RELEASE_VERSION" == *-SNAPSHOT ]]; then
echo "::error::Release version '$EFFECTIVE_RELEASE_VERSION' must not be a SNAPSHOT"
exit 1
fi

echo "CURRENT_VERSION=$CURRENT_VERSION" >> "$GITHUB_ENV"
echo "EFFECTIVE_RELEASE_VERSION=$EFFECTIVE_RELEASE_VERSION" >> "$GITHUB_ENV"
29 changes: 29 additions & 0 deletions .github/actions/setup-corretto8/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Use the runner image's preinstalled Corretto 8"
description: >
Points JAVA_HOME/PATH at the CodeBuild image's preinstalled Corretto 8
($JAVA_8_HOME) and writes a Maven toolchains.xml for JDK 8. The image defaults
JAVA_HOME to Java 25, so this is required before any Maven call. Avoids
actions/setup-java, which fetches from corretto.github.io + corretto.aws, both
blocked by the runner egress lock. $JAVA_8_HOME resolves per-arch
(x86_64/aarch64).

runs:
using: composite
steps:
- name: Use the runner image's preinstalled Corretto 8
shell: bash
run: |
echo "JAVA_HOME=$JAVA_8_HOME" >> "$GITHUB_ENV"
echo "$JAVA_8_HOME/bin" >> "$GITHUB_PATH"
"$JAVA_8_HOME/bin/java" -version
mkdir -p "$HOME/.m2"
cat > "$HOME/.m2/toolchains.xml" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
<toolchain>
<type>jdk</type>
<provides><version>8</version></provides>
<configuration><jdkHome>$JAVA_8_HOME</jdkHome></configuration>
</toolchain>
</toolchains>
EOF
105 changes: 62 additions & 43 deletions .github/workflows/release-runtime-interface-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ on:
description: 'Next development version override (optional, must end with -SNAPSHOT)'
required: false
type: string
changelogEntry:
description: 'Changelog entry (Markdown bullets, e.g. "- Fix X"). Prepended to the module RELEASE.CHANGELOG.md in the version-bump PR and used as the GitHub Release notes. Required.'
required: true
type: string
skip_publish:
description: 'Skip publish (dry-run validation)'
required: false
Expand All @@ -38,6 +42,7 @@ env:
MODULE: aws-lambda-java-runtime-interface-client
RELEASE_VERSION_INPUT: ${{ github.event.inputs.releaseVersion }}
DEVELOPMENT_VERSION_INPUT: ${{ github.event.inputs.developmentVersion }}
CHANGELOG_ENTRY_INPUT: ${{ github.event.inputs.changelogEntry }}
MAVEN_ARGS: "-B --no-transfer-progress"
AWS_REGION: ${{ vars.AWS_REGION_MAVEN_RELEASE }}
OIDC_ROLE_ARN: ${{ secrets.AWS_ROLE_MAVEN_RELEASE }}
Expand Down Expand Up @@ -75,27 +80,8 @@ jobs:

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

# Use the CodeBuild image's preinstalled Corretto 8. The image ships it at
# $JAVA_8_HOME but defaults JAVA_HOME to Java 25, so point JAVA_HOME/PATH at
# 8. Avoids actions/setup-java, which fetches from corretto.github.io +
# corretto.aws, both blocked by the runner egress lock. $JAVA_8_HOME
# resolves per-arch (x86_64/aarch64).
- name: Use the runner image's preinstalled Corretto 8
run: |
echo "JAVA_HOME=$JAVA_8_HOME" >> "$GITHUB_ENV"
echo "$JAVA_8_HOME/bin" >> "$GITHUB_PATH"
"$JAVA_8_HOME/bin/java" -version
mkdir -p "$HOME/.m2"
cat > "$HOME/.m2/toolchains.xml" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
<toolchain>
<type>jdk</type>
<provides><version>8</version></provides>
<configuration><jdkHome>$JAVA_8_HOME</jdkHome></configuration>
</toolchain>
</toolchains>
EOF
uses: ./.github/actions/setup-corretto8

# Route all mvn resolution through the CodeArtifact mirror. Must precede
# resolve-release-version, which invokes `mvn help:evaluate`. Ambient
Expand Down Expand Up @@ -160,31 +146,21 @@ jobs:
environment: Release
timeout-minutes: 30
steps:
# Defence-in-depth: build-natives already gates on main, but this job does
# the actual publish/tag/push on a separate runner, so re-verify here too.
- name: Verify release branch
run: |
if [[ "$GITHUB_REF_NAME" != "main" ]]; then
echo "::error::Releases must run from the main branch, got '$GITHUB_REF_NAME'"
exit 1
fi

- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0 # full history for tagging/pushing

# Use the CodeBuild image's preinstalled Corretto 8. The image ships it at
# $JAVA_8_HOME but defaults JAVA_HOME to Java 25, so point JAVA_HOME/PATH at
# 8. Avoids actions/setup-java, which fetches from corretto.github.io +
# corretto.aws, both blocked by the runner egress lock. $JAVA_8_HOME
# resolves per-arch (x86_64/aarch64).
- name: Use the runner image's preinstalled Corretto 8
run: |
echo "JAVA_HOME=$JAVA_8_HOME" >> "$GITHUB_ENV"
echo "$JAVA_8_HOME/bin" >> "$GITHUB_PATH"
"$JAVA_8_HOME/bin/java" -version
mkdir -p "$HOME/.m2"
cat > "$HOME/.m2/toolchains.xml" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<toolchains>
<toolchain>
<type>jdk</type>
<provides><version>8</version></provides>
<configuration><jdkHome>$JAVA_8_HOME</jdkHome></configuration>
</toolchain>
</toolchains>
EOF
uses: ./.github/actions/setup-corretto8

# Route all mvn resolution through the CodeArtifact mirror. Must precede
# resolve-release-version (which invokes `mvn help:evaluate`) and the OIDC
Expand Down Expand Up @@ -304,6 +280,10 @@ jobs:
gpgconf --kill gpg-agent || true
gpg --batch --import <<< "$GPG_PRIVATE_KEY"
GPG_KEYNAME=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}')
# Publish the primary key fingerprint (NOT secret) for the GitHub
# Release verification instructions. Only the public fingerprint
# crosses $GITHUB_ENV here; the passphrase and token never do.
echo "GPG_FINGERPRINT=$(gpg --list-secret-keys --with-colons | awk -F: '/^fpr:/ {print $10; exit}')" >> "$GITHUB_ENV"

# Global settings holding only the Sonatype "central" server for upload.
# Passed to Maven as -gs (global) so it MERGES with the CodeArtifact
Expand All @@ -319,9 +299,13 @@ jobs:
} > "$MAVEN_SETTINGS"

# --- Publish --- (-gs: merge Sonatype creds with the ~/.m2 mirror)
# Pass the passphrase via MAVEN_GPG_PASSPHRASE (read natively by
# maven-gpg-plugin) rather than -Dgpg.passphrase, so it never lands in
# the process argument list.
export MAVEN_GPG_PASSPHRASE="$GPG_PASSPHRASE"
mvn deploy -Prelease -DskipTests -DmultiArch=false \
-gs "$MAVEN_SETTINGS" \
-Dgpg.keyname="$GPG_KEYNAME" -Dgpg.passphrase="$GPG_PASSPHRASE" \
-Dgpg.keyname="$GPG_KEYNAME" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the plugin maven-gpg-plugin version are all v1.5, does it support this new feature? Do we need to bump the version of maven-gpg-plugin as well?

--file "$MODULE/pom.xml"

# main is protected (no direct push), so push the tag (tag pushes aren't
Expand All @@ -339,9 +323,26 @@ jobs:
git commit -am "chore(ric): release ${EFFECTIVE_RELEASE_VERSION}"
git tag "$TAG_NAME"

# Next development version commit.
# Single post-release commit: next development version bump, the
# lastPublished marker, and the changelog entry. Sits on top of the
# tag, so the tagged release commit is untouched. The changelog entry
# is passed via env, never interpolated into the script, so it can't
# inject shell.
mvn versions:set -DnewVersion="$NEXT_DEV_VERSION" -DgenerateBackupPoms=false --file "$MODULE/pom.xml"
git commit -am "chore(ric): prepare next development ${NEXT_DEV_VERSION}"
if grep -q "<!-- lastPublished:" "$MODULE/pom.xml"; then
sed -i "s|<!-- lastPublished:.*-->|<!-- lastPublished: ${EFFECTIVE_RELEASE_VERSION} (auto-updated by release-runtime-interface-client.yml) -->|" "$MODULE/pom.xml"
fi
CHANGELOG="$MODULE/RELEASE.CHANGELOG.md"
TMP="$(mktemp)"
{
echo "### $(date +'%B %d, %Y')"
echo "\`${EFFECTIVE_RELEASE_VERSION}\`:"
printf '%s\n\n' "$CHANGELOG_ENTRY_INPUT"
[ -f "$CHANGELOG" ] && cat "$CHANGELOG"
} > "$TMP"
mv "$TMP" "$CHANGELOG"
git add "$CHANGELOG"
git commit -am "chore(release): prepare next development, record ${MODULE} ${EFFECTIVE_RELEASE_VERSION} lastPublished and changelog"

# Tag push isn't gated by branch protection; the version-bump commits
# go to a release branch and land on main via PR.
Expand All @@ -354,6 +355,24 @@ jobs:
--title "chore(release): ${MODULE} ${EFFECTIVE_RELEASE_VERSION}" \
--body "Post-release version bump for ${MODULE} ${EFFECTIVE_RELEASE_VERSION} (already on Maven Central, tag ${TAG_NAME} pushed)."

# GitHub Release on the pushed tag: the signed artifacts (byte-for-byte the
# same detached GPG signatures uploaded to Maven Central) + verification
# instructions. `mvn deploy` signed in place, so the jars and their .asc
# siblings are in the module target dir and need no re-signing.
- name: Create GitHub Release
if: ${{ github.event.inputs.skip_publish != 'true' }}
uses: ./.github/actions/create-github-release
with:
tag: ${{ env.TAG_NAME }}
title: ${{ env.MODULE }} ${{ env.EFFECTIVE_RELEASE_VERSION }}
module: ${{ env.MODULE }}
version: ${{ env.EFFECTIVE_RELEASE_VERSION }}
changelog-entry: ${{ env.CHANGELOG_ENTRY_INPUT }}
fingerprint: ${{ env.GPG_FINGERPRINT }}
artifact-dir: ${{ env.MODULE }}/target
extra-notes: "Artifacts: main JAR + linux/linux_musl x x86_64/aarch_64 native classifier JARs."
github-token: ${{ github.token }}

# Dry-run: validate assembly, no publish/push.
- name: Dry-run assemble (no publish)
if: ${{ github.event.inputs.skip_publish == 'true' }}
Expand Down
Loading
Loading