From 511bbc03d9e8709cc59f6dc404096d3a19325cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Mon, 5 Oct 2026 10:48:15 +0200 Subject: [PATCH] Apply release train infrastructure This commit moves our previous release process using dedicated workflows to the use of the release orchestrator. The necessary workflows have been added in this commit as well as the two goals required to build and test the release. Post-release actions have been moved to a publicize-release workflow. Closes gh-51883 --- .../actions/publish-gradle-plugin/action.yml | 2 +- .../publish-gradle-plugin/artifacts.spec | 1 - .../actions/release-train-build/action.yml | 7 + .../release-train-build/deployment-spec.yml | 3 + .github/actions/release-train-test/action.yml | 7 + .../actions/sync-to-maven-central/action.yml | 36 ---- .../sync-to-maven-central/artifacts.spec | 20 -- .github/workflow-generator.yml | 20 ++ .github/workflows/distribute.yml | 45 ----- .github/workflows/publicize-release.yml | 98 ++++++++++ .github/workflows/release-milestone.yml | 131 ------------- .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 | 91 +++++++++ .github/workflows/release.yml | 178 ------------------ 18 files changed, 502 insertions(+), 412 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 delete mode 100644 .github/actions/sync-to-maven-central/action.yml delete mode 100644 .github/actions/sync-to-maven-central/artifacts.spec create mode 100644 .github/workflow-generator.yml delete mode 100644 .github/workflows/distribute.yml create mode 100644 .github/workflows/publicize-release.yml delete mode 100644 .github/workflows/release-milestone.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 delete mode 100644 .github/workflows/release.yml diff --git a/.github/actions/publish-gradle-plugin/action.yml b/.github/actions/publish-gradle-plugin/action.yml index 5707761dc5e..b0d006e5a4c 100644 --- a/.github/actions/publish-gradle-plugin/action.yml +++ b/.github/actions/publish-gradle-plugin/action.yml @@ -26,7 +26,7 @@ runs: JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - name: Download Artifacts shell: bash - run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-boot-{0}', inputs.plugin-version) }};buildNumber=${{ inputs.build-number }}' + run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-boot-{0}', inputs.plugin-version) }}' - name: Set Up Java uses: actions/setup-java@v6 with: diff --git a/.github/actions/publish-gradle-plugin/artifacts.spec b/.github/actions/publish-gradle-plugin/artifacts.spec index f84a25bb9b1..163a083f3ac 100644 --- a/.github/actions/publish-gradle-plugin/artifacts.spec +++ b/.github/actions/publish-gradle-plugin/artifacts.spec @@ -6,7 +6,6 @@ "$and": [ { "@build.name": "${buildName}", - "@build.number": "${buildNumber}", "path": { "$match": "org/springframework/boot/spring-boot-gradle-plugin/*" } diff --git a/.github/actions/release-train-build/action.yml b/.github/actions/release-train-build/action.yml new file mode 100644 index 00000000000..c67b712cb7d --- /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 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..69cbf8abd9d --- /dev/null +++ b/.github/actions/release-train-build/deployment-spec.yml @@ -0,0 +1,3 @@ +maven-central: + excludes: + - "org/springframework/boot/spring-boot-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..8351f7635c5 --- /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 build -x dockerTest -x reclaimDockerSpace diff --git a/.github/actions/sync-to-maven-central/action.yml b/.github/actions/sync-to-maven-central/action.yml deleted file mode 100644 index 2cf780c081d..00000000000 --- a/.github/actions/sync-to-maven-central/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Sync to Maven Central -description: 'Syncs a release to Maven Central and waits for it to be available for use' -inputs: - central-token-password: - description: 'Password for authentication with central.sonatype.com' - required: true - central-token-username: - description: 'Username for authentication with central.sonatype.com' - required: true - jfrog-cli-config-token: - description: 'Config token for the JFrog CLI' - required: true - spring-boot-version: - description: 'Version of Spring Boot that is being synced to Central' - required: true -runs: - using: composite - steps: - - name: Set Up JFrog CLI - uses: jfrog/setup-jfrog-cli@68c0efdc951dd2359bbbfa15c48bc67b3ae8e4d6 # v5.2.0 - env: - JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} - - name: Download Release Artifacts - shell: bash - run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-boot-{0}', inputs.spring-boot-version) }};buildNumber=${{ github.run_number }}' - - name: Sync - uses: spring-io/central-publish-action@0c03960e9b16fdfe70e2443e1d5393cbc3a35622 # v0.3.0 - with: - token: ${{ inputs.central-token-password }} - token-name: ${{ inputs.central-token-username }} - ignore-already-exists-error: true - timeout: "90m" - - name: Await - uses: ./.github/actions/await-http-resource - with: - url: ${{ format('https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot/{0}/spring-boot-{0}.jar', inputs.spring-boot-version) }} diff --git a/.github/actions/sync-to-maven-central/artifacts.spec b/.github/actions/sync-to-maven-central/artifacts.spec deleted file mode 100644 index ee3d451af33..00000000000 --- a/.github/actions/sync-to-maven-central/artifacts.spec +++ /dev/null @@ -1,20 +0,0 @@ -{ - "files": [ - { - "aql": { - "items.find": { - "$and": [ - { - "@build.name": "${buildName}", - "@build.number": "${buildNumber}", - "path": { - "$nmatch": "org/springframework/boot/spring-boot-docs/*" - } - } - ] - } - }, - "target": "nexus/" - } - ] -} diff --git a/.github/workflow-generator.yml b/.github/workflow-generator.yml new file mode 100644 index 00000000000..d5159bac482 --- /dev/null +++ b/.github/workflow-generator.yml @@ -0,0 +1,20 @@ +workflow: + generator: + project: + java: + versions: + primary: 25 + workflows: + release-train: + build: + env: + COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL + COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME + COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD + test: + env: + COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL + COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME + COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD + free-disk-space: true + runs-on: ubuntu22-4-16 diff --git a/.github/workflows/distribute.yml b/.github/workflows/distribute.yml deleted file mode 100644 index e8462178fe5..00000000000 --- a/.github/workflows/distribute.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Distribute -on: - workflow_dispatch: - inputs: - build-number: - description: 'Number of the build to use to create the bundle' - required: true - type: string - create-bundle: - description: 'Whether to create the bundle. If unchecked, only the bundle distribution is executed' - required: true - type: boolean - default: true - version: - description: 'Version to bundle and distribute' - required: true - type: string -permissions: - contents: read -jobs: - distribute-spring-enterprise-release-bundle: - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Create Bundle - if: ${{ vars.COMMERCIAL && inputs.create-bundle }} - shell: bash - run: | - curl -s -u "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}:${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}" \ - -X POST -H "X-JFrog-Signing-Key-Name: packagesKey" -H "Content-Type: application/json" \ - "https://usw1.packages.broadcom.com/lifecycle/api/v2/release_bundle?project=spring" \ - -d '{"release_bundle_name": "TNZ-spring-boot-commercial", "release_bundle_version": "${{ inputs.version }}", "skip_docker_manifest_resolution": true, "source_type": "builds", "source": {"builds": [ {"build_repository": "spring-build-info", "build_name": "spring-boot-commercial-${{ inputs.version }}", "build_number": "${{ inputs.build-number }}", "include_dependencies": false}]}}' | \ - jq -e 'if has("repository_key") then . else halt_error end' - - name: Sleep - if: ${{ vars.COMMERCIAL && inputs.create-bundle }} - shell: bash - run: sleep 30 - - name: Distribute Bundle - if: ${{ vars.COMMERCIAL }} - shell: bash - run: | - curl -s -u "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}:${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}" \ - -X POST -H "Content-Type: application/json" \ - "https://usw1.packages.broadcom.com/lifecycle/api/v2/distribution/distribute/TNZ-spring-boot-commercial/${{ inputs.version }}?project=spring" \ - -d '{"auto_create_missing_repositories": "false", "distribution_rules": [{"site_name": "JP-SaaS"}], "modifications": {"mappings": [{"input": "spring-enterprise-maven-prod-local/(.*)", "output": "spring-enterprise/$1"}]}}' | \ - jq -e 'if has("id") then . else halt_error end' diff --git a/.github/workflows/publicize-release.yml b/.github/workflows/publicize-release.yml new file mode 100644 index 00000000000..589e5a208b6 --- /dev/null +++ b/.github/workflows/publicize-release.yml @@ -0,0 +1,98 @@ +name: Publicize Release +on: + push: + tags: + - v4.0.[0-9]+ +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} +jobs: + read-version: + name: Read Version + runs-on: 'ubuntu-latest' + steps: + - name: Check Out Code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Read Version From gradle.properties + id: read-version + shell: bash + run: | + version=$(sed -n 's/version=\(.*\)/\1/p' gradle.properties) + echo "Version is $version" + echo "version=$version" >> $GITHUB_OUTPUT + outputs: + version: ${{ steps.read-version.outputs.version }} + publish-gradle-plugin: + name: Publish Gradle Plugin + if: ${{ !vars.COMMERCIAL }} + needs: + - read-version + runs-on: 'ubuntu-latest' + steps: + - name: Check Out Code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Publish + uses: ./.github/actions/publish-gradle-plugin + with: + build-number: '2026.4' + gradle-plugin-publish-key: ${{ secrets.GRADLE_PLUGIN_PUBLISH_KEY }} + gradle-plugin-publish-secret: ${{ secrets.GRADLE_PLUGIN_PUBLISH_SECRET }} + jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} + plugin-version: ${{ needs.read-version.outputs.version }} + publish-to-sdkman: + name: Publish to SDKMAN! + needs: + - read-version + runs-on: 'ubuntu-latest' + steps: + - name: Check Out Code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Publish to SDKMAN! + uses: ./.github/actions/publish-to-sdkman + with: + make-default: false + sdkman-consumer-key: ${{ secrets.SDKMAN_CONSUMER_KEY }} + sdkman-consumer-token: ${{ secrets.SDKMAN_CONSUMER_TOKEN }} + spring-boot-version: ${{ needs.read-version.outputs.version }} + update-homebrew-tap: + name: Update Homebrew Tap + needs: + - read-version + runs-on: 'ubuntu-latest' + steps: + - name: Check Out Code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Update Homebrew Tap + uses: ./.github/actions/update-homebrew-tap + with: + spring-boot-version: ${{ needs.read-version.outputs.version }} + token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} + trigger-docs-build: + name: Trigger Docs Build + needs: + - read-version + permissions: + actions: write + runs-on: 'ubuntu-latest' + steps: + - name: Run Deploy Docs Workflow + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }} -f build-version=${{ needs.read-version.outputs.version }} + create-github-release: + name: Create GitHub Release + needs: + - read-version + - publish-gradle-plugin + - publish-to-sdkman + - trigger-docs-build + - update-homebrew-tap + runs-on: 'ubuntu-latest' + steps: + - name: Check Out Code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Create GitHub Release + uses: ./.github/actions/create-github-release + with: + commercial: ${{ vars.COMMERCIAL }} + milestone: ${{ needs.read-version.outputs.version }} + token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release-milestone.yml b/.github/workflows/release-milestone.yml deleted file mode 100644 index 532e41d4817..00000000000 --- a/.github/workflows/release-milestone.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: Release Milestone -on: - push: - tags: - - v4.0.0-M[0-9] - - v4.0.0-RC[0-9] -permissions: - contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} -jobs: - build-and-stage-release: - name: Build and Stage Release - if: ${{ github.repository == 'spring-projects/spring-boot' }} - runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Build and Publish - id: build-and-publish - uses: ./.github/actions/build - with: - develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - gradle-cache-read-only: false - publish: true - - name: Stage Release - uses: spring-io/artifactory-deploy-action@9cb1c7df78cd8d1223de1dde974819fa6378af9f # v0.0.6 - with: - build-name: ${{ format('spring-boot-{0}', steps.build-and-publish.outputs.version)}} - folder: 'deployment-repository' - password: ${{ secrets.ARTIFACTORY_PASSWORD }} - repository: 'libs-staging-local' - signing-key: ${{ secrets.GPG_PRIVATE_KEY }} - signing-passphrase: ${{ secrets.GPG_PASSPHRASE }} - threads: 8 - uri: 'https://repo.spring.io' - username: ${{ secrets.ARTIFACTORY_USERNAME }} - outputs: - version: ${{ steps.build-and-publish.outputs.version }} - verify: - name: Verify - needs: build-and-stage-release - uses: ./.github/workflows/verify.yml - secrets: - commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_RO_PASSWORD }} - commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_RO_USERNAME }} - google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} - opensource-repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }} - opensource-repository-username: ${{ secrets.ARTIFACTORY_USERNAME }} - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} - with: - staging: true - version: ${{ needs.build-and-stage-release.outputs.version }} - sync-to-maven-central: - name: Sync to Maven Central - if: ${{ !vars.COMMERCIAL }} - needs: - - build-and-stage-release - - verify - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Sync to Maven Central - uses: ./.github/actions/sync-to-maven-central - with: - central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }} - central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }} - jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} - spring-boot-version: ${{ needs.build-and-stage-release.outputs.version }} - promote-release: - name: Promote Release - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@68c0efdc951dd2359bbbfa15c48bc67b3ae8e4d6 # v5.2.0 - env: - JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }} - - name: Promote build - run: jfrog rt build-promote ${{ format('spring-boot-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-milestone-local - publish-gradle-plugin: - name: Publish Gradle Plugin - if: ${{ !vars.COMMERCIAL }} - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Publish - uses: ./.github/actions/publish-gradle-plugin - with: - gradle-plugin-publish-key: ${{ secrets.GRADLE_PLUGIN_PUBLISH_KEY }} - gradle-plugin-publish-secret: ${{ secrets.GRADLE_PLUGIN_PUBLISH_SECRET }} - jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} - plugin-version: ${{ needs.build-and-stage-release.outputs.version }} - trigger-docs-build: - name: Trigger Docs Build - needs: - - build-and-stage-release - - promote-release - permissions: - actions: write - runs-on: ubuntu-latest - steps: - - name: Run Deploy Docs Workflow - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }} -f build-version=${{ needs.build-and-stage-release.outputs.version }} - create-github-release: - name: Create GitHub Release - needs: - - build-and-stage-release - - promote-release - - publish-gradle-plugin - - trigger-docs-build - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Create GitHub Release - uses: ./.github/actions/create-github-release - with: - commercial: ${{ vars.COMMERCIAL }} - milestone: ${{ needs.build-and-stage-release.outputs.version }} - pre-release: true - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} 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..b10767ae6e2 --- /dev/null +++ b/.github/workflows/release-train-test.yml @@ -0,0 +1,91 @@ +# 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-4-16" + 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: "Free Disk Space" + id: "free-disk-space" + if: "${{ runner.os == 'Linux' }}" + uses: "jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be" # v1.3.1 + with: + docker-images: "false" + tool-cache: "true" + - 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" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1064b41207f..00000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,178 +0,0 @@ -name: Release -on: - push: - tags: - - v4.0.[0-9]+ -permissions: - contents: read -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} -jobs: - build-and-stage-release: - name: Build and Stage Release - if: ${{ github.repository == 'spring-projects/spring-boot' || github.repository == 'spring-projects/spring-boot-commercial' }} - runs-on: ${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Build and Publish - id: build-and-publish - uses: ./.github/actions/build - with: - commercial-release-repository-url: ${{ vars.COMMERCIAL_RELEASE_REPO_URL }} - commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }} - commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }} - commercial-snapshot-repository-url: ${{ vars.COMMERCIAL_SNAPSHOT_REPO_URL }} - develocity-access-key: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - gradle-cache-read-only: false - publish: true - - name: Stage Release - uses: spring-io/artifactory-deploy-action@9cb1c7df78cd8d1223de1dde974819fa6378af9f # v0.0.6 - with: - build-name: ${{ vars.COMMERCIAL && format('spring-boot-commercial-{0}', steps.build-and-publish.outputs.version) || format('spring-boot-{0}', steps.build-and-publish.outputs.version) }} - folder: 'deployment-repository' - password: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_PASSWORD || secrets.ARTIFACTORY_PASSWORD }} - project: ${{ vars.COMMERCIAL && 'spring' }} - repository: ${{ vars.COMMERCIAL && 'spring-enterprise-maven-stage-local' || 'libs-staging-local' }} - signing-key: ${{ secrets.GPG_PRIVATE_KEY }} - signing-passphrase: ${{ secrets.GPG_PASSPHRASE }} - threads: 8 - uri: ${{ vars.COMMERCIAL_DEPLOY_REPO_URL || 'https://repo.spring.io' }} - username: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_ARTIFACTORY_USERNAME || secrets.ARTIFACTORY_USERNAME }} - - name: Send Notification - if: failure() - uses: ./.github/actions/send-notification - with: - run-name: ${{ format('{0} | Release Staging | {1}', github.ref_name, inputs.version) }} - status: ${{ job.status }} - webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} - outputs: - version: ${{ steps.build-and-publish.outputs.version }} - verify: - name: Verify - needs: build-and-stage-release - uses: ./.github/workflows/verify.yml - secrets: - commercial-repository-password: ${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }} - commercial-repository-username: ${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }} - google-chat-webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} - opensource-repository-password: ${{ secrets.ARTIFACTORY_PASSWORD }} - opensource-repository-username: ${{ secrets.ARTIFACTORY_USERNAME }} - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} - with: - staging: true - version: ${{ needs.build-and-stage-release.outputs.version }} - sync-to-maven-central: - name: Sync to Maven Central - if: ${{ !vars.COMMERCIAL }} - needs: - - build-and-stage-release - - verify - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Sync to Maven Central - uses: ./.github/actions/sync-to-maven-central - with: - central-token-password: ${{ secrets.CENTRAL_TOKEN_PASSWORD }} - central-token-username: ${{ secrets.CENTRAL_TOKEN_USERNAME }} - jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} - spring-boot-version: ${{ needs.build-and-stage-release.outputs.version }} - promote-release: - name: Promote Release - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Set up JFrog CLI - uses: jfrog/setup-jfrog-cli@68c0efdc951dd2359bbbfa15c48bc67b3ae8e4d6 # v5.2.0 - env: - JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }} - - name: Promote open source build - if: ${{ !vars.COMMERCIAL }} - run: jfrog rt build-promote ${{ format('spring-boot-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} libs-release-local - - name: Promote commercial build - if: ${{ vars.COMMERCIAL }} - run: jfrog rt build-promote ${{ format('spring-boot-commercial-{0}', needs.build-and-stage-release.outputs.version)}} ${{ github.run_number }} spring-enterprise-maven-prod-local --project spring - publish-gradle-plugin: - name: Publish Gradle Plugin - if: ${{ !vars.COMMERCIAL }} - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Publish - uses: ./.github/actions/publish-gradle-plugin - with: - gradle-plugin-publish-key: ${{ secrets.GRADLE_PLUGIN_PUBLISH_KEY }} - gradle-plugin-publish-secret: ${{ secrets.GRADLE_PLUGIN_PUBLISH_SECRET }} - jfrog-cli-config-token: ${{ secrets.JF_ARTIFACTORY_SPRING }} - plugin-version: ${{ needs.build-and-stage-release.outputs.version }} - publish-to-sdkman: - name: Publish to SDKMAN! - if: ${{ !vars.COMMERCIAL }} - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Publish to SDKMAN! - uses: ./.github/actions/publish-to-sdkman - with: - make-default: false - sdkman-consumer-key: ${{ secrets.SDKMAN_CONSUMER_KEY }} - sdkman-consumer-token: ${{ secrets.SDKMAN_CONSUMER_TOKEN }} - spring-boot-version: ${{ needs.build-and-stage-release.outputs.version }} - update-homebrew-tap: - name: Update Homebrew Tap - needs: - - build-and-stage-release - - sync-to-maven-central - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Update Homebrew Tap - uses: ./.github/actions/update-homebrew-tap - with: - spring-boot-version: ${{ needs.build-and-stage-release.outputs.version }} - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} - trigger-docs-build: - name: Trigger Docs Build - needs: - - build-and-stage-release - - promote-release - permissions: - actions: write - runs-on: ubuntu-latest - steps: - - name: Run Deploy Docs Workflow - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh workflow run deploy-docs.yml --repo ${{ github.repository }} -r docs-build -f build-refname=${{ github.ref_name }} -f build-version=${{ needs.build-and-stage-release.outputs.version }} - create-github-release: - name: Create GitHub Release - needs: - - build-and-stage-release - - promote-release - - publish-gradle-plugin - - publish-to-sdkman - - trigger-docs-build - - update-homebrew-tap - runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} - steps: - - name: Check Out Code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - name: Create GitHub Release - uses: ./.github/actions/create-github-release - with: - commercial: ${{ vars.COMMERCIAL }} - milestone: ${{ needs.build-and-stage-release.outputs.version }} - token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}