From e117b75fe58930fccc1cc042e94ba905dd9e63fd Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 3 Aug 2026 14:14:54 +0200 Subject: [PATCH] Add release train automation. Closes spring-projects/spring-data-build#2891 --- .../actions/release-train-build/action.yml | 19 ++++ .../release-train-build/deployment-spec.yml | 6 ++ .github/actions/release-train-test/action.yml | 19 ++++ .github/workflow-generator.yml | 26 ++++++ .github/workflows/ci.yml | 6 +- .github/workflows/codeql.yml | 1 + .github/workflows/release-train-build.yml | 92 +++++++++++++++++++ .github/workflows/release-train-join.yml | 43 +++++++++ .github/workflows/release-train-leave.yml | 34 +++++++ .github/workflows/release-train-ready.yml | 35 +++++++ .github/workflows/release-train-retry.yml | 34 +++++++ .github/workflows/release-train-test.yml | 85 +++++++++++++++++ .github/workflows/snapshots.yml | 14 ++- 13 files changed, 405 insertions(+), 9 deletions(-) create mode 100644 .github/actions/release-train-build/action.yml create mode 100644 .github/actions/release-train-build/deployment-spec.yml create mode 100644 .github/actions/release-train-test/action.yml create mode 100644 .github/workflow-generator.yml create mode 100644 .github/workflows/release-train-build.yml create mode 100644 .github/workflows/release-train-join.yml create mode 100644 .github/workflows/release-train-leave.yml create mode 100644 .github/workflows/release-train-ready.yml create mode 100644 .github/workflows/release-train-retry.yml create mode 100644 .github/workflows/release-train-test.yml diff --git a/.github/actions/release-train-build/action.yml b/.github/actions/release-train-build/action.yml new file mode 100644 index 000000000..051c6bcc5 --- /dev/null +++ b/.github/actions/release-train-build/action.yml @@ -0,0 +1,19 @@ +name: Build Release +runs: + using: composite + steps: + - name: Write settings.xml + if: ${{ env.RELEASE_TRAIN_SETTINGS_XML != '' }} + shell: bash + run: | + echo "::notice::Writing provided settings-xml input to release-train-settings.xml" + echo "${RELEASE_TRAIN_SETTINGS_XML}" > release-train-settings.xml + - name: Build Release + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean deploy --activate-profiles releaseTrain,release,ci -DaltDeploymentRepository="release-train::file://$(pwd)/deployment-repository" -DskipTests -Dgpg.skip + - name: Distribution build + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean deploy --activate-profiles releaseTrain,distribute -DaltDeploymentRepository="release-train::file://$(pwd)/deployment-repository" -DskipTests -Dgpg.skip + - name: Schema build + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean deploy --activate-profiles releaseTrain,distribute-schema -DaltDeploymentRepository="release-train::file://$(pwd)/deployment-repository" -DskipTests -Dgpg.skip diff --git a/.github/actions/release-train-build/deployment-spec.yml b/.github/actions/release-train-build/deployment-spec.yml new file mode 100644 index 000000000..9980de5e0 --- /dev/null +++ b/.github/actions/release-train-build/deployment-spec.yml @@ -0,0 +1,6 @@ +artifactory: + artifacts: + - pattern: "/**/spring-data-*-docs.zip" + properties: + zip.deployed: "false" + zip.type: "docs" diff --git a/.github/actions/release-train-test/action.yml b/.github/actions/release-train-test/action.yml new file mode 100644 index 000000000..a3a1fb84a --- /dev/null +++ b/.github/actions/release-train-test/action.yml @@ -0,0 +1,19 @@ +name: Test Release +runs: + using: composite + steps: + - name: Write settings.xml + if: ${{ env.RELEASE_TRAIN_SETTINGS_XML != '' }} + shell: bash + run: | + echo "::notice::Writing provided settings-xml input to release-train-settings.xml" + echo "${RELEASE_TRAIN_SETTINGS_XML}" > release-train-settings.xml + - name: Build Release + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean verify --activate-profiles releaseTrain,release,ci -Dgpg.skip + - name: Distribution build + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean verify --activate-profiles releaseTrain,distribute -Dgpg.skip + - name: Schema build + shell: bash + run: ./mvnw --batch-mode --settings release-train-settings.xml clean verify --activate-profiles releaseTrain,distribute-schema -Dgpg.skip diff --git a/.github/workflow-generator.yml b/.github/workflow-generator.yml new file mode 100644 index 000000000..03add55f2 --- /dev/null +++ b/.github/workflow-generator.yml @@ -0,0 +1,26 @@ +workflow: + generator: + project: + java: + versions: + primary: 25 + workflows: + release-train: + join: + watch: false + leave: + watch: false + ready: + watch: false + build: + env: + COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME + COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD + COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL + RELEASE_TRAIN_SETTINGS_XML: vars.RELEASE_TRAIN_SETTINGS_XML + test: + env: + COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME + COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD + COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL + RELEASE_TRAIN_SETTINGS_XML: vars.RELEASE_TRAIN_SETTINGS_XML diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6f73319c..001ed6532 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: matrix: java-version: [ base, main ] name: Build project - runs-on: ubuntu-latest + runs-on: ${{ vars.ACTION_RUNNER || 'ubuntu-latest' }} steps: - uses: actions/checkout@v6 - name: Setup Java and Maven @@ -24,6 +24,10 @@ jobs: - name: Build uses: spring-projects/spring-data-build/actions/maven-build@main env: + OSS_ARTIFACTORY_USR: '${{ secrets.ARTIFACTORY_USERNAME }}' + OSS_ARTIFACTORY_PSW: '${{ secrets.ARTIFACTORY_PASSWORD }}' + COMMERCIAL_ARTIFACTORY_USR: '${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}' + COMMERCIAL_ARTIFACTORY_PSW: '${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}' TESTCONTAINERS_REUSE_ENABLE: true with: settings-xml: '${{ vars.SETTINGS_XML }}' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 411d4a933..a478c78ed 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,6 +4,7 @@ name: "CodeQL Advanced" on: push: + branches: [ main ] pull_request: workflow_dispatch: schedule: diff --git a/.github/workflows/release-train-build.yml b/.github/workflows/release-train-build.yml new file mode 100644 index 000000000..dbe280fb6 --- /dev/null +++ b/.github/workflows/release-train-build.yml @@ -0,0 +1,92 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Build" +run-name: "${{ inputs.callback-ref }} – Build" +"on": + workflow_dispatch: + inputs: + callback: + description: "Repository to which a callback should be made upon completion" + required: true + type: "string" + callback-ref: + description: "Ref in the callback repository to which a callback should be made upon completion" + required: true + type: "string" + release-train-maven-repository-url: + description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train" + required: true + type: "string" +permissions: + contents: "read" +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" +jobs: + build-release: + name: "Build Release" + runs-on: "ubuntu22-2-8" + steps: + - name: "Prevent Re-runs" + id: "prevent-re-runs" + run: |- + if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then + echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry build failures" + exit 1 + fi + - name: "Set up Java" + id: "set-up-java" + uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0 + with: + distribution: "liberica" + java-version: "25" + - name: "Check Out Code" + id: "check-out-code" + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 + - name: "Build Release" + id: "build-release" + uses: "./.github/actions/release-train-build" + env: + COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}" + COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}" + COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}" + RELEASE_TRAIN_SETTINGS_XML: "${{ vars.RELEASE_TRAIN_SETTINGS_XML }}" + - name: "Upload Deployment Repository" + id: "upload-deployment-repository" + uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1 + with: + name: "deployment-repository" + path: "deployment-repository/**" + - name: "Upload Deployment Spec" + id: "upload-deployment-spec" + uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1 + with: + archive: "false" + if-no-files-found: "ignore" + name: "deployment-spec" + path: ".github/actions/release-train-build/deployment-spec.yml" + - name: "Save Build System Caches" + id: "save-build-system-caches" + uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0 + with: + key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}" + path: "~/.m2/repository" + - name: "Send Callback" + id: "send-callback" + if: "${{ !cancelled() }}" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run callback \ + --repo ${{ inputs.callback }} \ + --ref ${{ inputs.callback-ref }} \ + --field commit-hash=${{ steps.check-out-code.outputs.commit }} \ + --field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \ + --field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} \ + --field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \ + --field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.github/workflows/release-train-join.yml b/.github/workflows/release-train-join.yml new file mode 100644 index 000000000..6b5f34277 --- /dev/null +++ b/.github/workflows/release-train-join.yml @@ -0,0 +1,43 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Join" +run-name: "${{ inputs.release-train }} – Join" +"on": + workflow_dispatch: + inputs: + deployment-destination: + description: "Destination to which the release should be deployed" + options: + - "Maven Central" + - "Spring Enterprise" + required: true + type: "choice" + release-train: + description: "Release train" + required: true + type: "string" + release-train-repository: + default: "spring-io/release-train" + description: "Release train repository" + required: true + type: "string" +permissions: + contents: "none" +jobs: + join-release-train: + name: "Join Release Train" + runs-on: "ubuntu-latest" + steps: + - name: "Join Release Train" + id: "join-release-train" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run join \ + --repo ${{ inputs.release-train-repository }} \ + --ref ${{ inputs.release-train }} \ + --field commit-hash=${{ github.sha }} \ + --field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} diff --git a/.github/workflows/release-train-leave.yml b/.github/workflows/release-train-leave.yml new file mode 100644 index 000000000..855fbe8ff --- /dev/null +++ b/.github/workflows/release-train-leave.yml @@ -0,0 +1,34 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Leave" +run-name: "${{ inputs.release-train }} – Leave" +"on": + workflow_dispatch: + inputs: + release-train: + description: "Release train" + required: true + type: "string" + release-train-repository: + default: "spring-io/release-train" + description: "Release train repository" + required: true + type: "string" +permissions: + contents: "none" +jobs: + leave: + name: "Leave" + runs-on: "ubuntu-latest" + steps: + - name: "Leave" + id: "leave" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run leave \ + --repo ${{ inputs.release-train-repository }} \ + --ref ${{ inputs.release-train }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} diff --git a/.github/workflows/release-train-ready.yml b/.github/workflows/release-train-ready.yml new file mode 100644 index 000000000..631765d8e --- /dev/null +++ b/.github/workflows/release-train-ready.yml @@ -0,0 +1,35 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Ready" +run-name: "${{ inputs.release-train }} – Ready" +"on": + workflow_dispatch: + inputs: + release-train: + description: "Release train" + required: true + type: "string" + release-train-repository: + default: "spring-io/release-train" + description: "Release train repository" + required: true + type: "string" +permissions: + contents: "none" +jobs: + ready: + name: "Ready" + runs-on: "ubuntu-latest" + steps: + - name: "Ready" + id: "ready" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run ready \ + --repo ${{ inputs.release-train-repository }} \ + --ref ${{ inputs.release-train }} \ + --field commit-hash=${{ github.sha }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} diff --git a/.github/workflows/release-train-retry.yml b/.github/workflows/release-train-retry.yml new file mode 100644 index 000000000..539ccfd25 --- /dev/null +++ b/.github/workflows/release-train-retry.yml @@ -0,0 +1,34 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Retry" +run-name: "${{ inputs.release-train }} – Retry" +"on": + workflow_dispatch: + inputs: + release-train: + description: "Release train" + required: true + type: "string" + release-train-repository: + default: "spring-io/release-train" + description: "Release train repository" + required: true + type: "string" +permissions: + contents: "none" +jobs: + trigger-retry: + name: "Trigger Retry" + runs-on: "ubuntu-latest" + steps: + - name: "Trigger Retry" + id: "trigger-retry" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run retry \ + --repo ${{ inputs.release-train-repository }} \ + --ref ${{ inputs.release-train }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} diff --git a/.github/workflows/release-train-test.yml b/.github/workflows/release-train-test.yml new file mode 100644 index 000000000..301404158 --- /dev/null +++ b/.github/workflows/release-train-test.yml @@ -0,0 +1,85 @@ +# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit. +# To update it, modify .github/workflow-generator.yml as needed and re-run the generator. + +name: "Release Train – Test" +run-name: "${{ inputs.callback-ref }} – Test" +"on": + workflow_dispatch: + inputs: + callback: + description: "Repository to which a callback should be made upon completion" + required: true + type: "string" + callback-ref: + description: "Ref in the callback repository to which a callback should be made upon completion" + required: true + type: "string" + release-train-maven-repository-url: + description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train" + required: true + type: "string" +permissions: + contents: "read" +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" +jobs: + test-release: + name: "Test Release" + runs-on: "ubuntu22-2-8" + steps: + - name: "Prevent Re-runs" + id: "prevent-re-runs" + run: |- + if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then + echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry test failures" + exit 1 + fi + - name: "Set up Java" + id: "set-up-java" + uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0 + with: + distribution: "liberica" + java-version: "25" + - name: "Check Out Code" + id: "check-out-code" + uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0 + - name: "Restore Build System Caches" + id: "restore-build-system-caches" + uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0 + with: + key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}" + path: "~/.m2/repository" + - name: "Test Release" + id: "test-release" + uses: "./.github/actions/release-train-test" + env: + COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}" + COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}" + COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}" + RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}" + RELEASE_TRAIN_SETTINGS_XML: "${{ vars.RELEASE_TRAIN_SETTINGS_XML }}" + - name: "Send Callback" + id: "send-callback" + if: "${{ !cancelled() }}" + env: + GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}" + run: |- + gh workflow run callback \ + --repo ${{ inputs.callback }} \ + --ref ${{ inputs.callback-ref }} \ + --field commit-hash=${{ steps.check-out-code.outputs.commit }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} \ + --field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \ + --field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - name: "Upload Build System Reports" + id: "upload-build-system-reports" + if: "${{ failure() }}" + uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1 + with: + name: "build-system-reports" + path: |- + **/target/surefire-reports + **/target/failsafe-reports diff --git a/.github/workflows/snapshots.yml b/.github/workflows/snapshots.yml index dd1d4d2d1..0010b6e48 100644 --- a/.github/workflows/snapshots.yml +++ b/.github/workflows/snapshots.yml @@ -3,7 +3,7 @@ name: Snapshots on: workflow_dispatch: push: - branches: [ main, 'issue/**' ] + branches: [ main, 'issue/**', 'release/**' ] permissions: read-all @@ -11,7 +11,7 @@ jobs: build-snapshots: name: Build and deploy snapshots if: ${{ github.repository_owner == 'spring-projects' }} - runs-on: ubuntu-latest + runs-on: ${{ vars.ACTION_RUNNER || 'ubuntu-latest' }} steps: - uses: actions/checkout@v6 - name: Setup Java and Maven @@ -21,12 +21,10 @@ jobs: - name: Deploy to Artifactory uses: spring-projects/spring-data-build/actions/maven-artifactory-deploy@main env: - TESTCONTAINERS_REUSE_ENABLE: true + OSS_ARTIFACTORY_USR: '${{ secrets.ARTIFACTORY_USERNAME }}' + OSS_ARTIFACTORY_PSW: '${{ secrets.ARTIFACTORY_PASSWORD }}' + COMMERCIAL_ARTIFACTORY_USR: '${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}' + COMMERCIAL_ARTIFACTORY_PSW: '${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}' with: build-name: 'spring-data-elasticsearch' - username: '${{ secrets.ARTIFACTORY_USERNAME }}' - password: '${{ secrets.ARTIFACTORY_PASSWORD }}' - context-url: '${{ vars.ARTIFACTORY_CONTEXT_URL }}' - repository: '${{ vars.ARTIFACTORY_REPOSITORY }}' - project: '${{ vars.ARTIFACTORY_PROJECT }}' settings-xml: '${{ vars.SETTINGS_XML }}'