Skip to content

Release

Release #54

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
releaseVersion:
description: Version to be released (e.g. "5.12.0-M1")
required: true
deploymentId:
description: ID of the Maven Central Publish Portal deployment
required: true
dryRun:
type: boolean
description: Enable dry-run mode
required: false
default: false
permissions: {}
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
STAGING_REPO_URL: https://central.sonatype.com/api/v1/publisher/deployment/${{ inputs.deploymentId }}/download
RELEASE_TAG: r${{ inputs.releaseVersion }}
jobs:
verify_reproducibility:
name: Verify reproducibility
runs-on: ubuntu-latest
permissions:
attestations: write # required for build provenance attestation
id-token: write # required for build provenance attestation
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
ref: "refs/tags/${{ env.RELEASE_TAG }}"
- name: Prepare Maven Central user token
uses: ./.github/actions/maven-central-user-token
with:
username: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
- name: Download reference JAR from staging repository
id: referenceJar
run: |
curl --silent --fail --location --output /tmp/reference.jar \
--header "Authorization: Bearer $MAVEN_CENTRAL_USER_TOKEN" \
"${{ env.STAGING_REPO_URL }}/org/junit/jupiter/junit-jupiter-api/${{ inputs.releaseVersion }}/junit-jupiter-api-${{ inputs.releaseVersion }}.jar"
sudo apt-get update && sudo apt-get install --yes jc
unzip -c /tmp/reference.jar META-INF/MANIFEST.MF | jc --jar-manifest | jq '.[0]' > /tmp/manifest.json
echo "createdBy=$(jq --raw-output .Created_By /tmp/manifest.json)" >> "$GITHUB_OUTPUT"
echo "buildTimestamp=$(jq --raw-output .Build_Date /tmp/manifest.json) $(jq --raw-output .Build_Time /tmp/manifest.json)" >> "$GITHUB_OUTPUT"
- name: Verify artifacts
uses: ./.github/actions/run-gradle
with:
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
arguments: |
--rerun-tasks \
-Pmanifest.buildTimestamp="${{ steps.referenceJar.outputs.buildTimestamp }}" \
-Pmanifest.createdBy="${{ steps.referenceJar.outputs.createdBy }}" \
:verifyArtifactsInStagingRepositoryAreReproducible \
--remote-repo-url=${{ env.STAGING_REPO_URL }}
- name: Generate build provenance attestations
if: ${{ inputs.dryRun == false }}
uses: actions/attest-build-provenance@db473fddc028af60658334401dc6fa3ffd8669fd # v2.3.0
with:
subject-path: build/repo/**/*.jar
verify_consumability:
name: Verify consumability
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
ref: "refs/tags/${{ env.RELEASE_TAG }}"
path: junit-framework
- name: Check out examples repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ github.repository_owner }}/junit-examples
token: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_EXAMPLES_REPO }}
fetch-depth: 1
path: junit-examples
- name: Set up JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
java-version: 24
distribution: temurin
- uses: sbt/setup-sbt@26ab4b0fa1c47fa62fc1f6e51823a658fb6c760c # v1.1.7
- name: Update JUnit dependencies in examples
run: java src/Updater.java ${{ inputs.releaseVersion }}
working-directory: junit-examples
- name: Prepare Maven Central user token
uses: ./junit-framework/.github/actions/maven-central-user-token
with:
username: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
- name: Inject staging repository URL
run: java src/StagingRepoInjector.java ${{ env.STAGING_REPO_URL }}
working-directory: junit-examples
- name: Build examples
run: java src/Builder.java --exclude=junit-jupiter-starter-bazel,junit-jupiter-starter-sbt
working-directory: junit-examples
env:
MAVEN_ARGS: --settings ${{ github.workspace }}/junit-examples/src/central-staging-maven-settings.xml --activate-profiles central-staging
close_github_milestone:
name: Close GitHub milestone
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Close GitHub milestone
if: ${{ inputs.dryRun == false }}
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
result-encoding: string
script: |
const query = `
query ($owner: String!, $repo: String!, $title: String!) {
repository(owner: $owner, name: $repo) {
milestones(first: 100, query: $title) {
nodes {
title
number
openIssueCount
}
}
}
}
`;
const {repository} = await github.graphql(query, {
owner: context.repo.owner,
repo: context.repo.repo,
title: "${{ inputs.releaseVersion }}"
});
const [milestone] = repository.milestones.nodes.filter(it => it.title === "${{ inputs.releaseVersion }}")
if (!milestone) {
throw new Error('Milestone "${{ inputs.releaseVersion }}" not found');
}
if (milestone.openIssueCount > 0) {
throw new Error(`Milestone "${{ inputs.releaseVersion }}" has ${milestone.openIssueCount} open issue(s)`);
}
const requestBody = {
owner: context.repo.owner,
repo: context.repo.repo,
milestone_number: milestone.number,
state: 'closed',
due_on: new Date().toISOString()
};
console.log(requestBody);
await github.rest.issues.updateMilestone(requestBody);
publish_deployment:
name: Publish to Maven Central
needs: [ verify_reproducibility, verify_consumability, close_github_milestone ]
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
ref: "refs/tags/${{ env.RELEASE_TAG }}"
- name: Release staging repository
if: ${{ inputs.dryRun == false }}
uses: ./.github/actions/run-gradle
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
JRELEASER_MAVENCENTRAL_STAGE: PUBLISH
JRELEASER_MAVENCENTRAL_DEPLOYMENT_ID: ${{ inputs.deploymentId }}
with:
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
arguments: jreleaserDeploy
publish_documentation:
name: Publish documentation
needs: publish_deployment
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
ref: "refs/tags/${{ env.RELEASE_TAG }}"
- name: Install Graphviz and Poppler
run: |
sudo apt-get update
sudo apt-get install --yes graphviz poppler-utils
- name: Configure Git
run: |
git config --global user.name "JUnit Team"
git config --global user.email "team@junit.org"
- name: Build documentation
uses: ./.github/actions/run-gradle
with:
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
arguments: |
--no-build-cache \
--no-configuration-cache \
clean \
gitPublishCopy \
-Pdocumentation.replaceCurrentDocs=${{ contains(inputs.releaseVersion, '-') && 'false' || 'true' }}
- name: Publish documentation
if: ${{ inputs.dryRun == false }}
uses: ./.github/actions/run-gradle
env:
GIT_USERNAME: git
GIT_PASSWORD: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_DOCS_REPO }}
with:
encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
arguments: |
--no-build-cache \
--no-configuration-cache \
gitPublishPush \
-Pdocumentation.replaceCurrentDocs=${{ contains(inputs.releaseVersion, '-') && 'false' || 'true' }}
- name: Wait for deployment to GitHub Pages
if: ${{ inputs.dryRun == false }}
id: pagesDeployment
timeout-minutes: 20
run: |
URL="https://docs.junit.org/${{ inputs.releaseVersion }}/user-guide/junit-user-guide-${{ inputs.releaseVersion }}.pdf"
./.github/scripts/waitForUrl.sh "$URL"
echo "pdfUrl=$URL" >> "$GITHUB_OUTPUT"
- name: Verify integrity of PDF version of User Guide
if: ${{ inputs.dryRun == false }}
run: |
curl --silent --fail --location --output /tmp/junit-user-guide.pdf "${{ steps.pagesDeployment.outputs.pdfUrl }}"
pdfinfo /tmp/junit-user-guide.pdf
update_examples:
name: Update examples
needs: publish_deployment
runs-on: ubuntu-latest
steps:
- name: Check out examples repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ github.repository_owner }}/junit-examples
token: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_EXAMPLES_REPO }}
fetch-depth: 1
- name: Set up JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
java-version: 24
distribution: temurin
- uses: sbt/setup-sbt@26ab4b0fa1c47fa62fc1f6e51823a658fb6c760c # v1.1.7
- name: Update JUnit dependencies in examples
run: java src/Updater.java ${{ inputs.releaseVersion }}
- name: Build examples
if: ${{ inputs.dryRun == false }}
run: java src/Builder.java
- name: Create release branch
run: |
git config user.name "JUnit Team"
git config user.email "team@junit.org"
git switch -c "${{ env.RELEASE_TAG }}"
git status
git commit -a -m "Use ${{ inputs.releaseVersion }}"
- name: Push release branch
if: ${{ inputs.dryRun == false }}
run: |
git push origin "${{ env.RELEASE_TAG }}"
- name: Update main branch (only for GA releases)
if: ${{ inputs.dryRun == false && !contains(inputs.releaseVersion, '-') }}
run: |
git switch main
git merge --ff-only "${{ env.RELEASE_TAG }}"
git push origin main
create_github_release:
name: Create GitHub release
if: ${{ inputs.dryRun == false }}
needs: [ publish_documentation, update_examples ]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create GitHub release
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const releaseVersion = "${{ inputs.releaseVersion }}";
const jupiterVersion = releaseVersion;
const vintageVersion = releaseVersion;
const platformVersion = "1." + releaseVersion.substring(2);
const requestBody = {
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: `r${releaseVersion}`,
name: `JUnit ${releaseVersion}`,
generate_release_notes: true,
body: `JUnit ${jupiterVersion} = Platform ${platformVersion} + Jupiter ${jupiterVersion} + Vintage ${vintageVersion}\n\nSee [Release Notes](https://docs.junit.org/${releaseVersion}/release-notes/).`,
prerelease: releaseVersion.includes("-"),
};
console.log(requestBody);
await github.rest.repos.createRelease(requestBody);