Maven Release #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |