├── .gitattributes ├── .github ├── dco.yml ├── dependabot.yml └── workflows │ ├── build-verification.yml │ ├── submit-github-dependency-graph.yml │ └── wrapper-upgrade-execution.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── buildSrc ├── build.gradle ├── settings.gradle └── src │ └── main │ └── groovy │ └── CreateGitTag.groovy ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release ├── changes.md ├── distribution │ └── NOTICE └── version.txt ├── settings.gradle └── src ├── main └── java │ └── org │ └── gradle │ └── wrapperupgrade │ ├── BuildToolStrategy.java │ ├── ExecUtils.java │ ├── GradleBuildToolStrategy.java │ ├── GradleMetadataFetcher.java │ ├── MavenBuildToolStrategy.java │ ├── MavenMetadataFetcher.java │ ├── PullRequestUtils.java │ ├── UpgradeWrapper.java │ ├── WrapperUpgradeDomainObject.java │ ├── WrapperUpgradeExtension.java │ └── WrapperUpgradePlugin.java └── test ├── groovy └── org │ └── gradle │ └── wrapperupgrade │ ├── GradleBuildToolStrategyTest.groovy │ ├── GradleMetadataFetcherTest.groovy │ ├── GradleWrapperUpgradePluginFuncTest.groovy │ ├── MavenBuildToolStrategyTest.groovy │ ├── MavenMetadataFetcherTest.groovy │ ├── MavenWrapperUpgradePluginFuncTest.groovy │ └── PullRequestUtilsTest.groovy └── resources ├── gradle-metadata-all-unknown.json ├── gradle-metadata-all.json ├── gradle-metadata-current-unknown.json ├── gradle-metadata-current.json ├── maven-metadata-none.xml └── maven-metadata.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bat text eol=crlf 2 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | # Disable sign-off check for members of the Gradle GitHub organization 2 | require: 3 | members: false 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "gradle" 5 | directory: "/" 6 | registries: 7 | - gradle-plugin-portal 8 | schedule: 9 | interval: "daily" 10 | time: "02:00" 11 | groups: 12 | fasterxml-jackson: 13 | patterns: 14 | - "com.fasterxml.jackson*" 15 | 16 | - package-ecosystem: "github-actions" 17 | directory: "/" 18 | schedule: 19 | interval: "daily" 20 | time: "02:00" 21 | 22 | registries: 23 | gradle-plugin-portal: 24 | type: maven-repository 25 | url: https://plugins.gradle.org/m2 26 | -------------------------------------------------------------------------------- /.github/workflows/build-verification.yml: -------------------------------------------------------------------------------- 1 | name: Verify Build 2 | 3 | on: [ push, pull_request, workflow_dispatch ] 4 | 5 | jobs: 6 | verification: 7 | name: Verification 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | gradle-version: [ '7.6.2', '8.0.2', '8.1.1', '8.2', '8.3' ] 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Setup git user 16 | # Needed for functional tests executing git commands 17 | run: | 18 | git config --global user.name "bot-githubaction" 19 | git config --global user.email "bot-githubaction@gradle.com" 20 | - name: Set up JDK 21 21 | uses: actions/setup-java@v4 22 | with: 23 | java-version: '21' 24 | distribution: 'temurin' 25 | - name: Set up Gradle 26 | uses: gradle/actions/setup-gradle@v4 27 | with: 28 | develocity-access-key: ${{ secrets.GE_SOLUTIONS_ACCESS_TOKEN }} 29 | - name: Build with Gradle 30 | run: ./gradlew build -x signPluginMavenPublication -i -PtestGradleVersion=${{ matrix.gradle-version }} -Porg.gradle.java.installations.auto-download=false 31 | -------------------------------------------------------------------------------- /.github/workflows/submit-github-dependency-graph.yml: -------------------------------------------------------------------------------- 1 | name: Submit GitHub Dependency Graph 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | generate-and-submit: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-java@v4 17 | with: 18 | distribution: temurin 19 | java-version: 21 20 | - name: Set up Gradle 21 | uses: gradle/actions/setup-gradle@v4 22 | with: 23 | develocity-access-key: ${{ secrets.GE_SOLUTIONS_ACCESS_TOKEN }} 24 | dependency-graph: generate-and-submit 25 | - name: Run gradle to resolve dependencies 26 | run: ./gradlew :ForceDependencyResolutionPlugin_resolveAllDependencies 27 | -------------------------------------------------------------------------------- /.github/workflows/wrapper-upgrade-execution.yml: -------------------------------------------------------------------------------- 1 | name: Execute Wrapper Upgrade 2 | 3 | on: 4 | schedule: 5 | - cron: '0 2 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | upgrade_wrapper: 10 | name: Execution 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Set up Git configuration 14 | env: 15 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | run: git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/" 17 | - name: Import GPG key 18 | uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec 19 | with: 20 | gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }} 21 | passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }} 22 | git_user_signingkey: true 23 | git_commit_gpgsign: true 24 | git_config_global: true 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | - name: Set up JDK 21 28 | uses: actions/setup-java@v4 29 | with: 30 | java-version: '21' 31 | distribution: 'temurin' 32 | - name: Set up Gradle 33 | uses: gradle/actions/setup-gradle@v4 34 | with: 35 | develocity-access-key: ${{ secrets.GE_SOLUTIONS_ACCESS_TOKEN }} 36 | - name: Upgrade Wrappers 37 | run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false 38 | env: 39 | WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .gradle/ 3 | .tmp/ 4 | out/ 5 | build/ 6 | target/ 7 | *.DS_Store 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wrapper Upgrade Gradle Plugin 2 | 3 | [![Verify Build](https://github.com/gradle/wrapper-upgrade-gradle-plugin/actions/workflows/build-verification.yml/badge.svg?branch=main)](https://github.com/gradle/wrapper-upgrade-gradle-plugin/actions/workflows/build-verification.yml) 4 | [![Plugin Portal](https://img.shields.io/maven-metadata/v?metadataUrl=https://plugins.gradle.org/m2/org/gradle/wrapper-upgrade-gradle-plugin/maven-metadata.xml&label=Plugin%20Portal&color=blue)](https://plugins.gradle.org/plugin/org.gradle.wrapper-upgrade) 5 | [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.solutions-team.gradle.com/scans) 6 | 7 | The Wrapper Upgrade Gradle Plugin creates tasks to upgrade the Gradle Wrapper for target projects hosted on GitHub. 8 | 9 | ## Prerequisites 10 | 11 | To run the upgrade tasks, you'll need: 12 | 13 | * **Java 8 or later.** 14 | * **Git.** The plugin uses Git commands to commit, create branches, and push changes. 15 | 16 | * **Git author identity:** Make sure your Git author identity is configured. You can set this with: 17 | 18 | ```bash 19 | git config --global user.name "Your Name" 20 | git config --global user.email "your.email@example.com" 21 | ``` 22 | 23 | **Using GPG Signing (Optional):** If you use GitHub Actions and want to sign your commits with GPG, you can use the [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) action. This action imports your GPG key **and** configures the Git author identity. 24 | 25 | ```yaml 26 | - name: Import GPG key 27 | uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 28 | with: 29 | gpg_private_key: ${{ secrets.MY_GPG_PRIVATE_KEY }} 30 | passphrase: ${{ secrets.MY_GPG_PASSPHRASE }} 31 | git_user_signingkey: true 32 | git_commit_gpgsign: true 33 | git_config_global: true 34 | ``` 35 | 36 | * **Git credentials:** If you use GitHub Actions, you can configure your Git credentials from a GitHub token with this trick: 37 | 38 | ```yaml 39 | - name: Set up Git credentials 40 | env: 41 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | run: git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/" 43 | ``` 44 | 45 | ## Usage 46 | Apply the plugin to a dedicated project and configure which project needs to be upgraded. Example: 47 | 48 |
49 | 50 | Kotlin DSL 51 | 52 | ```build.gradle 53 | plugins { 54 | id("base") 55 | id("org.gradle.wrapper-upgrade") version "0.12" 56 | } 57 | 58 | wrapperUpgrade { 59 | gradle { 60 | register("some-gradle-project") { 61 | repo.set("my-org/some-gradle-project") 62 | baseBranch.set("release") 63 | } 64 | register("some-samples-gradle-project") { 65 | repo.set("my-org/some-samples-gradle-project") 66 | dir.set("samples") 67 | } 68 | } 69 | 70 | maven { 71 | register("some-maven-project") { 72 | repo.set("my-org/some-maven-project") 73 | baseBranch.set("release") 74 | } 75 | register("some-samples-maven-project") { 76 | repo.set("my-org/some-samples-maven-project") 77 | baseBranch.set("samples") 78 | } 79 | } 80 | } 81 | ``` 82 | 83 |
84 | 85 |
86 | 87 | Groovy DSL 88 | 89 | ```build.gradle 90 | plugins { 91 | id 'base' 92 | id 'org.gradle.wrapper-upgrade' version '0.11.4' 93 | } 94 | 95 | wrapperUpgrade { 96 | gradle { 97 | 'some-gradle-project' { 98 | repo = 'my-org/some-gradle-project' 99 | baseBranch = 'release' 100 | } 101 | 'some-samples-gradle-project' { 102 | repo = 'my-org/some-samples-gradle-project' 103 | dir = 'samples' 104 | } 105 | } 106 | 107 | maven { 108 | 'some-maven-project' { 109 | repo = 'my-org/some-maven-project' 110 | baseBranch = 'release' 111 | } 112 | 'some-samples-maven-project' { 113 | repo = 'my-org/some-samples-maven-project' 114 | dir = 'samples' 115 | } 116 | } 117 | } 118 | ``` 119 | 120 |
121 | 122 | This will create one task per configured project and 2 aggregating tasks: `upgradeGradleWrapperAll` and `upgradeMavenWrapperAll` that will run all the specific tasks. 123 | 124 | Running `./gradlew upgradeGradleWrapperXXX` will: 125 | - clone the project XXX in `build/git-clones` 126 | - run in the cloned project `./gradlew wrapper --gradle-version=` 127 | - run a second time `./gradlew wrapper --gradle-version=` 128 | - If changes occurred 129 | - create a specific branch 130 | - commit and push the branch 131 | - create a pull request on GitHub, it requires a GitHub personal access token (PAT), passed with `WRAPPER_UPGRADE_GIT_TOKEN` environment variable. 132 | This token is used to get the existing PRs on the repo and create one if needed, hence it requires: 133 | - the `repo` scope for a classic PAT 134 | - read-write permissions for "Pull requests" on the relevant repos for a fine-grained PAT 135 | 136 | Note that a check is done first to make sure the branch does not exist yet. That way you can run `upgradeGradleWrapperAll` and `upgradeMavenWrapperAll` periodically with a cron, CI job... a bit like dependabot does for upgrading libs. 137 | 138 | Running `upgradeMavenWrapperXXX` will do the same, executing `./mvnw wrapper:wrapper -Dmaven=` instead. 139 | 140 | ### Configuration 141 | 142 | ``` 143 | wrapperUpgrade { 144 | { 145 | name { 146 | repo = ... 147 | baseBranch = ... 148 | dir = ... 149 | options { 150 | gitCommitExtraArgs = [...] 151 | allowPreRelease = true 152 | labels = ['dependencies'] 153 | assignees = ['alextu'] 154 | reviewers = ['StefMa'] 155 | recreateClosedPullRequest = true 156 | } 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | | Field | description | 163 | |:-------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 164 | | `name` | A name identifying the upgrade, it can be different from the project name, for example when you need to upgrade multiple gradle projects in the same git project | 165 | | `repo` | The GitHub repository to clone, format 'organization/project` | 166 | | `dir` | The directory inside the project base directory to run the gradle or maven upgrade in | 167 | | `baseBranch` | The git branch to checkout and that the pull request will target | 168 | | `options.gitCommitExtraArgs` | List of additional git commit arguments | 169 | | `options.allowPreRelease` | Boolean: `true` will get the latest Maven/Gradle version even if it's a pre-release. Default is `false`. | 170 | | `options.labels` | Optional list of label (names) that will be added to the pull request. | 171 | | `options.assignees` | Optional list of GitHub usernames that will be added as assignees to the pull request. | 172 | | `options.reviewers` | Optional list of GitHub usernames that will be added as reviewers to the pull request. | 173 | | `options.recreateClosedPullRequest` | Boolean: `true` will recreate the pull request if a closed pull request with the same branch name exists. `false` will not recreate the pull request. Default is `false`. | 174 | 175 | ## License 176 | 177 | The Wrapper Upgrade Gradle Plugin is open-source software released under the [Apache 2.0 License][apache-license]. 178 | 179 | [apache-license]: https://www.apache.org/licenses/LICENSE-2.0.html 180 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.util.GradleVersion 2 | 3 | plugins { 4 | id 'groovy' 5 | id 'java-gradle-plugin' 6 | id 'maven-publish' 7 | id 'signing' 8 | id 'com.gradle.plugin-publish' version '1.3.1' 9 | id 'com.github.breadmoirai.github-release' version '2.5.2' 10 | id 'org.gradle.wrapper-upgrade' version '0.12' 11 | } 12 | 13 | def releaseVersion = releaseVersion() 14 | def releaseNotes = releaseNotes() 15 | 16 | group = 'org.gradle' 17 | version = releaseVersion.get() 18 | description = 'A Gradle plugin that detects and updates Gradle and Maven wrappers to the latest Gradle and Maven version.' 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | dependencies { 25 | implementation 'org.kohsuke:github-api:1.322' 26 | implementation 'com.fasterxml.jackson.core:jackson-core:2.16.0' 27 | implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.16.0' 28 | 29 | testImplementation gradleTestKit() 30 | testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0' 31 | testImplementation 'net.bytebuddy:byte-buddy:1.17.5' 32 | } 33 | 34 | wrapperUpgrade { 35 | gradle { 36 | 'wrapper-upgrade-gradle-plugin' { 37 | repo = 'gradle/wrapper-upgrade-gradle-plugin' 38 | } 39 | } 40 | } 41 | 42 | java { 43 | toolchain { 44 | languageVersion = JavaLanguageVersion.of(8) 45 | } 46 | } 47 | 48 | tasks.withType(Test).configureEach { 49 | useJUnitPlatform() 50 | 51 | def testGradleVersion = findProperty('testGradleVersion') ?: GradleVersion.current().version 52 | systemProperty 'testContext.gradleVersion', testGradleVersion 53 | project.develocity.buildScan.value(identityPath.path + "#gradleVersion", testGradleVersion) 54 | 55 | def jdkMajorVersion = findProperty('testJdkMajorVersion') ?: '8' 56 | jvmArgumentProviders.add( 57 | new JdkHomeArgumentProvider(javaToolchains).tap { 58 | it.jdkMajorVersion = jdkMajorVersion 59 | } 60 | ) 61 | project.develocity.buildScan.value(identityPath.path + "#jdkMajorVersion", jdkMajorVersion) 62 | } 63 | 64 | tasks.withType(Jar).configureEach { 65 | into(".") { 66 | from(layout.projectDirectory.file("LICENSE")) 67 | from(layout.projectDirectory.dir("release/distribution")) 68 | } 69 | } 70 | 71 | gradlePlugin { 72 | website = "https://github.com/gradle/wrapper-upgrade-gradle-plugin/" 73 | vcsUrl = "https://github.com/gradle/wrapper-upgrade-gradle-plugin.git" 74 | 75 | automatedPublishing = true 76 | 77 | plugins { 78 | wrapperUpgrade { 79 | id = 'org.gradle.wrapper-upgrade' 80 | displayName = 'Wrapper Upgrade Gradle Plugin' 81 | description = releaseNotes.get() 82 | implementationClass = 'org.gradle.wrapperupgrade.WrapperUpgradePlugin' 83 | tags.addAll("gradle", "maven", "wrapper") 84 | } 85 | } 86 | } 87 | 88 | tasks.withType(ValidatePlugins).configureEach { 89 | failOnWarning = true 90 | enableStricterValidation = true 91 | } 92 | 93 | signing { 94 | // Require publications to be signed on CI. Otherwise, publication will be signed only if keys are provided. 95 | required providers.environmentVariable('CI').isPresent() 96 | 97 | useInMemoryPgpKeys( 98 | providers.environmentVariable('PGP_SIGNING_KEY').orNull, 99 | providers.environmentVariable('PGP_SIGNING_KEY_PASSPHRASE').orNull 100 | ) 101 | } 102 | 103 | githubRelease { 104 | token = System.getenv('WRAPPER_UPGRADE_GRADLE_PLUGIN_GIT_TOKEN') ?: '' 105 | owner = 'gradle' 106 | repo = 'wrapper-upgrade-gradle-plugin' 107 | targetCommitish = 'main' 108 | releaseName = releaseVersion 109 | tagName = releaseVersion.map { "v$it" } 110 | prerelease = false 111 | overwrite = false 112 | generateReleaseNotes = false 113 | body = releaseNotes 114 | } 115 | 116 | publishing { 117 | publications { 118 | mavenJava(MavenPublication) { 119 | pom { 120 | name = 'Wrapper Upgrade Gradle Plugin' 121 | description = 'A Gradle plugin that detects and updates Gradle and Maven wrappers to the latest Gradle and Maven version.' 122 | url = 'https://github.com/gradle/wrapper-upgrade-gradle-plugin' 123 | licenses { 124 | license { 125 | name = "Apache-2.0" 126 | url = "https://www.apache.org/licenses/LICENSE-2.0.txt" 127 | } 128 | } 129 | developers { 130 | developer { 131 | name = "The Gradle team" 132 | organization = "Gradle Inc." 133 | organizationUrl = "https://gradle.com" 134 | } 135 | } 136 | scm { 137 | developerConnection = "scm:git:https://github.com/gradle/wrapper-upgrade-gradle-plugin.git" 138 | url = "https://github.com/gradle/wrapper-upgrade-gradle-plugin" 139 | } 140 | } 141 | } 142 | } 143 | } 144 | 145 | def createReleaseTag = tasks.register('createReleaseTag', CreateGitTag) { 146 | // Ensure tag is created only after a successful publishing 147 | mustRunAfter('publishPlugins') 148 | tagName = githubRelease.tagName.map { it.toString() } 149 | } 150 | 151 | tasks.named('githubRelease') { 152 | dependsOn(createReleaseTag) 153 | } 154 | 155 | tasks.withType(com.gradle.publish.PublishTask).configureEach { 156 | notCompatibleWithConfigurationCache("$name task does not support configuration caching") 157 | } 158 | 159 | def releaseVersion() { 160 | def releaseVersionFile = layout.projectDirectory.file('release/version.txt') 161 | return providers.fileContents(releaseVersionFile).asText.map { it -> it.trim() } 162 | } 163 | 164 | def releaseNotes() { 165 | def releaseNotesFile = layout.projectDirectory.file('release/changes.md') 166 | return providers.fileContents(releaseNotesFile).asText.map { it -> it.trim() } 167 | } 168 | 169 | class JdkHomeArgumentProvider implements CommandLineArgumentProvider { 170 | private JavaToolchainService javaToolchainService 171 | 172 | @Input 173 | String jdkMajorVersion 174 | 175 | JdkHomeArgumentProvider(JavaToolchainService javaToolchainService) { 176 | this.javaToolchainService = javaToolchainService 177 | } 178 | 179 | @Override 180 | Iterable asArguments() { 181 | def javaHome = javaToolchainService.launcherFor { 182 | it.languageVersion = JavaLanguageVersion.of(jdkMajorVersion) 183 | }.map { it.metadata.installationPath } 184 | 185 | return ['-DtestContext.javaHome=' + javaHome.get().asFile.absolutePath] 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | java { 2 | toolchain { 3 | languageVersion = JavaLanguageVersion.of(8) 4 | } 5 | } -------------------------------------------------------------------------------- /buildSrc/settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.gradle.toolchains.foojay-resolver-convention' 3 | } -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/CreateGitTag.groovy: -------------------------------------------------------------------------------- 1 | import org.gradle.api.DefaultTask 2 | import org.gradle.api.model.ObjectFactory 3 | import org.gradle.api.provider.Property 4 | import org.gradle.api.tasks.Input 5 | import org.gradle.api.tasks.Optional 6 | import org.gradle.api.tasks.TaskAction 7 | import org.gradle.process.ExecOperations 8 | import org.gradle.work.DisableCachingByDefault 9 | import javax.inject.Inject 10 | 11 | @DisableCachingByDefault(because = "Produces no cacheable output") 12 | abstract class CreateGitTag extends DefaultTask { 13 | 14 | @Inject 15 | abstract ObjectFactory getObjects() 16 | 17 | @Inject 18 | abstract ExecOperations getExecOperations() 19 | 20 | @Input 21 | abstract Property getTagName() 22 | 23 | @Input 24 | @Optional 25 | final abstract Property overwriteExisting = objects.property(Boolean).convention(false) 26 | 27 | @TaskAction 28 | def tag() { 29 | logger.info("Tagging HEAD as ${tagName.get()}") 30 | execOperations.exec { execSpec -> 31 | def args = ["git", "tag"] 32 | if (overwriteExisting.get()) { 33 | args.add("-f") 34 | } 35 | args.add(tagName.get()) 36 | execSpec.commandLine(args) 37 | } 38 | execOperations.exec { execSpec -> 39 | def args = ["git", "push", "origin"] 40 | if (overwriteExisting.get()) { 41 | args.add("-f") 42 | } 43 | args.add("--tags") 44 | execSpec.commandLine(args) 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.vfs.watch=true 2 | org.gradle.daemon=true 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | org.gradle.configuration-cache=true 6 | org.gradle.jvmargs=-Duser.language=en -Duser.country=US -Dfile.encoding=UTF-8 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradle/wrapper-upgrade-gradle-plugin/3a65069a6ac790ce5d9283ac74ad29bcb17c71f9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /release/changes.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradle/wrapper-upgrade-gradle-plugin/3a65069a6ac790ce5d9283ac74ad29bcb17c71f9/release/changes.md -------------------------------------------------------------------------------- /release/distribution/NOTICE: -------------------------------------------------------------------------------- 1 | The following copyright statements and licenses apply to various third party open 2 | source software packages (or portions thereof) that are distributed with 3 | this content. 4 | 5 | TABLE OF CONTENTS 6 | ================= 7 | 8 | The following is a listing of the open source components detailed in this 9 | document. This list is provided for your convenience; please read further if 10 | you wish to review the copyright notice(s) and the full text of the license 11 | associated with each component. 12 | 13 | 14 | **SECTION 1: Apache License, V2.0** 15 | * Jackson Core 16 | * com.fasterxml.jackson.core:jackson-core 17 | * Jackson Dataformat XML 18 | * com.fasterxml.jackson.dataformat:jackson-dataformat-xml 19 | 20 | **SECTION 2: MIT License** 21 | * Java API for GitHub 22 | * org.kohsuke:github-api 23 | 24 | SECTION 1: Apache License, V2.0 25 | ================================ 26 | 27 | Jackson Core 28 | Jackson Dataformat XML 29 | ----------------------------------- 30 | 31 | Copyright 2023 The Apache Software Foundation 32 | 33 | Licensed under the Apache License, Version 2.0 (the "License"); 34 | you may not use this file except in compliance with the License. 35 | You may obtain a copy of the License at 36 | 37 | http://www.apache.org/licenses/LICENSE-2.0 38 | 39 | Unless required by applicable law or agreed to in writing, software 40 | distributed under the License is distributed on an "AS IS" BASIS, 41 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 42 | See the License for the specific language governing permissions and 43 | limitations under the License. 44 | 45 | SECTION 2: MIT License 46 | ================================ 47 | 48 | Java API for GitHub 49 | ----------------------------------- 50 | 51 | Copyright (c) 2011- Kohsuke Kawaguchi and other contributors 52 | 53 | Permission is hereby granted, free of charge, to any person 54 | obtaining a copy of this software and associated documentation 55 | files (the "Software"), to deal in the Software without 56 | restriction, including without limitation the rights to use, 57 | copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | copies of the Software, and to permit persons to whom the 59 | Software is furnished to do so, subject to the following 60 | conditions: 61 | 62 | The above copyright notice and this permission notice shall be 63 | included in all copies or substantial portions of the Software. 64 | 65 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 66 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 67 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 68 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 69 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 70 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 71 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 72 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /release/version.txt: -------------------------------------------------------------------------------- 1 | 0.12.1 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.gradle.develocity' version '4.0.2' 3 | id 'com.gradle.common-custom-user-data-gradle-plugin' version '2.3' 4 | id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' 5 | } 6 | 7 | def isCI = System.getenv('GITHUB_ACTIONS') != null 8 | 9 | develocity { 10 | server = 'https://ge.solutions-team.gradle.com' 11 | buildScan { 12 | uploadInBackground = !isCI 13 | publishing.onlyIf { it.authenticated } 14 | obfuscation { 15 | ipAddresses { addresses -> addresses.collect { address -> "0.0.0.0"} } 16 | } 17 | } 18 | } 19 | 20 | buildCache { 21 | local { 22 | enabled = true 23 | } 24 | 25 | remote(develocity.buildCache) { 26 | enabled = true 27 | push = isCI 28 | } 29 | } 30 | 31 | rootProject.name = 'wrapper-upgrade-gradle-plugin' 32 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/BuildToolStrategy.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.process.ExecOperations; 4 | 5 | import javax.annotation.Nullable; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.Properties; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | public interface BuildToolStrategy { 17 | 18 | BuildToolStrategy GRADLE = new GradleBuildToolStrategy(); 19 | BuildToolStrategy MAVEN = new MavenBuildToolStrategy(); 20 | 21 | String buildToolName(); 22 | 23 | VersionInfo lookupLatestVersion(boolean allowPreRelease) throws IOException; 24 | 25 | VersionInfo extractCurrentVersion(Path rootProjectDir) throws IOException; 26 | 27 | void runWrapper(ExecOperations execOperations, Path rootProjectDir, VersionInfo version); 28 | 29 | List wrapperFiles(Path rootProjectDir); 30 | 31 | String releaseNotesLink(String buildToolVersion); 32 | 33 | static VersionInfo extractBuildToolVersion(Path rootProjectDir, String wrapperPropertiesFile, 34 | String distributionUrlProperty, String distributionChecksumProperty, 35 | String versionRegExp) throws IOException { 36 | try (InputStream is = Files.newInputStream(rootProjectDir.resolve(wrapperPropertiesFile))) { 37 | Properties wrapperProperties = new Properties(); 38 | wrapperProperties.load(is); 39 | return extractBuildToolVersion(wrapperProperties, wrapperPropertiesFile, distributionUrlProperty, distributionChecksumProperty, versionRegExp); 40 | } 41 | } 42 | 43 | static VersionInfo extractBuildToolVersion(Properties props, String wrapperPropertiesFile, 44 | String distributionUrlProperty, String distributionChecksumProperty, 45 | String versionRegExp) { 46 | String distributionUrl = props.getProperty(distributionUrlProperty); 47 | if (distributionUrl != null) { 48 | Matcher matcher = Pattern.compile(versionRegExp).matcher(distributionUrl); 49 | if (matcher.find()) { 50 | return new VersionInfo(matcher.group(1), distributionChecksumProperty != null ? props.getProperty(distributionChecksumProperty) : null); 51 | } else { 52 | throw new IllegalStateException(String.format("Could not extract version from property '%s': %s", distributionUrlProperty, distributionUrl)); 53 | } 54 | } else { 55 | throw new IllegalStateException(String.format("Could not find property '%s' in file %s", distributionUrlProperty, wrapperPropertiesFile)); 56 | } 57 | } 58 | 59 | final class VersionInfo { 60 | 61 | public String version; 62 | public Optional checksum; 63 | 64 | public VersionInfo(String version, @Nullable String checksum) { 65 | this.version = version; 66 | this.checksum = Optional.ofNullable(checksum); 67 | } 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/ExecUtils.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.process.ExecOperations; 4 | 5 | import java.nio.file.Path; 6 | import java.util.Arrays; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | final class ExecUtils { 11 | 12 | static void execGradleCmd(ExecOperations execOperations, Path workingDir, Object... args) { 13 | execCmd(execOperations, workingDir, "./gradlew", args); 14 | } 15 | 16 | static void execMavenCmd(ExecOperations execOperations, Path workingDir, Object... args) { 17 | execCmd(execOperations, workingDir, "./mvnw", args); 18 | } 19 | 20 | static void execGitCmd(ExecOperations execOperations, Path workingDir, Object... args) { 21 | execCmd(execOperations, workingDir, "git", args); 22 | } 23 | 24 | private static void execCmd(ExecOperations execOperations, Path workingDir, String cmd, Object... args) { 25 | List cmdLine = new LinkedList<>(); 26 | cmdLine.add(cmd); 27 | cmdLine.addAll(Arrays.asList(args)); 28 | execOperations.exec( 29 | execSpec -> { 30 | execSpec.workingDir(workingDir); 31 | execSpec.commandLine(cmdLine); 32 | }); 33 | } 34 | 35 | private ExecUtils() { 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/GradleBuildToolStrategy.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import org.gradle.process.ExecOperations; 5 | 6 | import java.io.IOException; 7 | import java.net.URL; 8 | import java.nio.file.Path; 9 | import java.util.LinkedList; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.Scanner; 13 | 14 | import static org.gradle.wrapperupgrade.BuildToolStrategy.extractBuildToolVersion; 15 | 16 | public final class GradleBuildToolStrategy implements BuildToolStrategy { 17 | 18 | private final GradleMetadataFetcher gradleMetadataFetcher = new GradleMetadataFetcher(); 19 | 20 | @Override 21 | public String buildToolName() { 22 | return "Gradle"; 23 | } 24 | 25 | @Override 26 | public VersionInfo lookupLatestVersion(boolean allowPreRelease) throws IOException { 27 | JsonNode latestVersion = gradleMetadataFetcher.fetchLatestVersion(allowPreRelease) 28 | .orElseThrow(() -> new IllegalStateException("Could not determine latest Gradle version")); 29 | 30 | JsonNode checksumUrl = latestVersion.get("checksumUrl"); 31 | if (checksumUrl != null) { 32 | URL url = new URL(checksumUrl.asText()); 33 | String checksum = new Scanner(url.openStream()).useDelimiter("\\A").next(); 34 | return new VersionInfo(latestVersion.get("version").asText(), checksum); 35 | } else { 36 | return new VersionInfo(latestVersion.get("version").asText(), null); 37 | } 38 | } 39 | 40 | @Override 41 | public VersionInfo extractCurrentVersion(Path rootProjectDir) throws IOException { 42 | return extractBuildToolVersion(rootProjectDir, 43 | "gradle/wrapper/gradle-wrapper.properties", 44 | "distributionUrl", "distributionSha256Sum", 45 | "distributions(?:-snapshots)?/gradle-(.*)-(bin|all).zip" 46 | ); 47 | } 48 | 49 | @Override 50 | public void runWrapper(ExecOperations execOperations, Path rootProjectDir, VersionInfo version) { 51 | if (version.checksum.isPresent()) { 52 | ExecUtils.execGradleCmd(execOperations, rootProjectDir, "--console=plain", "wrapper", "--gradle-version", version.version, 53 | "--gradle-distribution-sha256-sum", version.checksum.get()); 54 | } else { 55 | ExecUtils.execGradleCmd(execOperations, rootProjectDir, "--console=plain", "wrapper", "--gradle-version", version.version); 56 | } 57 | } 58 | 59 | @Override 60 | public List wrapperFiles(Path rootProjectDir) { 61 | List paths = new LinkedList<>(); 62 | paths.add(rootProjectDir.resolve("gradlew")); 63 | paths.add(rootProjectDir.resolve("gradlew.bat")); 64 | paths.add(rootProjectDir.resolve("gradle").resolve("wrapper")); 65 | return paths; 66 | } 67 | 68 | @Override 69 | public String releaseNotesLink(String buildToolVersion) { 70 | return "https://docs.gradle.org/$VERSION/release-notes.html".replace("$VERSION", buildToolVersion); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/GradleMetadataFetcher.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.gradle.util.internal.VersionNumber; 6 | 7 | import java.io.IOException; 8 | import java.net.MalformedURLException; 9 | import java.net.URL; 10 | import java.util.Comparator; 11 | import java.util.Optional; 12 | import java.util.stream.StreamSupport; 13 | 14 | public class GradleMetadataFetcher { 15 | private final URL gradleAllVersionsMetadata; 16 | private final URL gradleCurrentVersionMetadata; 17 | 18 | GradleMetadataFetcher(URL gradleAllVersionsMetadata, URL gradleCurrentVersionMetadata) { 19 | this.gradleAllVersionsMetadata = gradleAllVersionsMetadata; 20 | this.gradleCurrentVersionMetadata = gradleCurrentVersionMetadata; 21 | } 22 | 23 | GradleMetadataFetcher() { 24 | try { 25 | this.gradleAllVersionsMetadata = new URL("https://services.gradle.org/versions/all"); 26 | this.gradleCurrentVersionMetadata = new URL("https://services.gradle.org/versions/current"); 27 | } catch (MalformedURLException e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | 32 | Optional fetchLatestVersion(boolean allowPreRelease) throws IOException { 33 | ObjectMapper mapper = new ObjectMapper(); 34 | if (allowPreRelease) { 35 | return StreamSupport.stream(mapper.readTree(gradleAllVersionsMetadata).spliterator(), false) 36 | .max(Comparator.comparing(n -> VersionNumber.parse(n.path("version").asText()))) 37 | .flatMap(n -> n.path("version").isMissingNode() ? Optional.empty() : Optional.of(n)); 38 | } else { 39 | JsonNode gradleMetadata = mapper.readTree(gradleCurrentVersionMetadata); 40 | return gradleMetadata.path("version").isMissingNode() ? Optional.empty() : Optional.of(gradleMetadata); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/MavenBuildToolStrategy.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.process.ExecOperations; 4 | import org.gradle.util.internal.VersionNumber; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Path; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | import static org.gradle.wrapperupgrade.BuildToolStrategy.extractBuildToolVersion; 13 | 14 | public final class MavenBuildToolStrategy implements BuildToolStrategy { 15 | 16 | private final MavenMetadataFetcher mavenMetadataFetcher = new MavenMetadataFetcher(); 17 | @Override 18 | public String buildToolName() { 19 | return "Maven"; 20 | } 21 | 22 | @Override 23 | public VersionInfo lookupLatestVersion(boolean allowPreRelease) throws IOException { 24 | return mavenMetadataFetcher.fetchLatestVersion(allowPreRelease) 25 | .map(latestVersion -> new VersionInfo(latestVersion.toString(), null)) 26 | .orElseThrow(() -> new IllegalStateException("Could not determine latest Maven version")); 27 | } 28 | 29 | @Override 30 | public VersionInfo extractCurrentVersion(Path rootProjectDir) throws IOException { 31 | return extractBuildToolVersion(rootProjectDir, 32 | ".mvn/wrapper/maven-wrapper.properties", 33 | "distributionUrl", null, 34 | "apache-maven-(.*)-bin.zip" 35 | ); 36 | } 37 | 38 | @Override 39 | public void runWrapper(ExecOperations execOperations, Path rootProjectDir, VersionInfo version) { 40 | ExecUtils.execMavenCmd(execOperations, rootProjectDir, "-B", "-N", "wrapper:wrapper", "-Dmaven=" + version.version); 41 | } 42 | 43 | @Override 44 | public List wrapperFiles(Path rootProjectDir) { 45 | List paths = new LinkedList<>(); 46 | paths.add(rootProjectDir.resolve("mvnw")); 47 | paths.add(rootProjectDir.resolve("mvnw.cmd")); 48 | paths.add(rootProjectDir.resolve(".mvn").resolve("wrapper")); 49 | return paths; 50 | } 51 | 52 | @Override 53 | public String releaseNotesLink(String buildToolVersion) { 54 | return "https://maven.apache.org/docs/$VERSION/release-notes.html".replace("$VERSION", buildToolVersion); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/MavenMetadataFetcher.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.dataformat.xml.XmlMapper; 5 | import org.gradle.util.internal.VersionNumber; 6 | 7 | import java.io.IOException; 8 | import java.net.MalformedURLException; 9 | import java.net.URL; 10 | import java.util.Comparator; 11 | import java.util.Optional; 12 | import java.util.function.Predicate; 13 | import java.util.stream.StreamSupport; 14 | 15 | public class MavenMetadataFetcher { 16 | 17 | private final URL mavenMetadataUrl; 18 | 19 | MavenMetadataFetcher(URL mavenMetadataUrl) { 20 | this.mavenMetadataUrl = mavenMetadataUrl; 21 | } 22 | 23 | MavenMetadataFetcher() { 24 | try { 25 | this.mavenMetadataUrl = new URL("https://repo.maven.apache.org/maven2/org/apache/maven/maven-core/maven-metadata.xml"); 26 | } catch (MalformedURLException e) { 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | Optional fetchLatestVersion(boolean allowPreRelease) throws IOException { 32 | XmlMapper mapper = new XmlMapper(); 33 | JsonNode mavenMetadata = mapper.readTree(mavenMetadataUrl).path("versioning").path("versions").path("version"); 34 | if (mavenMetadata.isMissingNode()) { 35 | return Optional.empty(); 36 | } 37 | Predicate isReleaseOrIdentity = allowPreRelease ? v -> true : v -> v.getQualifier() == null; 38 | return StreamSupport.stream(mavenMetadata.spliterator(), false) 39 | .map(n -> VersionNumber.parse(n.asText())) 40 | .sorted(Comparator.reverseOrder()) 41 | .filter(isReleaseOrIdentity) 42 | .findFirst(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/PullRequestUtils.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.util.internal.VersionNumber; 4 | import org.kohsuke.github.GHIssueState; 5 | import org.kohsuke.github.GHPullRequest; 6 | 7 | import java.util.Set; 8 | import java.util.stream.Collectors; 9 | 10 | public class PullRequestUtils { 11 | 12 | private static final String BRANCH_PREFIX = "wrapperbot/%s/%s-wrapper-"; 13 | private final Set pullRequests; 14 | 15 | PullRequestUtils(Set pullRequests) { 16 | this.pullRequests = pullRequests; 17 | } 18 | 19 | static String branchPrefix(String project, String buildTool) { 20 | return String.format(BRANCH_PREFIX, project, buildTool.toLowerCase()); 21 | } 22 | 23 | Set pullRequestsToClose(String project, String buildTool, String latestBuildToolVersion) { 24 | VersionNumber latest = VersionNumber.parse(latestBuildToolVersion); 25 | return pullRequests.stream() 26 | .filter(p -> p.getState() != GHIssueState.CLOSED) 27 | .filter(p -> { 28 | String branch = p.getHead().getRef(); 29 | String prefix = branchPrefix(project, buildTool); 30 | int index = branch.lastIndexOf(prefix); 31 | return index == 0 && latest.compareTo(VersionNumber.parse(branch.substring(prefix.length()))) > 0; 32 | } 33 | ) 34 | .collect(Collectors.toSet()); 35 | } 36 | 37 | boolean closedPrExists(String branch) { 38 | return pullRequests.stream().anyMatch(p -> branch.equals(p.getHead().getRef()) && p.getState() == GHIssueState.CLOSED); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/UpgradeWrapper.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.api.DefaultTask; 4 | import org.gradle.api.file.Directory; 5 | import org.gradle.api.file.ProjectLayout; 6 | import org.gradle.api.provider.Provider; 7 | import org.gradle.api.tasks.TaskAction; 8 | import org.gradle.process.ExecOperations; 9 | import org.gradle.process.internal.ExecException; 10 | import org.gradle.util.internal.VersionNumber; 11 | import org.gradle.work.DisableCachingByDefault; 12 | import org.gradle.wrapperupgrade.BuildToolStrategy.VersionInfo; 13 | import org.kohsuke.github.GHFileNotFoundException; 14 | import org.kohsuke.github.GHIssueState; 15 | import org.kohsuke.github.GHPullRequest; 16 | import org.kohsuke.github.GHRepository; 17 | import org.kohsuke.github.GHUser; 18 | import org.kohsuke.github.GitHub; 19 | import org.kohsuke.github.GitHubBuilder; 20 | 21 | import javax.inject.Inject; 22 | import java.io.File; 23 | import java.io.IOException; 24 | import java.net.MalformedURLException; 25 | import java.net.URL; 26 | import java.nio.file.Path; 27 | import java.util.ArrayList; 28 | import java.util.Arrays; 29 | import java.util.Collections; 30 | import java.util.List; 31 | import java.util.Optional; 32 | import java.util.Objects; 33 | import java.util.Set; 34 | import java.util.stream.Collectors; 35 | 36 | import static java.lang.Boolean.parseBoolean; 37 | import static org.gradle.wrapperupgrade.ExecUtils.execGitCmd; 38 | import static org.gradle.wrapperupgrade.PullRequestUtils.*; 39 | 40 | @DisableCachingByDefault(because = "Produces no cacheable output") 41 | public abstract class UpgradeWrapper extends DefaultTask { 42 | 43 | private static final String GIT_TOKEN_ENV_VAR = "WRAPPER_UPGRADE_GIT_TOKEN"; 44 | 45 | private static final String UNSIGNED_COMMITS_SYS_PROP = "wrapperUpgrade.unsignedCommits"; 46 | private static final String DRY_RUN_SYS_PROP = "wrapperUpgrade.dryRun"; 47 | 48 | private final WrapperUpgradeDomainObject upgrade; 49 | private final BuildToolStrategy buildToolStrategy; 50 | private final ProjectLayout layout; 51 | private final ExecOperations execOperations; 52 | 53 | @Inject 54 | public UpgradeWrapper( 55 | WrapperUpgradeDomainObject upgrade, 56 | BuildToolStrategy buildToolStrategy, 57 | ProjectLayout layout, 58 | ExecOperations execOperations 59 | ) { 60 | this.upgrade = upgrade; 61 | this.buildToolStrategy = buildToolStrategy; 62 | this.layout = layout; 63 | this.execOperations = execOperations; 64 | getOutputs().dir(getCheckoutDir()); 65 | } 66 | 67 | private Provider getCheckoutDir() { 68 | return layout.getBuildDirectory().dir("git-clones" + File.separatorChar + upgrade.name); 69 | } 70 | 71 | @TaskAction 72 | void upgrade() throws IOException { 73 | GitHub gitHub = createGitHub(); 74 | boolean allowPreRelease = upgrade.getOptions().getAllowPreRelease().orElse(Boolean.FALSE).get(); 75 | boolean recreateClosedPr = upgrade.getOptions().getRecreateClosedPullRequest().orElse(Boolean.FALSE).get(); 76 | Params params = Params.create(upgrade, buildToolStrategy, allowPreRelease, layout.getProjectDirectory(), getCheckoutDir().get(), gitHub, recreateClosedPr, execOperations); 77 | 78 | if (branchExists(params)) { 79 | getLogger().lifecycle(String.format("GitHub branch '%s' to upgrade %s Wrapper to %s already exists for project '%s'", 80 | params.prBranch, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version, params.project)); 81 | return; 82 | } 83 | PullRequestUtils utils = new PullRequestUtils(pullRequests(params)); 84 | if (utils.closedPrExists(params.prBranch) && !params.recreateClosedPRs) { 85 | getLogger().lifecycle(String.format("A closed pull request from branch '%s' to upgrade %s Wrapper to %s already exists for project '%s'. Use `recreateClosedPullRequest` option to recreate it.", 86 | params.prBranch, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version, params.project)); 87 | return; 88 | } 89 | Set pullRequestsToClose = utils.pullRequestsToClose(params.project, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version); 90 | createPrIfWrapperUpgradeAvailable(params, pullRequestsToClose); 91 | } 92 | 93 | private static GitHub createGitHub() throws IOException { 94 | GitHubBuilder gitHub = new GitHubBuilder(); 95 | Optional.ofNullable(System.getenv(GIT_TOKEN_ENV_VAR)).ifPresent(gitHub::withOAuthToken); 96 | return gitHub.build(); 97 | } 98 | 99 | private void createPrIfWrapperUpgradeAvailable(Params params, Set prsToClose) throws IOException { 100 | runWrapperWithLatestBuildToolVersion(params); 101 | createPrIfWrapperChanged(params, prsToClose); 102 | } 103 | 104 | private void runWrapperWithLatestBuildToolVersion(Params params) { 105 | buildToolStrategy.runWrapper(execOperations, params.rootProjectDir, params.latestBuildToolVersion); 106 | buildToolStrategy.runWrapper(execOperations, params.rootProjectDir, params.latestBuildToolVersion); 107 | } 108 | 109 | private void createPrIfWrapperChanged(Params params, Set prsToClose) throws IOException { 110 | if (isWrapperChanged(params.gitCheckoutDir)) { 111 | createPr(params); 112 | closePullRequests(params, prsToClose); 113 | } else { 114 | getLogger().lifecycle(String.format("No pull request created to upgrade %s Wrapper to %s since already on latest version for project '%s'", 115 | buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version, params.project)); 116 | } 117 | } 118 | 119 | private boolean isWrapperChanged(Path gitCheckoutDir) { 120 | try { 121 | // `git diff --exit-code` returns exit code 0 when there's no diff, 1 when there's a diff (in which case execOperations throws an exception) 122 | execGitCmd(execOperations, gitCheckoutDir, "diff", "--quiet", "--exit-code"); 123 | return false; 124 | } catch (ExecException e) { 125 | return true; 126 | } 127 | } 128 | 129 | private void createPr(Params params) throws IOException { 130 | String shortDesc = createShortDescription(params); 131 | String longDesc = createLongDescription(params); 132 | gitCommitAndPush(params, longDesc); 133 | gitCreatePr(params, shortDesc, longDesc); 134 | } 135 | 136 | private String createShortDescription(Params params) { 137 | String buildToolName = buildToolStrategy.buildToolName(); 138 | String latestBuildToolVersion = params.latestBuildToolVersion.version; 139 | String usedBuildToolVersion = params.usedBuildToolVersion.version; 140 | String relativePath = params.rootProjectDirRelativePath.normalize().toString(); 141 | 142 | String title = latestBuildToolVersion.equals(usedBuildToolVersion) ? 143 | String.format("Update %s Wrapper version %s files", buildToolName, latestBuildToolVersion) : 144 | String.format("Bump %s Wrapper from %s to %s", buildToolName, usedBuildToolVersion, latestBuildToolVersion); 145 | 146 | StringBuilder description = new StringBuilder(); 147 | description.append(title); 148 | if (!relativePath.isEmpty()) { 149 | String path = relativePath.startsWith("/") ? relativePath : "/" + relativePath; 150 | description.append(String.format(" in %s", path)); 151 | } 152 | return description.toString(); 153 | } 154 | 155 | private String createLongDescription(Params params) { 156 | String buildToolName = buildToolStrategy.buildToolName(); 157 | String latestBuildToolVersion = params.latestBuildToolVersion.version; 158 | String usedBuildToolVersion = params.usedBuildToolVersion.version; 159 | String releaseNotesLink = buildToolStrategy.releaseNotesLink(latestBuildToolVersion); 160 | 161 | boolean updatedWrapperFilesOnly = latestBuildToolVersion.equals(usedBuildToolVersion); 162 | String title = updatedWrapperFilesOnly ? 163 | String.format("Update %s Wrapper version %s files.", buildToolName, latestBuildToolVersion) : 164 | String.format("Bump %s Wrapper from %s to %s.", buildToolName, usedBuildToolVersion, latestBuildToolVersion); 165 | 166 | String releaseNotes = updatedWrapperFilesOnly ? "" : 167 | "\n\n" + 168 | String.format("Release notes of %s %s can be found here:", buildToolName, latestBuildToolVersion) + 169 | "\n" + 170 | releaseNotesLink; 171 | 172 | return title + releaseNotes; 173 | } 174 | 175 | private void gitCommitAndPush(Params params, String commitMessage) { 176 | // Git add 177 | List wrapperFiles = buildToolStrategy.wrapperFiles(params.rootProjectDir); 178 | wrapperFiles.forEach(p -> execGitCmd(execOperations, params.gitCheckoutDir, "add", p)); 179 | 180 | // Git checkout 181 | execGitCmd(execOperations, params.gitCheckoutDir, "checkout", "--quiet", "-b", params.prBranch); 182 | 183 | // Git commit 184 | List argsAndExtraArgs = new ArrayList<>(Arrays.asList("commit", "--quiet", "--signoff", "-m", commitMessage)); 185 | argsAndExtraArgs.addAll(params.gitCommitExtraArgs); 186 | execGitCmd(execOperations, params.gitCheckoutDir, argsAndExtraArgs.toArray()); 187 | 188 | // Git push 189 | if (!isDryRun()) { 190 | execGitCmd(execOperations, params.gitCheckoutDir, "push", "--quiet", "-u", "origin", params.prBranch); 191 | } 192 | } 193 | 194 | private void gitCreatePr(Params params, String prTitle, String prBody) throws IOException { 195 | if (!isDryRun()) { 196 | GHPullRequest pr = params.gitHub.getRepository(params.repository).createPullRequest(prTitle, params.prBranch, params.baseBranch, prBody); 197 | getLogger().lifecycle(String.format("Pull request '%s' created at %s to upgrade %s Wrapper to %s for project '%s'", 198 | params.prBranch, pr.getHtmlUrl(), buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version, params.project)); 199 | addLabels(pr); 200 | requestReviewers(params.gitHub, pr); 201 | addAssignees(params.gitHub, pr); 202 | } else { 203 | getLogger().lifecycle(String.format("Dry run: Skipping creation of pull request '%s' that would upgrade %s Wrapper to %s for project '%s'", 204 | params.prBranch, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version, params.project)); 205 | } 206 | } 207 | 208 | private boolean branchExists(Params params) throws IOException { 209 | GHRepository repository = params.gitHub.getRepository(params.repository); 210 | try { 211 | repository.getBranch(params.prBranch); 212 | return true; 213 | } catch (GHFileNotFoundException e) { 214 | return false; 215 | } 216 | } 217 | 218 | private Set pullRequests(Params params) throws IOException { 219 | return params.gitHub.getRepository(params.repository).getPullRequests(GHIssueState.ALL) 220 | .stream() 221 | .filter(pr -> pr.getHead().getRef().startsWith(branchPrefix(params.project, buildToolStrategy.buildToolName().toLowerCase()))) 222 | .collect(Collectors.toSet()); 223 | } 224 | 225 | private void closePullRequests(Params params, Set prs) { 226 | for (GHPullRequest pr : prs) { 227 | try { 228 | closePullRequest(params, pr); 229 | } catch (IOException e) { 230 | getLogger().warn(String.format("Error closing pull request #%s", pr.getId()), e); 231 | } 232 | } 233 | } 234 | 235 | private void closePullRequest(Params params, GHPullRequest pr) throws IOException { 236 | if (!isDryRun()) { 237 | getLogger().lifecycle(String.format("Pull request #%s on project '%s' has been closed because target %s Wrapper version is older than %s", 238 | pr.getNumber(), params.project, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version)); 239 | pr.close(); 240 | } else { 241 | getLogger().lifecycle(String.format("Dry run: Skipping closure of pull request #%s on project '%s' because target %s Wrapper version is older than %s", 242 | pr.getNumber(), params.project, buildToolStrategy.buildToolName(), params.latestBuildToolVersion.version)); 243 | } 244 | } 245 | 246 | private static boolean isUnsignedCommits() { 247 | return Optional.ofNullable(System.getProperty(UNSIGNED_COMMITS_SYS_PROP)).map(p -> "".equals(p) || parseBoolean(p)).orElse(false); 248 | } 249 | 250 | private static boolean isDryRun() { 251 | return Optional.ofNullable(System.getProperty(DRY_RUN_SYS_PROP)).map(p -> "".equals(p) || parseBoolean(p)).orElse(false); 252 | } 253 | 254 | private static boolean isUrl(String url) { 255 | try { 256 | new URL(url); 257 | return true; 258 | } catch (MalformedURLException e) { 259 | return false; 260 | } 261 | } 262 | 263 | private void addLabels(GHPullRequest pr) { 264 | List labels = upgrade.getOptions().getLabels().get(); 265 | if (!labels.isEmpty()) { 266 | try { 267 | pr.addLabels(labels.toArray(new String[0])); 268 | } catch (IOException e) { 269 | getLogger().warn("Error adding labels: " + (e.getMessage() != null ? e.getMessage() : "Unable to add labels")); 270 | } 271 | } 272 | } 273 | 274 | private void requestReviewers(GitHub gitHub, GHPullRequest pr) { 275 | List reviewers = upgrade.getOptions().getReviewers().get(); 276 | if (!reviewers.isEmpty()) { 277 | List githubReviewers = mapToGHUsers(gitHub, reviewers); 278 | try { 279 | pr.requestReviewers(githubReviewers); 280 | } catch (IOException e) { 281 | getLogger().warn("Error requesting reviewers: " + (e.getMessage() != null ? e.getMessage() : "Unable to request reviewers")); 282 | } 283 | } 284 | } 285 | 286 | 287 | private void addAssignees(GitHub gitHub, GHPullRequest pr) { 288 | List assignees = upgrade.getOptions().getAssignees().get(); 289 | if (!assignees.isEmpty()) { 290 | List githubAssignees = mapToGHUsers(gitHub, assignees); 291 | try { 292 | pr.addAssignees(githubAssignees); 293 | } catch (IOException e) { 294 | getLogger().warn("Error adding assignees: " + (e.getMessage() != null ? e.getMessage() : "Unable to add assignees")); 295 | } 296 | } 297 | } 298 | 299 | private List mapToGHUsers(GitHub gitHub, List users) { 300 | return users.stream() 301 | .map(user -> { 302 | try { 303 | return gitHub.getUser(user); 304 | } catch (IOException e) { 305 | getLogger().warn(String.format("Error fetching GitHub user '%s'", user), e); 306 | return null; 307 | } 308 | }) 309 | .filter(Objects::nonNull) 310 | .collect(Collectors.toList()); 311 | } 312 | 313 | private static final class Params { 314 | 315 | private final String project; 316 | private final String repository; 317 | private final String baseBranch; 318 | private final String prBranch; 319 | private final Path gitCheckoutDir; 320 | private final Path rootProjectDir; 321 | private final Path rootProjectDirRelativePath; 322 | private final VersionInfo latestBuildToolVersion; 323 | private final VersionInfo usedBuildToolVersion; 324 | private final List gitCommitExtraArgs; 325 | private final boolean recreateClosedPRs; 326 | private final GitHub gitHub; 327 | 328 | private Params( 329 | String project, 330 | String repository, 331 | String baseBranch, 332 | String prBranch, 333 | Path gitCheckoutDir, 334 | Path rootProjectDir, 335 | Path rootProjectDirRelativePath, 336 | VersionInfo latestBuildToolVersion, 337 | VersionInfo usedBuildToolVersion, 338 | List gitCommitExtraArgs, 339 | boolean recreateClosedPRs, 340 | GitHub gitHub 341 | ) { 342 | this.project = project; 343 | this.repository = repository; 344 | this.baseBranch = baseBranch; 345 | this.prBranch = prBranch; 346 | this.gitCheckoutDir = gitCheckoutDir; 347 | this.rootProjectDir = rootProjectDir; 348 | this.rootProjectDirRelativePath = rootProjectDirRelativePath; 349 | this.latestBuildToolVersion = latestBuildToolVersion; 350 | this.usedBuildToolVersion = usedBuildToolVersion; 351 | this.gitCommitExtraArgs = gitCommitExtraArgs; 352 | this.gitHub = gitHub; 353 | this.recreateClosedPRs = recreateClosedPRs; 354 | } 355 | 356 | private static Params create 357 | ( 358 | WrapperUpgradeDomainObject upgrade, 359 | BuildToolStrategy buildToolStrategy, 360 | boolean allowPreRelease, 361 | Directory executionRootDirectory, 362 | Directory gitCheckoutDirectory, 363 | GitHub gitHub, 364 | boolean ignoreClosedPRs, 365 | ExecOperations exec 366 | ) throws IOException { 367 | String project = upgrade.name; 368 | String repository = upgrade.getRepo().get(); 369 | String baseBranch = upgrade.getBaseBranch().get(); 370 | Path executionRootDir = executionRootDirectory.getAsFile().toPath(); 371 | Path gitCheckoutDir = gitCheckoutDirectory.getAsFile().toPath(); 372 | Path rootProjectDir = gitCheckoutDir.resolve(upgrade.getDir().get()); 373 | 374 | cloneGitProject(repository, executionRootDir, baseBranch, gitCheckoutDir, exec); 375 | VersionInfo usedBuildToolVersion = buildToolStrategy.extractCurrentVersion(rootProjectDir); 376 | VersionInfo latestBuildToolVersion = getLatestBuildToolVersion(buildToolStrategy, allowPreRelease, usedBuildToolVersion); 377 | 378 | String prBranch = PullRequestUtils.branchPrefix(project, buildToolStrategy.buildToolName().toLowerCase()) + latestBuildToolVersion.version; 379 | Path rootProjectDirRelativePath = gitCheckoutDir.relativize(rootProjectDir); 380 | List gitCommitExtraArgs = upgrade.getOptions().getGitCommitExtraArgs().orElse(Collections.emptyList()).get(); 381 | return new Params(project, repository, baseBranch, prBranch, gitCheckoutDir, rootProjectDir, rootProjectDirRelativePath, latestBuildToolVersion, usedBuildToolVersion, gitCommitExtraArgs, ignoreClosedPRs, gitHub); 382 | } 383 | 384 | private static VersionInfo getLatestBuildToolVersion(BuildToolStrategy buildToolStrategy, boolean allowPreRelease, VersionInfo usedBuildToolVersion) throws IOException { 385 | VersionInfo latestBuildToolVersion = buildToolStrategy.lookupLatestVersion(allowPreRelease); 386 | if (VersionNumber.parse(usedBuildToolVersion.version) 387 | .compareTo(VersionNumber.parse(latestBuildToolVersion.version)) >= 0) { 388 | return usedBuildToolVersion; 389 | } else { 390 | return latestBuildToolVersion; 391 | } 392 | } 393 | 394 | private static void cloneGitProject(String repository, Path executionRootDir, String baseBranch, Path gitCheckoutDir, ExecOperations execOperations) { 395 | String gitUrl = isUrl(repository) ? repository : "https://github.com/" + repository + ".git"; 396 | execGitCmd(execOperations, executionRootDir, "clone", "--quiet", "--depth", "1", "-b", baseBranch, gitUrl, gitCheckoutDir); 397 | if (isUnsignedCommits()) { 398 | execGitCmd(execOperations, gitCheckoutDir, "config", "--local", "commit.gpgsign", "false"); 399 | } 400 | } 401 | 402 | } 403 | 404 | } 405 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/WrapperUpgradeDomainObject.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.api.Action; 4 | import org.gradle.api.model.ObjectFactory; 5 | import org.gradle.api.provider.ListProperty; 6 | import org.gradle.api.provider.Property; 7 | 8 | import javax.inject.Inject; 9 | 10 | public abstract class WrapperUpgradeDomainObject { 11 | 12 | final String name; 13 | private final Property repo; 14 | private final Property dir; 15 | private final Property baseBranch; 16 | private final Options options; 17 | 18 | @Inject 19 | public WrapperUpgradeDomainObject(String name, ObjectFactory objects) { 20 | this.name = name; 21 | this.repo = objects.property(String.class); 22 | this.dir = objects.property(String.class).convention("."); 23 | this.baseBranch = objects.property(String.class).convention("main"); 24 | this.options = objects.newInstance(Options.class); 25 | } 26 | 27 | public Property getRepo() { 28 | return repo; 29 | } 30 | 31 | public Property getDir() { 32 | return dir; 33 | } 34 | 35 | public Property getBaseBranch() { 36 | return baseBranch; 37 | } 38 | 39 | public Options getOptions() { 40 | return options; 41 | } 42 | 43 | /** 44 | * Called by Gradle at runtime when configuring this object with the `options { }` closure. 45 | */ 46 | public void options(Action action) { 47 | action.execute(options); 48 | } 49 | 50 | public static class Options { 51 | 52 | private final ListProperty gitCommitExtraArgs; 53 | private final Property allowPreRelease; 54 | private final ListProperty labels; 55 | private final ListProperty assignees; 56 | private final ListProperty reviewers; 57 | private final Property recreateClosedPullRequest; 58 | 59 | @Inject 60 | public Options(ObjectFactory objects) { 61 | this.gitCommitExtraArgs = objects.listProperty(String.class); 62 | this.allowPreRelease = objects.property(Boolean.class); 63 | this.labels = objects.listProperty(String.class); 64 | this.assignees = objects.listProperty(String.class); 65 | this.reviewers = objects.listProperty(String.class); 66 | this.recreateClosedPullRequest = objects.property(Boolean.class); 67 | } 68 | 69 | public ListProperty getGitCommitExtraArgs() { 70 | return gitCommitExtraArgs; 71 | } 72 | 73 | public Property getAllowPreRelease() { 74 | return allowPreRelease; 75 | } 76 | 77 | public ListProperty getAssignees() { 78 | return assignees; 79 | } 80 | 81 | public ListProperty getReviewers() { 82 | return reviewers; 83 | } 84 | 85 | public ListProperty getLabels() { 86 | return labels; 87 | } 88 | 89 | public Property getRecreateClosedPullRequest() { 90 | return recreateClosedPullRequest; 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/WrapperUpgradeExtension.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.api.NamedDomainObjectContainer; 4 | import org.gradle.api.model.ObjectFactory; 5 | 6 | import javax.inject.Inject; 7 | 8 | public abstract class WrapperUpgradeExtension { 9 | 10 | private final NamedDomainObjectContainer gradle; 11 | private final NamedDomainObjectContainer maven; 12 | 13 | @Inject 14 | public WrapperUpgradeExtension(ObjectFactory objects) { 15 | this.gradle = objects.domainObjectContainer(WrapperUpgradeDomainObject.class, name -> objects.newInstance(WrapperUpgradeDomainObject.class, name)); 16 | this.maven = objects.domainObjectContainer(WrapperUpgradeDomainObject.class, name -> objects.newInstance(WrapperUpgradeDomainObject.class, name)); 17 | } 18 | 19 | public NamedDomainObjectContainer getGradle() { 20 | return gradle; 21 | } 22 | 23 | public NamedDomainObjectContainer getMaven() { 24 | return maven; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/gradle/wrapperupgrade/WrapperUpgradePlugin.java: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade; 2 | 3 | import org.gradle.api.Plugin; 4 | import org.gradle.api.Project; 5 | import org.gradle.api.Task; 6 | import org.gradle.api.tasks.TaskProvider; 7 | import org.gradle.util.GradleVersion; 8 | 9 | @SuppressWarnings("unused") 10 | public abstract class WrapperUpgradePlugin implements Plugin { 11 | 12 | @Override 13 | public void apply(Project project) { 14 | if (GradleVersion.current().getBaseVersion().compareTo(GradleVersion.version("6.0")) < 0) { 15 | throw new IllegalStateException("This version of the Wrapper Upgrade Gradle plugin is not compatible with Gradle < 6.0"); 16 | } 17 | 18 | WrapperUpgradeExtension wrapperUpgrades = project.getExtensions().create("wrapperUpgrade", WrapperUpgradeExtension.class); 19 | 20 | TaskProvider upgradeGradleWrapperAllTask = project.getTasks().register("upgradeGradleWrapperAll", 21 | t -> { 22 | t.setGroup("Wrapper Upgrades"); 23 | t.setDescription("Updates the Gradle Wrapper on all configured projects."); 24 | }); 25 | 26 | wrapperUpgrades.getGradle().all(upgrade -> { 27 | String taskNameSuffix = upgrade.name.substring(0, 1).toUpperCase() + upgrade.name.substring(1); 28 | TaskProvider upgradeTask = project.getTasks().register("upgradeGradleWrapper" + taskNameSuffix, UpgradeWrapper.class, upgrade, BuildToolStrategy.GRADLE); 29 | upgradeGradleWrapperAllTask.configure(task -> task.dependsOn(upgradeTask)); 30 | }); 31 | 32 | TaskProvider upgradeMavenWrapperAllTask = project.getTasks().register("upgradeMavenWrapperAll", 33 | t -> { 34 | t.setGroup("Wrapper Upgrades"); 35 | t.setDescription("Updates the Maven Wrapper on all configured projects."); 36 | }); 37 | 38 | wrapperUpgrades.getMaven().all(upgrade -> { 39 | String taskNameSuffix = upgrade.name.substring(0, 1).toUpperCase() + upgrade.name.substring(1); 40 | TaskProvider upgradeTask = project.getTasks().register("upgradeMavenWrapper" + taskNameSuffix, UpgradeWrapper.class, upgrade, BuildToolStrategy.MAVEN); 41 | upgradeMavenWrapperAllTask.configure(task -> task.dependsOn(upgradeTask)); 42 | }); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/GradleBuildToolStrategyTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import spock.lang.Shared 4 | import spock.lang.Specification 5 | import spock.lang.TempDir 6 | 7 | import java.nio.file.Files 8 | import java.nio.file.Path 9 | 10 | class GradleBuildToolStrategyTest extends Specification { 11 | 12 | @Shared 13 | BuildToolStrategy gradleBuildToolStrategy = BuildToolStrategy.GRADLE 14 | 15 | @TempDir 16 | Path workingDir 17 | 18 | def "extract current Gradle version bin"() { 19 | given: 20 | createGradleWrapperProperties().text = standard('7.3.3', 'bin', '123') 21 | 22 | when: 23 | def version = gradleBuildToolStrategy.extractCurrentVersion(workingDir) 24 | 25 | then: 26 | version.version == '7.3.3' 27 | version.checksum == Optional.of('123') 28 | } 29 | 30 | def "extract current Gradle version all"() { 31 | given: 32 | createGradleWrapperProperties().text = standard('7.2', 'all', '456') 33 | 34 | when: 35 | def version = gradleBuildToolStrategy.extractCurrentVersion(workingDir) 36 | 37 | then: 38 | version.version == '7.2' 39 | version.checksum == Optional.of('456') 40 | } 41 | 42 | def "extract current Gradle distributionUrl not found"() { 43 | given: 44 | createGradleWrapperProperties().text = 'unexpected' 45 | 46 | when: 47 | gradleBuildToolStrategy.extractCurrentVersion(workingDir) 48 | 49 | then: 50 | def e = thrown(IllegalStateException) 51 | e.message == "Could not find property 'distributionUrl' in file gradle/wrapper/gradle-wrapper.properties" 52 | } 53 | 54 | def "extract current Gradle version unknown"() { 55 | given: 56 | createGradleWrapperProperties().text = 'distributionUrl=unknown' 57 | 58 | when: 59 | gradleBuildToolStrategy.extractCurrentVersion(workingDir) 60 | 61 | then: 62 | def e = thrown(IllegalStateException) 63 | e.message == "Could not extract version from property 'distributionUrl': unknown" 64 | } 65 | 66 | def "extract current Gradle version snapshot"() { 67 | given: 68 | createGradleWrapperProperties().text = standard('7.6-20221024231219+0000', 'bin', '789', true) 69 | 70 | when: 71 | def version = gradleBuildToolStrategy.extractCurrentVersion(workingDir) 72 | 73 | then: 74 | version.version == '7.6-20221024231219+0000' 75 | version.checksum == Optional.of('789') 76 | } 77 | 78 | def "resolve release notes links"() { 79 | given: 80 | def version = '7.4.1' 81 | 82 | when: 83 | def releaseNotesLink = gradleBuildToolStrategy.releaseNotesLink(version) 84 | 85 | then: 86 | releaseNotesLink == 'https://docs.gradle.org/7.4.1/release-notes.html' 87 | } 88 | 89 | private static String standard(String gradleVersion, String gradleDistro, String distributionChecksum, boolean isSnapshot = false) { 90 | """ 91 | distributionBase=GRADLE_USER_HOME 92 | distributionPath=wrapper/dists 93 | distributionSha256Sum=${distributionChecksum} 94 | distributionUrl=https\\://services.gradle.org/distributions${isSnapshot ? "-snapshots": ""}/gradle-${gradleVersion}-${gradleDistro}.zip 95 | zipStoreBase=GRADLE_USER_HOME 96 | zipStorePath=wrapper/dists 97 | """ 98 | } 99 | 100 | private Path createGradleWrapperProperties() { 101 | Files.createDirectories(workingDir.resolve('gradle/wrapper/')) 102 | Files.createFile(workingDir.resolve('gradle/wrapper/gradle-wrapper.properties')) 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/GradleMetadataFetcherTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import spock.lang.Shared 4 | import spock.lang.Specification 5 | 6 | class GradleMetadataFetcherTest extends Specification { 7 | 8 | @Shared 9 | def gradleMetadataFetcher = new GradleMetadataFetcher(getClass().getResource('/gradle-metadata-all.json'), 10 | getClass().getResource('/gradle-metadata-current.json')) 11 | 12 | def "fetch latest version allowing pre-releases"() { 13 | when: 14 | def version = gradleMetadataFetcher.fetchLatestVersion(true) 15 | 16 | then: 17 | version.map(node -> node.get('version').asText()).orElse(null) == '8.0-milestone-2' 18 | } 19 | 20 | def "fetch latest version ignoring pre-releases"() { 21 | when: 22 | def version = gradleMetadataFetcher.fetchLatestVersion(false) 23 | 24 | then: 25 | version.map(node -> node.get('version').asText()).orElse(null) == '7.5.1' 26 | } 27 | 28 | def "fetch unknown latest version allowing pre-releases"() { 29 | when: 30 | def version = new GradleMetadataFetcher(getClass().getResource('/gradle-metadata-all-unknown.json'), 31 | getClass().getResource('/gradle-metadata-current.json')).fetchLatestVersion(true) 32 | 33 | then: 34 | version == Optional.empty() 35 | } 36 | 37 | def "fetch unknown latest version ignoring pre-releases"() { 38 | when: 39 | def version = new GradleMetadataFetcher(getClass().getResource('/gradle-metadata-all.json'), 40 | getClass().getResource('/gradle-metadata-current-unknown.json')).fetchLatestVersion(false) 41 | 42 | then: 43 | version == Optional.empty() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/GradleWrapperUpgradePluginFuncTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import org.gradle.testkit.runner.GradleRunner 4 | import org.gradle.util.GradleVersion 5 | import spock.lang.Ignore 6 | import spock.lang.Requires 7 | import spock.lang.Shared 8 | import spock.lang.Specification 9 | import spock.lang.TempDir 10 | 11 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS 12 | 13 | class GradleWrapperUpgradePluginFuncTest extends Specification { 14 | 15 | @Shared 16 | String latestGradleVersion 17 | 18 | @TempDir 19 | File testProjectDir 20 | File settingsFile 21 | File buildFile 22 | String plugins 23 | 24 | static boolean allowPreRelease = false 25 | 26 | def setupSpec() { 27 | latestGradleVersion = BuildToolStrategy.GRADLE.lookupLatestVersion(allowPreRelease).version 28 | } 29 | 30 | def setup() { 31 | settingsFile = new File(testProjectDir, 'settings.gradle') 32 | buildFile = new File(testProjectDir, 'build.gradle') 33 | 34 | settingsFile << "rootProject.name = 'wrapper-upgrade-gradle-plugin-example'" 35 | plugins = """ 36 | plugins { 37 | id 'base' 38 | id 'org.gradle.wrapper-upgrade' 39 | } 40 | """.stripMargin() 41 | 42 | buildFile << """ 43 | ${plugins} 44 | wrapperUpgrade { 45 | gradle { 46 | 'wrapper-upgrade-gradle-plugin-for-func-tests' { 47 | repo = 'gradle/wrapper-upgrade-gradle-plugin' 48 | baseBranch = 'func-test-do-not-delete' 49 | dir = 'samples/gradle' 50 | options { 51 | recreateClosedPullRequest = true 52 | allowPreRelease = ${allowPreRelease} 53 | labels = ["dependencies", "java"] 54 | assignees = ["wrapperbot"] 55 | reviewers = ["wrapperbot"] 56 | } 57 | } 58 | } 59 | } 60 | """.stripMargin() 61 | } 62 | 63 | def "plugin requires at least Gradle 6.0"() { 64 | when: 65 | def result = GradleRunner.create() 66 | .withProjectDir(testProjectDir) 67 | .withPluginClasspath() 68 | .withGradleVersion(GradleVersion.version('5.6.4').version) 69 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 70 | .withArguments('clean', 'upgradeGradleWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 71 | .buildAndFail() 72 | 73 | then: 74 | result.output.contains('This version of the Wrapper Upgrade Gradle plugin is not compatible with Gradle < 6.0') 75 | } 76 | 77 | @Ignore("Hard to maintain") 78 | def "upgrade wrapper on junit project with dry run"() { 79 | when: 80 | buildFile.text = """ 81 | ${plugins} 82 | wrapperUpgrade { 83 | gradle { 84 | 'junit-func-test' { 85 | repo = 'junit-team/junit5' 86 | baseBranch = 'main' 87 | options { 88 | allowPreRelease = ${allowPreRelease} 89 | } 90 | } 91 | } 92 | } 93 | """.stripMargin() 94 | def result = GradleRunner.create() 95 | .withProjectDir(testProjectDir) 96 | .withPluginClasspath() 97 | .withGradleVersion(determineGradleVersion().version) 98 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 99 | .withArguments('clean', 'upgradeGradleWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 100 | .build() 101 | 102 | then: 103 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 104 | 105 | and: 106 | result.output.contains "No pull request created to upgrade Gradle Wrapper to 8.2-rc-2 since already on latest version for project 'junit-func-test'" 107 | } 108 | 109 | def "upgrade wrapper on wrapper-upgrade-gradle-plugin with dry run"() { 110 | when: 111 | def result = GradleRunner.create() 112 | .withProjectDir(testProjectDir) 113 | .withPluginClasspath() 114 | .withGradleVersion(determineGradleVersion().version) 115 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 116 | .withArguments('clean', 'upgradeGradleWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 117 | .build() 118 | 119 | then: 120 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 121 | 122 | and: 123 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/gradle-wrapper-${latestGradleVersion}") 124 | 125 | and: 126 | def gitDir = new File(testProjectDir, 'build/git-clones/wrapper-upgrade-gradle-plugin-for-func-tests/samples/gradle') 127 | def proc = 'git show --oneline --name-only HEAD'.execute(null, gitDir) 128 | def output = proc.in.text 129 | with(output) { 130 | contains "gradle/wrapper/gradle-wrapper.jar" 131 | contains "gradle/wrapper/gradle-wrapper.properties" 132 | contains "gradlew" 133 | } 134 | 135 | and: 136 | def proc2 = 'git show --oneline HEAD'.execute(null, gitDir) 137 | def output2 = proc2.in.text 138 | with(output2) { 139 | contains "Bump Gradle Wrapper from 6.9 to ${latestGradleVersion}" 140 | contains "Binary files a/samples/gradle/gradle/wrapper/gradle-wrapper.jar and b/samples/gradle/gradle/wrapper/gradle-wrapper.jar differ" 141 | contains "-distributionUrl=https\\://services.gradle.org/distributions/gradle-6.9-bin.zip" 142 | contains "+distributionUrl=https\\://services.gradle.org/distributions/gradle-${latestGradleVersion}-bin.zip" 143 | } 144 | 145 | and: 146 | def proc3 = 'git log --format=%B -n 1 HEAD'.execute(null, gitDir) 147 | def output3 = proc3.in.text 148 | with(output3) { 149 | contains "Signed-off-by:" 150 | contains "Release notes of Gradle ${latestGradleVersion} can be found here" 151 | } 152 | } 153 | 154 | def "upgrade wrapper on wrapper-upgrade-gradle-plugin configured with Kotlin (#factoryFunc) with dry run"(factoryFunc) { 155 | given: 156 | buildFile.delete() 157 | settingsFile.delete() 158 | settingsFile = new File(testProjectDir, 'settings.gradle.kts') 159 | buildFile = new File(testProjectDir, 'build.gradle.kts') 160 | 161 | settingsFile << '''rootProject.name = "wrapper-upgrade-gradle-plugin-example"''' 162 | plugins = """ 163 | plugins { 164 | id("base") 165 | id("org.gradle.wrapper-upgrade") 166 | } 167 | """.stripMargin() 168 | 169 | buildFile.text = """ 170 | ${plugins} 171 | wrapperUpgrade { 172 | gradle { 173 | ${factoryFunc}("wrapper-upgrade-gradle-plugin-for-func-tests") { 174 | repo.set("gradle/wrapper-upgrade-gradle-plugin") 175 | baseBranch.set("func-test-do-not-delete") 176 | dir.set("samples/gradle") 177 | options { 178 | allowPreRelease.set(${allowPreRelease}) 179 | } 180 | } 181 | } 182 | } 183 | """.stripMargin() 184 | 185 | when: 186 | def result = GradleRunner.create() 187 | .withProjectDir(testProjectDir) 188 | .withPluginClasspath() 189 | .withGradleVersion(determineGradleVersion().version) 190 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 191 | .withArguments('clean', 'upgradeGradleWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 192 | .build() 193 | 194 | then: 195 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 196 | 197 | and: 198 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/gradle-wrapper-${latestGradleVersion}") 199 | 200 | and: 201 | def gitDir = new File(testProjectDir, 'build/git-clones/wrapper-upgrade-gradle-plugin-for-func-tests/samples/gradle') 202 | def proc = 'git show --oneline --name-only HEAD'.execute(null, gitDir) 203 | def output = proc.in.text 204 | output.contains "gradle/wrapper/gradle-wrapper.jar" 205 | output.contains "gradle/wrapper/gradle-wrapper.properties" 206 | output.contains "gradlew" 207 | 208 | and: 209 | def proc2 = 'git show --oneline HEAD'.execute(null, gitDir) 210 | def output2 = proc2.in.text 211 | output2.contains "Bump Gradle Wrapper from 6.9 to ${latestGradleVersion}" 212 | output2.contains "Binary files a/samples/gradle/gradle/wrapper/gradle-wrapper.jar and b/samples/gradle/gradle/wrapper/gradle-wrapper.jar differ" 213 | output2.contains "-distributionUrl=https\\://services.gradle.org/distributions/gradle-6.9-bin.zip" 214 | output2.contains "+distributionUrl=https\\://services.gradle.org/distributions/gradle-${latestGradleVersion}-bin.zip" 215 | 216 | where: 217 | factoryFunc << ['create', 'register'] 218 | } 219 | 220 | @Requires({ determineGradleVersion().baseVersion >= GradleVersion.version('7.1') }) 221 | def "upgrade wrapper on wrapper-upgrade-gradle-plugin with dry run and configuration cache"() { 222 | when: 223 | def result = GradleRunner.create() 224 | .withProjectDir(testProjectDir) 225 | .withPluginClasspath() 226 | .withGradleVersion(determineGradleVersion().version) 227 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 228 | .withArguments('clean', 'upgradeGradleWrapperAll', '--configuration-cache', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 229 | .build() 230 | 231 | then: 232 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 233 | 234 | and: 235 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/gradle-wrapper-${latestGradleVersion}") 236 | result.output.contains('Configuration cache entry stored.') 237 | 238 | when: 239 | result = GradleRunner.create() 240 | .withProjectDir(testProjectDir) 241 | .withPluginClasspath() 242 | .withGradleVersion(determineGradleVersion().version) 243 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 244 | .withArguments('clean', 'upgradeGradleWrapperAll', '--configuration-cache', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 245 | .build() 246 | 247 | then: 248 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 249 | 250 | and: 251 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/gradle-wrapper-${latestGradleVersion}") 252 | result.output.contains('Reusing configuration cache.') 253 | } 254 | 255 | def "upgrade wrapper on wrapper-upgrade-gradle-plugin with dry run and optional Git arguments"() { 256 | given: 257 | buildFile.text = """ 258 | 259 | plugins { 260 | id 'base' 261 | id 'org.gradle.wrapper-upgrade' 262 | } 263 | 264 | wrapperUpgrade { 265 | gradle { 266 | 'wrapper-upgrade-gradle-plugin-for-func-tests' { 267 | repo = 'gradle/wrapper-upgrade-gradle-plugin' 268 | baseBranch = 'func-test-do-not-delete' 269 | dir = 'samples/gradle' 270 | options { 271 | gitCommitExtraArgs = ['--date="Wed Mar 23 15:00:00 CET 2022"'] 272 | } 273 | } 274 | } 275 | } 276 | """ 277 | when: 278 | def result = GradleRunner.create() 279 | .withProjectDir(testProjectDir) 280 | .withPluginClasspath() 281 | .withGradleVersion(determineGradleVersion().version) 282 | .withEnvironment(System.getenv() + ["JAVA_HOME": determineJavaHome()]) 283 | .withArguments('clean', 'upgradeGradleWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 284 | .build() 285 | 286 | then: 287 | result.task(':upgradeGradleWrapperAll').outcome == SUCCESS 288 | 289 | and: 290 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/gradle-wrapper-${latestGradleVersion}") 291 | 292 | and: 293 | def gitDir = new File(testProjectDir, 'build/git-clones/wrapper-upgrade-gradle-plugin-for-func-tests/samples/gradle') 294 | def proc = 'git show -s HEAD'.execute(null, gitDir) 295 | def output = proc.in.text 296 | output.contains "Bump Gradle Wrapper from 6.9 to ${latestGradleVersion}" 297 | output.contains 'Date: Wed Mar 23 15:00:00 2022 +0100' 298 | } 299 | 300 | private static GradleVersion determineGradleVersion() { 301 | def injectedGradleVersionString = System.getProperty('testContext.gradleVersion') 302 | injectedGradleVersionString ? GradleVersion.version(injectedGradleVersionString) : GradleVersion.current() 303 | } 304 | 305 | private static String determineJavaHome() { 306 | return System.getProperty('testContext.javaHome') 307 | } 308 | 309 | } 310 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/MavenBuildToolStrategyTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import spock.lang.Shared 4 | import spock.lang.Specification 5 | import spock.lang.TempDir 6 | 7 | import java.nio.file.Files 8 | import java.nio.file.Path 9 | 10 | class MavenBuildToolStrategyTest extends Specification { 11 | 12 | @Shared 13 | BuildToolStrategy mavenBuildToolStrategy = BuildToolStrategy.MAVEN 14 | 15 | @TempDir 16 | Path workingDir 17 | 18 | def "extract current Maven version"() { 19 | given: 20 | createMavenWrapperProperties().text = standard('3.8.2') 21 | 22 | when: 23 | def version = mavenBuildToolStrategy.extractCurrentVersion(workingDir) 24 | 25 | then: 26 | version.version == '3.8.2' 27 | version.checksum == Optional.empty() 28 | } 29 | 30 | def "extract current Maven distributionUrl not found"() { 31 | given: 32 | createMavenWrapperProperties().text = 'unexpected' 33 | 34 | when: 35 | mavenBuildToolStrategy.extractCurrentVersion(workingDir) 36 | 37 | then: 38 | def e = thrown(IllegalStateException) 39 | e.message == "Could not find property 'distributionUrl' in file .mvn/wrapper/maven-wrapper.properties" 40 | } 41 | 42 | def "extract current Maven version unknown"() { 43 | given: 44 | createMavenWrapperProperties().text = 'distributionUrl=unknown' 45 | 46 | when: 47 | mavenBuildToolStrategy.extractCurrentVersion(workingDir) 48 | 49 | then: 50 | def e = thrown(IllegalStateException) 51 | e.message == "Could not extract version from property 'distributionUrl': unknown" 52 | } 53 | 54 | def "resolve release notes links"() { 55 | given: 56 | def version = '3.8.5' 57 | 58 | when: 59 | def releaseNotesLink = mavenBuildToolStrategy.releaseNotesLink(version) 60 | 61 | then: 62 | releaseNotesLink == 'https://maven.apache.org/docs/3.8.5/release-notes.html' 63 | } 64 | 65 | private static String standard(String mavenVersion) { 66 | """ 67 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/${mavenVersion}/apache-maven-${mavenVersion}-bin.zip 68 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 69 | """ 70 | } 71 | 72 | private Path createMavenWrapperProperties() { 73 | Files.createDirectories(workingDir.resolve('.mvn/wrapper/')) 74 | Files.createFile(workingDir.resolve('.mvn/wrapper/maven-wrapper.properties')) 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/MavenMetadataFetcherTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import spock.lang.Shared 4 | import spock.lang.Specification 5 | 6 | class MavenMetadataFetcherTest extends Specification { 7 | 8 | @Shared 9 | def mavenMetadataFetcher = new MavenMetadataFetcher(getClass().getResource('/maven-metadata.xml')) 10 | 11 | def "fetch latest version allowing pre-releases"() { 12 | when: 13 | def version = mavenMetadataFetcher.fetchLatestVersion(true) 14 | 15 | then: 16 | version.map(v -> v as String).orElse(null) == '4.0.0-alpha-2' 17 | } 18 | 19 | def "fetch latest version ignoring pre-releases"() { 20 | when: 21 | def version = mavenMetadataFetcher.fetchLatestVersion(false) 22 | 23 | then: 24 | version.map(v -> v as String).orElse(null) == '3.8.6' 25 | } 26 | 27 | def "fetch unknown latest version"() { 28 | when: 29 | def version = new MavenMetadataFetcher(getClass().getResource('/maven-metadata-none.xml')).fetchLatestVersion(false) 30 | 31 | then: 32 | version == Optional.empty() 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/MavenWrapperUpgradePluginFuncTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import org.gradle.testkit.runner.GradleRunner 4 | import spock.lang.Shared 5 | import spock.lang.Specification 6 | import spock.lang.TempDir 7 | 8 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS 9 | 10 | class MavenWrapperUpgradePluginFuncTest extends Specification { 11 | 12 | @Shared 13 | String latestMavenVersion 14 | 15 | @TempDir 16 | File testProjectDir 17 | File settingsFile 18 | File buildFile 19 | 20 | static boolean allowPreRelease = false 21 | 22 | def setupSpec() { 23 | latestMavenVersion = BuildToolStrategy.MAVEN.lookupLatestVersion(allowPreRelease).version 24 | } 25 | 26 | def setup() { 27 | settingsFile = new File(testProjectDir, 'settings.gradle') 28 | buildFile = new File(testProjectDir, 'build.gradle') 29 | 30 | settingsFile << "rootProject.name = 'wrapper-upgrade-gradle-plugin-example'" 31 | buildFile << """ 32 | 33 | plugins { 34 | id 'base' 35 | id 'org.gradle.wrapper-upgrade' 36 | } 37 | 38 | wrapperUpgrade { 39 | maven { 40 | 'wrapper-upgrade-gradle-plugin-for-func-tests' { 41 | repo = 'gradle/wrapper-upgrade-gradle-plugin' 42 | baseBranch = 'func-test-do-not-delete' 43 | dir = 'samples/maven' 44 | options { 45 | recreateClosedPullRequest = true 46 | allowPreRelease = ${allowPreRelease} 47 | labels = ["dependencies", "java"] 48 | assignees = ["wrapperbot"] 49 | reviewers = ["wrapperbot"] 50 | } 51 | } 52 | } 53 | } 54 | """ 55 | } 56 | 57 | def "upgrade wrapper on CCUD extension with dry run"() { 58 | when: 59 | def result = GradleRunner.create() 60 | .withProjectDir(testProjectDir) 61 | .withPluginClasspath() 62 | .withArguments('clean', 'upgradeMavenWrapperAll', '-DwrapperUpgrade.dryRun', '-DwrapperUpgrade.unsignedCommits') 63 | .build() 64 | 65 | then: 66 | result.task(':upgradeMavenWrapperAll').outcome == SUCCESS 67 | 68 | and: 69 | result.output.contains("Dry run: Skipping creation of pull request 'wrapperbot/wrapper-upgrade-gradle-plugin-for-func-tests/maven-wrapper-${latestMavenVersion}") 70 | 71 | and: 72 | def gitDir = new File(testProjectDir, 'build/git-clones/wrapper-upgrade-gradle-plugin-for-func-tests/samples/maven') 73 | def proc = 'git show --oneline --name-only HEAD'.execute(null, gitDir) 74 | def output = proc.in.text 75 | with(output) { 76 | contains ".mvn/wrapper/maven-wrapper.jar" 77 | contains ".mvn/wrapper/maven-wrapper.properties" 78 | } 79 | 80 | and: 81 | def proc2 = 'git show --oneline HEAD'.execute(null, gitDir) 82 | def output2 = proc2.in.text 83 | with(output2) { 84 | contains "Bump Maven Wrapper from 3.6.3 to ${latestMavenVersion}" 85 | contains 'Binary files a/samples/maven/.mvn/wrapper/maven-wrapper.jar and /dev/null differ' 86 | contains "-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip" 87 | contains "+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/${latestMavenVersion}/apache-maven-${latestMavenVersion}-bin.zip" 88 | contains "-wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 89 | contains "+wrapperVersion=" // we do not include the version as it's updated frequently 90 | } 91 | 92 | and: 93 | def proc3 = 'git log --format=%B -n 1 HEAD'.execute(null, gitDir) 94 | def output3 = proc3.in.text 95 | with(output3) { 96 | contains "Signed-off-by:" 97 | contains "Release notes of Maven ${latestMavenVersion} can be found here" 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/test/groovy/org/gradle/wrapperupgrade/PullRequestUtilsTest.groovy: -------------------------------------------------------------------------------- 1 | package org.gradle.wrapperupgrade 2 | 3 | import org.kohsuke.github.GHCommitPointer 4 | import org.kohsuke.github.GHIssueState 5 | import org.kohsuke.github.GHPullRequest 6 | import spock.lang.Specification 7 | 8 | class PullRequestUtilsTest extends Specification { 9 | 10 | def "closed pr exists"() { 11 | given: 12 | def pullRequests = [ 13 | stub('wrapperbot/someproj/gradle-wrapper-8.10', GHIssueState.OPEN), 14 | stub('wrapperbot/someproj/gradle-wrapper-8.11.1', GHIssueState.OPEN), 15 | stub('wrapperbot/someproj/gradle-wrapper-8.11.1', GHIssueState.CLOSED) 16 | ] as Set 17 | 18 | def utils = new PullRequestUtils(pullRequests) 19 | 20 | when: 21 | def result = utils.closedPrExists(branch) 22 | 23 | then: 24 | result == exists 25 | 26 | where: 27 | branch | exists 28 | 'wrapperbot/someproj/gradle-wrapper-8.11.1' | true 29 | 'wrapperbot/someproj/gradle-wrapper-8.10' | false 30 | } 31 | 32 | def "pull requests to close"() { 33 | given: 34 | def pullRequests = [ 35 | stub('somebranch', GHIssueState.OPEN), 36 | stub('someotherbranch', GHIssueState.CLOSED), 37 | stub('wrapperbot/someproj/gradle-wrapper-8.9', GHIssueState.OPEN), 38 | stub('wrapperbot/someproj/gradle-wrapper-8.10', GHIssueState.CLOSED), 39 | stub('wrapperbot/someproj/gradle-wrapper-8.11.1', GHIssueState.OPEN) 40 | ] as Set 41 | 42 | def utils = new PullRequestUtils(pullRequests) 43 | 44 | when: 45 | def result = utils.pullRequestsToClose('someproj', 'gradle', latestBuildToolVersion) 46 | 47 | then: 48 | result.collect { it.head.ref } as Set == toClose as Set 49 | 50 | where: 51 | latestBuildToolVersion | toClose 52 | '7.13' | [] 53 | '8.10' | ['wrapperbot/someproj/gradle-wrapper-8.9'] 54 | '8.11.1' | ['wrapperbot/someproj/gradle-wrapper-8.9'] 55 | '8.11.2' | ['wrapperbot/someproj/gradle-wrapper-8.11.1', 'wrapperbot/someproj/gradle-wrapper-8.9'] 56 | '8.12' | ['wrapperbot/someproj/gradle-wrapper-8.11.1', 'wrapperbot/someproj/gradle-wrapper-8.9'] 57 | } 58 | 59 | private GHPullRequest stub(String branchName, GHIssueState state) { 60 | def pr = Stub(GHPullRequest) 61 | def pointer = Stub(GHCommitPointer) 62 | pointer.getRef() >> branchName 63 | pr.getHead() >> pointer 64 | pr.getState() >> state 65 | return pr 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/resources/gradle-metadata-all-unknown.json: -------------------------------------------------------------------------------- 1 | [ { 2 | "buildTime" : "20221024231219+0000" 3 | } ] 4 | -------------------------------------------------------------------------------- /src/test/resources/gradle-metadata-current-unknown.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildTime" : "20220805211756+0000" 3 | } 4 | -------------------------------------------------------------------------------- /src/test/resources/gradle-metadata-current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version" : "7.5.1", 3 | "buildTime" : "20220805211756+0000", 4 | "current" : true, 5 | "snapshot" : false, 6 | "nightly" : false, 7 | "releaseNightly" : false, 8 | "activeRc" : false, 9 | "rcFor" : "", 10 | "milestoneFor" : "", 11 | "broken" : false, 12 | "downloadUrl" : "https://services.gradle.org/distributions/gradle-7.5.1-bin.zip", 13 | "checksumUrl" : "https://services.gradle.org/distributions/gradle-7.5.1-bin.zip.sha256", 14 | "wrapperChecksumUrl" : "https://services.gradle.org/distributions/gradle-7.5.1-wrapper.jar.sha256" 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/maven-metadata-none.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.apache.maven 4 | maven-core 5 | 6 | -------------------------------------------------------------------------------- /src/test/resources/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.apache.maven 4 | maven-core 5 | 6 | 4.0.0-alpha-2 7 | 4.0.0-alpha-2 8 | 9 | 2.0-alpha-1 10 | 2.0-alpha-2 11 | 2.0-alpha-3 12 | 2.0-beta-1 13 | 2.0-beta-2 14 | 2.0-beta-3 15 | 2.0 16 | 2.0.1 17 | 2.0.2 18 | 2.0.3 19 | 2.0.4 20 | 2.0.5 21 | 2.0.6 22 | 2.0.7 23 | 2.0.8 24 | 2.0.9 25 | 2.0.10 26 | 2.0.11 27 | 2.1.0-M1 28 | 2.1.0 29 | 2.2.0 30 | 2.2.1 31 | 3.0-alpha-1 32 | 3.0-alpha-2 33 | 3.0-alpha-3 34 | 3.0-alpha-4 35 | 3.0-alpha-5 36 | 3.0-alpha-6 37 | 3.0-alpha-7 38 | 3.0-beta-1 39 | 3.0-beta-2 40 | 3.0-beta-3 41 | 3.0 42 | 3.0.1 43 | 3.0.2 44 | 3.0.3 45 | 3.0.4 46 | 3.0.5 47 | 3.1.0-alpha-1 48 | 3.1.0 49 | 3.1.1 50 | 3.2.1 51 | 3.2.2 52 | 3.2.3 53 | 3.2.5 54 | 3.3.1 55 | 3.3.3 56 | 3.3.9 57 | 3.5.0-alpha-1 58 | 3.5.0-beta-1 59 | 3.5.0 60 | 3.5.2 61 | 3.5.3 62 | 3.5.4 63 | 3.6.0 64 | 3.6.1 65 | 3.6.2 66 | 3.6.3 67 | 3.8.1 68 | 3.8.2 69 | 3.8.3 70 | 3.8.4 71 | 3.8.5 72 | 3.8.6 73 | 4.0.0-alpha-2 74 | 75 | 20221024100320 76 | 77 | 78 | --------------------------------------------------------------------------------