├── .editorconfig ├── .gitattributes ├── .github ├── renovate.json5 └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── groovy │ └── ca │ └── stellardrift │ └── gitpatcher │ ├── Git.groovy │ ├── GitPatcher.groovy │ ├── GitPatcherExtension.java │ ├── GitPatcherExtensionImpl.java │ ├── PatchExtension.java │ ├── PatchExtensionImpl.java │ ├── RepoPatchDetails.java │ ├── RepoPatchDetailsImpl.java │ └── task │ ├── FindGitTask.groovy │ ├── GitTask.groovy │ ├── SubmoduleTask.groovy │ ├── UpdateSubmodulesTask.groovy │ └── patch │ ├── ApplyPatchesTask.groovy │ ├── MakePatchesTask.groovy │ └── PatchTask.groovy └── test ├── java └── ca │ └── stellardrift │ └── gitpatcher │ ├── GitPatcherFunctionalTest.java │ └── GitPatcherTest.java └── resources └── ca └── stellardrift └── gitpatcher └── pluginSimplyApplies └── in ├── build.gradle └── settings.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 9999 10 | tab_width = 4 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bat text eol=crlf 3 | gradlew text eol=lf 4 | *.sh text eol=lf 5 | 6 | ################# 7 | ## Java 8 | ################# 9 | *.java text 10 | *.java diff=java 11 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | extends: [ 3 | "local>KyoriPowered/.github:renovate-config" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: "build" 2 | 3 | on: 4 | push: 5 | branches: "**" 6 | tags-ignore: ["**"] 7 | pull_request: 8 | 9 | jobs: 10 | call-build: 11 | uses: "KyoriPowered/.github/.github/workflows/shared-ci.yaml@trunk" 12 | permissions: 13 | actions: write 14 | contents: write 15 | secrets: 16 | STELLARDRIFT_USERNAME: ${{ secrets.STELLARDRIFT_USERNAME }} 17 | STELLARDRIFT_PASSWORD: ${{ secrets.STELLARDRIFT_PASSWORD }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ########################## 2 | #### General 3 | ########################## 4 | # https://gist.github.com/octocat/9257657 5 | 6 | *.log 7 | *.bak 8 | 9 | ################# 10 | ## Compiled source 11 | ################# 12 | *.com 13 | *.class 14 | *.dll 15 | *.exe 16 | *.o 17 | *.so 18 | 19 | ################# 20 | ## Archives 21 | ################# 22 | # https://github.com/github/gitignore/blob/master/Global/Archives.gitignore 23 | 24 | # It's better to unpack these files and commit the raw source because 25 | # git has its own built in compression methods. 26 | *.7z 27 | *.jar 28 | *.rar 29 | *.zip 30 | *.gz 31 | *.bzip 32 | *.bz2 33 | *.xz 34 | *.lzma 35 | *.cab 36 | 37 | #packing-only formats 38 | *.iso 39 | *.tar 40 | 41 | #package management formats 42 | *.dmg 43 | *.xpi 44 | *.gem 45 | *.egg 46 | *.deb 47 | *.rpm 48 | *.msi 49 | *.msm 50 | *.msp 51 | 52 | ########################## 53 | #### Operating Systems 54 | ########################## 55 | 56 | ################# 57 | ## Windows 58 | ################# 59 | # https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 60 | 61 | # Windows image file caches 62 | Thumbs.db 63 | ehthumbs.db 64 | 65 | # Folder config file 66 | Desktop.ini 67 | 68 | # Recycle Bin used on file shares 69 | $RECYCLE.BIN/ 70 | 71 | # Windows Installer files 72 | *.cab 73 | *.msi 74 | *.msm 75 | *.msp 76 | 77 | # Windows shortcuts 78 | *.lnk 79 | 80 | ################# 81 | ## OSX 82 | ################# 83 | # https://github.com/github/gitignore/blob/master/Global/OSX.gitignore 84 | 85 | .DS_Store 86 | .AppleDouble 87 | .LSOverride 88 | 89 | # Icon must end with two \r 90 | Icon 91 | 92 | 93 | # Thumbnails 94 | ._* 95 | 96 | # Files that might appear on external disk 97 | .Spotlight-V100 98 | .Trashes 99 | 100 | # Directories potentially created on remote AFP share 101 | .AppleDB 102 | .AppleDesktop 103 | Network Trash Folder 104 | Temporary Items 105 | .apdisk 106 | 107 | ################# 108 | ## Linux 109 | ################# 110 | # https://github.com/github/gitignore/blob/master/Global/Linux.gitignore 111 | 112 | *~ 113 | 114 | # KDE directory preferences 115 | .directory 116 | 117 | ########################## 118 | #### IDE 119 | ########################## 120 | 121 | ################# 122 | ## Eclipse 123 | ################# 124 | # https://github.com/github/gitignore/blob/master/Global/Eclipse.gitignore 125 | 126 | # Don't commit project files 127 | .classpath 128 | .project 129 | 130 | *.pydevproject 131 | .metadata 132 | .gradle 133 | bin/ 134 | tmp/ 135 | *.tmp 136 | *.bak 137 | *.swp 138 | *~.nib 139 | local.properties 140 | .settings/ 141 | .loadpath 142 | 143 | # External tool builders 144 | .externalToolBuilders/ 145 | 146 | # Locally stored "Eclipse launch configurations" 147 | *.launch 148 | 149 | # CDT-specific 150 | .cproject 151 | 152 | # PDT-specific 153 | .buildpath 154 | 155 | # sbteclipse plugin 156 | .target 157 | 158 | # TeXlipse plugin 159 | .texlipse 160 | 161 | ################# 162 | ## NetBeans 163 | ################# 164 | # https://github.com/github/gitignore/blob/master/Global/NetBeans.gitignore 165 | 166 | nbproject/private/ 167 | build/ 168 | nbbuild/ 169 | dist/ 170 | nbdist/ 171 | nbactions.xml 172 | nb-configuration.xml 173 | 174 | ################# 175 | ## JetBrains 176 | ################# 177 | # https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore 178 | 179 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 180 | 181 | *.iml 182 | 183 | ## Directory-based project format: 184 | .idea/ 185 | # if you remove the above rule, at least ignore the following: 186 | 187 | # User-specific stuff: 188 | # .idea/workspace.xml 189 | # .idea/tasks.xml 190 | # .idea/dictionaries 191 | 192 | # Sensitive or high-churn files: 193 | # .idea/dataSources.ids 194 | # .idea/dataSources.xml 195 | # .idea/sqlDataSources.xml 196 | # .idea/dynamic.xml 197 | # .idea/uiDesigner.xml 198 | 199 | # Gradle: 200 | # .idea/gradle.xml 201 | # .idea/libraries 202 | 203 | # Mongo Explorer plugin: 204 | # .idea/mongoSettings.xml 205 | 206 | ## File-based project format: 207 | *.ipr 208 | *.iws 209 | 210 | ## Plugin-specific files: 211 | 212 | # IntelliJ 213 | out/ 214 | 215 | # mpeltonen/sbt-idea plugin 216 | .idea_modules/ 217 | 218 | # JIRA plugin 219 | atlassian-ide-plugin.xml 220 | 221 | # Crashlytics plugin (for Android Studio and IntelliJ) 222 | com_crashlytics_export_strings.xml 223 | 224 | ########################## 225 | #### Project 226 | ########################## 227 | 228 | ################# 229 | ## Java 230 | ################# 231 | # https://github.com/github/gitignore/blob/master/Java.gitignore 232 | 233 | *.class 234 | 235 | # Mobile Tools for Java (J2ME) 236 | .mtj.tmp/ 237 | 238 | # Package Files # 239 | *.jar 240 | *.war 241 | *.ear 242 | 243 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 244 | hs_err_pid* 245 | 246 | ################# 247 | ## Gradle 248 | ################# 249 | # https://github.com/github/gitignore/blob/master/Gradle.gitignore 250 | 251 | .gradle 252 | build/ 253 | 254 | # Ignore Gradle GUI config 255 | gradle-app.setting 256 | 257 | # Allow the Gradle wrapper 258 | !gradle-wrapper.jar 259 | /repo/ 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) $YEAR, Stellardrift and contributors 2 | Copyright (c) 2015, Minecrell 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitpatcher 2 | gitpatcher is a Gradle plugin that can manage patches for Git repositories for you ([example]). This is useful if you need a few smaller changes for a Git repository that can't be contributed upstream, but you still easily want to keep up to date with upstream. 3 | gitpatcher manages a local submodule as base, and applies patches from a configurable folder in an extra repository. A local Git installation on 4 | the PATH is required for it to run. 5 | 6 | # Installation 7 | 1. Add a submodule for the project you want to patch. 8 | 2. Apply gitpatcher to your Gradle project: 9 | 10 | ```gradle 11 | plugins { 12 | id 'ca.stellardrift.gitpatcher' version '1.1.0' 13 | } 14 | ``` 15 | 3. Configure gitpatcher: 16 | 17 | ```gradle 18 | gitPatcher.patchedRepos { 19 | // 'repo' is arbitrary; it will be used for task names (see below section) 20 | 'repo' { 21 | // The submodule path you just created 22 | submodule = 'upstream' 23 | // The target folder for the patched repositories 24 | target = file('target') 25 | // The folder where the patches are saved 26 | patches = file('patches') 27 | } 28 | } 29 | ``` 30 | 2. Add the `target` directory to your `.gitignore` 31 | 3. For any gradle projects within the patched directory, create an empty directory for the project directory, with nothing but a `.gitkeep` to tell Git to keep the directory on checkouts (hint: `git add -f target/.gitkeep` will bypass your `.gitignore` to track this file) 32 | 4. That's it! Now you can initialize your repository (see below) and start making commits to it. Then just make the patches and you can apply it to the target repository as often as you want. 33 | 34 | # Tasks 35 | | Name | Description | 36 | |-------------------------------------|--------------------------------------------------------------------------------------| 37 | | `update[CapitalizedName]Submodules` | Initializes the submodule and updates it if it is outdated. | 38 | | `apply[CapitalizedName]Patches` | Initializes the target repository and applies the patches from the patch folder. | 39 | | `make[CapitalizedName]Patches` | Creates or updates the patches in the patch folder. | 40 | | `updateSubmodules` | Lifecycle task which depends on all other `update[CapitalizedName]Submodules` tasks. | 41 | | `applyPatches` | Lifecycle task which depends on all other `apply[CapitalizedName]Patches` tasks. | 42 | | `makePatches` | Lifecycle task which depends on all other `make[CapitalizedName]Patches` tasks. | 43 | 44 | [example]: https://github.com/LapisBlue/Pore/tree/master/patches 45 | 46 | # History 47 | 48 | gitpatcher was originally produced by Minecrell and the Cadix team. The MinecraftForge team picked up development for a period of a few years. 49 | This project is now maintained by Stellardrift. 50 | 51 | gitpatcher is released under [the MIT license](./LICENSE) 52 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-gradle-plugin' 3 | id 'groovy' 4 | 5 | alias libs.plugins.indra 6 | alias libs.plugins.indra.gradlePluginPublish 7 | alias libs.plugins.indra.licenserSpotless 8 | alias libs.plugins.spotless 9 | alias libs.plugins.pluginPublishPlugin 10 | } 11 | 12 | group = 'ca.stellardrift' 13 | description = 'A Gradle plugin to manage patches for Git repositories' 14 | 15 | dependencies { 16 | testImplementation platform(libs.junit.bom) 17 | testImplementation libs.mammoth.test 18 | testImplementation libs.junit.jupiter.api 19 | testRuntimeOnly libs.junit.jupiter.engine 20 | testRuntimeOnly libs.junit.launcher 21 | } 22 | 23 | tasks.withType(GroovyCompile).configureEach { 24 | groovyOptions.optimizationOptions.indy = true 25 | } 26 | 27 | indra { 28 | javaVersions { 29 | target 8 30 | testWith 11, 17, 21 31 | } 32 | github("zml2008", "gitpatcher") 33 | mitLicense() 34 | 35 | publishSnapshotsTo("stellardrift", "https://repo.stellardrift.ca/maven/snapshots/") 36 | publishReleasesTo("stellardrift", "https://repo.stellardrift.ca/maven/releases/") 37 | configurePublications { 38 | pom { 39 | url = 'https://gitpatcher.stellardrift.ca' 40 | developers { 41 | developer { 42 | id = "zml" 43 | name = "zml" 44 | timezone = "America/Vancouver" 45 | } 46 | } 47 | contributors { 48 | contributor { 49 | id = "minecrell" 50 | name = "Minecrell" 51 | roles = ["Former Maintainer"] 52 | } 53 | contributor { 54 | id = "forge" 55 | name = "MinecraftForge" 56 | roles = ["Former Maintainer"] 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | indraSpotlessLicenser { 64 | licenseHeaderFile file('LICENSE') 65 | } 66 | 67 | spotless { 68 | ratchetFrom 'origin/trunk' 69 | } 70 | 71 | tasks.named('javadoc') { 72 | enabled = false 73 | } 74 | 75 | tasks.named('javadocJar', Jar).configure { 76 | dependsOn 'groovydoc' 77 | archiveClassifier = 'groovydoc' 78 | from groovydoc.destinationDir 79 | } 80 | 81 | tasks.named('jar', Jar) { 82 | indraGit.applyVcsInformationToManifest(manifest) 83 | } 84 | 85 | validatePlugins { 86 | ignoreFailures = true 87 | failOnWarning = false // abstract classes should be ok without annotations? 88 | } 89 | 90 | indraPluginPublishing { 91 | website 'https://gitpatcher.stellardrift.ca' 92 | plugin( 93 | "gitpatcher", 94 | "ca.stellardrift.gitpatcher.GitPatcher", 95 | "GitPatcher", 96 | description, 97 | ["git", "patching"] 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.1.3-SNAPSHOT 2 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [metadata] 2 | version = "1.0" 3 | 4 | [versions] 5 | indra = "3.1.3" 6 | jspecify = "1.0.0" 7 | junit = "5.12.0" 8 | mammoth = "1.4.0" 9 | spotless = "7.0.2" 10 | pluginPublish = "1.3.1" 11 | 12 | [libraries] 13 | jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" } 14 | junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } 15 | junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api" } 16 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine" } 17 | junit-launcher = { module = "org.junit.platform:junit-platform-launcher" } 18 | mammoth-test = { module = "net.kyori:mammoth-test", version.ref = "mammoth" } 19 | 20 | # testing 21 | 22 | [plugins] 23 | indra = { id = "net.kyori.indra", version.ref = "indra" } 24 | indra-gradlePluginPublish = { id = "net.kyori.indra.publishing.gradle-plugin", version.ref = "indra" } 25 | indra-licenserSpotless = { id = "net.kyori.indra.licenser.spotless", version.ref = "indra" } 26 | pluginPublishPlugin = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } 27 | spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } 28 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zml2008/gitpatcher/4de52bf9bfbc791075ec94c536a002de99add33d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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 | org.gradle.wrapper.GradleWrapperMain \ 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=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = "stellardriftReleases" 5 | url = "https://repo.stellardrift.ca/maven/internal/" 6 | mavenContent { releasesOnly() } 7 | } 8 | maven { 9 | name = "stellardriftSnapshots" 10 | url = "https://repo.stellardrift.ca/maven/snapshots/" 11 | mavenContent { snapshotsOnly() } 12 | } 13 | } 14 | } 15 | 16 | plugins { 17 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' 18 | } 19 | 20 | rootProject.name = 'gitpatcher' 21 | 22 | dependencyResolutionManagement { 23 | repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS 24 | pluginManagement.repositories.each(repositories.&add) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/Git.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher 24 | 25 | import groovy.transform.CompileStatic 26 | import org.gradle.api.file.Directory 27 | import org.gradle.api.file.DirectoryProperty 28 | import org.gradle.api.logging.Logger 29 | import org.gradle.api.logging.Logging 30 | 31 | import javax.annotation.Nullable 32 | 33 | class Git { 34 | 35 | private static final Logger LOGGER = Logging.getLogger(Git) 36 | 37 | File repo 38 | @Nullable String committerNameOverride 39 | @Nullable String committerEmailOverride 40 | 41 | Git(DirectoryProperty repo) { 42 | this(repo.get().asFile) 43 | } 44 | 45 | Git(File repo) { 46 | setRepo(repo) 47 | } 48 | 49 | private def decorateEnv(Map env) { 50 | if (this.committerNameOverride != null) { 51 | env["GIT_COMMITTER_NAME"] = this.committerNameOverride 52 | } 53 | 54 | if (this.committerEmailOverride != null) { 55 | env["GIT_COMMITTER_EMAIL"] = this.committerEmailOverride 56 | } 57 | } 58 | 59 | void setRepo(File repo) { 60 | this.repo = repo 61 | assert repo.exists() 62 | } 63 | 64 | void setRepo(Directory repo) { 65 | this.setRepo(repo.asFile) 66 | } 67 | 68 | void setRepo(DirectoryProperty repo) { 69 | this.setRepo(repo.get().asFile) 70 | } 71 | 72 | String getStatus() { 73 | return run('status', ['-z']).text 74 | } 75 | 76 | String getRef() { 77 | return rev_parse('HEAD').text.readLines().first().trim() 78 | } 79 | 80 | Command run(String name, Object input) { 81 | def args = ['git', '--no-pager', name.replace('_' as char, '-' as char), *input] 82 | LOGGER.info("gitpatcher: executing {}", args) 83 | def builder = new ProcessBuilder(*args) 84 | this.decorateEnv(builder.environment()) 85 | builder.directory = repo 86 | return new Command(builder.start()) 87 | } 88 | 89 | @Override 90 | Command invokeMethod(String name, Object input) { 91 | return run(name, input) 92 | } 93 | 94 | @CompileStatic 95 | static class Command { 96 | 97 | final Process process 98 | 99 | private Command(Process process) { 100 | this.process = process 101 | } 102 | 103 | int run() { 104 | def result = process.waitFor() 105 | return result 106 | } 107 | 108 | void execute() { 109 | def result = run() 110 | assert result == 0, 'Process returned error code' 111 | } 112 | 113 | void writeTo(OutputStream out) { 114 | process.consumeProcessOutput(out, System.err) 115 | execute() 116 | } 117 | 118 | void forceWriteTo(OutputStream out) { 119 | process.consumeProcessOutput(out, out) 120 | run() 121 | } 122 | 123 | def rightShift = this.&writeTo 124 | def rightShiftUnsigned = this.&forceWriteTo 125 | 126 | String getText() { 127 | process.consumeProcessErrorStream((OutputStream) System.err) 128 | def text = process.inputStream.text.trim() 129 | execute() 130 | return text 131 | } 132 | 133 | String readText() { 134 | process.consumeProcessErrorStream((OutputStream) System.err) 135 | def text = process.inputStream.text.trim() 136 | return run() == 0 ? text : null 137 | } 138 | 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/GitPatcher.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher 24 | 25 | import groovy.transform.CompileStatic 26 | import ca.stellardrift.gitpatcher.task.FindGitTask 27 | import ca.stellardrift.gitpatcher.task.UpdateSubmodulesTask 28 | import ca.stellardrift.gitpatcher.task.patch.ApplyPatchesTask 29 | import ca.stellardrift.gitpatcher.task.patch.MakePatchesTask 30 | import ca.stellardrift.gitpatcher.task.patch.PatchTask 31 | import org.gradle.api.Plugin 32 | import org.gradle.api.Project 33 | import org.gradle.api.tasks.TaskProvider 34 | 35 | class GitPatcher implements Plugin { 36 | 37 | private static String GITPATCHER_TASK_GROUP = "gitpatcher" 38 | 39 | protected Project project 40 | protected GitPatcherExtension extension 41 | 42 | @Override 43 | void apply(Project project) { 44 | this.project = project 45 | 46 | def rootApply = project.tasks.register("applyPatches") { group = GITPATCHER_TASK_GROUP } 47 | def rootRebuild = project.tasks.register("makePatches") { group = GITPATCHER_TASK_GROUP } 48 | def rootUpdate = project.tasks.register("updateSubmodules") { group = GITPATCHER_TASK_GROUP } 49 | 50 | project.with { 51 | this.extension = extensions.create(GitPatcherExtension, 'gitPatcher', GitPatcherExtensionImpl) 52 | extensions.create(PatchExtension, 'patches', PatchExtensionImpl, extension) 53 | 54 | def findGit = tasks.register('findGit', FindGitTask) { group = GITPATCHER_TASK_GROUP } 55 | 56 | extension.patchedRepos.all { RepoPatchDetails r -> 57 | r.addAsSafeDirectory.convention(extension.addAsSafeDirectory) 58 | r.committerNameOverride.convention(extension.committerNameOverride) 59 | r.committerEmailOverride.convention(extension.committerEmailOverride) 60 | 61 | def capitalizedName = r.name.capitalize() 62 | 63 | def updateSubmodules = tasks.register('update' + capitalizedName + 'Submodules', UpdateSubmodulesTask) { 64 | group = GITPATCHER_TASK_GROUP 65 | dependsOn findGit 66 | } 67 | rootUpdate.configure { dependsOn(updateSubmodules) } 68 | 69 | def apply = tasks.register('apply' + capitalizedName +'Patches', ApplyPatchesTask) { 70 | group = GITPATCHER_TASK_GROUP 71 | /*, dependsOn: 'updateSubmodules' We don't want to update the submodule if we're targeting a specific commit */ 72 | } 73 | rootApply.configure { dependsOn(apply) } 74 | 75 | def rebuild = tasks.register('make' + capitalizedName + 'Patches', MakePatchesTask) { 76 | group = GITPATCHER_TASK_GROUP 77 | dependsOn findGit 78 | } 79 | rootRebuild.configure { dependsOn(rebuild) } 80 | 81 | // groovy moment? 82 | List> patchTasks = new ArrayList<>() 83 | patchTasks.add(apply) 84 | patchTasks.add(rebuild) 85 | 86 | patchTasks.each { taskProvider -> 87 | taskProvider.configure { 88 | addAsSafeDirectory.convention(r.addAsSafeDirectory) 89 | committerName.convention(r.committerNameOverride) 90 | committerEmail.convention(r.committerEmailOverride) 91 | 92 | repo.set(r.target) 93 | root.set(r.root) 94 | submodule.set(r.submodule) 95 | patchDir.set(r.patches) 96 | } 97 | } 98 | 99 | updateSubmodules.configure { 100 | repo.convention(r.root) 101 | submodule.convention(r.submodule) 102 | } 103 | 104 | afterEvaluate { 105 | apply.configure { updateTask = updateSubmodules.get() } 106 | } 107 | } 108 | } 109 | } 110 | 111 | @CompileStatic 112 | Project getProject() { 113 | return project 114 | } 115 | 116 | @CompileStatic 117 | GitPatcherExtension getExtension() { 118 | return extension 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/GitPatcherExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import org.gradle.api.NamedDomainObjectContainer; 26 | import org.gradle.api.provider.Property; 27 | 28 | public interface GitPatcherExtension { 29 | /** 30 | * Container holding the repos to patch. 31 | * 32 | *

