From 89e62e7e31125de9068a06b8828b227d752b77af Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 31 Jul 2026 18:08:03 +0200 Subject: [PATCH] Add release train infrastructure Closes gh-37097 --- .../actions/release-train-build/action.yml | 7 ++ .../release-train-build/deployment-spec.yml | 16 ++++ .github/actions/release-train-test/action.yml | 7 ++ .github/workflow-generator.yml | 18 ++++ .github/workflows/release-train-build.yml | 93 +++++++++++++++++++ .github/workflows/release-train-join.yml | 55 +++++++++++ .github/workflows/release-train-leave.yml | 46 +++++++++ .github/workflows/release-train-ready.yml | 47 ++++++++++ .github/workflows/release-train-retry.yml | 34 +++++++ .github/workflows/release-train-test.yml | 84 +++++++++++++++++ 10 files changed, 407 insertions(+) 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 00000000000..0c7bd36f2e8 --- /dev/null +++ b/.github/actions/release-train-build/action.yml @@ -0,0 +1,7 @@ +name: Build Release +runs: + using: composite + steps: + - name: Build Release + shell: bash + run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository \ No newline at end of file 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 00000000000..7fa3e184919 --- /dev/null +++ b/.github/actions/release-train-build/deployment-spec.yml @@ -0,0 +1,16 @@ +artifactory: + artifacts: + - pattern: "/**/framework-api-*.zip" + properties: + zip.deployed: "false" + zip.name: "spring-framework" + - pattern: "/**/framework-api-*-docs.zip" + properties: + zip.type: "docs" + - pattern: "/**/framework-api-*-schema.zip" + properties: + zip.type: "schema" +maven-central: + excludes: + - "org/springframework/framework-api/**" + - "org/springframework/framework-docs/**" diff --git a/.github/actions/release-train-test/action.yml b/.github/actions/release-train-test/action.yml new file mode 100644 index 00000000000..937f68ecf36 --- /dev/null +++ b/.github/actions/release-train-test/action.yml @@ -0,0 +1,7 @@ +name: Test Release +runs: + using: composite + steps: + - name: Test Release + shell: bash + run: ./gradlew check \ No newline at end of file diff --git a/.github/workflow-generator.yml b/.github/workflow-generator.yml new file mode 100644 index 00000000000..30afeb9d298 --- /dev/null +++ b/.github/workflow-generator.yml @@ -0,0 +1,18 @@ +workflow: + generator: + project: + java: + versions: + primary: 25 + workflows: + release-train: + 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 + 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 \ No newline at end of file diff --git a/.github/workflows/release-train-build.yml b/.github/workflows/release-train-build.yml new file mode 100644 index 00000000000..e2789a7dc1c --- /dev/null +++ b/.github/workflows/release-train-build.yml @@ -0,0 +1,93 @@ +# 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 }}" + - 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: |- + ~/.gradle/caches + ~/.gradle/wrapper + - 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 00000000000..729914b3f90 --- /dev/null +++ b/.github/workflows/release-train-join.yml @@ -0,0 +1,55 @@ +# 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: |- + run_url=$( + 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 }} + ) + echo "Dispatched workflow run. Waiting for $run_url to complete." + run_id=${run_url##*/} + watch_exit_code=0 + gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$? + if [[ $watch_exit_code -eq 0 ]]; then + echo "Workflow run succeeded." + else + echo "Workflow run failed." + fi + exit $watch_exit_code diff --git a/.github/workflows/release-train-leave.yml b/.github/workflows/release-train-leave.yml new file mode 100644 index 00000000000..bc0be6711a7 --- /dev/null +++ b/.github/workflows/release-train-leave.yml @@ -0,0 +1,46 @@ +# 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: |- + run_url=$( + gh workflow run leave \ + --repo ${{ inputs.release-train-repository }} \ + --ref ${{ inputs.release-train }} \ + --field release-branch=${{ github.ref_name }} \ + --field release-repository=${{ github.repository }} + ) + echo "Dispatched workflow run. Waiting for $run_url to complete." + run_id=${run_url##*/} + watch_exit_code=0 + gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$? + if [[ $watch_exit_code -eq 0 ]]; then + echo "Workflow run succeeded." + else + echo "Workflow run failed." + fi + exit $watch_exit_code diff --git a/.github/workflows/release-train-ready.yml b/.github/workflows/release-train-ready.yml new file mode 100644 index 00000000000..794a29a6292 --- /dev/null +++ b/.github/workflows/release-train-ready.yml @@ -0,0 +1,47 @@ +# 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: |- + run_url=$( + 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 }} + ) + echo "Dispatched workflow run. Waiting for $run_url to complete." + run_id=${run_url##*/} + watch_exit_code=0 + gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$? + if [[ $watch_exit_code -eq 0 ]]; then + echo "Workflow run succeeded." + else + echo "Workflow run failed." + fi + exit $watch_exit_code diff --git a/.github/workflows/release-train-retry.yml b/.github/workflows/release-train-retry.yml new file mode 100644 index 00000000000..539ccfd2504 --- /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 00000000000..58415ff4dab --- /dev/null +++ b/.github/workflows/release-train-test.yml @@ -0,0 +1,84 @@ +# 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: |- + ~/.gradle/caches + ~/.gradle/wrapper + - 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 }}" + - 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: "**/build/reports"