Skip to content

Maven Release

Maven Release #7

Workflow file for this run

name: Maven Release
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release version (e.g. 1.2.0)'
required: true
type: string
next_version:
description: 'Next development version (e.g. 1.3.0-SNAPSHOT)'
required: true
type: string
is_pre_release:
description: 'Is pre-release?'
required: false
default: false
type: boolean
permissions:
contents: write
id-token: write
pull-requests: read
env:
AWS_REGION: us-west-2
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Determine previous release tag
id: previous_release
shell: bash
run: |
previous_tag="$(git tag --merged HEAD --list 'v*' --sort=-version:refname | head -n 1)"
if [ -z "$previous_tag" ]; then
echo "No previous release tag found. Skipping BREAKING change validation."
echo "tag=" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Found previous release tag: $previous_tag"
echo "tag=$previous_tag" >> "$GITHUB_OUTPUT"
- name: Collect release commit SHAs
id: release_commits
if: steps.previous_release.outputs.tag != ''
shell: bash
run: |
{
echo "shas<<EOF"
git rev-list "${{ steps.previous_release.outputs.tag }}..HEAD"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Collect release pull request numbers from git history
id: release_pull_requests
if: steps.previous_release.outputs.tag != ''
shell: bash
run: |
{
echo "numbers<<EOF"
git log --format='%s' "${{ steps.previous_release.outputs.tag }}..HEAD" |
sed -nE 's/^.*\(#([0-9]+)\)$/\1/p; s/^Merge pull request #([0-9]+).*$/\1/p' |
sort -u
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Validate BREAKING changes require major version bump
if: steps.previous_release.outputs.tag != ''
uses: actions/github-script@v8
env:
PREVIOUS_TAG: ${{ steps.previous_release.outputs.tag }}
RELEASE_VERSION: ${{ github.event.inputs.release_version }}
RELEASE_COMMITS: ${{ steps.release_commits.outputs.shas }}
RELEASE_PULL_REQUESTS: ${{ steps.release_pull_requests.outputs.numbers }}
with:
script: |
const previousTag = process.env.PREVIOUS_TAG;
const releaseVersion = process.env.RELEASE_VERSION;
const commitShas = process.env.RELEASE_COMMITS
.split('\n')
.map((sha) => sha.trim())
.filter(Boolean);
const releasePullRequestNumbers = process.env.RELEASE_PULL_REQUESTS
.split('\n')
.map((number) => number.trim())
.filter(Boolean)
.map((number) => Number.parseInt(number, 10))
.filter((number) => Number.isInteger(number));
const owner = context.repo.owner;
const repo = context.repo.repo;
const breakingLabel = 'BREAKING';
const parseMajor = (version) => {
const match = version.match(/^v?(\d+)\.\d+\.\d+(?:[-+].*)?$/);
if (!match) {
throw new Error(`Unable to parse semantic version from "${version}"`);
}
return Number.parseInt(match[1], 10);
};
const previousMajor = parseMajor(previousTag);
const releaseMajor = parseMajor(releaseVersion);
if (commitShas.length === 0) {
core.info(`No commits found between ${previousTag} and HEAD. Skipping BREAKING change validation.`);
return;
}
const prsByNumber = new Map();
const linkedIssuesByPullRequest = new Map();
for (const commitSha of commitShas) {
const { data: pullRequests } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: commitSha,
});
for (const pullRequest of pullRequests) {
if (pullRequest.merged_at) {
prsByNumber.set(pullRequest.number, pullRequest);
}
}
}
for (const pullRequestNumber of releasePullRequestNumbers) {
if (prsByNumber.has(pullRequestNumber)) {
continue;
}
const { data: pullRequest } = await github.rest.pulls.get({
owner,
repo,
pull_number: pullRequestNumber,
});
if (pullRequest.merged_at) {
prsByNumber.set(pullRequest.number, pullRequest);
}
}
const hasBreakingLabel = (labels) =>
labels.some((label) => label.name?.toUpperCase() === breakingLabel);
const getLinkedIssues = async (pullRequestNumber) => {
if (linkedIssuesByPullRequest.has(pullRequestNumber)) {
return linkedIssuesByPullRequest.get(pullRequestNumber);
}
const query = `
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
closingIssuesReferences(first: 50) {
nodes {
number
labels(first: 50) {
nodes {
name
}
}
}
}
}
}
}
`;
const result = await github.graphql(query, {
owner,
repo,
number: pullRequestNumber,
});
const linkedIssues =
result.repository.pullRequest.closingIssuesReferences.nodes.map((issue) => ({
number: issue.number,
labels: issue.labels.nodes,
}));
linkedIssuesByPullRequest.set(pullRequestNumber, linkedIssues);
return linkedIssues;
};
core.info(
`Evaluating ${prsByNumber.size} pull requests since ${previousTag}: ${[...prsByNumber.keys()]
.map((number) => `#${number}`)
.join(', ')}`
);
const breakingPullRequests = [];
for (const pullRequest of prsByNumber.values()) {
const prHasBreakingLabel = hasBreakingLabel(pullRequest.labels);
const linkedIssues = await getLinkedIssues(pullRequest.number);
const breakingIssues = linkedIssues.filter((issue) => hasBreakingLabel(issue.labels));
if (prHasBreakingLabel || breakingIssues.length > 0) {
breakingPullRequests.push({
number: pullRequest.number,
breakingIssues,
});
}
}
if (breakingPullRequests.length === 0) {
core.info(`No BREAKING-labeled pull requests or linked issues found since ${previousTag}.`);
return;
}
const formatBreakingReferences = (pullRequest) => {
const issueSuffix =
pullRequest.breakingIssues.length === 0
? ''
: ` (linked issues: ${pullRequest.breakingIssues.map((issue) => `#${issue.number}`).join(', ')})`;
return `#${pullRequest.number}${issueSuffix}`;
};
if (releaseMajor > previousMajor) {
core.info(
`Found BREAKING-labeled pull requests or linked issues (${breakingPullRequests
.map(formatBreakingReferences)
.join(', ')}), and release version ${releaseVersion} bumps the major version from ${previousMajor} to ${releaseMajor}.`
);
return;
}
core.setFailed(
`Release ${releaseVersion} includes BREAKING-labeled pull requests or linked issues (${breakingPullRequests
.map(formatBreakingReferences)
.join(', ')}) since ${previousTag}, but the major version was not bumped from ${previousMajor}.`
);
- name: Setup Java
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'corretto'
cache: maven
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v6.2.0
with:
role-to-assume: "${{ secrets.ACTIONS_MVN_ROLE_NAME }}"
role-session-name: mavenreleasesession
aws-region: ${{ env.AWS_REGION }}
- name: Set release version
run: mvn -q versions:set -DnewVersion=${{ github.event.inputs.release_version }} -DgenerateBackupPoms=false
- name: Commit release version
run: |
git config user.email "${{ github.actor }}+github-actions[bot]@users.noreply.github.com"
git config user.name "${{ github.actor }}+github-actions[bot]"
git add .
git commit -m "chore: release version ${{ github.event.inputs.release_version }}"
- name: Push changes
uses: ad-m/github-push-action@881a6320fdb16eb5318c5054f31c218aec2b324c # master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Build artifacts
run: mvn clean install -q -Dlog4j2.level=WARN -Dlog4j.configurationFile=log4j2-quiet.xml --no-transfer-progress
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
with:
tag_name: v${{ github.event.inputs.release_version }}
name: Release v${{ github.event.inputs.release_version }}
generate_release_notes: true
prerelease: ${{ github.event.inputs.is_pre_release }}
files: |
sdk/target/aws-durable-execution-sdk-java-${{ github.event.inputs.release_version }}.jar
sdk-testing/target/aws-durable-execution-sdk-java-testing-${{ github.event.inputs.release_version }}.jar
- name: Get Env variables
uses: aws-actions/aws-secretsmanager-get-secrets@v3
with:
secret-ids: |
mvn_gpg_keys
mvn_account_keys
parse-json-secrets: true
- name: Sign and publish
run: bash .github/scripts/maven_publish.sh
env:
RELEASE_VERSION: ${{ github.event.inputs.release_version }}
- name: Set next development version
run: mvn -q versions:set -DnewVersion=${{ github.event.inputs.next_version }} -DgenerateBackupPoms=false
- name: Commit release version
run: |
git add .
git commit -m "chore: bump version to ${{ github.event.inputs.next_version }}"
- name: Push changes
uses: ad-m/github-push-action@881a6320fdb16eb5318c5054f31c218aec2b324c # master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
notify-release:
needs: [release]
uses: ./.github/workflows/notify-release.yml
with:
tag_name: v${{ github.event.inputs.release_version }}
release_url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/v${{ github.event.inputs.release_version }}
secrets: inherit