Each {@link RepoPatchDetails} will 33 | * have a {@code apply[CapitalizedName]Patches}, {@code make[CapitalizedName]Patches}, 34 | * and {@code update[CapitalizedName]Submodules} task.

35 | * 36 | *

{@code applyPatches}, {@code rebuildPatches}, and {@code updateSubmodules} 37 | * depend on the respective tasks of all registered repos.

38 | * 39 | * @return repo container 40 | * @since 1.1.0 41 | */ 42 | NamedDomainObjectContainer getPatchedRepos(); 43 | 44 | /** 45 | * Whether to add the patched repo to git's safe directories list. 46 | * 47 | * @return the add as safe directory property 48 | * @since 1.1.0 49 | */ 50 | Property getAddAsSafeDirectory(); 51 | 52 | /** 53 | * A temporary committer name to use for applied patches. 54 | * 55 | * @return the committer name property 56 | * @since 1.1.0 57 | */ 58 | Property getCommitterNameOverride(); 59 | 60 | /** 61 | * A temporary committer name to use for applied patches. 62 | * 63 | * @return the committer name property 64 | * @since 1.1.0 65 | */ 66 | Property getCommitterEmailOverride(); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/GitPatcherExtensionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import javax.inject.Inject; 26 | import org.gradle.api.NamedDomainObjectContainer; 27 | import org.gradle.api.model.ObjectFactory; 28 | import org.gradle.api.provider.Property; 29 | import org.gradle.api.provider.ProviderFactory; 30 | 31 | class GitPatcherExtensionImpl implements GitPatcherExtension { 32 | private final NamedDomainObjectContainer patchedRepos; 33 | private final Property addAsSafeDirectory; 34 | private final Property committerNameOverride; 35 | private final Property committerEmailOverride; 36 | 37 | @Inject 38 | public GitPatcherExtensionImpl(final ObjectFactory objects, final ProviderFactory providers) { 39 | this.patchedRepos = objects.domainObjectContainer(RepoPatchDetailsImpl.class); 40 | this.addAsSafeDirectory = objects.property(Boolean.class) 41 | .convention( 42 | providers.environmentVariable("GITPATCHER_ADD_GIT_SAFEDIR") 43 | .map(it -> it.equals("true")) 44 | .orElse(false) 45 | ); 46 | this.committerNameOverride = objects.property(String.class).convention("GitPatcher"); 47 | this.committerEmailOverride = objects.property(String.class).convention("gitpatcher@noreply"); 48 | } 49 | 50 | @Override 51 | @SuppressWarnings({"unchecked", "rawtypes"}) 52 | public NamedDomainObjectContainer getPatchedRepos() { 53 | return (NamedDomainObjectContainer) this.patchedRepos; 54 | } 55 | 56 | @Override 57 | public Property getAddAsSafeDirectory() { 58 | return this.addAsSafeDirectory; 59 | } 60 | 61 | @Override 62 | public Property getCommitterNameOverride() { 63 | return this.committerNameOverride; 64 | } 65 | 66 | @Override 67 | public Property getCommitterEmailOverride() { 68 | return this.committerEmailOverride; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/PatchExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import org.gradle.api.file.DirectoryProperty; 26 | import org.gradle.api.provider.Property; 27 | 28 | /** 29 | * @deprecated register a {@link RepoPatchDetails} to {@link GitPatcherExtension#getPatchedRepos()} 30 | * instead, that is all this extension does now internally. 31 | */ 32 | @Deprecated 33 | public interface PatchExtension { 34 | /** 35 | * The root/project directory. 36 | * 37 | *

This usually does not need to be manually set.

38 | * 39 | * @return the root 40 | * @since 1.0.0 41 | * @deprecated See {@link PatchExtension} 42 | */ 43 | @Deprecated 44 | DirectoryProperty getRoot(); 45 | 46 | /** 47 | * The name of the submodule directory created. 48 | * 49 | * @return the submodule 50 | * @since 1.0.0 51 | * @deprecated See {@link PatchExtension} 52 | */ 53 | @Deprecated 54 | Property getSubmodule(); 55 | 56 | /** 57 | * The target folder for the patched repository. 58 | * 59 | * @return the target folder 60 | * @since 1.0.0 61 | * @deprecated See {@link PatchExtension} 62 | */ 63 | @Deprecated 64 | DirectoryProperty getTarget(); 65 | 66 | /** 67 | * The folder where the patches are saved 68 | * 69 | * @return the patch directory 70 | * @since 1.0.0 71 | * @deprecated See {@link PatchExtension} 72 | */ 73 | @Deprecated 74 | DirectoryProperty getPatches(); 75 | 76 | /** 77 | * Whether to add the patched repo to git's safe directories list. 78 | * 79 | * @return the add as safe directory property 80 | * @since 1.0.0 81 | * @deprecated See {@link PatchExtension} 82 | */ 83 | @Deprecated 84 | Property getAddAsSafeDirectory(); 85 | 86 | /** 87 | * A temporary committer name to use for applied patches. 88 | * 89 | * @return the committer name property 90 | * @since 1.0.0 91 | * @deprecated See {@link PatchExtension} 92 | */ 93 | @Deprecated 94 | Property getCommitterNameOverride(); 95 | 96 | /** 97 | * A temporary committer name to use for applied patches. 98 | * 99 | * @return the committer name property 100 | * @since 1.0.0 101 | * @deprecated See {@link PatchExtension} 102 | */ 103 | @Deprecated 104 | Property getCommitterEmailOverride(); 105 | } 106 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/PatchExtensionImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import javax.inject.Inject; 26 | import org.gradle.api.file.DirectoryProperty; 27 | import org.gradle.api.provider.Property; 28 | 29 | @Deprecated 30 | abstract class PatchExtensionImpl implements PatchExtension { 31 | private final GitPatcherExtension ext; 32 | private volatile RepoPatchDetails details = null; 33 | 34 | @Inject 35 | public PatchExtensionImpl(final GitPatcherExtension ext) { 36 | this.ext = ext; 37 | } 38 | 39 | private RepoPatchDetails details() { 40 | if (this.details == null) { 41 | synchronized (this) { 42 | if (this.details == null) { 43 | this.details = this.ext.getPatchedRepos().create("repo"); 44 | } 45 | } 46 | } 47 | return this.details; 48 | } 49 | 50 | @Override 51 | public DirectoryProperty getRoot() { 52 | return this.details().getRoot(); 53 | } 54 | 55 | @Override 56 | public Property getSubmodule() { 57 | return this.details().getSubmodule(); 58 | } 59 | 60 | @Override 61 | public DirectoryProperty getTarget() { 62 | return this.details().getTarget(); 63 | } 64 | 65 | @Override 66 | public DirectoryProperty getPatches() { 67 | return this.details().getPatches(); 68 | } 69 | 70 | @Override 71 | public Property getAddAsSafeDirectory() { 72 | return this.details().getAddAsSafeDirectory(); 73 | } 74 | 75 | @Override 76 | public Property getCommitterNameOverride() { 77 | return this.details().getCommitterNameOverride(); 78 | } 79 | 80 | @Override 81 | public Property getCommitterEmailOverride() { 82 | return this.details().getCommitterEmailOverride(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/RepoPatchDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import org.gradle.api.file.DirectoryProperty; 26 | import org.gradle.api.provider.Property; 27 | 28 | public interface RepoPatchDetails { 29 | /** 30 | * Get the name of this {@link RepoPatchDetails}. 31 | * 32 | * @return the name 33 | */ 34 | String getName(); 35 | 36 | /** 37 | * The root/project directory. 38 | * 39 | *

This usually does not need to be manually set.

40 | * 41 | * @return the root 42 | * @since 1.1.0 43 | */ 44 | DirectoryProperty getRoot(); 45 | 46 | /** 47 | * The name of the submodule directory created. 48 | * 49 | * @return the submodule 50 | * @since 1.1.0 51 | */ 52 | Property getSubmodule(); 53 | 54 | /** 55 | * The target folder for the patched repository. 56 | * 57 | * @return the target folder 58 | * @since 1.1.0 59 | */ 60 | DirectoryProperty getTarget(); 61 | 62 | /** 63 | * The folder where the patches are saved 64 | * 65 | * @return the patch directory 66 | * @since 1.1.0 67 | */ 68 | DirectoryProperty getPatches(); 69 | 70 | /** 71 | * Whether to add the patched repo to git's safe directories list. 72 | * 73 | * @return the add as safe directory property 74 | * @since 1.1.0 75 | */ 76 | Property getAddAsSafeDirectory(); 77 | 78 | /** 79 | * A temporary committer name to use for applied patches. 80 | * 81 | * @return the committer name property 82 | * @since 1.1.0 83 | */ 84 | Property getCommitterNameOverride(); 85 | 86 | /** 87 | * A temporary committer name to use for applied patches. 88 | * 89 | * @return the committer name property 90 | * @since 1.1.0 91 | */ 92 | Property getCommitterEmailOverride(); 93 | } 94 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/RepoPatchDetailsImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import org.gradle.api.file.DirectoryProperty; 26 | import org.gradle.api.file.ProjectLayout; 27 | import org.gradle.api.model.ObjectFactory; 28 | import org.gradle.api.provider.Property; 29 | 30 | import javax.inject.Inject; 31 | 32 | abstract class RepoPatchDetailsImpl implements RepoPatchDetails { 33 | private final String name; 34 | private final DirectoryProperty root; 35 | private final Property submodule; 36 | private final DirectoryProperty target; 37 | private final DirectoryProperty patches; 38 | private final Property addAsSafeDirectory; 39 | private final Property committerNameOverride; 40 | 41 | private final Property committerEmailOverride; 42 | 43 | @Inject 44 | public RepoPatchDetailsImpl(final String name, final ObjectFactory objects, final ProjectLayout layout) { 45 | this.name = name; 46 | this.root = objects.directoryProperty().convention(layout.getProjectDirectory()); 47 | this.submodule = objects.property(String.class); 48 | this.target = objects.directoryProperty(); 49 | this.patches = objects.directoryProperty(); 50 | this.addAsSafeDirectory = objects.property(Boolean.class); 51 | this.committerNameOverride = objects.property(String.class); 52 | this.committerEmailOverride = objects.property(String.class); 53 | } 54 | 55 | @Override 56 | public String getName() { 57 | return this.name; 58 | } 59 | 60 | @Override 61 | public DirectoryProperty getRoot() { 62 | return this.root; 63 | } 64 | 65 | @Override 66 | public Property getSubmodule() { 67 | return this.submodule; 68 | } 69 | 70 | @Override 71 | public DirectoryProperty getTarget() { 72 | return this.target; 73 | } 74 | 75 | @Override 76 | public DirectoryProperty getPatches() { 77 | return this.patches; 78 | } 79 | 80 | @Override 81 | public Property getAddAsSafeDirectory() { 82 | return this.addAsSafeDirectory; 83 | } 84 | 85 | @Override 86 | public Property getCommitterNameOverride() { 87 | return this.committerNameOverride; 88 | } 89 | 90 | @Override 91 | public Property getCommitterEmailOverride() { 92 | return this.committerEmailOverride; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/FindGitTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task 24 | 25 | import ca.stellardrift.gitpatcher.Git 26 | import org.gradle.api.DefaultTask 27 | import org.gradle.api.file.DirectoryProperty 28 | import org.gradle.api.tasks.Input 29 | import org.gradle.api.tasks.TaskAction 30 | 31 | abstract class FindGitTask extends DefaultTask { 32 | 33 | @Input 34 | protected abstract DirectoryProperty getRootDir(); 35 | 36 | FindGitTask() { 37 | this.rootDir.set(project.rootDir) // todo: pull this from ProjectLayout in 8.13+ 38 | } 39 | 40 | @TaskAction 41 | void findGit() { 42 | def git = new Git(rootDir.get().asFile) 43 | try { 44 | def version = git.version().text.readLines().join(', ') 45 | logger.lifecycle("Using $version for patching submodules.") 46 | } catch (Throwable e) { 47 | throw new UnsupportedOperationException( 48 | 'Failed to verify Git version. Make sure running the Gradle build in an environment where Git is in your PATH.', e); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/GitTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task 24 | 25 | import groovy.transform.CompileStatic 26 | import org.gradle.api.DefaultTask 27 | import org.gradle.api.file.DirectoryProperty 28 | import org.gradle.api.tasks.Internal 29 | import org.gradle.api.tasks.UntrackedTask 30 | 31 | @UntrackedTask(because = "State is tracked by git") 32 | @CompileStatic 33 | abstract class GitTask extends DefaultTask { 34 | @Internal 35 | abstract DirectoryProperty getRepo() 36 | } 37 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/SubmoduleTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task 24 | 25 | import groovy.transform.CompileStatic 26 | import org.gradle.api.provider.Property 27 | import org.gradle.api.tasks.Input 28 | import org.gradle.api.tasks.Optional 29 | 30 | @CompileStatic 31 | abstract class SubmoduleTask extends GitTask { 32 | 33 | @Input 34 | @Optional 35 | abstract Property getSubmodule(); 36 | 37 | { 38 | onlyIf { submodule.isPresent() } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/UpdateSubmodulesTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task 24 | 25 | import org.gradle.api.file.DirectoryProperty 26 | 27 | import static java.lang.System.out 28 | 29 | import ca.stellardrift.gitpatcher.Git 30 | import org.gradle.api.tasks.InputDirectory 31 | import org.gradle.api.tasks.Internal 32 | import org.gradle.api.tasks.TaskAction 33 | 34 | abstract class UpdateSubmodulesTask extends SubmoduleTask { 35 | 36 | private String ref 37 | 38 | @TaskAction 39 | void updateSubmodules() { 40 | def git = new Git(repo) 41 | def result = git.submodule('status', '--', submodule.get()).text 42 | 43 | this.ref = result[1 .. result.indexOf(' ', 1) - 1] 44 | 45 | if (result.startsWith(' ')) { 46 | didWork = false 47 | return 48 | } 49 | 50 | git.submodule('update', '--init', '--recursive') >> out 51 | } 52 | 53 | @Internal 54 | String getRef() { 55 | ref 56 | } 57 | 58 | @Override @InputDirectory 59 | abstract DirectoryProperty getRepo() 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/patch/ApplyPatchesTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task.patch 24 | 25 | import groovy.io.FileType 26 | import org.gradle.api.file.DirectoryProperty 27 | import org.gradle.api.file.RegularFile 28 | import org.gradle.api.provider.Provider 29 | 30 | import static java.lang.System.out 31 | 32 | import ca.stellardrift.gitpatcher.Git 33 | import ca.stellardrift.gitpatcher.task.UpdateSubmodulesTask 34 | import org.gradle.api.tasks.InputFiles 35 | import org.gradle.api.tasks.Internal 36 | import org.gradle.api.tasks.OutputDirectory 37 | import org.gradle.api.tasks.OutputFile 38 | import org.gradle.api.tasks.TaskAction 39 | import org.gradle.api.tasks.UntrackedTask 40 | 41 | @UntrackedTask(because = "State is tracked by git") 42 | abstract class ApplyPatchesTask extends PatchTask { 43 | 44 | @Internal 45 | UpdateSubmodulesTask updateTask 46 | 47 | @Override @Internal 48 | abstract DirectoryProperty getPatchDir() 49 | 50 | @Override @InputFiles 51 | File[] getPatches() { 52 | return super.getPatches() 53 | } 54 | 55 | @OutputDirectory 56 | abstract DirectoryProperty getDestRepo() 57 | 58 | @Override @OutputFile 59 | Provider getRefCache() { 60 | return super.getRefCache() 61 | } 62 | 63 | { 64 | destRepo.set(repo) 65 | outputs.upToDateWhen { 66 | if (!repo.get().asFile.directory) { 67 | return false 68 | } 69 | 70 | def git = new Git(repo) 71 | return git.status.empty && cachedRef == git.ref && cachedSubmoduleRef == updateTask.ref 72 | } 73 | } 74 | 75 | @TaskAction 76 | void applyPatches() { 77 | def repoFile = repo.get().asFile 78 | def git = new Git(submoduleRoot.get().asFile) 79 | def safeState = setupGit(git) 80 | try { 81 | git.branch('-f', 'upstream') >> null 82 | 83 | def gitDir = repo.get().dir('.git').asFile 84 | if (!gitDir.isDirectory() || gitDir.list().length == 0) { 85 | logger.lifecycle 'Creating {} repository...', repoFile 86 | 87 | def rootDir = repo.get().asFile 88 | assert gitDir.deleteDir() 89 | 90 | // remove any .gitkeep files within the tree and the directories that are within them 91 | git.repo = root 92 | if (new File(rootDir, '.gitkeep').delete()) { 93 | git."update-index"('--assume-unchanged', new File(rootDir, '.gitkeep').absolutePath) 94 | } 95 | 96 | rootDir.traverse type: FileType.DIRECTORIES, postDir: { 97 | def keep = new File(it, '.gitkeep') 98 | if (keep.delete()) { 99 | git."update-index"('--assume-unchanged', keep.absolutePath) 100 | } 101 | assert it.delete() // directory should be empty 102 | } 103 | 104 | git.clone('--recursive', submodule.get(), repo.get().asFile.absolutePath, '-b', 'upstream') >> out 105 | } 106 | 107 | logger.lifecycle 'Resetting {}...', repoFile 108 | 109 | git.setRepo(repo) 110 | // reset origin url to handle cases where the project has been moved 111 | git.remote('set-url', 'origin', submoduleRoot.get().asFile.absolutePath) >> null 112 | git.fetch('origin') >> null 113 | git.checkout('-B', 'master', 'origin/upstream') >> null 114 | git.reset('--hard') >> out 115 | 116 | if (!patchDir.get().asFile.directory) { 117 | assert patchDir.get().asFile.mkdirs(), 'Failed to create patch directory' 118 | } 119 | 120 | if ('true'.equalsIgnoreCase(git.config('commit.gpgsign').readText())) { 121 | logger.warn("Disabling GPG signing for the gitpatcher repository") 122 | git.config('commit.gpgsign', 'false') >> out 123 | } 124 | 125 | def patches = this.patches 126 | if (patches.length > 0) { 127 | logger.lifecycle 'Applying patches from {} to {}', patchDir.get().asFile, repoFile 128 | 129 | git.am('--abort') >>> null 130 | git.am('--3way', *patches.collect { it.absolutePath }) >> out 131 | 132 | logger.lifecycle 'Successfully applied patches from {} to {}', patchDir.get().asFile, repoFile 133 | } 134 | 135 | refCache.get().asFile.text = git.ref + '\n' + updateTask.ref 136 | } finally { 137 | cleanUpSafeRepo(git, safeState) 138 | } 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/patch/MakePatchesTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task.patch 24 | 25 | import org.gradle.api.file.DirectoryProperty 26 | import org.gradle.api.file.RegularFile 27 | import org.gradle.api.provider.Provider 28 | 29 | import static java.lang.System.out 30 | 31 | import ca.stellardrift.gitpatcher.Git 32 | import org.gradle.api.tasks.InputDirectory 33 | import org.gradle.api.tasks.Internal 34 | import org.gradle.api.tasks.OutputDirectory 35 | import org.gradle.api.tasks.TaskAction 36 | import org.gradle.api.tasks.UntrackedTask 37 | 38 | @UntrackedTask(because = "State is tracked by git") 39 | abstract class MakePatchesTask extends PatchTask { 40 | 41 | private static final Closure HUNK = { it.startsWith('@@') } 42 | 43 | @Override @InputDirectory 44 | abstract DirectoryProperty getRepo() 45 | 46 | @Override @Internal 47 | Provider getRefCache() { // not used in this task 48 | return super.getRefCache() 49 | } 50 | 51 | @Override @OutputDirectory 52 | abstract DirectoryProperty getPatchDir() 53 | 54 | @Override @Internal 55 | File[] getPatches() { 56 | return super.getPatches() 57 | } 58 | 59 | { 60 | outputs.upToDateWhen { 61 | if (!repo.get().asFile.directory) { 62 | return false 63 | } 64 | 65 | def git = new Git(repo) 66 | return cachedRef == git.ref 67 | } 68 | } 69 | 70 | @TaskAction 71 | void makePatches() { 72 | if (patchDir.get().asFile.isDirectory()) { 73 | def patches = this.patches 74 | if (patches) { 75 | assert patches*.delete(), 'Failed to delete old patch' 76 | } 77 | } else { 78 | assert patchDir.mkdirs(), 'Failed to create patch directory' 79 | } 80 | 81 | def git = new Git(repo) 82 | def safeState = setupGit(git) 83 | try { 84 | git.format_patch('--no-stat', '--zero-commit', '--full-index', '--no-signature', '-N', '-o', patchDir.get().asFile.absolutePath, 'origin/upstream') >> null 85 | 86 | git.repo = root 87 | git.add('-A', patchDir.get().asFile.absolutePath) >> out 88 | 89 | didWork = false 90 | for (def patch : patches) { 91 | List diff = git.diff('--no-color', '-U1', '--staged', patch.absolutePath).text.readLines() 92 | if (isUpToDate(diff)) { 93 | logger.lifecycle 'Skipping {} (up-to-date)', patch.name 94 | git.reset('HEAD', patch.absolutePath) >> null 95 | git.checkout('--', patch.absolutePath) >> null 96 | } else { 97 | didWork = true 98 | logger.lifecycle 'Generating {}', patch.name 99 | } 100 | } 101 | } finally { 102 | cleanUpSafeRepo(git, safeState) 103 | } 104 | } 105 | 106 | private static boolean isUpToDate(List diff) { 107 | if (diff.empty) { 108 | return true 109 | } 110 | 111 | if (diff.contains('--- /dev/null')) { 112 | return false 113 | } 114 | 115 | // Check if there are max. 2 diff hunks (once for the hash, and once for the Git version) 116 | def count = diff.count(HUNK) 117 | if (count == 0) { 118 | return true 119 | } 120 | 121 | if (count > 2) { 122 | return false 123 | } 124 | 125 | for (def i = 0; i < diff.size(); i++) { 126 | if (HUNK(diff[i])) { 127 | def change = diff[i + 1] 128 | if (!change.startsWith('From', 1) && !change.startsWith('--', 1)) { 129 | return false 130 | } 131 | } 132 | } 133 | 134 | return true 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/groovy/ca/stellardrift/gitpatcher/task/patch/PatchTask.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher.task.patch 24 | 25 | import groovy.transform.CompileStatic 26 | import groovy.transform.Immutable 27 | import ca.stellardrift.gitpatcher.Git 28 | import ca.stellardrift.gitpatcher.task.SubmoduleTask 29 | import org.gradle.api.file.Directory 30 | import org.gradle.api.file.DirectoryProperty 31 | import org.gradle.api.file.RegularFile 32 | import org.gradle.api.provider.Property 33 | import org.gradle.api.provider.Provider 34 | import org.gradle.api.tasks.Console 35 | import org.gradle.api.tasks.Internal 36 | 37 | abstract class PatchTask extends SubmoduleTask { 38 | 39 | private static final String SAFE_DIRECTORY = "safe.directory" 40 | 41 | @Internal 42 | abstract DirectoryProperty getRoot(); 43 | 44 | abstract DirectoryProperty getPatchDir(); 45 | 46 | @Console 47 | public abstract Property getAddAsSafeDirectory() 48 | 49 | @Console 50 | public abstract Property getCommitterName() 51 | 52 | @Console 53 | public abstract Property getCommitterEmail() 54 | 55 | protected File[] getPatches() { 56 | def patchDir = this.patchDir.get().asFile 57 | if (!patchDir.directory) { 58 | return new File[0] 59 | } 60 | 61 | return patchDir.listFiles({ dir, name -> name.endsWith('.patch') } as FilenameFilter).sort() 62 | } 63 | 64 | @Internal 65 | Provider getSubmoduleRoot() { 66 | return root.zip(submodule) { r, s -> r.dir(s) } 67 | } 68 | 69 | @Internal 70 | Provider getGitDir() { 71 | return repo.map { it.dir('.git') } 72 | } 73 | 74 | Provider getRefCache() { 75 | return gitDir.map { it.file('.gitpatcher_ref') } 76 | } 77 | 78 | private List cachedRefs 79 | 80 | @CompileStatic 81 | private void readCache() { 82 | if (cachedRefs == null) { 83 | File refCache = this.refCache.get().asFile 84 | if (refCache.file) { 85 | this.cachedRefs = refCache.readLines().findResults { 86 | def trimmed = it.trim() 87 | !trimmed.empty && !trimmed.startsWith('#') ? trimmed : null 88 | }.asList().asImmutable() 89 | } else { 90 | this.cachedRefs = Collections.emptyList() 91 | } 92 | } 93 | } 94 | 95 | @Internal 96 | String getCachedRef() { 97 | readCache() 98 | return cachedRefs[0] 99 | } 100 | 101 | @Internal 102 | String getCachedSubmoduleRef() { 103 | readCache() 104 | return cachedRefs[1] 105 | } 106 | 107 | protected RepoState setupGit(final Git git) { 108 | if (this.committerName.isPresent()) { 109 | git.committerNameOverride = this.committerName.get() 110 | } 111 | 112 | if (this.committerEmail.isPresent()) { 113 | git.committerEmailOverride = this.committerEmail.get() 114 | } 115 | 116 | return this.addAsSafeRepo(git) 117 | } 118 | 119 | /** 120 | * Maybe add the configured {@code repo} as a git safe repository. 121 | * 122 | * @return whether the repo was added by us, so should be removed at the end of the task 123 | */ 124 | protected RepoState addAsSafeRepo(Git git) { 125 | if (!this.addAsSafeDirectory.get()) { 126 | this.getLogger().info("Not adding submodules as safe directories due to configuration parameter being disabled") 127 | return null 128 | } 129 | 130 | def safeDirs = safeDirs(git) 131 | def hasPatched = safeDirs.contains(repo.absolutePath) 132 | def hasUpstream = safeDirs.contains(this.submoduleRoot.absolutePath) 133 | 134 | if (!hasPatched) { 135 | // add patched root 136 | git.config('--global', '--add', SAFE_DIRECTORY, repo.absolutePath) >> null 137 | } 138 | 139 | if (!hasUpstream) { 140 | // add submodule 141 | git.config('--global', '--add', SAFE_DIRECTORY, this.submoduleRoot.absolutePath) >> null 142 | } 143 | 144 | return new RepoState(hasUpstream, hasPatched) 145 | } 146 | 147 | protected void cleanUpSafeRepo(Git git, RepoState state) { 148 | if (state == null) { 149 | return 150 | } 151 | 152 | def safeDirs = safeDirs(git) 153 | 154 | def changed = false 155 | if (!state.hadPatched) { 156 | safeDirs.remove(repo.get().asFile.absolutePath) 157 | changed = true 158 | } 159 | 160 | if (!state.hadUpstream) { 161 | safeDirs.remove(this.submoduleRoot.get().asFile.absolutePath) 162 | changed = true 163 | } 164 | 165 | if (changed) { 166 | git.config('--global', '--unset-all', SAFE_DIRECTORY) 167 | safeDirs.each { 168 | git.config('--global', '--add', SAFE_DIRECTORY, it) 169 | } 170 | } 171 | } 172 | 173 | private List safeDirs(final Git git) { 174 | String safeDirs = git.config('--global', '--get-all', SAFE_DIRECTORY).readText() 175 | return safeDirs == null ? [] : safeDirs.split('\n').toList() 176 | } 177 | 178 | @Immutable 179 | static class RepoState { 180 | boolean hadUpstream 181 | boolean hadPatched 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/test/java/ca/stellardrift/gitpatcher/GitPatcherFunctionalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023-2024, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import net.kyori.mammoth.test.GradleFunctionalTest; 26 | import net.kyori.mammoth.test.GradleParameters; 27 | import net.kyori.mammoth.test.TestVariant; 28 | import org.junit.jupiter.api.DisplayName; 29 | 30 | import java.lang.annotation.ElementType; 31 | import java.lang.annotation.Retention; 32 | import java.lang.annotation.RetentionPolicy; 33 | import java.lang.annotation.Target; 34 | 35 | @GradleFunctionalTest 36 | @GradleParameters({"--warning-mode", "fail"}) 37 | @TestVariant(gradleVersion = "8.9", maximumRuntimeVersion = 16) 38 | @TestVariant(gradleVersion = "8.10", minimumRuntimeVersion = 17) 39 | @Retention(RetentionPolicy.RUNTIME) 40 | @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) 41 | public @interface GitPatcherFunctionalTest { 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/ca/stellardrift/gitpatcher/GitPatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, Stellardrift and contributors 3 | * Copyright (c) 2015, Minecrell 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | package ca.stellardrift.gitpatcher; 24 | 25 | import net.kyori.mammoth.test.TestContext; 26 | import org.junit.jupiter.api.DisplayName; 27 | 28 | import java.io.IOException; 29 | 30 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 31 | 32 | public class GitPatcherTest { 33 | 34 | @GitPatcherFunctionalTest 35 | @DisplayName("pluginSimplyApplies") 36 | void testPluginSimplyApplies(final TestContext ctx) throws IOException { 37 | ctx.copyInput("build.gradle"); 38 | ctx.copyInput("settings.gradle"); 39 | 40 | assertDoesNotThrow(() -> ctx.build("help")); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/resources/ca/stellardrift/gitpatcher/pluginSimplyApplies/in/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'ca.stellardrift.gitpatcher' 3 | } 4 | -------------------------------------------------------------------------------- /src/test/resources/ca/stellardrift/gitpatcher/pluginSimplyApplies/in/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'pluginSimplyApplies' 2 | --------------------------------------------------------------------------------