diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..e12d999ad6d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Community Support + url: https://stackoverflow.com/tags/spring-boot + about: Please ask and answer questions on StackOverflow with the tag `spring-boot`. diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 00000000000..e94a911d37c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,37 @@ +--- +name: General +about: Bugs, enhancements, documentation, tasks. +title: '' +labels: '' +assignees: '' +--- + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..8ef2b756d14 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ + diff --git a/.github/actions/await-http-resource/action.yml b/.github/actions/await-http-resource/action.yml new file mode 100644 index 00000000000..ba177fb757b --- /dev/null +++ b/.github/actions/await-http-resource/action.yml @@ -0,0 +1,20 @@ +name: Await HTTP Resource +description: 'Waits for an HTTP resource to be available (a HEAD request succeeds)' +inputs: + url: + description: 'URL of the resource to await' + required: true +runs: + using: composite + steps: + - name: Await HTTP resource + shell: bash + run: | + url=${{ inputs.url }} + echo "Waiting for $url" + until curl --fail --head --silent ${{ inputs.url }} > /dev/null + do + echo "." + sleep 60 + done + echo "$url is available" diff --git a/.github/actions/create-github-release/action.yml b/.github/actions/create-github-release/action.yml new file mode 100644 index 00000000000..275ff632f58 --- /dev/null +++ b/.github/actions/create-github-release/action.yml @@ -0,0 +1,34 @@ +name: Create GitHub Release +description: 'Create the release on GitHub with a changelog' +inputs: + commercial: + description: 'Whether to generate the changelog for the commercial release' + required: true + latest: + description: 'Whether the release is the latest release' + required: false + default: 'false' + milestone: + description: 'Name of the GitHub milestone for which a release will be created' + required: true + pre-release: + description: 'Whether the release is a pre-release (a milestone or release candidate)' + required: false + default: 'false' + token: + description: 'Token to use for authentication with GitHub' + required: true +runs: + using: composite + steps: + - name: Generate Changelog + uses: spring-io/github-changelog-generator@f7d7a87a3e7c627ecb8c26cf086c38ac5a939721 #v0.0.14 + with: + config-file: ${{ inputs.commercial && '.github/actions/create-github-release/changelog-generator-commercial.yml' || '.github/actions/create-github-release/changelog-generator-oss.yml' }} + milestone: ${{ inputs.milestone }} + token: ${{ inputs.token }} + - name: Create GitHub Release + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.token }} + run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.pre-release == 'true' && '--prerelease' || format('--latest={0}', inputs.latest) }} diff --git a/.github/actions/create-github-release/changelog-generator-commercial.yml b/.github/actions/create-github-release/changelog-generator-commercial.yml new file mode 100644 index 00000000000..e7ff74533e7 --- /dev/null +++ b/.github/actions/create-github-release/changelog-generator-commercial.yml @@ -0,0 +1,30 @@ +changelog: + sections: + - title: ":warning: Attention Required" + labels: + - "for: upgrade-attention" + summary: + mode: "member-comment" + config: + prefix: "Attention required:" + - title: ":star: New Features" + labels: + - "type: enhancement" + - title: ":lady_beetle: Bug Fixes" + labels: + - "type: bug" + - "type: regression" + - title: ":notebook_with_decorative_cover: Documentation" + labels: + - "type: documentation" + - title: ":hammer: Dependency Upgrades" + sort: "title" + labels: + - "type: dependency-upgrade" + issues: + generate_links: false + ports: + - label: "status: forward-port" + bodyExpression: 'Forward port of issue #(\d+).*' + - label: "status: back-port" + bodyExpression: 'Back port of issue #(\d+).*' diff --git a/.github/actions/create-github-release/changelog-generator-oss.yml b/.github/actions/create-github-release/changelog-generator-oss.yml new file mode 100644 index 00000000000..56f6608cd7a --- /dev/null +++ b/.github/actions/create-github-release/changelog-generator-oss.yml @@ -0,0 +1,34 @@ +changelog: + sections: + - title: ":warning: Attention Required" + labels: + - "for: upgrade-attention" + summary: + mode: "member-comment" + config: + prefix: "Attention required:" + - title: ":star: New Features" + labels: + - "type: enhancement" + - title: ":lady_beetle: Bug Fixes" + labels: + - "type: bug" + - "type: regression" + - title: ":notebook_with_decorative_cover: Documentation" + labels: + - "type: documentation" + - title: ":hammer: Dependency Upgrades" + sort: "title" + labels: + - "type: dependency-upgrade" + summary: + mode: "body-regex" + config: + expression: '(Upgrade to \[.*\]\(.*\)).*' + issues: + generate_links: true + ports: + - label: "status: forward-port" + bodyExpression: 'Forward port of issue #(\d+).*' + - label: "status: back-port" + bodyExpression: 'Back port of issue #(\d+).*' diff --git a/.github/actions/prepare-gradle-build/action.yml b/.github/actions/prepare-gradle-build/action.yml new file mode 100644 index 00000000000..508c88ae787 --- /dev/null +++ b/.github/actions/prepare-gradle-build/action.yml @@ -0,0 +1,69 @@ +name: Prepare Gradle Build +description: 'Prepares a Gradle build. Sets up Java and Gradle and configures Gradle properties' +inputs: + cache-read-only: + description: 'Whether Gradle''s cache should be read only' + required: false + default: 'true' + develocity-access-key: + description: 'Access key for authentication with ge.spring.io' + required: false + java-distribution: + description: 'Java distribution to use' + required: false + default: 'liberica' + java-early-access: + description: 'Whether the Java version is in early access. When true, forces java-distribution to temurin' + required: false + default: 'false' + java-toolchain: + description: 'Whether a Java toolchain should be used' + required: false + default: 'false' + java-version: + description: 'Java version to use for the build' + required: false + default: '25' +runs: + using: composite + steps: + - name: Free Disk Space + if: ${{ runner.os == 'Linux' }} + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + with: + tool-cache: true + docker-images: false + - name: Set Up Java + uses: actions/setup-java@v5 + with: + distribution: ${{ inputs.java-early-access == 'true' && 'temurin' || (inputs.java-distribution || 'liberica') }} + java-version: | + ${{ inputs.java-early-access == 'true' && format('{0}-ea', inputs.java-version) || inputs.java-version }} + ${{ inputs.java-toolchain == 'true' && '25' || '' }} + - name: Set Up Gradle With Read/Write Cache + if: ${{ inputs.cache-read-only == 'false' }} + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + with: + cache-provider: basic + cache-read-only: false + develocity-access-key: ${{ inputs.develocity-access-key }} + - name: Set Up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + with: + cache-provider: basic + develocity-access-key: ${{ inputs.develocity-access-key }} + develocity-token-expiry: 4 + - name: Configure Gradle Properties + shell: bash + run: | + echo 'systemProp.user.name=spring-builds+github' >> $GRADLE_USER_HOME/gradle.properties + echo 'systemProp.org.gradle.internal.launcher.welcomeMessageEnabled=false' >> $GRADLE_USER_HOME/gradle.properties + echo 'org.gradle.daemon=false' >> $GRADLE_USER_HOME/gradle.properties + - name: Configure Toolchain Properties + if: ${{ inputs.java-toolchain == 'true' }} + shell: bash + run: | + echo toolchainVersion=${{ inputs.java-version }} >> $GRADLE_USER_HOME/gradle.properties + echo systemProp.org.gradle.java.installations.auto-detect=false >> $GRADLE_USER_HOME/gradle.properties + echo systemProp.org.gradle.java.installations.auto-download=false >> $GRADLE_USER_HOME/gradle.properties + echo systemProp.org.gradle.java.installations.paths=${{ format('$JAVA_HOME_{0}_X64', inputs.java-version) }} >> $GRADLE_USER_HOME/gradle.properties diff --git a/.github/actions/print-jvm-thread-dumps/action.yml b/.github/actions/print-jvm-thread-dumps/action.yml new file mode 100644 index 00000000000..bcaebf3676a --- /dev/null +++ b/.github/actions/print-jvm-thread-dumps/action.yml @@ -0,0 +1,17 @@ +name: Print JVM thread dumps +description: 'Prints a thread dump for all running JVMs' +runs: + using: composite + steps: + - if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do + jcmd $jvm_pid Thread.print + done + - if: ${{ runner.os == 'Windows' }} + shell: powershell + run: | + foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) { + jcmd $jvm_pid Thread.print + } diff --git a/.github/actions/publish-gradle-plugin/action.yml b/.github/actions/publish-gradle-plugin/action.yml new file mode 100644 index 00000000000..e01647c9efd --- /dev/null +++ b/.github/actions/publish-gradle-plugin/action.yml @@ -0,0 +1,38 @@ +name: Publish Gradle Plugin +description: 'Publishes Spring Boot''s Gradle plugin to the Plugin Portal' +inputs: + build-number: + description: 'Build number to use when downloading plugin artifacts' + required: false + default: ${{ github.run_number }} + gradle-plugin-publish-key: + description: 'Gradle publishing key' + required: true + gradle-plugin-publish-secret: + description: 'Gradle publishing secret' + required: true + jfrog-cli-config-token: + description: 'Config token for the JFrog CLI' + required: true + plugin-version: + description: 'Version of the plugin' + required: true +runs: + using: composite + steps: + - name: Set Up JFrog CLI + uses: jfrog/setup-jfrog-cli@deda456d982fc5e9a7a020b63eb0d2968aedd33e # v5.1.0 + env: + 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 }}' + - name: Set Up Java + uses: actions/setup-java@v5 + with: + distribution: 'liberica' + java-version: '17' + - name: Publish + shell: bash + working-directory: ${{ github.action_path }} + run: ${{ github.workspace }}/gradlew publishExisting -Pgradle.publish.key=${{ inputs.gradle-plugin-publish-key }} -Pgradle.publish.secret=${{ inputs.gradle-plugin-publish-secret }} -PbootVersion=${{ inputs.plugin-version }} -PrepositoryRoot=${{ github.workspace }}/repository diff --git a/.github/actions/publish-gradle-plugin/artifacts.spec b/.github/actions/publish-gradle-plugin/artifacts.spec new file mode 100644 index 00000000000..f84a25bb9b1 --- /dev/null +++ b/.github/actions/publish-gradle-plugin/artifacts.spec @@ -0,0 +1,20 @@ +{ + "files": [ + { + "aql": { + "items.find": { + "$and": [ + { + "@build.name": "${buildName}", + "@build.number": "${buildNumber}", + "path": { + "$match": "org/springframework/boot/spring-boot-gradle-plugin/*" + } + } + ] + } + }, + "target": "repository/" + } + ] +} diff --git a/.github/actions/publish-gradle-plugin/build.gradle b/.github/actions/publish-gradle-plugin/build.gradle new file mode 100644 index 00000000000..0ce1e04d4b2 --- /dev/null +++ b/.github/actions/publish-gradle-plugin/build.gradle @@ -0,0 +1,30 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id "com.gradle.plugin-publish" version "1.2.1" +} + +tasks.register("publishExisting", com.gradle.publish.PublishExistingTask) { + pluginId = "org.springframework.boot" + fileRepositoryRoot = new File("${repositoryRoot}") + pluginVersion = "${bootVersion}" + pluginCoordinates = "org.springframework.boot:spring-boot-gradle-plugin:${bootVersion}" + displayName = "Spring Boot Gradle Plugin" + pluginDescription = "Spring Boot Gradle Plugin" + website = "https://spring.io/projects/spring-boot" + vcsUrl = "https://github.com/spring-projects/spring-boot" +} diff --git a/.github/actions/publish-gradle-plugin/settings.gradle b/.github/actions/publish-gradle-plugin/settings.gradle new file mode 100644 index 00000000000..1a5705e043c --- /dev/null +++ b/.github/actions/publish-gradle-plugin/settings.gradle @@ -0,0 +1,16 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + diff --git a/.github/actions/publish-to-sdkman/action.yml b/.github/actions/publish-to-sdkman/action.yml new file mode 100644 index 00000000000..3abdd67e27b --- /dev/null +++ b/.github/actions/publish-to-sdkman/action.yml @@ -0,0 +1,40 @@ +name: Publish to SDKMAN! +description: 'Publishes the release as a new candidate version on SDKMAN!' +inputs: + make-default: + description: 'Whether the release should be made the default version' + required: false + default: 'false' + sdkman-consumer-key: + description: 'Key for publishing to SDKMAN!' + required: true + sdkman-consumer-token: + description: 'Token for publishing to SDKMAN!' + required: true + spring-boot-version: + description: 'Version to publish' + required: true +runs: + using: composite + steps: + - name: Publish Release + shell: bash + run: > + curl -X POST \ + -H "Consumer-Key: ${{ inputs.sdkman-consumer-key }}" \ + -H "Consumer-Token: ${{ inputs.sdkman-consumer-token }}" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{"candidate": "springboot", "version": "${{ inputs.spring-boot-version }}", "url": "${{ format('https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-cli/{0}/spring-boot-cli-{0}-bin.zip', inputs.spring-boot-version) }}"}' \ + https://vendors.sdkman.io/release + - name: Flag Release as Default + if: ${{ inputs.make-default == 'true' }} + shell: bash + run: > + curl -X PUT \ + -H "Consumer-Key: ${{ inputs.sdkman-consumer-key }}" \ + -H "Consumer-Token: ${{ inputs.sdkman-consumer-token }}" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{"candidate": "springboot", "version": "${{ inputs.spring-boot-version }}"}' \ + https://vendors.sdkman.io/default diff --git a/.github/actions/release-train-build/action.yml b/.github/actions/release-train-build/action.yml deleted file mode 100644 index c67b712cb7d..00000000000 --- a/.github/actions/release-train-build/action.yml +++ /dev/null @@ -1,7 +0,0 @@ -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-test/action.yml b/.github/actions/release-train-test/action.yml deleted file mode 100644 index 8351f7635c5..00000000000 --- a/.github/actions/release-train-test/action.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Test Release -runs: - using: composite - steps: - - name: Test Release - shell: bash - run: ./gradlew build -x dockerTest -x reclaimDockerSpace diff --git a/.github/actions/send-notification/action.yml b/.github/actions/send-notification/action.yml new file mode 100644 index 00000000000..b379e67897d --- /dev/null +++ b/.github/actions/send-notification/action.yml @@ -0,0 +1,39 @@ +name: Send Notification +description: 'Sends a Google Chat message as a notification of the job''s outcome' +inputs: + build-scan-url: + description: 'URL of the build scan to include in the notification' + required: false + run-name: + description: 'Name of the run to include in the notification' + required: false + default: ${{ format('{0} {1}', github.ref_name, github.job) }} + status: + description: 'Status of the job' + required: true + webhook-url: + description: 'Google Chat Webhook URL' + required: true +runs: + using: composite + steps: + - name: Prepare Variables + shell: bash + run: | + echo "BUILD_SCAN=${{ inputs.build-scan-url == '' && ' [build scan unavailable]' || format(' [<{0}|Build Scan>]', inputs.build-scan-url) }}" >> "$GITHUB_ENV" + echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV" + - name: Success Notification + if: ${{ inputs.status == 'success' }} + shell: bash + run: | + curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful ${{ env.BUILD_SCAN }}"}' || true + - name: Failure Notification + if: ${{ inputs.status == 'failure' }} + shell: bash + run: | + curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: " *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed* ${{ env.BUILD_SCAN }}"}' || true + - name: Cancel Notification + if: ${{ inputs.status == 'cancelled' }} + shell: bash + run: | + curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true diff --git a/.github/actions/sync-to-maven-central/action.yml b/.github/actions/sync-to-maven-central/action.yml new file mode 100644 index 00000000000..02152b40647 --- /dev/null +++ b/.github/actions/sync-to-maven-central/action.yml @@ -0,0 +1,36 @@ +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@deda456d982fc5e9a7a020b63eb0d2968aedd33e # v5.1.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 new file mode 100644 index 00000000000..ee3d451af33 --- /dev/null +++ b/.github/actions/sync-to-maven-central/artifacts.spec @@ -0,0 +1,20 @@ +{ + "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/actions/update-homebrew-tap/action.yml b/.github/actions/update-homebrew-tap/action.yml new file mode 100644 index 00000000000..e240cb0b119 --- /dev/null +++ b/.github/actions/update-homebrew-tap/action.yml @@ -0,0 +1,36 @@ +name: Update Homebrew Tap +description: Updates the Homebrew Tap for the Spring Boot CLI +inputs: + spring-boot-version: + description: 'The version to publish' + required: true + token: + description: 'Token to use for GitHub authentication' + required: true +runs: + using: composite + steps: + - name: Check Out Homebrew Tap Repo + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: updated-homebrew-tap-repo + repository: spring-io/homebrew-tap + token: ${{ inputs.token }} + - name: Await Formula + uses: ./.github/actions/await-http-resource + with: + url: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-cli/${{ inputs.spring-boot-version }}/spring-boot-cli-${{ inputs.spring-boot-version }}-homebrew.rb + - name: Update Homebrew Tap + shell: bash + run: | + pushd updated-homebrew-tap-repo > /dev/null + curl https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-cli/${{ inputs.spring-boot-version }}/spring-boot-cli-${{ inputs.spring-boot-version }}-homebrew.rb --output spring-boot-cli-${{ inputs.spring-boot-version }}-homebrew.rb + rm spring-boot.rb + mv spring-boot-cli-*.rb spring-boot.rb + git config user.name "Spring Builds" > /dev/null + git config user.email "spring-builds@users.noreply.github.com" > /dev/null + git add spring-boot.rb > /dev/null + git commit -m "Upgrade to Spring Boot ${{ inputs.spring-boot-version }}" > /dev/null + git push + echo "DONE" + popd > /dev/null diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 00000000000..0c4b142e9a7 --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1,2 @@ +require: + members: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..9eab3b40a60 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "type: task" + - "status: waiting-for-triage" + - package-ecosystem: "npm" + directory: "/antora" + schedule: + interval: "weekly" + labels: + - "type: task" + - "status: waiting-for-triage" diff --git a/.github/scripts/reclaim-docker-diskspace.sh b/.github/scripts/reclaim-docker-diskspace.sh new file mode 100755 index 00000000000..e32f3b1d8c8 --- /dev/null +++ b/.github/scripts/reclaim-docker-diskspace.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo "Reclaiming Docker Disk Space" +echo + +docker image ls --format "{{.Size}} {{.ID}} {{.Repository}} {{.Tag}}" | LANG=en_US sort -rh | while read line; do + size=$( echo "$line" | cut -d' ' -f1 | sed -e 's/\.[0-9]*//' | sed -e 's/MB/000000/' | sed -e 's/GB/000000000/' ) + image=$( echo "$line" | cut -d' ' -f2 ) + repository=$( echo "$line" | cut -d' ' -f3 ) + tag=$( echo "$line" | cut -d' ' -f4 ) + echo "Considering $image $repository:$tag $size" + if [[ "$tag" =~ ^[a-f0-9]{32}$ ]]; then + echo "Ignoring GitHub action image $image $repository:$tag" + elif [[ "$tag" == "" ]]; then + echo "Ignoring untagged image $image $repository:$tag" + elif [[ "$size" -lt 200000000 ]]; then + echo "Ignoring small image $image $repository:$tag" + else + echo "Cleaning $image $repository:$tag" + docker image rm $image + fi +done + +echo "Finished cleanup, leaving the following containers:" +echo +docker image ls --format "{{.Size}} {{.ID}} {{.Repository}}:{{.Tag}}" | LANG=en_US sort -rh +echo +df -h +echo diff --git a/.github/workflow-generator.yml b/.github/workflow-generator.yml deleted file mode 100644 index d5159bac482..00000000000 --- a/.github/workflow-generator.yml +++ /dev/null @@ -1,20 +0,0 @@ -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/release-train-build.yml b/.github/workflows/release-train-build.yml deleted file mode 100644 index e2789a7dc1c..00000000000 --- a/.github/workflows/release-train-build.yml +++ /dev/null @@ -1,93 +0,0 @@ -# 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 deleted file mode 100644 index 729914b3f90..00000000000 --- a/.github/workflows/release-train-join.yml +++ /dev/null @@ -1,55 +0,0 @@ -# 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 deleted file mode 100644 index bc0be6711a7..00000000000 --- a/.github/workflows/release-train-leave.yml +++ /dev/null @@ -1,46 +0,0 @@ -# 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 deleted file mode 100644 index 794a29a6292..00000000000 --- a/.github/workflows/release-train-ready.yml +++ /dev/null @@ -1,47 +0,0 @@ -# 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 deleted file mode 100644 index 539ccfd2504..00000000000 --- a/.github/workflows/release-train-retry.yml +++ /dev/null @@ -1,34 +0,0 @@ -# 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 deleted file mode 100644 index b10767ae6e2..00000000000 --- a/.github/workflows/release-train-test.yml +++ /dev/null @@ -1,91 +0,0 @@ -# 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/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 00000000000..a3b956570d3 --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,94 @@ +name: Verify +on: + workflow_call: + inputs: + staging: + description: 'Whether the release to verify is in the staging repository' + required: false + default: false + type: boolean + version: + description: 'Version to verify' + required: true + type: string + secrets: + commercial-repository-password: + description: 'Password for authentication with the commercial repository' + required: false + commercial-repository-username: + description: 'Username for authentication with the commercial repository' + required: false + google-chat-webhook-url: + description: 'Google Chat Webhook URL' + required: true + opensource-repository-password: + description: 'Password for authentication with the open-source repository' + required: false + opensource-repository-username: + description: 'Username for authentication with the open-source repository' + required: false + token: + description: 'Token to use for authentication with GitHub' + required: true +permissions: + contents: read +jobs: + verify: + name: Verify + runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }} + steps: + - name: Check Out Release Verification Tests + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: 'v0.0.15' + repository: spring-projects/spring-boot-release-verification + token: ${{ secrets.token }} + - name: Check Out Send Notification Action + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: send-notification + sparse-checkout: .github/actions/send-notification + - name: Set Up Java + uses: actions/setup-java@v5 + with: + distribution: 'liberica' + java-version: 17 + - name: Set Up Homebrew + if: ${{ !vars.COMMERCIAL }} + uses: Homebrew/actions/setup-homebrew@c8707045ccae42888fe98e86f2ee8938bc7cc193 + with: + stable: true + - name: Set Up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + with: + cache-provider: basic + cache-read-only: false + - name: Configure Gradle Properties + shell: bash + run: | + mkdir -p $HOME/.gradle + echo 'org.gradle.daemon=false' >> $HOME/.gradle/gradle.properties + - name: Run Release Verification Tests + env: + RVT_COMMERCIAL_REPOSITORY_PASSWORD: ${{ secrets.commercial-repository-password }} + RVT_COMMERCIAL_REPOSITORY_USERNAME: ${{ secrets.commercial-repository-username }} + RVT_OSS_REPOSITORY_PASSWORD: ${{ secrets.opensource-repository-password }} + RVT_OSS_REPOSITORY_USERNAME: ${{ secrets.opensource-repository-username }} + RVT_RELEASE_TYPE: ${{ vars.COMMERCIAL && 'commercial' || 'oss' }} + RVT_STAGING: ${{ inputs.staging }} + RVT_VERSION: ${{ inputs.version }} + HOMEBREW_NO_SANDBOX_LINUX: "1" + run: ./gradlew spring-boot-release-verification-tests:test + - name: Upload Build Reports on Failure + if: failure() + uses: actions/upload-artifact@v7 + with: + name: build-reports + path: '**/build/reports/' + - name: Send Notification + if: always() + uses: ./send-notification/.github/actions/send-notification + with: + run-name: ${{ format('{0} | Verification | {1}', github.ref_name, inputs.version) }} + status: ${{ job.status }} + webhook-url: ${{ secrets.google-chat-webhook-url }}