├── .editorconfig
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build.gradle
├── gradle
├── HEADER
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
├── gh-pages
└── index.html
├── main
├── ghpages
│ └── index.html
├── groovy
│ └── org
│ │ └── ajoberstar
│ │ └── gradle
│ │ ├── git
│ │ ├── auth
│ │ │ └── BasicPasswordCredentials.java
│ │ ├── base
│ │ │ └── GrgitPlugin.groovy
│ │ ├── ghpages
│ │ │ ├── GithubPagesPlugin.groovy
│ │ │ └── GithubPagesPluginExtension.groovy
│ │ └── release
│ │ │ ├── base
│ │ │ ├── BaseReleasePlugin.groovy
│ │ │ ├── DefaultVersionStrategy.groovy
│ │ │ ├── ReleasePluginExtension.groovy
│ │ │ ├── ReleaseVersion.groovy
│ │ │ ├── TagStrategy.groovy
│ │ │ ├── VersionStrategy.groovy
│ │ │ └── package-info.groovy
│ │ │ ├── experimental
│ │ │ └── ExperimentalReleasePlugin.groovy
│ │ │ ├── opinion
│ │ │ ├── OpinionReleasePlugin.groovy
│ │ │ ├── Strategies.groovy
│ │ │ └── package-info.groovy
│ │ │ └── semver
│ │ │ ├── ChangeScope.groovy
│ │ │ ├── NearestVersion.groovy
│ │ │ ├── NearestVersionLocator.groovy
│ │ │ ├── PartialSemVerStrategy.groovy
│ │ │ ├── RebuildVersionStrategy.groovy
│ │ │ ├── SemVerStrategy.groovy
│ │ │ ├── SemVerStrategyState.groovy
│ │ │ ├── StrategyUtil.groovy
│ │ │ └── package-info.groovy
│ │ └── util
│ │ └── ObjectUtil.java
└── resources
│ └── META-INF
│ └── gradle-plugins
│ ├── org.ajoberstar.github-pages.properties
│ ├── org.ajoberstar.grgit.properties
│ ├── org.ajoberstar.release-base.properties
│ ├── org.ajoberstar.release-experimental.properties
│ └── org.ajoberstar.release-opinion.properties
└── test
└── groovy
└── org
└── ajoberstar
└── gradle
└── git
├── ghpages
├── GithubPagesPluginExtensionSpec.groovy
└── GithubPagesPluginSpec.groovy
└── release
├── base
├── BaseReleasePluginSpec.groovy
├── ReleasePluginExtensionSpec.groovy
└── TagStrategySpec.groovy
├── opinion
├── OpinionReleasePluginSpec.groovy
└── StrategiesSpec.groovy
└── semver
├── NearestVersionLocatorSpec.groovy
├── RebuildVersionStrategySpec.groovy
├── SemVerStrategySpec.groovy
└── StrategyUtilSpec.groovy
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # defaults
7 | [*]
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 | charset = utf-8
11 | indent_style = space
12 | indent_size = 4
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build
3 | .settings
4 | .project
5 | .classpath
6 | bin
7 | *~
8 | *.iml
9 | *.iws
10 | *.ipr
11 | .idea
12 | out
13 | *.sublime-*
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | sudo: false
3 | install: ""
4 | script: ./gradlew clean check
5 | jdk:
6 | - oraclejdk8
7 | cache:
8 | directories:
9 | - $HOME/.gradle/caches
10 | - $HOME/.gradle/wrapper
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 |
3 | Version 2.0, January 2004
4 |
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
16 |
17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
18 |
19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
20 |
21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
22 |
23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
24 |
25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
26 |
27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
28 |
29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
30 |
31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
32 |
33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
34 |
35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
36 |
37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and
38 | You must cause any modified files to carry prominent notices stating that You changed the files; and
39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
41 |
42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
44 |
45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
46 |
47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
48 |
49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
50 |
51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
52 |
53 | END OF TERMS AND CONDITIONS
54 |
55 | APPENDIX: How to apply the Apache License to your work
56 | To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
57 |
58 | Copyright [yyyy] [name of copyright owner]
59 |
60 | Licensed under the Apache License, Version 2.0 (the "License");
61 | you may not use this file except in compliance with the License.
62 | You may obtain a copy of the License at
63 |
64 | http://www.apache.org/licenses/LICENSE-2.0
65 |
66 | Unless required by applicable law or agreed to in writing, software
67 | distributed under the License is distributed on an "AS IS" BASIS,
68 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
69 | See the License for the specific language governing permissions and
70 | limitations under the License.
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gradle-git
2 |
3 | [](https://bintray.com/ajoberstar/maven/gradle-git/_latestVersion)
4 | [](https://travis-ci.org/ajoberstar/gradle-git)
5 | [](https://github.com/ajoberstar/gradle-git/blob/master/LICENSE)
6 |
7 | ## Project Status
8 |
9 | gradle-git has been around since 2012 and has evolved quite a bit from the original release. In order to continue to evolve these features, this project is being broken up into multiple repositories. As such:
10 |
11 | - gradle-git will no longer be maintained
12 |
13 | | feature | replacement | comments |
14 | |---------|-------------|----------|
15 | | `org.ajoberstar.grgit` | [grgit](https://github.com/ajoberstar/grgit) | Grgit has been an independent project since 2013 and has been stable for quite a while. Version 2.0 removed some deprecated features, but otherwise is fully compatible with existing usage. It also integrates the `org.ajoberstar.grgit` plugin directly into the project. |
16 | | `org.ajoberstar.github-pages` | [gradle-git-publish](https://github.com/ajoberstar/gradle-git-publish) | `org.ajoberstar.git-publish` is a more robust version of the old plugin. It is functionally equivalent (or better), but does require porting configuration over as noted in the README. |
17 | | `org.ajoberstar.release-*` | [reckon](https://github.com/ajoberstar/reckon) | Reckon focuses solely on determining your project version (and assisting with tagging and pushing that tag). It provides an opinionated model of how to apply [semantic versioning](http://semver.org), with more finite configuration options. |
18 | | `org.ajoberstar.release-*` | [nebula-release](https://github.com/nebula-plugins/nebula-release-plugin) | If reckon doesn't suit your needs, nebula-release has forked the gradle-git release plugin and can serve as a replacement for this. |
19 |
20 | ## Why do you care?
21 |
22 | Git is immensely popular and being able to interact with it as part of a build process can be very valuable
23 | to provide a more powerful and consistent result.
24 |
25 | ## What is it?
26 |
27 | gradle-git is a set of [Gradle](http://gradle.org) plugins:
28 |
29 | * `org.ajoberstar.grgit` - provides a `Grgit` instance, allowing interaction with the Git repository
30 | the Gradle project is contained in
31 | * `org.ajoberstar.github-pages` - publishes files to the `gh-pages` branch of a Github repository
32 | * `org.ajoberstar.release-base` - general structure for inferring a project version and releasing it
33 | * `org.ajoberstar.release-opinion` - opinionated defaults for `org.ajoberstar.release-base`
34 |
35 | See [Grgit](https://github.com/ajoberstar/grgit) for details on the Git library used underneath, including
36 | configuration for authentication.
37 |
38 | ## Usage
39 |
40 | **NOTE:** gradle-git modules require Java 7 (or higher).
41 |
42 | * [Release Notes](https://github.com/ajoberstar/gradle-git/releases)
43 | * [Wiki](https://github.com/ajoberstar/gradle-git/wiki)
44 | * [Javadoc](http://ajoberstar.org/gradle-git/docs/javadoc)
45 | * [Groovydoc](http://ajoberstar.org/gradle-git/docs/groovydoc)
46 |
47 | ## Questions, Bugs, and Features
48 |
49 | gradle-git is not maintained anymore. See the _Project Status_ section above for details.
50 |
51 | ## Contributing
52 |
53 | gradle-git is not maintained anymore. See the _Project Status_ section above for details.
54 |
55 | ## Acknowledgements
56 |
57 | Thanks to all of the [contributors](https://github.com/ajoberstar/gradle-git/graphs/contributors).
58 |
59 | Credit goes to [Peter Ledbrook](https://github.com/pledbrook) for the initial
60 | idea for the `org.ajoberstar.github-pages` plugin.
61 |
62 | Thanks to [Zafar Khaja](https://github.com/zafarkhaja) for the very helpful
63 | [java-semver](https://github.com/zafarkhaja/jsemver) library.
64 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'groovy'
3 | id 'maven-publish'
4 | id 'com.jfrog.bintray' version '1.1'
5 | id 'org.ajoberstar.defaults' version '0.5.3'
6 | }
7 |
8 | group = 'org.ajoberstar'
9 | description = 'Git plugins for Gradle.'
10 |
11 | defaults {
12 | id = 'ajoberstar'
13 |
14 | bintrayRepo = 'maven'
15 | bintrayPkg = 'gradle-git'
16 | bintrayLabels = ['gradle', 'git', 'semver']
17 |
18 | developers = [
19 | [id: 'ajoberstar', name: 'Andrew Oberstar', email: 'andrew@ajoberstar.org']
20 | ]
21 |
22 | copyrightYears = '2012-2017'
23 | }
24 |
25 | sourceCompatibility = '1.7'
26 |
27 | configurations {
28 | // use local groovy
29 | all*.exclude group: 'org.codehaus.groovy'
30 | }
31 |
32 | repositories {
33 | jcenter()
34 | }
35 |
36 | dependencies {
37 | // groovy
38 | compile localGroovy()
39 |
40 | // gradle api
41 | compile gradleApi()
42 |
43 | // grgit
44 | compile 'org.ajoberstar:grgit:1.9.3'
45 |
46 | // semver
47 | compile 'com.github.zafarkhaja:java-semver:0.9.0'
48 |
49 | // testing
50 | testCompile 'org.spockframework:spock-core:1.0-groovy-2.3'
51 | testRuntime 'cglib:cglib-nodep:3.1'
52 | }
53 |
54 | wrapper {
55 | gradleVersion = '2.1'
56 | }
57 |
--------------------------------------------------------------------------------
/gradle/HEADER:
--------------------------------------------------------------------------------
1 | Copyright ${year} the original author or authors.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ajoberstar/gradle-git/03b08b9f8dd2b2ff0ee9228906a6e9781b85ec79/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 19 16:00:40 CDT 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/src/gh-pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/ghpages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Gradle-Git - Git plugins for Gradle
6 |
29 |
30 |
31 |
32 |
33 |
34 |

35 |

36 |
37 |
38 |
39 |
40 |
41 | Git plugins for Gradle
42 |
43 |
44 |
Documentation
45 |
50 |
51 |
License
52 |
Apache License v2
53 |
54 |
Authors
55 |
58 |
59 |
Download
60 |
61 | You can download this project in either
62 | zip or
63 | tar formats.
64 |
65 |
You can also clone the project with Git
66 | by running:
67 |
$ git clone git://github.com/ajoberstar/gradle-git
68 |
69 |
70 |
73 |
74 |
75 |
79 |
85 |
86 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/auth/BasicPasswordCredentials.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.auth;
17 |
18 | import java.io.Serializable;
19 |
20 | import org.ajoberstar.grgit.Credentials;
21 |
22 | import org.gradle.api.artifacts.repositories.PasswordCredentials;
23 |
24 | /**
25 | * Basic implementation of {@link PasswordCredentials}.
26 | * @since 0.1.0
27 | */
28 | public class BasicPasswordCredentials implements PasswordCredentials, Serializable {
29 | private static final long serialVersionUID = 1L;
30 | private String username;
31 | private String password;
32 |
33 | /**
34 | * Constructs credentials with {@code null} username and password.
35 | */
36 | public BasicPasswordCredentials() {
37 | this(null, null);
38 | }
39 |
40 | /**
41 | * Constructs credentials with the given arguments.
42 | * @param username the username to set
43 | * @param password the password to set
44 | */
45 | public BasicPasswordCredentials(String username, String password) {
46 | this.username = username;
47 | this.password = password;
48 | }
49 |
50 | /**
51 | * {@inheritDoc}
52 | */
53 | @Override
54 | public String getUsername() {
55 | return username;
56 | }
57 |
58 | /**
59 | * {@inheritDoc}
60 | */
61 | @Override
62 | public void setUsername(String username) {
63 | this.username = username;
64 | }
65 |
66 | /**
67 | * {@inheritDoc}
68 | */
69 | @Override
70 | public String getPassword() {
71 | return password;
72 | }
73 |
74 | /**
75 | * {@inheritDoc}
76 | */
77 | @Override
78 | public void setPassword(String password) {
79 | this.password = password;
80 | }
81 |
82 | /**
83 | * Converts to credentials for use in Grgit.
84 | * @return {@code null} if both username and password are {@code null},
85 | * otherwise returns credentials in Grgit format.
86 | */
87 | public Credentials toGrgit() {
88 | if (username != null && password != null) {
89 | return new Credentials(username, password);
90 | } else {
91 | return null;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/base/GrgitPlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.ajoberstar.grgit.exception.GrgitException
20 | import org.eclipse.jgit.errors.RepositoryNotFoundException
21 | import org.gradle.api.Plugin
22 | import org.gradle.api.Project
23 |
24 | /**
25 | * Plugin adding a {@code grgit} property to all projects
26 | * that searches for a Git repo from the root project's
27 | * directory.
28 | * @since 1.2.0
29 | */
30 | class GrgitPlugin implements Plugin {
31 | @Override
32 | void apply(Project project) {
33 | try {
34 | Grgit grgit = Grgit.open(currentDir: project.rootProject.rootDir)
35 | project.rootProject.allprojects { prj ->
36 | project.ext.grgit = grgit
37 | }
38 | } catch (RepositoryNotFoundException | GrgitException ignored) {
39 | // not a git repo or invalid/corrupt
40 | project.logger.warn 'No git repository found. Build may fail with NPE.'
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/ghpages/GithubPagesPlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.ghpages
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.ajoberstar.grgit.operation.ResetOp
20 | import org.ajoberstar.grgit.exception.GrgitException
21 | import org.eclipse.jgit.errors.RepositoryNotFoundException
22 | import org.gradle.api.Plugin
23 | import org.gradle.api.Project
24 | import org.gradle.api.Task
25 | import org.gradle.api.tasks.Copy
26 |
27 | /**
28 | * Plugin to enable publishing to gh-pages branch of Github.
29 | * @since 0.1.0
30 | */
31 | class GithubPagesPlugin implements Plugin {
32 | static final String PREPARE_TASK_NAME = 'prepareGhPages'
33 | static final String PUBLISH_TASK_NAME = 'publishGhPages'
34 |
35 | /**
36 | * Applies the plugin to the given project.
37 | * @param project the project
38 | */
39 | void apply(Project project) {
40 | project.logger.warn('org.ajoberstar.github-pages is deprecated will be removed in gradle-git 2.0.0. Users should migrate to org.ajoberstar.git-publish (https://github.com/ajoberstar/gradle-git-publish).')
41 | GithubPagesPluginExtension extension = project.extensions.create('githubPages', GithubPagesPluginExtension, project)
42 | configureTasks(project, extension)
43 | }
44 |
45 | /**
46 | * Configures the tasks to publish to gh-pages.
47 | * @param project the project to configure
48 | * @param extension the plugin extension
49 | */
50 | private void configureTasks(final Project project, final GithubPagesPluginExtension extension) {
51 | Task prepare = createPrepareTask(project, extension)
52 | Task publish = createPublishTask(project, extension)
53 | publish.dependsOn(prepare)
54 | }
55 |
56 | private Task createPrepareTask(Project project, GithubPagesPluginExtension extension) {
57 | Task task = project.tasks.create(PREPARE_TASK_NAME, Copy)
58 | task.with {
59 | description = 'Prepare the gh-pages changes locally'
60 | with extension.pages.realSpec
61 | into { extension.workingDir }
62 | doFirst {
63 | def repo = repo(project, extension)
64 | if (extension.deleteExistingFiles) {
65 | def relDestDir = extension.pages.relativeDestinationDir
66 | def targetDir = new File(extension.workingDir, relDestDir)
67 | def filesList = targetDir.list { dir, name -> !name.equals('.git') }
68 | if(filesList) {
69 | def removePatterns = filesList
70 | if(relDestDir && relDestDir != '.') {
71 | removePatterns = filesList.collect {name -> "$relDestDir/$name"}
72 | }
73 | repo.remove(patterns: removePatterns)
74 | }
75 | }
76 | }
77 | doLast {
78 | def repo = repo(project, extension)
79 | repo.with {
80 | add(patterns: ['.'])
81 | if (status().clean) {
82 | project.logger.warn 'Nothing to commit, skipping publish.'
83 | } else {
84 | commit(message: extension.commitMessage)
85 | }
86 | }
87 | }
88 | }
89 | return task
90 | }
91 |
92 | private Task createPublishTask(Project project, GithubPagesPluginExtension extension) {
93 | return project.tasks.create(PUBLISH_TASK_NAME) {
94 | description = 'Publishes all gh-pages changes to Github'
95 | group = 'publishing'
96 | // only push if there are commits to push
97 | onlyIf {
98 | def repo = repo(project, extension)
99 | def status = repo.branch.status(name: repo.branch.current)
100 | status.aheadCount > 0
101 | }
102 | doLast {
103 | repo(project, extension).push()
104 | }
105 | }
106 | }
107 |
108 | private Grgit repo(Project project, GithubPagesPluginExtension extension) {
109 | if (extension.ext.has('repo')) {
110 | return extension.ext.repo
111 | }
112 | def repo = null
113 | try {
114 | // attempt to reuse existing repository
115 | repo = Grgit.open(dir: extension.workingDir)
116 | if (extension.repoUri == repo.remote.list().find { it.name == 'origin' }?.url &&
117 | repo.branch.current.name == extension.targetBranch) {
118 | repo.clean(directories: true, ignore: false)
119 | repo.fetch()
120 | repo.reset(commit: 'origin/' + extension.targetBranch, mode: ResetOp.Mode.HARD)
121 | }
122 | else {
123 | project.logger.warn('Found a git repository at workingDir, but it does not match configuration. A fresh clone will be used.')
124 | repo.close()
125 | repo = null
126 | }
127 | } catch (RepositoryNotFoundException ignored) {
128 | // not a git repo
129 | } catch (GrgitException ignored) {
130 | // invalid/corrup git repo
131 | }
132 |
133 | if (!repo) {
134 | extension.workingDir.deleteDir()
135 | repo = Grgit.clone(
136 | uri: extension.repoUri,
137 | refToCheckout: extension.targetBranch,
138 | dir: extension.workingDir,
139 | credentials: extension.credentials?.toGrgit()
140 | )
141 |
142 | // check if on the correct branch, which implies it doesn't exist
143 | if (repo.branch.current.name != extension.targetBranch) {
144 | repo.checkout(branch: extension.targetBranch, orphan: true)
145 | // need to wipe out the current files
146 | extension.deleteExistingFiles = true
147 | }
148 | }
149 | extension.ext.repo = repo
150 | return repo
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/ghpages/GithubPagesPluginExtension.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.ghpages
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.ajoberstar.grgit.exception.GrgitException
20 |
21 | import org.ajoberstar.gradle.git.auth.BasicPasswordCredentials
22 | import org.ajoberstar.gradle.util.ObjectUtil
23 |
24 | import org.gradle.api.Project
25 | import org.gradle.api.artifacts.repositories.AuthenticationSupported
26 | import org.gradle.api.artifacts.repositories.PasswordCredentials
27 | import org.gradle.api.file.CopySpec
28 | import org.gradle.util.ConfigureUtil
29 |
30 | /**
31 | * Extension for gh-pages specific properties.
32 | * @since 0.1.0
33 | */
34 | class GithubPagesPluginExtension implements AuthenticationSupported {
35 | private final Project project
36 | PasswordCredentials credentials = new BasicPasswordCredentials()
37 |
38 | /**
39 | * The URI of the Github repository.
40 | */
41 | Object repoUri
42 |
43 | /**
44 | * The branch of the Github repository to push to.
45 | * Defaults to {@code gh-pages}
46 | */
47 | Object targetBranch = 'gh-pages'
48 |
49 | /**
50 | * The distribution of files to put in gh-pages. Defaults
51 | * to including {@code src/main/ghpages}.
52 | */
53 | final CopySpec pages
54 |
55 | /**
56 | * The path to put the github repository in. Defaults to
57 | * {@code build/ghpages}.
58 | */
59 | Object workingPath = "${project.buildDir}/ghpages"
60 |
61 | /**
62 | * Whether to delete existing files in the branch, replacing the
63 | * entire contents. Defaults to {@code true}.
64 | */
65 | boolean deleteExistingFiles = true
66 |
67 | /**
68 | * The message used when committing changes to Github pages branch.
69 | * Defaults to 'Publish of Github pages from Gradle.'.
70 | */
71 | String commitMessage = 'Publish of Github pages from Gradle.'
72 |
73 | /**
74 | * Constructs the plugin extension.
75 | * @param project the project to create
76 | * the extension for
77 | */
78 | GithubPagesPluginExtension(Project project) {
79 | this.project = project
80 | this.pages = new DestinationCopySpec(project)
81 | pages.from 'src/main/ghpages'
82 |
83 | // defaulting the repoUri to the project repo's origin
84 | try {
85 | Grgit grgit = Grgit.open(currentDir: project.projectDir)
86 | this.repoUri = grgit.remote.list().find { it.name == 'origin' }?.url
87 | grgit.close()
88 | } catch (IllegalArgumentException e) {
89 | // there isn't a git repo
90 | this.repoUri = null
91 | } catch (GrgitException e) {
92 | // failed to open the repo of the current project
93 | this.repoUri = null
94 | }
95 | }
96 |
97 | /**
98 | * Gets the URI of the Github repository. This
99 | * will be used to clone the repository.
100 | * @return the repo URI
101 | */
102 | String getRepoUri() {
103 | return ObjectUtil.unpackString(repoUri)
104 | }
105 |
106 | /**
107 | * Gets the working directory that the repo will be places in.
108 | * @return the working directory
109 | */
110 | File getWorkingDir() {
111 | return project.file(workingPath)
112 | }
113 |
114 | /**
115 | * Configures the gh-pages copy spec.
116 | * @param closure the configuration closure
117 | */
118 | void pages(Closure closure) {
119 | ConfigureUtil.configure(closure, pages)
120 | }
121 |
122 | /**
123 | * Configured the credentials to be used when interacting with
124 | * the repo. This will be passed a {@link PasswordCredentials}
125 | * instance.
126 | * @param closure the configuration closure
127 | */
128 | void credentials(Closure closure) {
129 | ConfigureUtil.configure(closure, credentials)
130 | }
131 |
132 | static class DestinationCopySpec implements CopySpec {
133 | private final Project project
134 | private Object destPath
135 |
136 | @Delegate
137 | CopySpec realSpec
138 |
139 | DestinationCopySpec(Project project) {
140 | this.project = project
141 | this.realSpec = project.copySpec {}
142 | }
143 |
144 | String getRelativeDestinationDir() {
145 | return destPath ?: '.'
146 | }
147 |
148 | @Override
149 | CopySpec into(Object destPath) {
150 | this.destPath = destPath
151 | realSpec.into(destPath)
152 | return this
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/BaseReleasePlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.gradle.api.GradleException
19 | import org.gradle.api.Plugin
20 | import org.gradle.api.Project
21 |
22 | import org.slf4j.Logger
23 | import org.slf4j.LoggerFactory
24 |
25 | /**
26 | * Plugin providing the base structure of gradle-git's flavor of release
27 | * behavior. The plugin can be applied using the {@code org.ajoberstar.release-base} id.
28 | *
29 | *
30 | * The plugin adds the {@link ReleasePluginExtension} and a {@code release} task.
31 | *
32 | *
33 | * @see org.ajoberstar.gradle.git.release.opinion.Strategies
34 | * @see org.ajoberstar.gradle.git.release.opinion.OpinionReleasePlugin
35 | * @see Wiki Doc
36 | */
37 | class BaseReleasePlugin implements Plugin {
38 | private static final Logger logger = LoggerFactory.getLogger(BaseReleasePlugin)
39 | private static final String PREPARE_TASK_NAME = 'prepare'
40 | private static final String RELEASE_TASK_NAME = 'release'
41 |
42 | void apply(Project project) {
43 | def extension = project.extensions.create('release', ReleasePluginExtension, project)
44 | addPrepareTask(project, extension)
45 | addReleaseTask(project, extension)
46 | project.plugins.withId('org.ajoberstar.grgit') {
47 | extension.grgit = project.grgit
48 | }
49 | }
50 |
51 | private void addPrepareTask(Project project, ReleasePluginExtension extension) {
52 | project.tasks.create(PREPARE_TASK_NAME) {
53 | description = 'Verifies that the project could be released.'
54 | doLast {
55 | ext.grgit = extension.grgit
56 |
57 | logger.info('Fetching changes from remote: {}', extension.remote)
58 | grgit.fetch(remote: extension.remote)
59 |
60 | // if branch is tracking another, make sure it's not behind
61 | if (grgit.branch.current.trackingBranch && grgit.branch.status(branch: grgit.branch.current.fullName).behindCount > 0) {
62 | throw new GradleException('Current branch is behind the tracked branch. Cannot release.')
63 | }
64 | }
65 | }
66 |
67 | project.tasks.all { task ->
68 | if (name != PREPARE_TASK_NAME) {
69 | task.shouldRunAfter PREPARE_TASK_NAME
70 | }
71 | }
72 | }
73 |
74 | private void addReleaseTask(Project project, ReleasePluginExtension extension) {
75 | project.tasks.create(RELEASE_TASK_NAME) {
76 | description = 'Releases this project.'
77 | dependsOn PREPARE_TASK_NAME
78 | doLast {
79 | // force version inference if it hasn't happened already
80 | project.version.toString()
81 |
82 | ext.grgit = extension.grgit
83 | ext.toPush = []
84 |
85 | // if not on detached HEAD, push branch
86 | if (grgit.branch.current.fullName != 'HEAD') {
87 | ext.toPush << grgit.branch.current.fullName
88 | }
89 |
90 | ext.tagName = extension.tagStrategy.maybeCreateTag(grgit, project.version.inferredVersion)
91 | if (tagName) {
92 | toPush << tagName
93 | }
94 |
95 | if (toPush) {
96 | logger.warn('Pushing changes in {} to {}', toPush, extension.remote)
97 | grgit.push(remote: extension.remote, refsOrSpecs: toPush)
98 | } else {
99 | logger.warn('Nothing to push.')
100 | }
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/DefaultVersionStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 |
20 | import org.gradle.api.Project
21 |
22 | /**
23 | * Strategy to infer a version from the project's and Git repository's state. This
24 | * also supports being selected as a default strategy. This is a temporary interface
25 | * and should be replaced in some other way in gradle-git 2.0.0.
26 | * @see org.ajoberstar.gradle.git.release.semver.SemVerStrategy
27 | * @see org.ajoberstar.gradle.git.release.opinion.Strategies
28 | */
29 | interface DefaultVersionStrategy extends VersionStrategy {
30 | /**
31 | * Determines if the strategy can be used as a default strategy for inferring
32 | * the project's version. A return of {@code false} does not mean that the
33 | * strategy cannot be used as the default.
34 | * @param project the project the version should be inferred for
35 | * @param grgit the repository the version should be inferred from
36 | * @return {@code true} if the strategy can be used to infer the version
37 | */
38 | boolean defaultSelector(Project project, Grgit grgit)
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/ReleasePluginExtension.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.ajoberstar.grgit.util.ConfigureUtil
20 |
21 | import org.gradle.api.GradleException
22 | import org.gradle.api.Project
23 |
24 | import org.slf4j.Logger
25 | import org.slf4j.LoggerFactory
26 |
27 | /**
28 | * Extension providing configuration options for gradle-git's release plugins.
29 | *
30 | *
31 | * Sets the version to a {@link DelayedVersion} which will infer the version
32 | * when {@code toString()} is called on it. A strategy will be selected from the
33 | * ones configured on this extension and then used to infer the version.
34 | *
35 | *
36 | * @see org.ajoberstar.gradle.git.release.base.BaseReleasePlugin
37 | * @see org.ajoberstar.gradle.git.release.opinion.OpinionReleasePlugin
38 | */
39 | class ReleasePluginExtension {
40 | private static final Logger logger = LoggerFactory.getLogger(ReleasePluginExtension)
41 | protected final Project project
42 | private final Map versionStrategies = [:]
43 |
44 | /**
45 | * The strategy to use when creating a tag for the inferred version.
46 | */
47 | final TagStrategy tagStrategy = new TagStrategy()
48 |
49 | /**
50 | * The strategy to use if all of the ones in {@code versionStrategies} return
51 | * false from their {@code selector()} methods. This strategy can be, but is
52 | * not required to be, one from {@code versionStrategies}.
53 | */
54 | VersionStrategy defaultVersionStrategy
55 |
56 | /**
57 | * The repository to infer the version from.
58 | */
59 | Grgit grgit
60 |
61 | /**
62 | * The remote to fetch changes from and push changes to.
63 | */
64 | String remote = 'origin'
65 |
66 | ReleasePluginExtension(Project project) {
67 | this.project = project
68 | def sharedVersion = new DelayedVersion()
69 | project.rootProject.allprojects {
70 | version = sharedVersion
71 | }
72 | }
73 |
74 | /**
75 | * Gets all strategies in the order they were inserted into the extension.
76 | */
77 | List getVersionStrategies() {
78 | return versionStrategies.collect { key, value -> value }.asImmutable()
79 | }
80 |
81 | /**
82 | * Adds a strategy to the extension. If the strategy has the same name as
83 | * one already configured, it will replace the existing one.
84 | */
85 | void versionStrategy(VersionStrategy strategy) {
86 | versionStrategies[strategy.name] = strategy
87 | }
88 |
89 | /**
90 | * Configures the tag strategy with the provided closure.
91 | */
92 | void tagStrategy(Closure closure) {
93 | ConfigureUtil.configure(tagStrategy, closure)
94 | }
95 |
96 | // TODO: Decide if this should be thread-safe.
97 | private class DelayedVersion {
98 | ReleaseVersion inferredVersion
99 |
100 | private void infer() {
101 | VersionStrategy selectedStrategy = versionStrategies.find { strategy ->
102 | strategy.selector(project, grgit)
103 | }
104 |
105 | if (!selectedStrategy) {
106 | boolean useDefault
107 | if (defaultVersionStrategy instanceof DefaultVersionStrategy) {
108 | useDefault = defaultVersionStrategy.defaultSelector(project, grgit)
109 | } else {
110 | useDefault = defaultVersionStrategy?.selector(project, grgit)
111 | }
112 |
113 | if (useDefault) {
114 | logger.info('Falling back to default strategy: {}', defaultVersionStrategy.name)
115 | selectedStrategy = defaultVersionStrategy
116 | } else {
117 | throw new GradleException('No version strategies were selected. Run build with --info for more detail.')
118 | }
119 | }
120 |
121 | inferredVersion = selectedStrategy.infer(project, grgit)
122 | }
123 |
124 | @Override
125 | String toString() {
126 | if (!inferredVersion) {
127 | infer()
128 | }
129 | return inferredVersion.version
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/ReleaseVersion.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import groovy.transform.Immutable
19 |
20 | /**
21 | * Represents an inferred version and any related metadata to be used after the
22 | * inference.
23 | */
24 | @Immutable
25 | class ReleaseVersion {
26 | /**
27 | * The version that should be used by the project.
28 | */
29 | String version
30 | /**
31 | * The latest version, as determined by the strategy's logic.
32 | */
33 | String previousVersion
34 | /**
35 | * Whether or not to create a tag for the release.
36 | */
37 | boolean createTag
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/TagStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import com.github.zafarkhaja.semver.ParseException
19 | import com.github.zafarkhaja.semver.Version
20 | import org.ajoberstar.grgit.Grgit
21 | import org.ajoberstar.grgit.Tag
22 | import org.slf4j.Logger
23 | import org.slf4j.LoggerFactory
24 |
25 | /**
26 | * Strategy for creating a Git tag associated with a release.
27 | */
28 | class TagStrategy {
29 |
30 | /**
31 | * Closure taking a version String as an argument and returning a string to be used as a tag name.
32 | */
33 | Closure toTagString
34 |
35 | /**
36 | * Closure taking a {@link Tag tag} as an argument and returning a {@link Version version} if the tag could be
37 | * parsed, else 'null
'
38 | */
39 | Closure parseTag = { Tag tag ->
40 | try {
41 | Version.valueOf(tag.name[0] == 'v' ? tag.name[1..-1] : tag.name)
42 | } catch (ParseException e) {
43 | null
44 | }
45 | }
46 |
47 | TagStrategy() {
48 | setPrefixNameWithV(true)
49 | }
50 |
51 | private static final Logger logger = LoggerFactory.getLogger(TagStrategy)
52 |
53 | /**
54 | * Added for backwards compatibility.
55 | * @param prefix whether or not to prefix the tag with a 'v'
56 | */
57 | void setPrefixNameWithV(boolean prefix) {
58 | toTagString = { versionString -> prefix ? "v${versionString}" : versionString }
59 | }
60 |
61 | /**
62 | * Closure taking a {@link ReleaseVersion} as an argument and returning
63 | * a string to be used as the tag's message.
64 | */
65 | Closure generateMessage = { version -> "Release of ${version.version}" }
66 |
67 | /**
68 | * If the release version specifies a tag should be created, create a tag
69 | * using the provided {@code Grgit} instance and this instance's state to
70 | * determine the tag name and message.
71 | * @param grgit the repository to create the tag in
72 | * @param version the version to create the tag for
73 | * @return the name of the tag created, or {@code null} if it wasn't
74 | */
75 | String maybeCreateTag(Grgit grgit, ReleaseVersion version) {
76 | if (version.createTag) {
77 | String name = toTagString(version.version)
78 | String message = generateMessage(version)
79 |
80 | logger.warn('Tagging repository as {}', name)
81 | grgit.tag.add(name: name, message: message)
82 | return name
83 | } else {
84 | return null
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/VersionStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 |
20 | import org.gradle.api.Project
21 |
22 | /**
23 | * Strategy to infer a version from the project's and Git repository's state.
24 | * @see org.ajoberstar.gradle.git.release.semver.SemVerStrategy
25 | * @see org.ajoberstar.gradle.git.release.opinion.Strategies
26 | */
27 | interface VersionStrategy {
28 | /**
29 | * The name of the strategy.
30 | * @return the name of the strategy
31 | */
32 | String getName()
33 |
34 | /**
35 | * Determines if the strategy should be used to infer the project's version.
36 | * A return of {@code false} does not mean that the strategy cannot be used
37 | * as the default.
38 | * @param project the project the version should be inferred for
39 | * @param grgit the repository the version should be inferred from
40 | * @return {@code true} if the strategy should be used to infer the version
41 | */
42 | boolean selector(Project project, Grgit grgit)
43 |
44 | /**
45 | * Infers the project version from the repository.
46 | * @param project the project the version should be inferred for
47 | * @param grgit the repository the version should be inferred from
48 | * @return the inferred version
49 | */
50 | ReleaseVersion infer(Project project, Grgit grgit)
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/base/package-info.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * The core structure of gradle-git's flavor of release behavior. The code
18 | * provided in this package can be used on its own, as long as users provide
19 | * their own implementation of {@code VersionStrategy}.
20 | */
21 | package org.ajoberstar.gradle.git.release.base
22 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/experimental/ExperimentalReleasePlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.experimental
17 |
18 | import org.gradle.api.GradleException
19 | import org.gradle.api.Plugin
20 | import org.gradle.api.Project
21 |
22 | import org.slf4j.Logger
23 | import org.slf4j.LoggerFactory
24 |
25 | /**
26 | * Experimental release plugin that removes some previous coupling to extensions.
27 | * Inteded to support semver-vcs, but may serve as a better minimal base.
28 | * @since 1.3.0
29 | */
30 | class ExperimentalReleasePlugin implements Plugin {
31 | private static final Logger logger = LoggerFactory.getLogger(ExperimentalReleasePlugin)
32 | private static final String PREPARE_TASK_NAME = 'prepare'
33 | private static final String RELEASE_TASK_NAME = 'release'
34 |
35 | void apply(Project project) {
36 | project.plugins.apply('org.ajoberstar.grgit')
37 | addPrepareTask(project)
38 | addReleaseTask(project)
39 | }
40 |
41 | private void addPrepareTask(Project project) {
42 | project.tasks.create(PREPARE_TASK_NAME) {
43 | description = 'Verifies that the project could be released.'
44 | doLast {
45 | logger.info('Fetching changes from remote.')
46 | project.grgit.fetch()
47 |
48 | if (project.grgit.branch.status(branch: project.grgit.branch.current).behindCount > 0) {
49 | throw new GradleException('Current branch is behind the tracked branch. Cannot release.')
50 | }
51 | }
52 | }
53 |
54 | project.tasks.all { task ->
55 | if (name != PREPARE_TASK_NAME) {
56 | task.shouldRunAfter PREPARE_TASK_NAME
57 | }
58 | }
59 | }
60 |
61 | private void addReleaseTask(Project project) {
62 | project.tasks.create(RELEASE_TASK_NAME) {
63 | description = 'Releases this project.'
64 | dependsOn PREPARE_TASK_NAME
65 | doLast {
66 | ext.toPush = [project.grgit.branch.current.fullName]
67 |
68 | // force version inference if it hasn't happened already
69 | ext.tagName = project.version.toString()
70 | if (tagName) {
71 | toPush << tagName
72 | }
73 |
74 | logger.warn('Pushing changes in {} to remote.', toPush)
75 | project.grgit.push(refsOrSpecs: toPush)
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/opinion/OpinionReleasePlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.opinion
17 |
18 | import org.ajoberstar.gradle.git.release.semver.RebuildVersionStrategy
19 | import org.ajoberstar.grgit.Grgit
20 | import org.ajoberstar.grgit.exception.GrgitException
21 |
22 | import org.gradle.api.Plugin
23 | import org.gradle.api.Project
24 |
25 | /**
26 | * Plugin providing the base structure of gradle-git's flavor of release
27 | * behavior. The plugin can be applied using the {@code org.ajoberstar.release-base} id.
28 | *
29 | *
30 | * The plugin applies the {@code org.ajoberstar.release-base} plugin and configures it to
31 | * use the following strategies (in order):
32 | *
33 | *
34 | *
35 | * - {@link RebuildVersionStrategy}
36 | * - {@link Strategies#DEVELOPMENT} (also set as the default)
37 | * - {@link Strategies#PRE_RELEASE}
38 | * - {@link Strategies#FINAL}
39 | *
40 | *
41 | *
42 | * Additionally it configures the tag strategy to generate a message from
43 | * the short messages of the commits since the previous version.
44 | *
45 | *
46 | * @see org.ajoberstar.gradle.git.release.opinion.Strategies
47 | * @see org.ajoberstar.gradle.git.release.base.BaseReleasePlugin
48 | * @see Wiki Doc
49 | */
50 | class OpinionReleasePlugin implements Plugin {
51 | void apply(Project project) {
52 | project.plugins.apply('org.ajoberstar.release-base')
53 |
54 | project.release {
55 | versionStrategy RebuildVersionStrategy.INSTANCE
56 | versionStrategy Strategies.DEVELOPMENT
57 | versionStrategy Strategies.PRE_RELEASE
58 | versionStrategy Strategies.FINAL
59 | defaultVersionStrategy = Strategies.DEVELOPMENT
60 | tagStrategy {
61 | generateMessage = { version ->
62 | StringBuilder builder = new StringBuilder()
63 | builder << 'Release of '
64 | builder << version.version
65 | builder << '\n\n'
66 |
67 | String previousVersion = "${project.release.tagStrategy.toTagString(version.previousVersion)}^{commit}"
68 | List excludes = []
69 | if (tagExists(grgit, previousVersion)) {
70 | excludes << previousVersion
71 | }
72 | grgit.log(
73 | includes: ['HEAD'],
74 | excludes: excludes
75 | ).inject(builder) { bldr, commit ->
76 | bldr << '- '
77 | bldr << commit.shortMessage
78 | bldr << '\n'
79 | }
80 | builder.toString()
81 | }
82 | }
83 | }
84 | }
85 |
86 | private boolean tagExists(Grgit grgit, String revStr) {
87 | try {
88 | grgit.resolve.toCommit(revStr)
89 | return true
90 | } catch (GrgitException e) {
91 | return false
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/opinion/Strategies.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.opinion
17 |
18 | import static org.ajoberstar.gradle.git.release.semver.StrategyUtil.*
19 |
20 | import java.util.regex.Pattern
21 |
22 | import org.ajoberstar.gradle.git.release.semver.ChangeScope
23 | import org.ajoberstar.gradle.git.release.semver.PartialSemVerStrategy
24 | import org.ajoberstar.gradle.git.release.semver.SemVerStrategy
25 |
26 | import org.gradle.api.GradleException
27 |
28 | /**
29 | * Opinionated sample strategies. These can either be used as-is or as an
30 | * example for others.
31 | * @see org.ajoberstar.gradle.git.release.base.VersionStrategy
32 | * @see org.ajoberstar.gradle.git.release.semver.SemVerStrategy
33 | * @see org.ajoberstar.gradle.git.release.semver.SemVerStrategyState
34 | * @see org.ajoberstar.gradle.git.release.semver.PartialSemVerStrategy
35 | */
36 | final class Strategies {
37 | /**
38 | * Sample strategies that infer the normal component of a version.
39 | */
40 | static final class Normal {
41 | /**
42 | * Increments the nearest normal version using the scope specified
43 | * in the {@link SemVerStrategyState#scopeFromProp}.
44 | */
45 | static final PartialSemVerStrategy USE_SCOPE_PROP = closure { state ->
46 | return incrementNormalFromScope(state, state.scopeFromProp)
47 | }
48 |
49 | /**
50 | * If the nearest any is different from the nearest normal, sets the
51 | * normal component to the nearest any's normal component. Otherwise
52 | * do nothing.
53 | *
54 | *
55 | * For example, if the nearest any is {@code 1.2.3-alpha.1} and the
56 | * nearest normal is {@code 1.2.2}, this will infer the normal
57 | * component as {@code 1.2.3}.
58 | *
59 | */
60 | static final PartialSemVerStrategy USE_NEAREST_ANY = closure { state ->
61 | def nearest = state.nearestVersion
62 | if (nearest.any == nearest.normal) {
63 | return state
64 | } else {
65 | return state.copyWith(inferredNormal: nearest.any.normalVersion)
66 | }
67 | }
68 |
69 | /**
70 | * Enforces that the normal version complies with the current branch's major version.
71 | * If the branch is not in the format {@code #.x} (e.g. {@code 2.x}), this will do
72 | * nothing.
73 | *
74 | *
75 | * - If the current branch doesn't match the pattern do nothing.
76 | * - If the the nearest normal already complies with the branch name.
77 | * - If the major component can be incremented to comply with the branch, do so.
78 | * - Otherwise fail, because the version can't comply with the branch.
79 | *
80 | */
81 | static final PartialSemVerStrategy ENFORCE_BRANCH_MAJOR_X = fromBranchPattern(~/^(\d+)\.x$/)
82 |
83 | /**
84 | * Enforces that the normal version complies with the current branch's major version.
85 | * If the branch is not in the format {@code release/#.x} (e.g. {@code release/2.x}) or
86 | * {@code release-#.x} (e.g. {@code release-3.x}, this will do nothing.
87 | *
88 | *
89 | * - If the current branch doesn't match the pattern do nothing.
90 | * - If the the nearest normal already complies with the branch name.
91 | * - If the major component can be incremented to comply with the branch, do so.
92 | * - Otherwise fail, because the version can't comply with the branch.
93 | *
94 | */
95 | static final PartialSemVerStrategy ENFORCE_GITFLOW_BRANCH_MAJOR_X = fromBranchPattern(~/^release(?:\/|-)(\d+)\.x$/)
96 |
97 | /**
98 | * Enforces that the normal version complies with the current branch's major version.
99 | * If the branch is not in the format {@code #.#.x} (e.g. {@code 2.3.x}), this will do
100 | * nothing.
101 | *
102 | *
103 | * - If the current branch doesn't match the pattern do nothing.
104 | * - If the the nearest normal already complies with the branch name.
105 | * - If the major component can be incremented to comply with the branch, do so.
106 | * - If the minor component can be incremented to comply with the branch, do so.
107 | * - Otherwise fail, because the version can't comply with the branch.
108 | *
109 | */
110 | static final PartialSemVerStrategy ENFORCE_BRANCH_MAJOR_MINOR_X = fromBranchPattern(~/^(\d+)\.(\d+)\.x$/)
111 |
112 | /**
113 | * Enforces that the normal version complies with the current branch's major version.
114 | * If the branch is not in the format {@code release/#.#.x} (e.g. {@code release/2.3.x}) or
115 | * {@code release-#.#.x} (e.g. {@code release-3.11.x}, this will do nothing.
116 | *
117 | *
118 | * - If the current branch doesn't match the pattern do nothing.
119 | * - If the the nearest normal already complies with the branch name.
120 | * - If the major component can be incremented to comply with the branch, do so.
121 | * - Otherwise fail, because the version can't comply with the branch.
122 | *
123 | */
124 | static final PartialSemVerStrategy ENFORCE_GITFLOW_BRANCH_MAJOR_MINOR_X = fromBranchPattern(~/^release(?:\/|-)(\d+)\.(\d+)\.x$/)
125 |
126 | /**
127 | * Uses the specified pattern to enforce that versions inferred on this branch
128 | * comply. Patterns should have 1 or 2 capturing groups representing the
129 | * major and, optionally, the minor component of the version.
130 | *
131 | *
132 | * - If the current branch doesn't match the pattern do nothing.
133 | * - If only the major is specified in the branch name, and the nearest normal complies with that major, do nothing.
134 | * - If the patch component can be incremented and still comply with the branch, do so.
135 | * - If the minor component can be incremented to comply with the branch, do so.
136 | * - If the major component can be incremented to comply with the branch, do so.
137 | * - Otherwise fail, because the version can't comply with the branch.
138 | *
139 | */
140 | static PartialSemVerStrategy fromBranchPattern(Pattern pattern) {
141 | return closure { state ->
142 | def m = state.currentBranch.name =~ pattern
143 | if (m) {
144 | def major = m.groupCount() >= 1 ? parseIntOrZero(m[0][1]) : -1
145 | def minor = m.groupCount() >= 2 ? parseIntOrZero(m[0][2]) : -1
146 |
147 | def normal = state.nearestVersion.normal
148 | def majorDiff = major - normal.majorVersion
149 | def minorDiff = minor - normal.minorVersion
150 |
151 | if (majorDiff == 1 && minor <= 0) {
152 | // major is off by one and minor is either 0 or not in the branch name
153 | return incrementNormalFromScope(state, ChangeScope.MAJOR)
154 | } else if (minorDiff == 1 && minor > 0) {
155 | // minor is off by one and specified in the branch name
156 | return incrementNormalFromScope(state, ChangeScope.MINOR)
157 | } else if (majorDiff == 0 && minorDiff == 0 && minor >= 0) {
158 | // major and minor match, both are specified in branch name
159 | return incrementNormalFromScope(state, ChangeScope.PATCH)
160 | } else if (majorDiff == 0 && minor < 0) {
161 | // only major specified in branch name and already matches
162 | return state
163 | } else {
164 | throw new GradleException("Invalid branch (${state.currentBranch.name}) for nearest normal (${normal}).")
165 | }
166 | } else {
167 | return state
168 | }
169 | }
170 | }
171 |
172 | /**
173 | * Always use the scope provided to increment the normal component.
174 | */
175 | static PartialSemVerStrategy useScope(ChangeScope scope) {
176 | return closure { state -> incrementNormalFromScope(state, scope) }
177 | }
178 | }
179 |
180 | /**
181 | * Sample strategies that infer the pre-release component of a version.
182 | */
183 | static final class PreRelease {
184 | /**
185 | * Do not modify the pre-release component.
186 | */
187 | static final PartialSemVerStrategy NONE = closure { state -> state }
188 |
189 | /**
190 | * Sets the pre-release component to the value of {@link SemVerStrategyState#stageFromProp}.
191 | */
192 | static final PartialSemVerStrategy STAGE_FIXED = closure { state -> state.copyWith(inferredPreRelease: state.stageFromProp)}
193 |
194 | /**
195 | * If the value of {@link SemVerStrategyState#stageFromProp} has a higher or the same precedence than
196 | * the nearest any's pre-release component, set the pre-release component to
197 | * {@link SemVerStrategyState#scopeFromProp}. If not, append the {@link SemVerStrategyState#scopeFromProp}
198 | * to the nearest any's pre-release.
199 | */
200 | static final PartialSemVerStrategy STAGE_FLOAT = closure { state ->
201 | def sameNormal = state.inferredNormal == state.nearestVersion.any.normalVersion
202 | def nearestAnyPreRelease = state.nearestVersion.any.preReleaseVersion
203 | if (sameNormal && nearestAnyPreRelease != null && nearestAnyPreRelease > state.stageFromProp) {
204 | state.copyWith(inferredPreRelease: "${nearestAnyPreRelease}.${state.stageFromProp}")
205 | } else {
206 | state.copyWith(inferredPreRelease: state.stageFromProp)
207 | }
208 | }
209 |
210 | /**
211 | * If the nearest any's pre-release component starts with the so far inferred pre-release component,
212 | * increment the count of the nearest any and append it to the so far inferred pre-release
213 | * component. Otherwise append 1 to the so far inferred pre-release component.
214 | */
215 | static final PartialSemVerStrategy COUNT_INCREMENTED = closure { state ->
216 | def nearest = state.nearestVersion
217 | def currentPreIdents = state.inferredPreRelease ? state.inferredPreRelease.split('\\.') as List : []
218 | if (nearest.any == nearest.normal || nearest.any.normalVersion != state.inferredNormal) {
219 | currentPreIdents << '1'
220 | } else {
221 | def nearestPreIdents = nearest.any.preReleaseVersion.split('\\.')
222 | if (nearestPreIdents.size() <= currentPreIdents.size()) {
223 | currentPreIdents << '1'
224 | } else if (currentPreIdents == nearestPreIdents[0..(currentPreIdents.size() - 1)]) {
225 | def count = parseIntOrZero(nearestPreIdents[currentPreIdents.size()])
226 | currentPreIdents << Integer.toString(count + 1)
227 | } else {
228 | currentPreIdents << '1'
229 | }
230 | }
231 | return state.copyWith(inferredPreRelease: currentPreIdents.join('.'))
232 | }
233 |
234 | /**
235 | * Append the count of commits since the nearest any to the so far inferred pre-release component.
236 | */
237 | static final PartialSemVerStrategy COUNT_COMMITS_SINCE_ANY = closure { state ->
238 | def count = state.nearestVersion.distanceFromAny
239 | def inferred = state.inferredPreRelease ? "${state.inferredPreRelease}.${count}" : "${count}"
240 | return state.copyWith(inferredPreRelease: inferred)
241 | }
242 |
243 | /**
244 | * If the repo has uncommitted changes append "uncommitted" to the so far inferred pre-release component.
245 | */
246 | static final PartialSemVerStrategy SHOW_UNCOMMITTED = closure { state ->
247 | if (state.repoDirty) {
248 | def inferred = state.inferredPreRelease ? "${state.inferredPreRelease}.uncommitted" : 'uncommitted'
249 | state.copyWith(inferredPreRelease: inferred)
250 | } else {
251 | state
252 | }
253 | }
254 | }
255 |
256 | /**
257 | * Sample strategies that infer the build metadata component of a version.
258 | */
259 | static final class BuildMetadata {
260 | /**
261 | * Do not modify the build metadata.
262 | */
263 | static final PartialSemVerStrategy NONE = closure { state -> state }
264 | /**
265 | * Set the build metadata to the abbreviated ID of the current HEAD.
266 | */
267 | static final PartialSemVerStrategy COMMIT_ABBREVIATED_ID = closure { state -> state.copyWith(inferredBuildMetadata: state.currentHead.abbreviatedId) }
268 | /**
269 | * Set the build metadata to the full ID of the current HEAD.
270 | */
271 | static final PartialSemVerStrategy COMMIT_FULL_ID = closure { state -> state.copyWith(inferredBuildMetadata: state.currentHead.id) }
272 | /**
273 | * Set the build metadata to the current timestamp in {@code YYYY.MM.DD.HH.MM.SS} format.
274 | */
275 | static final PartialSemVerStrategy TIMESTAMP = closure { state -> state.copyWith(inferredBuildMetadata: new Date().format('yyyy.MM.dd.hh.mm.ss')) }
276 | }
277 |
278 | /**
279 | * Provides opinionated defaults for a strategy. The primary behavior is for the normal component.
280 | * If the {@code release.scope} property is set, use it. Or if the nearest any's normal component is different
281 | * than the nearest normal version, use it. Or, if nothing else, use PATCH scope.
282 | */
283 | static final SemVerStrategy DEFAULT = new SemVerStrategy(
284 | name: '',
285 | stages: [] as SortedSet,
286 | allowDirtyRepo: false,
287 | normalStrategy: one(Normal.USE_SCOPE_PROP, Normal.USE_NEAREST_ANY, Normal.useScope(ChangeScope.PATCH)),
288 | preReleaseStrategy: PreRelease.NONE,
289 | buildMetadataStrategy: BuildMetadata.NONE,
290 | createTag: true,
291 | enforcePrecedence: true
292 | )
293 |
294 | /**
295 | * Provides a single "SNAPSHOT" stage that can be used in dirty repos and will
296 | * not enforce precedence. The pre-release compoment will always be "SNAPSHOT"
297 | * and no build metadata will be used. Tags will not be created for these versions.
298 | */
299 | static final SemVerStrategy SNAPSHOT = DEFAULT.copyWith(
300 | name: 'snapshot',
301 | stages: ['SNAPSHOT'] as SortedSet,
302 | allowDirtyRepo: true,
303 | preReleaseStrategy: PreRelease.STAGE_FIXED,
304 | createTag: false,
305 | enforcePrecedence: false
306 | )
307 |
308 | /**
309 | * Provides a single "dev" stage that can be used in dirty repos but will
310 | * enforce precedence. If this strategy is used after a nearest any with a
311 | * higher precedence pre-release component (e.g. "rc.1"), the dev component
312 | * will be appended rather than replace. The commit count since the nearest
313 | * any will be used to disambiguate versions and the pre-release component
314 | * will note if the repository is dirty. The abbreviated ID of the HEAD will
315 | * be used as build metadata.
316 | */
317 | static final SemVerStrategy DEVELOPMENT = DEFAULT.copyWith(
318 | name: 'development',
319 | stages: ['dev'] as SortedSet,
320 | allowDirtyRepo: true,
321 | preReleaseStrategy: all(PreRelease.STAGE_FLOAT, PreRelease.COUNT_COMMITS_SINCE_ANY, PreRelease.SHOW_UNCOMMITTED),
322 | buildMetadataStrategy: BuildMetadata.COMMIT_ABBREVIATED_ID,
323 | createTag: false
324 | )
325 |
326 | /**
327 | * Provides "milestone" and "rc" stages that can only be used in clean repos
328 | * and will enforce precedence. The pre-release component will always be set
329 | * to the stage with an incremented count to disambiguate successive
330 | * releases of the same stage. No build metadata component will be added. Please
331 | * note that this strategy uses the same name as {@code PRE_RELEASE_ALPHA_BETA}
332 | * so it cannot be used at the same time.
333 | */
334 | static final SemVerStrategy PRE_RELEASE = DEFAULT.copyWith(
335 | name: 'pre-release',
336 | stages: ['milestone', 'rc'] as SortedSet,
337 | preReleaseStrategy: all(PreRelease.STAGE_FIXED, PreRelease.COUNT_INCREMENTED)
338 | )
339 |
340 | /**
341 | * Provides "alpha", "beta" and "rc" stages that can only be used in clean repos
342 | * and will enforce precedence. The pre-release-alpha-beta component will always be set
343 | * to the stage with an incremented count to disambiguate successive
344 | * releases of the same stage. No build metadata component will be added. Please note
345 | * that this strategy uses the same name as {@code PRE_RELEASE} so it cannot be used
346 | * at the same time.
347 | */
348 | static final SemVerStrategy PRE_RELEASE_ALPHA_BETA = PRE_RELEASE.copyWith(
349 | name: 'pre-release',
350 | stages: ['alpha', 'beta', 'rc'] as SortedSet
351 | )
352 |
353 | /**
354 | * Provides a single "final" stage that can only be used in clean repos and
355 | * will enforce precedence. The pre-release and build metadata components
356 | * will always be empty.
357 | */
358 | static final SemVerStrategy FINAL = DEFAULT.copyWith(
359 | name: 'final',
360 | stages: ['final'] as SortedSet
361 | )
362 | }
363 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/opinion/package-info.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * Opinionated plugin and set of strategies build on top of the base package.
18 | */
19 | package org.ajoberstar.gradle.git.release.opinion
20 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/ChangeScope.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | enum ChangeScope {
19 | MAJOR,
20 | MINOR,
21 | PATCH
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/NearestVersion.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import groovy.transform.Immutable
19 |
20 | import com.github.zafarkhaja.semver.Version
21 |
22 | /**
23 | * Nearest version tags reachable from the current HEAD. The version 0.0.0
24 | * will be returned for any
25 | * @since 0.8.0
26 | */
27 | @Immutable(knownImmutableClasses=[Version])
28 | class NearestVersion {
29 | /**
30 | * The nearest version that is tagged.
31 | */
32 | Version any
33 |
34 | /**
35 | * The nearest normal (i.e. non-prerelease) version that is tagged.
36 | */
37 | Version normal
38 |
39 | /**
40 | * The number of commits since {@code any} reachable from HEAD.
41 | */
42 | int distanceFromAny
43 |
44 | /**
45 | * The number of commits since {@code normal} reachable from HEAD.
46 | */
47 | int distanceFromNormal
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/NearestVersionLocator.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import com.github.zafarkhaja.semver.Version
19 | import org.ajoberstar.gradle.git.release.base.TagStrategy
20 | import org.ajoberstar.grgit.Grgit
21 | import org.ajoberstar.grgit.Tag
22 | import org.eclipse.jgit.lib.ObjectId
23 | import org.eclipse.jgit.revwalk.RevCommit
24 | import org.eclipse.jgit.revwalk.RevWalk
25 | import org.eclipse.jgit.revwalk.RevWalkUtils
26 | import org.slf4j.Logger
27 | import org.slf4j.LoggerFactory
28 |
29 | /**
30 | * Locates the nearest {@link org.ajoberstar.grgit.Tag tag}s whose names can be
31 | * parsed as a {@link com.github.zafarkhaja.semver.Version version}. Both the
32 | * absolute nearest version tag and the nearest "normal version" tag are
33 | * included.
34 | *
35 | *
36 | * Primarily used as part of version inference to determine the previous
37 | * version.
38 | *
39 | *
40 | * @since 0.8.0
41 | */
42 | class NearestVersionLocator {
43 | private static final Logger logger = LoggerFactory.getLogger(NearestVersionLocator)
44 | private static final Version UNKNOWN = Version.valueOf('0.0.0')
45 |
46 | final TagStrategy strategy
47 |
48 | NearestVersionLocator(TagStrategy strategy) {
49 | this.strategy = strategy
50 | }
51 |
52 | /**
53 | * Locate the nearest version in the given repository
54 | * starting from the current HEAD.
55 | *
56 | *
57 | * All tag names are parsed to determine if they are valid
58 | * version strings. Tag names can begin with "v" (which will
59 | * be stripped off).
60 | *
61 | *
62 | *
63 | * The nearest tag is determined by getting a commit log between
64 | * the tag and {@code HEAD}. The version tag with the smallest
65 | * log from a pure count of commits will have its version returned. If two
66 | * version tags have a log of the same size, the versions will be compared
67 | * to find the one with the highest precedence according to semver rules.
68 | * For example, {@code 1.0.0} has higher precedence than {@code 1.0.0-rc.2}.
69 | * For tags with logs of the same size and versions of the same precedence
70 | * it is undefined which will be returned.
71 | *
72 | *
73 | *
74 | * Two versions will be returned: the "any" version and the "normal" version.
75 | * "Any" is the absolute nearest tagged version. "Normal" is the nearest
76 | * tagged version that does not include a pre-release segment.
77 | *
78 | *
79 | * @param grgit the repository to locate the tag in
80 | * @param fromRevStr the revision to consider current.
81 | * Defaults to {@code HEAD}.
82 | * @return the version corresponding to the nearest tag
83 | */
84 | NearestVersion locate(Grgit grgit) {
85 | logger.debug('Locate beginning on branch: {}', grgit.branch.current.fullName)
86 |
87 | // Reuse a single walk to make use of caching.
88 | RevWalk walk = new RevWalk(grgit.repository.jgit.repo)
89 | try {
90 | walk.retainBody = false
91 |
92 | def toRev = { obj ->
93 | def commit = grgit.resolve.toCommit(obj)
94 | def id = ObjectId.fromString(commit.id)
95 | walk.parseCommit(id)
96 | }
97 |
98 | List tags = grgit.tag.list().collect { tag ->
99 | [version: strategy.parseTag(tag), tag: tag, rev: toRev(tag)]
100 | }.findAll {
101 | it.version
102 | }
103 |
104 | List normalTags = tags.findAll { !it.version.preReleaseVersion }
105 | RevCommit head = toRev(grgit.head())
106 |
107 | def normal = findNearestVersion(walk, head, normalTags)
108 | def any = findNearestVersion(walk, head, tags)
109 |
110 | logger.debug('Nearest release: {}, nearest any: {}.', normal, any)
111 | return new NearestVersion(any.version, normal.version, any.distance, normal.distance)
112 | } finally {
113 | walk.close()
114 | }
115 | }
116 |
117 | private Map findNearestVersion(RevWalk walk, RevCommit head, List versionTags) {
118 | walk.reset()
119 | walk.markStart(head)
120 | Map versionTagsByRev = versionTags.groupBy { it.rev }
121 |
122 | def reachableVersionTags = walk.collectMany { rev ->
123 | def matches = versionTagsByRev[rev]
124 | if (matches) {
125 | // Parents can't be "nearer". Exclude them to avoid extra walking.
126 | rev.parents.each { walk.markUninteresting(it) }
127 | }
128 | matches ?: []
129 | }.each { versionTag ->
130 | versionTag.distance = RevWalkUtils.count(walk, head, versionTag.rev)
131 | }
132 |
133 | if (reachableVersionTags) {
134 | return reachableVersionTags.min { a, b ->
135 | def distanceCompare = a.distance <=> b.distance
136 | def versionCompare = (a.version <=> b.version) * -1
137 | distanceCompare == 0 ? versionCompare : distanceCompare
138 | }
139 | } else {
140 | return [version: UNKNOWN, distance: RevWalkUtils.count(walk, head, null)]
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/PartialSemVerStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | /**
19 | * Strategy to infer portions of a semantic version.
20 | * @see SemVerStrategy
21 | */
22 | interface PartialSemVerStrategy {
23 | /**
24 | * Infers a portion of a semantic version and returns the new state
25 | * to be used as inference continues.
26 | */
27 | SemVerStrategyState infer(SemVerStrategyState state)
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/RebuildVersionStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import org.ajoberstar.gradle.git.release.base.ReleasePluginExtension
19 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
20 | import org.ajoberstar.gradle.git.release.base.VersionStrategy
21 | import org.ajoberstar.grgit.Commit
22 | import org.ajoberstar.grgit.Grgit
23 |
24 | import org.gradle.api.Project
25 | import org.slf4j.Logger
26 | import org.slf4j.LoggerFactory
27 |
28 | /**
29 | * Strategy that infers the version based on the tag on the current
30 | * HEAD.
31 | */
32 | class RebuildVersionStrategy implements VersionStrategy {
33 | private static final Logger logger = LoggerFactory.getLogger(RebuildVersionStrategy)
34 | public static final RebuildVersionStrategy INSTANCE = new RebuildVersionStrategy()
35 |
36 | private RebuildVersionStrategy() {
37 | // just hiding the constructor
38 | }
39 |
40 | @Override
41 | String getName() {
42 | return 'rebuild'
43 | }
44 |
45 | /**
46 | * Determines whether this strategy should be used to infer the version.
47 | *
48 | * - Return {@code false}, if any project properties starting with "release." are set.
49 | * - Return {@code false}, if there aren't any tags on the current HEAD that can be parsed as a version.
50 | * - Return {@code true}, otherwise.
51 | *
52 | */
53 | @Override
54 | boolean selector(Project project, Grgit grgit) {
55 | def clean = grgit.status().clean
56 | def propsSpecified = project.hasProperty(SemVerStrategy.SCOPE_PROP) || project.hasProperty(SemVerStrategy.STAGE_PROP)
57 | def headVersion = getHeadVersion(project, grgit)
58 |
59 | if (clean && !propsSpecified && headVersion) {
60 | logger.info('Using {} strategy because repo is clean, no "release." properties found and head version is {}', name, headVersion)
61 | return true
62 | } else {
63 | logger.info('Skipping {} strategy because clean is {}, "release." properties are {} and head version is {}', name, clean, propsSpecified, headVersion)
64 | return false
65 | }
66 | }
67 |
68 | /**
69 | * Infers the version based on the version tag on the current HEAD with the
70 | * highest precendence.
71 | */
72 | @Override
73 | ReleaseVersion infer(Project project, Grgit grgit) {
74 | String version = getHeadVersion(project, grgit)
75 | def releaseVersion = new ReleaseVersion(version, version, false)
76 | logger.debug('Inferred version {} by strategy {}', releaseVersion, name)
77 | return releaseVersion
78 | }
79 |
80 | private String getHeadVersion(Project project, Grgit grgit) {
81 | def tagStrategy = project.extensions.getByType(ReleasePluginExtension).tagStrategy
82 | Commit head = grgit.head()
83 | return grgit.tag.list().findAll {
84 | it.commit == head
85 | }.collect {
86 | tagStrategy.parseTag(it)
87 | }.findAll {
88 | it != null
89 | }.max()?.toString()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/SemVerStrategy.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import groovy.transform.Immutable
19 | import groovy.transform.PackageScope
20 |
21 | import com.github.zafarkhaja.semver.Version
22 | import org.ajoberstar.gradle.git.release.base.ReleasePluginExtension
23 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
24 | import org.ajoberstar.gradle.git.release.base.DefaultVersionStrategy
25 | import org.ajoberstar.grgit.Grgit
26 |
27 | import org.gradle.api.GradleException
28 | import org.gradle.api.Project
29 |
30 | import org.slf4j.Logger
31 | import org.slf4j.LoggerFactory
32 |
33 | /**
34 | * Strategy to infer versions that comply with Semantic Versioning.
35 | * @see PartialSemVerStrategy
36 | * @see SemVerStrategyState
37 | * @see Wiki Doc
38 | */
39 | @Immutable(copyWith=true, knownImmutableClasses=[PartialSemVerStrategy])
40 | final class SemVerStrategy implements DefaultVersionStrategy {
41 | private static final Logger logger = LoggerFactory.getLogger(SemVerStrategy)
42 | static final String SCOPE_PROP = 'release.scope'
43 | static final String STAGE_PROP = 'release.stage'
44 |
45 | /**
46 | * The name of the strategy.
47 | */
48 | String name
49 |
50 | /**
51 | * The stages supported by this strategy.
52 | */
53 | SortedSet stages
54 |
55 | /**
56 | * Whether or not this strategy can be used if the repo has uncommited changes.
57 | */
58 | boolean allowDirtyRepo
59 |
60 | /**
61 | * The strategy used to infer the normal component of the version. There is no enforcement that
62 | * this strategy only modify that part of the state.
63 | */
64 | PartialSemVerStrategy normalStrategy
65 |
66 | /**
67 | * The strategy used to infer the pre-release component of the version. There is no enforcement that
68 | * this strategy only modify that part of the state.
69 | */
70 | PartialSemVerStrategy preReleaseStrategy
71 |
72 | /**
73 | * The strategy used to infer the build metadata component of the version. There is no enforcement that
74 | * this strategy only modify that part of the state.
75 | */
76 | PartialSemVerStrategy buildMetadataStrategy
77 |
78 | /**
79 | * Whether or not to create tags for versions inferred by this strategy.
80 | */
81 | boolean createTag
82 |
83 | /**
84 | * Whether or not to enforce that versions inferred by this strategy are of higher precedence
85 | * than the nearest any.
86 | */
87 | boolean enforcePrecedence
88 |
89 | /**
90 | * Determines whether this strategy can be used to infer the version as a default.
91 | *
92 | * - Return {@code false}, if the {@code release.stage} is not one listed in the {@code stages} property.
93 | * - Return {@code false}, if the repository has uncommitted changes and {@code allowDirtyRepo} is {@code false}.
94 | * - Return {@code true}, otherwise.
95 | *
96 | */
97 | @Override
98 | boolean defaultSelector(Project project, Grgit grgit) {
99 | String stage = getPropertyOrNull(project, STAGE_PROP)
100 | if (stage != null && !stages.contains(stage)) {
101 | logger.info('Skipping {} default strategy because stage ({}) is not one of: {}', name, stage, stages)
102 | return false
103 | } else if (!allowDirtyRepo && !grgit.status().clean) {
104 | logger.info('Skipping {} default strategy because repo is dirty.', name)
105 | return false
106 | } else {
107 | String status = grgit.status().clean ? 'clean' : 'dirty'
108 | logger.info('Using {} default strategy because repo is {} and no stage defined', name, status)
109 | return true
110 | }
111 | }
112 |
113 | /**
114 | * Determines whether this strategy should be used to infer the version.
115 | *
116 | * - Return {@code false}, if the {@code release.stage} is not one listed in the {@code stages} property.
117 | * - Return {@code false}, if the repository has uncommitted changes and {@code allowDirtyRepo} is {@code false}.
118 | * - Return {@code true}, otherwise.
119 | *
120 | */
121 | @Override
122 | boolean selector(Project project, Grgit grgit) {
123 | String stage = getPropertyOrNull(project, STAGE_PROP)
124 | if (stage == null || !stages.contains(stage)) {
125 | logger.info('Skipping {} strategy because stage ({}) is not one of: {}', name, stage, stages)
126 | return false
127 | } else if (!allowDirtyRepo && !grgit.status().clean) {
128 | logger.info('Skipping {} strategy because repo is dirty.', name)
129 | return false
130 | } else {
131 | String status = grgit.status().clean ? 'clean' : 'dirty'
132 | logger.info('Using {} strategy because repo is {} and stage ({}) is one of: {}', name, status, stage, stages)
133 | return true
134 | }
135 | }
136 |
137 | /**
138 | * Infers the version to use for this build. Uses the normal, pre-release, and build metadata
139 | * strategies in order to infer the version. If the {@code release.stage} is not set, uses the
140 | * first value in the {@code stages} set (i.e. the one with the lowest precedence). After inferring
141 | * the version precedence will be enforced, if required by this strategy.
142 | */
143 | @Override
144 | ReleaseVersion infer(Project project, Grgit grgit) {
145 | def tagStrategy = project.extensions.getByType(ReleasePluginExtension).tagStrategy
146 | return doInfer(project, grgit, new NearestVersionLocator(tagStrategy))
147 | }
148 |
149 | @PackageScope
150 | ReleaseVersion doInfer(Project project, Grgit grgit, NearestVersionLocator locator) {
151 | ChangeScope scope = getPropertyOrNull(project, SCOPE_PROP).with { scope ->
152 | scope == null ? null : ChangeScope.valueOf(scope.toUpperCase())
153 | }
154 | String stage = getPropertyOrNull(project, STAGE_PROP) ?: stages.first()
155 | if (!stages.contains(stage)) {
156 | throw new GradleException("Stage ${stage} is not one of ${stages} allowed for strategy ${name}.")
157 | }
158 | logger.info('Beginning version inference using {} strategy and input scope ({}) and stage ({})', name, scope, stage)
159 |
160 | NearestVersion nearestVersion = locator.locate(grgit)
161 | logger.debug('Located nearest version: {}', nearestVersion)
162 |
163 | SemVerStrategyState state = new SemVerStrategyState(
164 | scopeFromProp: scope,
165 | stageFromProp: stage,
166 | currentHead: grgit.head(),
167 | currentBranch: grgit.branch.current,
168 | repoDirty: !grgit.status().clean,
169 | nearestVersion: nearestVersion
170 | )
171 |
172 | Version version = StrategyUtil.all(
173 | normalStrategy, preReleaseStrategy, buildMetadataStrategy).infer(state).toVersion()
174 |
175 | logger.warn('Inferred project: {}, version: {}', project.name, version)
176 |
177 | if (enforcePrecedence && version < nearestVersion.any) {
178 | throw new GradleException("Inferred version (${version}) cannot be lower than nearest (${nearestVersion.any}). Required by selected strategy.")
179 | }
180 |
181 | return new ReleaseVersion(version.toString(), nearestVersion.normal.toString(), createTag)
182 | }
183 |
184 | private String getPropertyOrNull(Project project, String name) {
185 | return project.hasProperty(name) ? project.property(name) : null
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/SemVerStrategyState.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import groovy.transform.Immutable
19 | import groovy.transform.ToString
20 |
21 | import com.github.zafarkhaja.semver.Version
22 |
23 | import org.ajoberstar.grgit.Branch
24 | import org.ajoberstar.grgit.Commit
25 |
26 | /**
27 | * Working state used by {@link PartialSemVerStrategy}.
28 | */
29 | @Immutable(copyWith=true)
30 | @ToString(includeNames=true)
31 | final class SemVerStrategyState {
32 | ChangeScope scopeFromProp
33 | String stageFromProp
34 | Commit currentHead
35 | Branch currentBranch
36 | boolean repoDirty
37 | NearestVersion nearestVersion
38 | String inferredNormal
39 | String inferredPreRelease
40 | String inferredBuildMetadata
41 |
42 | Version toVersion() {
43 | return new Version.Builder().with {
44 | normalVersion = inferredNormal
45 | preReleaseVersion = inferredPreRelease
46 | buildMetadata =inferredBuildMetadata
47 | build()
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/StrategyUtil.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | /**
19 | * Utility class to more easily create {@link PartialSemVerStrategy} instances.
20 | */
21 | final class StrategyUtil {
22 | private StrategyUtil() {
23 | throw new AssertionError('Cannot instantiate this class.')
24 | }
25 |
26 | /**
27 | * Creates a strategy backed by the given closure. It should accept and return
28 | * a {@link SemVerStrategyState}.
29 | */
30 | static final PartialSemVerStrategy closure(Closure behavior) {
31 | return new ClosureBackedPartialSemVerStrategy(behavior)
32 | }
33 |
34 | /**
35 | * Creates a strategy that applies all of the given strategies in order.
36 | */
37 | static final PartialSemVerStrategy all(PartialSemVerStrategy... strategies) {
38 | return new ApplyAllChainedPartialSemVerStrategy(strategies as List)
39 | }
40 |
41 | /**
42 | * Creates a strategy that applies each strategy in order, until one changes
43 | * the state, which is then returned.
44 | */
45 | static final PartialSemVerStrategy one(PartialSemVerStrategy... strategies) {
46 | return new ChooseOneChainedPartialSemVerStrategy(strategies as List)
47 | }
48 |
49 | /**
50 | * Returns the int value of a string or returns 0 if it cannot be parsed.
51 | */
52 | static final int parseIntOrZero(String str) {
53 | try {
54 | return Integer.parseInt(str)
55 | } catch (NumberFormatException e) {
56 | return 0
57 | }
58 | }
59 |
60 | /**
61 | * Increments the nearest normal version using the specified scope.
62 | */
63 | static final SemVerStrategyState incrementNormalFromScope(SemVerStrategyState state, ChangeScope scope) {
64 | def oldNormal = state.nearestVersion.normal
65 | switch (scope) {
66 | case ChangeScope.MAJOR:
67 | return state.copyWith(inferredNormal: oldNormal.incrementMajorVersion())
68 | case ChangeScope.MINOR:
69 | return state.copyWith(inferredNormal: oldNormal.incrementMinorVersion())
70 | case ChangeScope.PATCH:
71 | return state.copyWith(inferredNormal: oldNormal.incrementPatchVersion())
72 | default:
73 | return state
74 | }
75 | }
76 |
77 | private static class ClosureBackedPartialSemVerStrategy implements PartialSemVerStrategy {
78 | private final Closure behavior
79 |
80 | ClosureBackedPartialSemVerStrategy(Closure behavior) {
81 | this.behavior = behavior
82 | }
83 |
84 | @Override
85 | SemVerStrategyState infer(SemVerStrategyState state) {
86 | return behavior(state)
87 | }
88 | }
89 |
90 | private static class ApplyAllChainedPartialSemVerStrategy implements PartialSemVerStrategy {
91 | private final List strategies
92 |
93 | ApplyAllChainedPartialSemVerStrategy(List strategies) {
94 | this.strategies = strategies
95 | }
96 |
97 | @Override
98 | SemVerStrategyState infer(SemVerStrategyState initialState) {
99 | return strategies.inject(initialState) { state, strategy ->
100 | strategy.infer(state)
101 | }
102 | }
103 | }
104 |
105 | private static class ChooseOneChainedPartialSemVerStrategy implements PartialSemVerStrategy {
106 | private final List strategies
107 |
108 | ChooseOneChainedPartialSemVerStrategy(List strategies) {
109 | this.strategies = strategies
110 | }
111 |
112 | @Override
113 | SemVerStrategyState infer(SemVerStrategyState oldState) {
114 | def result = strategies.findResult { strategy ->
115 | def newState = strategy.infer(oldState)
116 | oldState == newState ? null : newState
117 | }
118 | return result ?: oldState
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/git/release/semver/package-info.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | /**
17 | * Support for version strategies that support semantic versioning.
18 | */
19 | package org.ajoberstar.gradle.git.release.semver
20 |
--------------------------------------------------------------------------------
/src/main/groovy/org/ajoberstar/gradle/util/ObjectUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.util;
17 |
18 | import java.util.concurrent.Callable;
19 |
20 | import groovy.lang.Closure;
21 |
22 | /**
23 | * Utility class for general {@code Object} related operations.
24 | * @since 0.1.0
25 | */
26 | public final class ObjectUtil {
27 | /**
28 | * Cannot instantiate
29 | * @throws AssertionError always
30 | */
31 | private ObjectUtil() {
32 | throw new AssertionError("Cannot instantiate this class");
33 | }
34 |
35 | /**
36 | * Unpacks the given object by recursively
37 | * calling the {@code call()} method if the
38 | * object is a {@code Closure} or {@code Callable}.
39 | * @param obj the object to unpack
40 | * @return the unpacked value of the object
41 | */
42 | @SuppressWarnings("rawtypes")
43 | public static Object unpack(Object obj) {
44 | Object value = obj;
45 | while (value != null) {
46 | if (value instanceof Closure) {
47 | value = ((Closure) value).call();
48 | } else if (value instanceof Callable) {
49 | try {
50 | value = ((Callable) value).call();
51 | } catch (Exception e) {
52 | throw new RuntimeException(e);
53 | }
54 | } else {
55 | return value;
56 | }
57 | }
58 | return value;
59 | }
60 |
61 | /**
62 | * Unpacks the given object to its {@code String}
63 | * value. Same behavior as the other {@code unpack}
64 | * method ending with a call to {@code toString()}.
65 | * @param obj the value to unpack
66 | * @return the unpacked string value
67 | * @see ObjectUtil#unpack(Object)
68 | */
69 | public static String unpackString(Object obj) {
70 | Object value = unpack(obj);
71 | return value == null ? null : value.toString();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/org.ajoberstar.github-pages.properties:
--------------------------------------------------------------------------------
1 | implementation-class=org.ajoberstar.gradle.git.ghpages.GithubPagesPlugin
2 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/org.ajoberstar.grgit.properties:
--------------------------------------------------------------------------------
1 | implementation-class=org.ajoberstar.gradle.git.base.GrgitPlugin
2 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/org.ajoberstar.release-base.properties:
--------------------------------------------------------------------------------
1 | implementation-class=org.ajoberstar.gradle.git.release.base.BaseReleasePlugin
2 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/org.ajoberstar.release-experimental.properties:
--------------------------------------------------------------------------------
1 | implementation-class=org.ajoberstar.gradle.git.release.experimental.ExperimentalReleasePlugin
2 |
--------------------------------------------------------------------------------
/src/main/resources/META-INF/gradle-plugins/org.ajoberstar.release-opinion.properties:
--------------------------------------------------------------------------------
1 | implementation-class=org.ajoberstar.gradle.git.release.opinion.OpinionReleasePlugin
2 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/ghpages/GithubPagesPluginExtensionSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.ghpages
17 |
18 | import org.ajoberstar.gradle.git.auth.BasicPasswordCredentials
19 | import org.gradle.api.Project
20 | import org.gradle.api.file.CopySpec
21 | import org.gradle.testfixtures.ProjectBuilder
22 | import spock.lang.Specification
23 | import spock.lang.Unroll
24 |
25 | class GithubPagesPluginExtensionSpec extends Specification {
26 | Project project = ProjectBuilder.builder().withProjectDir(new File('.')).build()
27 |
28 | def 'Verify the defaults'() {
29 | def ext = new GithubPagesPluginExtension(project)
30 |
31 | expect:
32 | ext.commitMessage == 'Publish of Github pages from Gradle.'
33 | ext.credentials instanceof BasicPasswordCredentials
34 | ext.pages instanceof CopySpec
35 | ext.targetBranch == 'gh-pages'
36 | ext.workingDir == project.file("${project.buildDir}/ghpages")
37 | ext.workingPath == "${project.buildDir}/ghpages"
38 | ext.deleteExistingFiles
39 | ext.pages.relativeDestinationDir == '.'
40 | // TODO ideally would check the repoUri, but need a more stable case
41 | }
42 |
43 | @Unroll
44 | def 'Repo uri [#repoUri] results in [#expected]'() {
45 | def ext = new GithubPagesPluginExtension(project)
46 | ext.repoUri = repoUri
47 |
48 | expect:
49 | ext.getRepoUri() == expected
50 |
51 | where:
52 | repoUri || expected
53 | 'git@domain.tld:user/repo.git' || 'git@domain.tld:user/repo.git'
54 | // as repoUri is an Object which gets "unpacked", I expect there are
55 | // use-cases for this. But I've none
56 | }
57 |
58 | def 'Property workingDir is based on workingPath'() {
59 | def ext = new GithubPagesPluginExtension(project)
60 | ext.workingPath = "${project.buildDir}${File.separator}some-path"
61 |
62 | when:
63 | def workingDir = ext.getWorkingDir()
64 |
65 | then:
66 | workingDir.absolutePath.endsWith(old(ext.workingPath))
67 | }
68 |
69 | def 'Property pages.relativeDestinationDir is relative to workingDir'() {
70 | def ext = new GithubPagesPluginExtension(project)
71 | ext.workingPath = "${project.buildDir}${File.separator}some-path"
72 | ext.pages {
73 | into 'docs'
74 | }
75 |
76 | when:
77 | def workingDir = ext.workingDir
78 | def relativeDestinationDir = ext.pages.relativeDestinationDir
79 |
80 | then:
81 | workingDir.absolutePath == old(ext.workingPath)
82 | relativeDestinationDir == 'docs'
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/ghpages/GithubPagesPluginSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.ghpages
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.gradle.api.Project
20 | import org.gradle.api.Task
21 | import org.gradle.testfixtures.ProjectBuilder
22 | import spock.lang.Specification
23 | import spock.lang.Unroll
24 |
25 | class GithubPagesPluginSpec extends Specification {
26 | public static final String PLUGIN_NAME = 'org.ajoberstar.github-pages'
27 | public static final String EXTENSION_NAME = 'githubPages'
28 | Project project = ProjectBuilder.builder().withProjectDir(new File('.')).build()
29 |
30 | def 'Creates the [githubPages] extension'() {
31 | assert !project.plugins.hasPlugin(PLUGIN_NAME)
32 | assert !project.extensions.findByName(EXTENSION_NAME)
33 |
34 | when:
35 | project.plugins.apply(PLUGIN_NAME)
36 |
37 | then:
38 | def extension = project.extensions.findByName(EXTENSION_NAME)
39 | extension instanceof GithubPagesPluginExtension
40 | }
41 |
42 | @Unroll
43 | def 'Implements the [#taskName] task'() {
44 | assert !project.plugins.hasPlugin(PLUGIN_NAME)
45 | assert !project.tasks.findByName(taskName)
46 |
47 | when:
48 | project.plugins.apply(PLUGIN_NAME)
49 |
50 | then:
51 | project.tasks.findByName(taskName)
52 |
53 | where:
54 | taskName || _
55 | 'prepareGhPages' || _
56 | 'publishGhPages' || _
57 | }
58 |
59 | def '[publishGhPages] task depends on [prepareGhPages] tasks'() {
60 | project.plugins.apply(PLUGIN_NAME)
61 |
62 | when:
63 | def task = project.tasks.findByName('publishGhPages')
64 |
65 | then:
66 | task.taskDependencies.getDependencies(task).find { Task it ->
67 | it.name == 'prepareGhPages'
68 | }
69 | }
70 |
71 | @Unroll
72 | def 'Task [#taskName] has description [#description]'() {
73 | project.plugins.apply(PLUGIN_NAME)
74 |
75 | when:
76 | def task = project.tasks.findByName(taskName)
77 |
78 | then:
79 | task.description == description
80 |
81 | where:
82 | taskName || description
83 | 'prepareGhPages' || 'Prepare the gh-pages changes locally'
84 | 'publishGhPages' || 'Publishes all gh-pages changes to Github'
85 | }
86 |
87 | def '[prepareGhPages] depends on tasks add to the pages spec from'() {
88 | project.plugins.apply(PLUGIN_NAME)
89 | project.plugins.apply('java')
90 | when:
91 | project.githubPages.pages {
92 | from project.tasks.javadoc
93 | }
94 | def task = project.tasks.findByName('prepareGhPages')
95 | then:
96 | task.taskDependencies.getDependencies(task).find { it.name == 'javadoc' }
97 | }
98 |
99 | // TODO need a test for gh-pages branch creatino if didn't exist before
100 | }
101 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/base/BaseReleasePluginSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Branch
19 | import org.ajoberstar.grgit.BranchStatus
20 | import org.ajoberstar.grgit.Grgit
21 | import org.ajoberstar.grgit.service.BranchService
22 | import org.ajoberstar.grgit.service.TagService
23 |
24 | import org.gradle.api.GradleException
25 | import org.gradle.api.Project
26 | import org.gradle.testfixtures.ProjectBuilder
27 |
28 | import spock.lang.Specification
29 |
30 | class BaseReleasePluginSpec extends Specification {
31 | Project project = ProjectBuilder.builder().build()
32 |
33 | def setup() {
34 | project.plugins.apply('org.ajoberstar.release-base')
35 | }
36 |
37 | def 'prepare task succeeds if branch is up to date'() {
38 | given:
39 | Grgit repo = GroovyMock()
40 | BranchService branch = GroovyMock()
41 | repo.branch >> branch
42 | branch.current >> new Branch(fullName: 'refs/heads/master')
43 | branch.status([branch: 'refs/heads/master']) >> new BranchStatus(behindCount: 0)
44 |
45 | project.release {
46 | grgit = repo
47 | }
48 | when:
49 | project.tasks.prepare.execute()
50 | then:
51 | notThrown(GradleException)
52 | 1 * repo.fetch([remote: 'origin'])
53 | }
54 |
55 | def 'prepare task fails if branch is behind'() {
56 | given:
57 | Grgit repo = GroovyMock()
58 | BranchService branch = GroovyMock()
59 | repo.branch >> branch
60 | branch.current >> new Branch(fullName: 'refs/heads/master', trackingBranch: new Branch(fullName: 'refs/remotes/origin/master'))
61 | branch.status([branch: 'refs/heads/master']) >> new BranchStatus(behindCount: 2)
62 |
63 | project.release {
64 | grgit = repo
65 | }
66 | when:
67 | project.tasks.prepare.execute()
68 | then:
69 | thrown(GradleException)
70 | 1 * repo.fetch([remote: 'origin'])
71 | }
72 |
73 | def 'release task pushes branch and tag if created'() {
74 | given:
75 | Grgit repo = GroovyMock()
76 | BranchService branch = GroovyMock()
77 | repo.branch >> branch
78 | TagService tag = GroovyMock()
79 | repo.tag >> tag
80 | branch.current >> new Branch(fullName: 'refs/heads/master', trackingBranch: new Branch(fullName: 'refs/remotes/origin/master'))
81 | project.release {
82 | versionStrategy([
83 | getName: { 'a' },
84 | selector: {proj, repo2 -> true },
85 | infer: {proj, repo2 -> new ReleaseVersion('1.2.3', null, true)}] as VersionStrategy)
86 | grgit = repo
87 | }
88 | when:
89 | project.tasks.release.execute()
90 | then:
91 | 1 * repo.push([remote: 'origin', refsOrSpecs: ['refs/heads/master', 'v1.2.3']])
92 | }
93 |
94 | def 'release task pushes branch but not tag if it was not created'() {
95 | given:
96 | Grgit repo = GroovyMock()
97 | BranchService branch = GroovyMock()
98 | repo.branch >> branch
99 | TagService tag = GroovyMock()
100 | repo.tag >> tag
101 | branch.current >> new Branch(fullName: 'refs/heads/master', trackingBranch: new Branch(fullName: 'refs/remotes/origin/master'))
102 | project.release {
103 | versionStrategy([
104 | getName: { 'a' },
105 | selector: {proj, repo2 -> true },
106 | infer: {proj, repo2 -> new ReleaseVersion('1.2.3', null, false)}] as VersionStrategy)
107 | grgit = repo
108 | }
109 | when:
110 | project.tasks.release.execute()
111 | then:
112 | 1 * repo.push([remote: 'origin', refsOrSpecs: ['refs/heads/master']])
113 | }
114 |
115 | def 'release task skips push if on detached head'() {
116 | given:
117 | Grgit repo = GroovyMock()
118 | BranchService branch = GroovyMock()
119 | repo.branch >> branch
120 | TagService tag = GroovyMock()
121 | repo.tag >> tag
122 | branch.current >> new Branch(fullName: 'HEAD')
123 | project.release {
124 | versionStrategy([
125 | getName: { 'a' },
126 | selector: {proj, repo2 -> true },
127 | infer: {proj, repo2 -> new ReleaseVersion('1.2.3', null, false)}] as VersionStrategy)
128 | grgit = repo
129 | }
130 | when:
131 | project.tasks.release.execute()
132 | then:
133 | 0 * repo.push(_)
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/base/ReleasePluginExtensionSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 |
20 | import org.gradle.api.GradleException
21 | import org.gradle.api.Project
22 | import org.gradle.testfixtures.ProjectBuilder
23 |
24 | import spock.lang.Specification
25 |
26 | class ReleasePluginExtensionSpec extends Specification {
27 | def 'infers default version if selector returns false for all but default'() {
28 | given:
29 | Project project = ProjectBuilder.builder().build()
30 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
31 | extension.grgit = GroovyMock(Grgit)
32 | extension.versionStrategy([
33 | getName: { 'b' },
34 | selector: { proj, grgit -> false },
35 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
36 | extension.defaultVersionStrategy = [
37 | getName: { 'a' },
38 | selector: { proj, grgit -> true },
39 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as VersionStrategy
40 | expect:
41 | project.version.toString() == '1.2.3'
42 | }
43 |
44 | def 'infers using first strategy selector returns true for'() {
45 | Project project = ProjectBuilder.builder().build()
46 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
47 | extension.grgit = GroovyMock(Grgit)
48 | extension.versionStrategy([
49 | getName: { 'b' },
50 | selector: { proj, grgit -> false },
51 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
52 | extension.versionStrategy([
53 | getName: { 'a' },
54 | selector: { proj, grgit -> true },
55 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as VersionStrategy)
56 | expect:
57 | project.version.toString() == '1.2.3'
58 | }
59 |
60 | def 'infers using first strategy selector returns true for in order'() {
61 | Project project = ProjectBuilder.builder().build()
62 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
63 | extension.grgit = GroovyMock(Grgit)
64 | extension.versionStrategy([
65 | getName: { 'b' },
66 | selector: { proj, grgit -> true },
67 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
68 | extension.versionStrategy([
69 | getName: { 'a' },
70 | selector: { proj, grgit -> true },
71 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as VersionStrategy)
72 | expect:
73 | project.version.toString() == '1.0.0'
74 | }
75 |
76 | def 'infer uses default if it has default selector that passes when selector doesnt'() {
77 | given:
78 | Project project = ProjectBuilder.builder().build()
79 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
80 | extension.grgit = GroovyMock(Grgit)
81 | extension.versionStrategy([
82 | getName: { 'b' },
83 | selector: { proj, grgit -> false },
84 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
85 | extension.defaultVersionStrategy = [
86 | getName: { 'a' },
87 | selector: { proj, grgit -> false },
88 | defaultSelector: { proj, grgit -> true },
89 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as DefaultVersionStrategy
90 | expect:
91 | project.version.toString() == '1.2.3'
92 | }
93 |
94 | def 'infer fails if no strategy selected including the default strategy'() {
95 | given:
96 | Project project = ProjectBuilder.builder().build()
97 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
98 | extension.grgit = GroovyMock(Grgit)
99 | extension.versionStrategy([
100 | getName: { 'b' },
101 | selector: { proj, grgit -> false },
102 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
103 | extension.defaultVersionStrategy = [
104 | getName: { 'a' },
105 | selector: { proj, grgit -> false },
106 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as VersionStrategy
107 | when:
108 | project.version.toString()
109 | then:
110 | thrown(GradleException)
111 | }
112 |
113 | def 'infer fails if no strategy selected and no default set'() {
114 | Project project = ProjectBuilder.builder().build()
115 | ReleasePluginExtension extension = new ReleasePluginExtension(project)
116 | extension.grgit = GroovyMock(Grgit)
117 | extension.versionStrategy([
118 | getName: { 'b' },
119 | selector: { proj, grgit -> false },
120 | infer: { proj, grgit -> new ReleaseVersion('1.0.0', null, true) }] as VersionStrategy)
121 | extension.versionStrategy([
122 | getName: { 'a' },
123 | selector: { proj, grgit -> false },
124 | infer: { proj, grgit -> new ReleaseVersion('1.2.3', null, true) }] as VersionStrategy)
125 | when:
126 | project.version.toString()
127 | then:
128 | thrown(GradleException)
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/base/TagStrategySpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.base
17 |
18 | import org.ajoberstar.grgit.Grgit
19 | import org.ajoberstar.grgit.service.TagService
20 | import spock.lang.Specification
21 |
22 | class TagStrategySpec extends Specification {
23 | def 'maybeCreateTag with version create tag true will create a tag'() {
24 | given:
25 | Grgit grgit = GroovyMock()
26 | TagService tag = GroovyMock()
27 | grgit.tag >> tag
28 | 1 * tag.add([name: 'v1.2.3', message: 'Release of 1.2.3'])
29 | 0 * tag._
30 | expect:
31 | new TagStrategy().maybeCreateTag(grgit, new ReleaseVersion('1.2.3', null, true)) == 'v1.2.3'
32 | }
33 |
34 | def 'maybeCreateTag with version create tag false does not create a tag'() {
35 | given:
36 | Grgit grgit = GroovyMock()
37 | TagService tag = GroovyMock()
38 | grgit.tag >> tag
39 | 0 * tag._
40 | expect:
41 | new TagStrategy().maybeCreateTag(grgit, new ReleaseVersion('1.2.3', null, false)) == null
42 | }
43 |
44 | def 'maybeCreateTag with version create tag true and prefix name with v false will create a tag'() {
45 | given:
46 | Grgit grgit = GroovyMock()
47 | TagService tag = GroovyMock()
48 | grgit.tag >> tag
49 | 1 * tag.add([name: '1.2.3', message: 'Release of 1.2.3'])
50 | 0 * tag._
51 | def strategy = new TagStrategy()
52 | strategy.prefixNameWithV = false
53 | expect:
54 | strategy.maybeCreateTag(grgit, new ReleaseVersion('1.2.3', null, true)) == '1.2.3'
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/opinion/OpinionReleasePluginSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.opinion
17 |
18 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
19 | import org.ajoberstar.gradle.git.release.semver.RebuildVersionStrategy
20 | import org.ajoberstar.grgit.Commit
21 | import org.ajoberstar.grgit.Grgit
22 | import org.ajoberstar.grgit.exception.GrgitException
23 | import org.ajoberstar.grgit.service.ResolveService
24 |
25 | import org.gradle.api.Project
26 | import org.gradle.testfixtures.ProjectBuilder
27 |
28 | import spock.lang.Specification
29 |
30 | class OpinionReleasePluginSpec extends Specification {
31 | Project project = ProjectBuilder.builder().build()
32 | ReleaseVersion version = new ReleaseVersion(version: '1.2.3', previousVersion: '1.2.2')
33 |
34 | def 'plugin adds correct strategies'() {
35 | given:
36 | project.plugins.apply('org.ajoberstar.release-opinion')
37 | expect:
38 | project.release.versionStrategies == [RebuildVersionStrategy.INSTANCE, Strategies.DEVELOPMENT, Strategies.PRE_RELEASE, Strategies.FINAL]
39 | project.release.defaultVersionStrategy == Strategies.DEVELOPMENT
40 | }
41 |
42 | def 'plugin tag strategy creates correct message if previous tag exists'() {
43 | given:
44 | project.plugins.apply('org.ajoberstar.release-opinion')
45 | Grgit grgit = GroovyMock()
46 | project.release.grgit = grgit
47 | ResolveService resolve = Mock()
48 | (1.._) * grgit.resolve >> resolve
49 | 1 * resolve.toCommit('v1.2.2^{commit}') >> new Commit(shortMessage: 'Commit 1')
50 | 1 * grgit.log([includes: ['HEAD'], excludes: ['v1.2.2^{commit}']]) >> [
51 | new Commit(shortMessage: 'Commit 2'),
52 | new Commit(shortMessage: 'Next commit')]
53 | 0 * grgit._
54 | expect:
55 | project.release.tagStrategy.generateMessage(version).trim() == '''
56 | Release of 1.2.3
57 |
58 | - Commit 2
59 | - Next commit
60 | '''.trim()
61 | }
62 |
63 | def 'plugin tag strategy creates correct message if previous tag exists and no prefix with v'() {
64 | given:
65 | project.plugins.apply('org.ajoberstar.release-opinion')
66 | Grgit grgit = GroovyMock()
67 | project.release.grgit = grgit
68 | project.release.tagStrategy.prefixNameWithV = false
69 | ResolveService resolve = Mock()
70 | (1.._) * grgit.resolve >> resolve
71 | 1 * resolve.toCommit('1.2.2^{commit}') >> new Commit(shortMessage: 'Commit 1')
72 | 1 * grgit.log([includes: ['HEAD'], excludes: ['1.2.2^{commit}']]) >> [
73 | new Commit(shortMessage: 'Commit 2'),
74 | new Commit(shortMessage: 'Next commit')]
75 | 0 * grgit._
76 | expect:
77 | project.release.tagStrategy.generateMessage(version).trim() == '''
78 | Release of 1.2.3
79 |
80 | - Commit 2
81 | - Next commit
82 | '''.trim()
83 | }
84 |
85 | def 'plugin tag strategy creates correct message if previous tag does not exist'() {
86 | given:
87 | project.plugins.apply('org.ajoberstar.release-opinion')
88 | Grgit grgit = GroovyMock()
89 | project.release.grgit = grgit
90 | ResolveService resolve = Mock()
91 | (1.._) * grgit.resolve >> resolve
92 | 1 * grgit.resolve.toCommit('v1.2.2^{commit}') >> { throw new GrgitException('fail') }
93 | 1 * grgit.log([includes: ['HEAD'], excludes: []]) >> [
94 | new Commit(shortMessage: 'Commit 1'),
95 | new Commit(shortMessage: 'Commit 2'),
96 | new Commit(shortMessage: 'Next commit')]
97 | 0 * grgit._
98 | expect:
99 | project.release.tagStrategy.generateMessage(version).trim() == '''
100 | Release of 1.2.3
101 |
102 | - Commit 1
103 | - Commit 2
104 | - Next commit
105 | '''.trim()
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/opinion/StrategiesSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.opinion
17 |
18 | import com.github.zafarkhaja.semver.Version
19 |
20 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
21 | import org.ajoberstar.gradle.git.release.semver.ChangeScope
22 | import org.ajoberstar.gradle.git.release.semver.NearestVersion
23 | import org.ajoberstar.gradle.git.release.semver.NearestVersionLocator
24 | import org.ajoberstar.gradle.git.release.semver.SemVerStrategyState
25 | import org.ajoberstar.gradle.git.release.semver.StrategyUtil
26 | import org.ajoberstar.grgit.Branch
27 | import org.ajoberstar.grgit.Commit
28 | import org.ajoberstar.grgit.Grgit
29 | import org.ajoberstar.grgit.Status
30 | import org.ajoberstar.grgit.service.BranchService
31 |
32 | import org.gradle.api.GradleException
33 | import org.gradle.api.Project
34 |
35 | import spock.lang.Specification
36 | import spock.lang.Unroll
37 |
38 | class StrategiesSpec extends Specification {
39 | SemVerStrategyState initialState = new SemVerStrategyState([:])
40 | @Unroll('Normal.USE_SCOPE_PROP with scope #scope results in #version')
41 | def 'Normal.USE_SCOPE_PROP'() {
42 | def nearestVersion = new NearestVersion(normal: Version.valueOf('1.2.3'))
43 | def initialState = new SemVerStrategyState(
44 | scopeFromProp: scope,
45 | nearestVersion: nearestVersion)
46 | expect:
47 | Strategies.Normal.USE_SCOPE_PROP.infer(initialState) ==
48 | new SemVerStrategyState(
49 | scopeFromProp: scope,
50 | nearestVersion: nearestVersion,
51 | inferredNormal: version)
52 | where:
53 | scope | version
54 | ChangeScope.PATCH | '1.2.4'
55 | ChangeScope.MINOR | '1.3.0'
56 | ChangeScope.MAJOR | '2.0.0'
57 | }
58 |
59 | def 'Normal.USE_NEAREST_ANY with different nearest any and normal uses any\'s normal'() {
60 | given:
61 | def nearestVersion = new NearestVersion(
62 | any: Version.valueOf('1.2.5-beta.1'),
63 | normal: Version.valueOf('1.2.4'))
64 | def initialState = new SemVerStrategyState(nearestVersion: nearestVersion)
65 | expect:
66 | Strategies.Normal.USE_NEAREST_ANY.infer(initialState) ==
67 | new SemVerStrategyState(nearestVersion: nearestVersion, inferredNormal: '1.2.5')
68 | }
69 |
70 |
71 | def 'Normal.USE_NEAREST_ANY with same nearest any and normal does nothing'() {
72 | given:
73 | def initialState = new SemVerStrategyState(
74 | nearestVersion: new NearestVersion(
75 | any: Version.valueOf('1.2.4'),
76 | normal: Version.valueOf('1.2.4')))
77 | expect:
78 | Strategies.Normal.USE_NEAREST_ANY.infer(initialState) == initialState
79 | }
80 |
81 | def 'Normal.ENFORCE_BRANCH_MAJOR_X fails if nearest normal can\'t be incremented to something that complies with branch'() {
82 | given:
83 | def initialState = new SemVerStrategyState(
84 | nearestVersion: new NearestVersion(normal: Version.valueOf('1.2.3')),
85 | currentBranch: new Branch(fullName: 'refs/heads/3.x'))
86 | when:
87 | Strategies.Normal.ENFORCE_BRANCH_MAJOR_X.infer(initialState)
88 | then:
89 | thrown(GradleException)
90 | where:
91 | version << ['1.2.3', '3.1.0', '4.0.0']
92 | }
93 |
94 | def 'Normal.ENFORCE_BRANCH_MAJOR_X correctly increments version to comply with branch'() {
95 | given:
96 | def initialState = new SemVerStrategyState(
97 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
98 | currentBranch: new Branch(fullName: 'refs/heads/3.x'))
99 | expect:
100 | Strategies.Normal.ENFORCE_BRANCH_MAJOR_X.infer(initialState) ==
101 | initialState.copyWith(inferredNormal: inferred)
102 | where:
103 | nearest | inferred
104 | '2.0.0' | '3.0.0'
105 | '2.3.0' | '3.0.0'
106 | '2.3.4' | '3.0.0'
107 | '3.0.0' | null
108 | '3.1.0' | null
109 | '3.1.2' | null
110 | }
111 |
112 | def 'Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_X correctly increments version to comply with branch'() {
113 | given:
114 | def initialState = new SemVerStrategyState(
115 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
116 | currentBranch: new Branch(fullName: "refs/heads/${branch}"))
117 | expect:
118 | Strategies.Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_X.infer(initialState) ==
119 | initialState.copyWith(inferredNormal: inferred)
120 | where:
121 | branch | nearest | inferred
122 | 'release/3.x' | '2.0.0' | '3.0.0'
123 | 'release-3.x' | '2.3.4' | '3.0.0'
124 | 'release-3.x' | '3.0.0' | null
125 | 'release/3.x' | '3.1.0' | null
126 | }
127 |
128 | def 'Normal.ENFORCE_BRANCH_MAJOR_MINOR_X fails if nearest normal can\'t be incremented to something that complies with branch'() {
129 | given:
130 | def initialState = new SemVerStrategyState(
131 | nearestVersion: new NearestVersion(normal: Version.valueOf(version)),
132 | currentBranch: new Branch(fullName: 'refs/heads/3.2.x'))
133 | when:
134 | Strategies.Normal.ENFORCE_BRANCH_MAJOR_MINOR_X.infer(initialState)
135 | then:
136 | thrown(GradleException)
137 | where:
138 | version << ['1.2.3', '3.0.0', '3.3.0', '4.2.0']
139 | }
140 |
141 | def 'Normal.ENFORCE_BRANCH_MAJOR_MINOR_X correctly increments version to comply with branch'() {
142 | given:
143 | def initialState = new SemVerStrategyState(
144 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
145 | currentBranch: new Branch(fullName: "refs/heads/${branch}"))
146 | expect:
147 | Strategies.Normal.ENFORCE_BRANCH_MAJOR_MINOR_X.infer(initialState) ==
148 | initialState.copyWith(inferredNormal: inferred)
149 | where:
150 | branch | nearest | inferred
151 | '3.0.x' | '2.0.0' | '3.0.0'
152 | '3.0.x' | '2.1.0' | '3.0.0'
153 | '3.0.x' | '2.1.2' | '3.0.0'
154 | '3.0.x' | '3.0.0' | '3.0.1'
155 | '3.0.x' | '3.0.1' | '3.0.2'
156 | '3.2.x' | '3.1.0' | '3.2.0'
157 | '3.2.x' | '3.1.2' | '3.2.0'
158 | '3.2.x' | '3.2.0' | '3.2.1'
159 | '3.2.x' | '3.2.1' | '3.2.2'
160 | }
161 |
162 | def 'Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_MINOR_X correctly increments version to comply with branch'() {
163 | given:
164 | def initialState = new SemVerStrategyState(
165 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
166 | currentBranch: new Branch(fullName: "refs/heads/${branch}"))
167 | expect:
168 | Strategies.Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_MINOR_X.infer(initialState) ==
169 | initialState.copyWith(inferredNormal: inferred)
170 | where:
171 | branch | nearest | inferred
172 | 'release/3.0.x' | '2.0.0' | '3.0.0'
173 | 'release-3.0.x' | '3.0.0' | '3.0.1'
174 | 'release-3.2.x' | '3.1.2' | '3.2.0'
175 | 'release/3.2.x' | '3.2.1' | '3.2.2'
176 | }
177 |
178 | def 'Normal.ENFORCE_BRANCH_MAJOR_MINOR_X correctly increments version to comply with branch if normalStrategy is to increment minor instead of patch'() {
179 | given:
180 | def initialState = new SemVerStrategyState(
181 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
182 | currentBranch: new Branch(fullName: "refs/heads/${branch}"))
183 | def strategy = StrategyUtil.one(Strategies.Normal.ENFORCE_BRANCH_MAJOR_MINOR_X, Strategies.Normal.useScope(ChangeScope.MINOR))
184 | expect:
185 | strategy.infer(initialState) == initialState.copyWith(inferredNormal: inferred)
186 | where:
187 | branch | nearest | inferred
188 | '3.0.x' | '2.0.0' | '3.0.0'
189 | '3.0.x' | '2.1.0' | '3.0.0'
190 | '3.0.x' | '2.1.2' | '3.0.0'
191 | '3.0.x' | '3.0.0' | '3.0.1'
192 | '3.0.x' | '3.0.1' | '3.0.2'
193 | '3.2.x' | '3.1.0' | '3.2.0'
194 | '3.2.x' | '3.1.2' | '3.2.0'
195 | '3.2.x' | '3.2.0' | '3.2.1'
196 | '3.2.x' | '3.2.1' | '3.2.2'
197 | }
198 |
199 | def 'Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_MINOR_X correctly increments version to comply with branch if normalStrategy is to increment minor instead of patch'() {
200 | given:
201 | def initialState = new SemVerStrategyState(
202 | nearestVersion: new NearestVersion(normal: Version.valueOf(nearest)),
203 | currentBranch: new Branch(fullName: "refs/heads/${branch}"))
204 | def strategy = StrategyUtil.one(Strategies.Normal.ENFORCE_GITFLOW_BRANCH_MAJOR_MINOR_X, Strategies.Normal.useScope(ChangeScope.MINOR))
205 | expect:
206 | strategy.infer(initialState) == initialState.copyWith(inferredNormal: inferred)
207 | where:
208 | branch | nearest | inferred
209 | 'release/3.0.x' | '2.0.0' | '3.0.0'
210 | 'release-3.0.x' | '3.0.0' | '3.0.1'
211 | 'release-3.2.x' | '3.1.2' | '3.2.0'
212 | 'release/3.2.x' | '3.2.1' | '3.2.2'
213 | }
214 |
215 | def 'Normal.useScope correctly increments normal'() {
216 | given:
217 | def initialState = new SemVerStrategyState(
218 | nearestVersion: new NearestVersion(normal: Version.valueOf('1.2.3')))
219 | expect:
220 | Strategies.Normal.useScope(scope).infer(initialState) == initialState.copyWith(inferredNormal: inferred)
221 | where:
222 | scope | inferred
223 | ChangeScope.PATCH | '1.2.4'
224 | ChangeScope.MINOR | '1.3.0'
225 | ChangeScope.MAJOR | '2.0.0'
226 | }
227 |
228 | def 'PreRelease.NONE does nothing'() {
229 | expect:
230 | Strategies.PreRelease.NONE.infer(new SemVerStrategyState([:])) == new SemVerStrategyState([:])
231 | }
232 |
233 | def 'PreRelease.STAGE_FIXED always replaces inferredPreRelease with stageFromProp'() {
234 | given:
235 | def initialState = new SemVerStrategyState(
236 | stageFromProp: 'boom',
237 | inferredPreRelease: initialPreRelease)
238 | expect:
239 | Strategies.PreRelease.STAGE_FIXED.infer(initialState) == initialState.copyWith(inferredPreRelease: 'boom')
240 | where:
241 | initialPreRelease << [null, 'other']
242 | }
243 |
244 | def 'PreRelease.STAGE_FLOAT will append the stageFromProp to the nearest any\'s pre release, if any, unless it has higher precedence'() {
245 | given:
246 | def initialState = new SemVerStrategyState(
247 | stageFromProp: 'boom',
248 | inferredNormal: '1.1.0',
249 | inferredPreRelease: 'other',
250 | nearestVersion: new NearestVersion(
251 | normal: Version.valueOf('1.0.0'),
252 | any: Version.valueOf(nearest)))
253 | expect:
254 | Strategies.PreRelease.STAGE_FLOAT.infer(initialState) == initialState.copyWith(inferredPreRelease: expected)
255 | where:
256 | nearest | expected
257 | '1.0.0' | 'boom'
258 | '1.0.1-cat.something.else' | 'boom'
259 | '1.1.0-and.1' | 'boom'
260 | '1.1.0-cat.1' | 'cat.1.boom'
261 | '1.1.0-cat.something.else' | 'cat.something.else.boom'
262 | }
263 |
264 | def 'PreRelease.COUNT_INCREMENTED will increment the nearest any\'s pre release or set to 1 if not found'() {
265 | given:
266 | def initialState = new SemVerStrategyState(
267 | inferredNormal: '1.1.0',
268 | inferredPreRelease: initialPreRelease,
269 | nearestVersion: new NearestVersion(
270 | normal: Version.valueOf('1.0.0'),
271 | any: Version.valueOf(nearestAny)))
272 | expect:
273 | Strategies.PreRelease.COUNT_INCREMENTED.infer(initialState) == initialState.copyWith(inferredPreRelease: expected)
274 | where:
275 | nearestAny | initialPreRelease | expected
276 | '1.0.0' | null | '1'
277 | '1.0.0' | 'other' | 'other.1'
278 | '1.0.1-beta.1' | 'other' | 'other.1'
279 | '2.0.0-beta.1' | 'other' | 'other.1'
280 | '1.1.0-beta.1' | 'other' | 'other.1'
281 | '1.1.0-beta.1' | 'beta' | 'beta.2'
282 | '1.1.0-beta.99' | 'beta' | 'beta.100'
283 | '1.1.0-beta' | 'beta' | 'beta.1'
284 | '1.1.0-beta.1' | 'beta.1.alpha' | 'beta.1.alpha.1'
285 | '1.1.0-beta.1' | 'beta.1.alpha' | 'beta.1.alpha.1'
286 | '1.1.0-beta.2.alpha' | 'beta' | 'beta.3'
287 | }
288 |
289 | def 'PreRelease.COUNT_COMMITS_SINCE_ANY will append distanceFromAny'() {
290 | given:
291 | def initialState = new SemVerStrategyState(
292 | inferredPreRelease: initialPreRelease,
293 | nearestVersion: new NearestVersion(distanceFromAny: distance))
294 | expect:
295 | Strategies.PreRelease.COUNT_COMMITS_SINCE_ANY.infer(initialState) == initialState.copyWith(inferredPreRelease: expected)
296 | where:
297 | initialPreRelease | distance | expected
298 | null | 0 | '0'
299 | null | 54 | '54'
300 | 'other' | 0 | 'other.0'
301 | 'other' | 54 | 'other.54'
302 | }
303 |
304 | def 'PreRelease.SHOW_UNCOMMITTED appends uncommitted only if repo is dirty'() {
305 | given:
306 | def initialState = new SemVerStrategyState(
307 | inferredPreRelease: initialPreRelease,
308 | repoDirty: dirty)
309 | expect:
310 | Strategies.PreRelease.SHOW_UNCOMMITTED.infer(initialState) == initialState.copyWith(inferredPreRelease: expected)
311 | where:
312 | initialPreRelease | dirty | expected
313 | null | false | null
314 | null | true | 'uncommitted'
315 | 'other' | false | 'other'
316 | 'other' | true | 'other.uncommitted'
317 | }
318 |
319 | def 'BuildMetadata.NONE does nothing'() {
320 | expect:
321 | Strategies.BuildMetadata.NONE.infer(new SemVerStrategyState([:])) == new SemVerStrategyState([:])
322 | }
323 |
324 | def 'BuildMetadata.COMMIT_ABBREVIATED_ID uses current HEAD\'s abbreviated id'() {
325 | given:
326 | def initialState = new SemVerStrategyState(currentHead: new Commit(id: '5e9b2a1e98b5670a90a9ed382a35f0d706d5736c'))
327 | expect:
328 | Strategies.BuildMetadata.COMMIT_ABBREVIATED_ID.infer(initialState) ==
329 | initialState.copyWith(inferredBuildMetadata: '5e9b2a1')
330 | }
331 |
332 | def 'BuildMetadata.COMMIT_FULL_ID uses current HEAD\'s abbreviated id'() {
333 | given:
334 | def id = '5e9b2a1e98b5670a90a9ed382a35f0d706d5736c'
335 | def initialState = new SemVerStrategyState(currentHead: new Commit(id: id))
336 | expect:
337 | Strategies.BuildMetadata.COMMIT_FULL_ID.infer(initialState) ==
338 | initialState.copyWith(inferredBuildMetadata: id)
339 | }
340 |
341 | def 'BuildMetadata.TIMESTAMP uses current time'() {
342 | expect:
343 | def newState = Strategies.BuildMetadata.TIMESTAMP.infer(new SemVerStrategyState([:]))
344 | def metadata = newState.inferredBuildMetadata
345 | metadata ==~ /\d{4}\.\d{2}\.\d{2}\.\d{2}\.\d{2}\.\d{2}/
346 | newState == new SemVerStrategyState(inferredBuildMetadata: metadata)
347 | }
348 |
349 | def 'SNAPSHOT works as expected'() {
350 | given:
351 | def project = mockProject(scope, stage)
352 | def grgit = mockGrgit(repoDirty)
353 | def locator = mockLocator(nearestNormal, nearestAny)
354 | expect:
355 | Strategies.SNAPSHOT.doInfer(project, grgit, locator) == new ReleaseVersion(expected, nearestNormal, false)
356 | where:
357 | scope | stage | nearestNormal | nearestAny | repoDirty | expected
358 | null | null | '1.0.0' | '1.0.0' | false | '1.0.1-SNAPSHOT'
359 | null | null | '1.0.0' | '1.0.0' | true | '1.0.1-SNAPSHOT'
360 | null | 'SNAPSHOT' | '1.0.0' | '1.1.0-beta' | true | '1.1.0-SNAPSHOT'
361 | null | 'SNAPSHOT' | '1.0.0' | '1.1.0-zed' | true | '1.1.0-SNAPSHOT'
362 | 'PATCH' | 'SNAPSHOT' | '1.0.0' | '1.1.0-zed' | true | '1.0.1-SNAPSHOT'
363 | 'MINOR' | 'SNAPSHOT' | '1.0.0' | '1.1.0-zed' | true | '1.1.0-SNAPSHOT'
364 | 'MAJOR' | 'SNAPSHOT' | '1.0.0' | '1.1.0-zed' | true | '2.0.0-SNAPSHOT'
365 | }
366 |
367 | def 'DEVELOPMENT works as expected'() {
368 | given:
369 | def project = mockProject(scope, stage)
370 | def grgit = mockGrgit(repoDirty)
371 | def locator = mockLocator(nearestNormal, nearestAny)
372 | expect:
373 | Strategies.DEVELOPMENT.doInfer(project, grgit, locator) == new ReleaseVersion(expected, nearestNormal, false)
374 | where:
375 | scope | stage | nearestNormal | nearestAny | repoDirty | expected
376 | null | null | '1.0.0' | '1.0.0' | false | '1.0.1-dev.2+5e9b2a1'
377 | null | null | '1.0.0' | '1.0.0' | true | '1.0.1-dev.2.uncommitted+5e9b2a1'
378 | null | null | '1.0.0' | '1.1.0-alpha.1' | false | '1.1.0-dev.2+5e9b2a1'
379 | null | null | '1.0.0' | '1.1.0-rc.3' | false | '1.1.0-rc.3.dev.2+5e9b2a1'
380 | null | null | '1.0.0' | '1.1.0-rc.3' | true | '1.1.0-rc.3.dev.2.uncommitted+5e9b2a1'
381 | 'PATCH' | 'dev' | '1.0.0' | '1.0.0' | false | '1.0.1-dev.2+5e9b2a1'
382 | 'MINOR' | 'dev' | '1.0.0' | '1.0.0' | false | '1.1.0-dev.2+5e9b2a1'
383 | 'MAJOR' | 'dev' | '1.0.0' | '1.0.0' | false | '2.0.0-dev.2+5e9b2a1'
384 | }
385 |
386 | def 'PRE_RELEASE works as expected'() {
387 | def project = mockProject(scope, stage)
388 | def grgit = mockGrgit(repoDirty)
389 | def locator = mockLocator(nearestNormal, nearestAny)
390 | expect:
391 | Strategies.PRE_RELEASE.doInfer(project, grgit, locator) == new ReleaseVersion(expected, nearestNormal, true)
392 | where:
393 | scope | stage | nearestNormal | nearestAny | repoDirty | expected
394 | null | null | '1.0.0' | '1.0.0' | false | '1.0.1-milestone.1'
395 | null | 'milestone' | '1.0.0' | '1.0.0' | false | '1.0.1-milestone.1'
396 | null | 'rc' | '1.0.0' | '1.0.0' | false | '1.0.1-rc.1'
397 | 'PATCH' | 'milestone' | '1.0.0' | '1.0.0' | false | '1.0.1-milestone.1'
398 | 'MINOR' | 'milestone' | '1.0.0' | '1.0.0' | false | '1.1.0-milestone.1'
399 | 'MAJOR' | 'milestone' | '1.0.0' | '1.0.0' | false | '2.0.0-milestone.1'
400 | null | 'rc' | '1.0.0' | '1.1.0-milestone.1' | false | '1.1.0-rc.1'
401 | null | 'milestone' | '1.0.0' | '1.1.0-milestone.1' | false | '1.1.0-milestone.2'
402 | null | 'rc' | '1.0.0' | '1.1.0-rc' | false | '1.1.0-rc.1'
403 | null | 'rc' | '1.0.0' | '1.1.0-rc.4.dev.1' | false | '1.1.0-rc.5'
404 | }
405 |
406 | def 'Strategies.FINAL works as expected'() {
407 | def project = mockProject(scope, stage)
408 | def grgit = mockGrgit(repoDirty)
409 | def locator = mockLocator(nearestNormal, nearestAny)
410 | expect:
411 | Strategies.FINAL.doInfer(project, grgit, locator) == new ReleaseVersion(expected, nearestNormal, true)
412 | where:
413 | scope | stage | nearestNormal | nearestAny | repoDirty | expected
414 | null | null | '1.0.0' | '1.0.0' | false | '1.0.1'
415 | 'PATCH' | null | '1.0.0' | '1.0.0' | false | '1.0.1'
416 | 'MINOR' | null | '1.0.0' | '1.0.0' | false | '1.1.0'
417 | 'MAJOR' | null | '1.0.0' | '1.0.0' | false | '2.0.0'
418 | 'MAJOR' | 'final' | '1.0.0' | '1.0.0' | false | '2.0.0'
419 | 'MINOR' | 'final' | '1.0.0' | '1.1.0-alpha.2' | false | '1.1.0'
420 | }
421 |
422 | def 'PRE_RELEASE_ALPHA_BETA works as expected'() {
423 | def project = mockProject(scope, stage)
424 | def grgit = mockGrgit(repoDirty)
425 | def locator = mockLocator(nearestNormal, nearestAny)
426 | expect:
427 | Strategies.PRE_RELEASE_ALPHA_BETA.doInfer(project, grgit, locator) == new ReleaseVersion(expected, nearestNormal, true)
428 | where:
429 | scope | stage | nearestNormal | nearestAny | repoDirty | expected
430 | null | null | '1.0.0' | '1.0.0' | false | '1.0.1-alpha.1'
431 | null | 'alpha' | '1.0.0' | '1.0.0' | false | '1.0.1-alpha.1'
432 | null | 'beta' | '1.0.0' | '1.0.0' | false | '1.0.1-beta.1'
433 | null | 'rc' | '1.0.0' | '1.0.0' | false | '1.0.1-rc.1'
434 | 'PATCH' | 'alpha' | '1.0.0' | '1.0.0' | false | '1.0.1-alpha.1'
435 | 'MINOR' | 'alpha' | '1.0.0' | '1.0.0' | false | '1.1.0-alpha.1'
436 | 'MAJOR' | 'alpha' | '1.0.0' | '1.0.0' | false | '2.0.0-alpha.1'
437 | 'PATCH' | 'beta' | '1.0.0' | '1.0.0' | false | '1.0.1-beta.1'
438 | 'MINOR' | 'beta' | '1.0.0' | '1.0.0' | false | '1.1.0-beta.1'
439 | 'MAJOR' | 'beta' | '1.0.0' | '1.0.0' | false | '2.0.0-beta.1'
440 | null | 'rc' | '1.0.0' | '1.1.0-beta.1' | false | '1.1.0-rc.1'
441 | null | 'beta' | '1.0.0' | '1.1.0-beta.1' | false | '1.1.0-beta.2'
442 | null | 'rc' | '1.0.0' | '1.1.0-rc' | false | '1.1.0-rc.1'
443 | null | 'rc' | '1.0.0' | '1.1.0-rc.4.dev.1' | false | '1.1.0-rc.5'
444 | }
445 |
446 | def mockProject(String scope, String stage) {
447 | Project project = Mock()
448 |
449 | project.hasProperty('release.scope') >> (scope as boolean)
450 | project.property('release.scope') >> scope
451 |
452 | project.hasProperty('release.stage') >> (stage as boolean)
453 | project.property('release.stage') >> stage
454 |
455 | return project
456 | }
457 |
458 | def mockGrgit(boolean repoDirty, String branchName = 'master') {
459 | Grgit grgit = GroovyMock()
460 |
461 | Status status = Mock()
462 | status.clean >> !repoDirty
463 | grgit.status() >> status
464 |
465 | grgit.head() >> new Commit(id: '5e9b2a1e98b5670a90a9ed382a35f0d706d5736c')
466 |
467 | BranchService branch = GroovyMock()
468 | branch.current >> new Branch(fullName: "refs/heads/${branchName}")
469 | grgit.branch >> branch
470 |
471 | return grgit
472 | }
473 |
474 | def mockLocator(String nearestNormal, String nearestAny) {
475 | NearestVersionLocator locator = Mock()
476 | locator.locate(_) >> new NearestVersion(
477 | normal: Version.valueOf(nearestNormal),
478 | distanceFromNormal: 5,
479 | any: Version.valueOf(nearestAny),
480 | distanceFromAny: 2
481 | )
482 | return locator
483 | }
484 | }
485 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/semver/NearestVersionLocatorSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import com.github.zafarkhaja.semver.Version
19 | import org.ajoberstar.gradle.git.release.base.TagStrategy
20 | import org.ajoberstar.grgit.Grgit
21 | import spock.lang.Shared
22 | import spock.lang.Specification
23 | import spock.lang.Unroll
24 |
25 | import java.nio.file.Files
26 | import java.security.SecureRandom
27 |
28 | class NearestVersionLocatorSpec extends Specification {
29 | @Shared File repoDir
30 |
31 | @Shared Grgit grgit
32 |
33 | @Shared SecureRandom random = new SecureRandom()
34 |
35 | def setupSpec() {
36 | repoDir = Files.createTempDirectory('repo').toFile()
37 | grgit = Grgit.init(dir: repoDir)
38 |
39 | commit()
40 | commit()
41 | addBranch('unreachable')
42 |
43 | commit()
44 | addTag('0.0.1-beta.3')
45 | addBranch('no-normal')
46 |
47 | commit()
48 | addTag('0.1.0')
49 |
50 | commit()
51 | addBranch('RB_0.1')
52 |
53 | commit()
54 | commit()
55 | addTag('0.2.0')
56 |
57 | checkout('RB_0.1')
58 |
59 | commit()
60 | addTag('v0.1.1+2010.01.01.12.00.00')
61 |
62 | commit()
63 | commit()
64 | commit()
65 | commit()
66 | addTag('v0.1.2-beta.1')
67 | addTag('v0.1.2-alpha.1')
68 | addTag('not-a-version')
69 |
70 | commit()
71 | commit()
72 | commit()
73 | checkout('master')
74 |
75 | commit()
76 | addTag('v1.0.0')
77 | addTag('v1.0.0-rc.3')
78 | addBranch('RB_1.0')
79 |
80 | commit()
81 | addTag('1.1.0-rc.1+abcde')
82 | addTag('also-not-a-version')
83 |
84 | addBranch('test')
85 | checkout('test')
86 | commit()
87 | addTag('2.0.0-rc.1')
88 | addBranch('REL_2.1')
89 | commit()
90 | commit()
91 | checkout('REL_2.1')
92 | commit('2.txt')
93 | addTag('2.1.0-rc.1')
94 | checkout('test')
95 | merge('REL_2.1')
96 | }
97 |
98 | def cleanupSpec() {
99 | assert !repoDir.exists() || repoDir.deleteDir()
100 | }
101 |
102 | @Unroll('when on #head, locator finds normal #normal with nearest #any')
103 | def 'locator returns correct value'() {
104 | given:
105 | grgit.checkout(branch: head)
106 | expect:
107 | def nearest = new NearestVersionLocator(new TagStrategy()).locate(grgit)
108 | nearest.any == Version.valueOf(any)
109 | nearest.normal == Version.valueOf(normal)
110 | nearest.distanceFromAny == anyDistance
111 | nearest.distanceFromNormal == normalDistance
112 | where:
113 | head | any | normal | anyDistance | normalDistance
114 | 'master' | '1.1.0-rc.1+abcde' | '1.0.0' | 0 | 1
115 | 'RB_0.1' | '0.1.2-beta.1' | '0.1.1+2010.01.01.12.00.00' | 3 | 7
116 | 'RB_1.0' | '1.0.0' | '1.0.0' | 0 | 0
117 | 'no-normal' | '0.0.1-beta.3' | '0.0.0' | 0 | 3
118 | 'unreachable' | '0.0.0' | '0.0.0' | 2 | 2
119 | 'test' | '2.1.0-rc.1' | '1.0.0' | 3 | 6
120 | }
121 |
122 | private void commit(String name = '1.txt') {
123 | byte[] bytes = new byte[128]
124 | random.nextBytes(bytes)
125 | new File(grgit.repository.rootDir, name) << bytes
126 | grgit.add(patterns: [name])
127 | def commit = grgit.commit(message: 'do')
128 | println "Created commit: ${commit.abbreviatedId}"
129 | }
130 |
131 | private void addBranch(String name) {
132 | def currentHead = grgit.head()
133 | def currentBranch = grgit.branch.current
134 | def newBranch = grgit.branch.add(name: name)
135 | def atCommit = grgit.resolve.toCommit(newBranch.fullName)
136 | println "Added new branch ${name} at ${atCommit.abbreviatedId}"
137 | assert currentBranch == grgit.branch.current
138 | assert currentHead == atCommit
139 | }
140 |
141 | private void addTag(String name) {
142 | def currentHead = grgit.head()
143 | def newTag = grgit.tag.add(name: name)
144 | def atCommit = grgit.resolve.toCommit(newTag.fullName)
145 | println "Added new tag ${name} at ${atCommit.abbreviatedId}"
146 | assert currentHead == atCommit
147 | }
148 |
149 | private void checkout(String name) {
150 | def currentHead = grgit.head()
151 | grgit.checkout(branch: name)
152 | def atCommit = grgit.resolve.toCommit(name)
153 | def newHead = grgit.head()
154 | println "Checkout out ${name}, which is at ${atCommit.abbreviatedId}"
155 | assert atCommit == newHead
156 | assert name == grgit.branch.current.name
157 | }
158 |
159 | private void merge(String name) {
160 | def currentHead = grgit.head()
161 | grgit.merge(head: name)
162 | println "Merged ${name}, now at ${grgit.head().abbreviatedId}"
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/semver/RebuildVersionStrategySpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
19 | import org.ajoberstar.grgit.Commit
20 | import org.ajoberstar.grgit.Grgit
21 | import org.ajoberstar.grgit.Status
22 | import org.ajoberstar.grgit.Tag
23 | import org.ajoberstar.grgit.service.TagService
24 |
25 | import org.gradle.api.Project
26 | import org.gradle.testfixtures.ProjectBuilder
27 | import spock.lang.Specification
28 |
29 | class RebuildVersionStrategySpec extends Specification {
30 | RebuildVersionStrategy strategy = new RebuildVersionStrategy()
31 | Grgit grgit = GroovyMock()
32 |
33 | def getProject(Map properties) {
34 | Project p = ProjectBuilder.builder().withName("testproject").build()
35 | p.apply plugin: "org.ajoberstar.release-base"
36 | properties.each { k, v ->
37 | p.ext[k] = v
38 | }
39 | p
40 | }
41 |
42 | def 'selector returns false if repo is dirty'() {
43 | given:
44 | mockClean(false)
45 | Project project = getProject([:])
46 | mockTagsAtHead('v1.0.0')
47 | expect:
48 | !strategy.selector(project, grgit)
49 | }
50 |
51 | def 'selector returns false if any release properties are set'() {
52 | given:
53 | mockClean(true)
54 | Project project = getProject('release.scope': 'value')
55 | mockTagsAtHead('v1.0.0')
56 | expect:
57 | !strategy.selector(project, grgit)
58 | }
59 |
60 | def 'selector returns false if no version tag at HEAD'() {
61 | given:
62 | mockClean(true)
63 | Project project = getProject([:])
64 | mockTagsAtHead('non-version-tag')
65 | expect:
66 | !strategy.selector(project, grgit)
67 | }
68 |
69 | def 'selector returns true if rebuild is attempted'() {
70 | given:
71 | mockClean(true)
72 | Project project = getProject([:])
73 | mockTagsAtHead('v0.1.1', 'v1.0.0', '0.19.1')
74 | expect:
75 | strategy.selector(project, grgit)
76 | }
77 |
78 | def 'infer returns HEAD version is inferred and previous with create tag false'() {
79 | given:
80 | mockClean(true)
81 | Project project = getProject([:])
82 | mockTagsAtHead('v0.1.1', 'v1.0.0', '0.19.1')
83 | expect:
84 | strategy.infer(project, grgit) == new ReleaseVersion('1.0.0', '1.0.0', false)
85 | }
86 |
87 | private void mockTagsAtHead(String... tagNames) {
88 | Commit head = new Commit()
89 | grgit.head() >> head
90 | TagService tag = GroovyMock()
91 | grgit.tag >> tag
92 | tag.list() >> tagNames.collect { new Tag(commit: head, fullName: "refs/heads/${it}") }
93 | }
94 |
95 | private void mockClean(boolean clean) {
96 | Status status = GroovyMock()
97 | grgit.status() >> status
98 | status.clean >> clean
99 | }
100 |
101 | private void mockProperties(Map props) {
102 | project.properties.clear()
103 | project.properties.putAll(props)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/semver/SemVerStrategySpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import com.github.zafarkhaja.semver.Version
19 |
20 | import org.ajoberstar.gradle.git.release.base.ReleaseVersion
21 | import org.ajoberstar.grgit.Branch
22 | import org.ajoberstar.grgit.Grgit
23 | import org.ajoberstar.grgit.Status
24 | import org.ajoberstar.grgit.service.BranchService
25 |
26 | import org.gradle.api.GradleException
27 | import org.gradle.api.Project
28 |
29 | import spock.lang.Specification
30 |
31 | class SemVerStrategySpec extends Specification {
32 | Project project = GroovyMock()
33 | Grgit grgit = GroovyMock()
34 |
35 | def 'selector returns false if stage is not set to valid value'() {
36 | given:
37 | def strategy = new SemVerStrategy(stages: ['one', 'two'] as SortedSet)
38 | mockStage(stageProp)
39 | expect:
40 | !strategy.selector(project, grgit)
41 | where:
42 | stageProp << [null, 'test']
43 | }
44 |
45 |
46 | def 'selector returns false if repo is dirty and not allowed to be'() {
47 | given:
48 | def strategy = new SemVerStrategy(stages: ['one'] as SortedSet, allowDirtyRepo: false)
49 | mockStage('one')
50 | mockRepoClean(false)
51 | expect:
52 | !strategy.selector(project, grgit)
53 | }
54 |
55 | def 'selector returns true if repo is dirty and allowed and other criteria met'() {
56 | given:
57 | def strategy = new SemVerStrategy(stages: ['one'] as SortedSet, allowDirtyRepo: true)
58 | mockStage('one')
59 | mockRepoClean(false)
60 | mockBranchService()
61 | expect:
62 | strategy.selector(project, grgit)
63 | }
64 |
65 | def 'selector returns true if all criteria met'() {
66 | given:
67 | def strategy = new SemVerStrategy(stages: ['one', 'and'] as SortedSet, allowDirtyRepo: false)
68 | mockStage('one')
69 | mockRepoClean(true)
70 | mockBranchService()
71 | expect:
72 | strategy.selector(project, grgit)
73 | }
74 |
75 | def 'default selector returns false if stage is defined but not set to valid value'() {
76 | given:
77 | def strategy = new SemVerStrategy(stages: ['one', 'two'] as SortedSet)
78 | mockStage('test')
79 | expect:
80 | !strategy.defaultSelector(project, grgit)
81 | }
82 |
83 | def 'default selector returns true if stage is not defined'() {
84 | given:
85 | def strategy = new SemVerStrategy(stages: ['one', 'two'] as SortedSet)
86 | mockStage(null)
87 | mockRepoClean(true)
88 | expect:
89 | strategy.defaultSelector(project, grgit)
90 | }
91 |
92 | def 'default selector returns false if repo is dirty and not allowed to be'() {
93 | given:
94 | def strategy = new SemVerStrategy(stages: ['one'] as SortedSet, allowDirtyRepo: false)
95 | mockStage(stageProp)
96 | mockRepoClean(false)
97 | expect:
98 | !strategy.defaultSelector(project, grgit)
99 | where:
100 | stageProp << [null, 'one']
101 | }
102 |
103 | def 'default selector returns true if repo is dirty and allowed and other criteria met'() {
104 | given:
105 | def strategy = new SemVerStrategy(stages: ['one'] as SortedSet, allowDirtyRepo: true)
106 | mockStage('one')
107 | mockRepoClean(false)
108 | mockBranchService()
109 | expect:
110 | strategy.defaultSelector(project, grgit)
111 | }
112 |
113 | def 'default selector returns true if all criteria met'() {
114 | given:
115 | def strategy = new SemVerStrategy(stages: ['one', 'and'] as SortedSet, allowDirtyRepo: false)
116 | mockStage('one')
117 | mockRepoClean(true)
118 | mockBranchService()
119 | expect:
120 | strategy.defaultSelector(project, grgit)
121 | }
122 |
123 | def 'infer returns correct version'() {
124 | given:
125 | mockScope(scope)
126 | mockStage(stage)
127 | mockRepoClean(false)
128 | mockBranchService()
129 | def nearest = new NearestVersion(
130 | normal: Version.valueOf('1.2.2'),
131 | any: Version.valueOf(nearestAny))
132 | def locator = mockLocator(nearest)
133 | def strategy = mockStrategy(scope, stage, nearest, createTag, enforcePrecedence)
134 | expect:
135 | strategy.doInfer(project, grgit, locator) == new ReleaseVersion('1.2.3-beta.1+abc123', '1.2.2', createTag)
136 | where:
137 | scope | stage | nearestAny | createTag | enforcePrecedence
138 | 'patch' | 'one' | '1.2.3' | true | false
139 | 'minor' | 'one' | '1.2.2' | true | true
140 | 'major' | 'one' | '1.2.2' | false | true
141 | 'patch' | null | '1.2.2' | false | true
142 | }
143 |
144 | def 'infer fails if stage is not listed in stages property'() {
145 | given:
146 | mockStage('other')
147 | def strategy = new SemVerStrategy(stages: ['one'] as SortedSet)
148 | when:
149 | strategy.doInfer(project, grgit, null)
150 | then:
151 | thrown(GradleException)
152 | }
153 |
154 | def 'infer fails if precedence enforced and violated'() {
155 | given:
156 | mockRepoClean(false)
157 | mockBranchService()
158 | def nearest = new NearestVersion(any: Version.valueOf('1.2.3'))
159 | def locator = mockLocator(nearest)
160 | def strategy = mockStrategy(null, 'and', nearest, false, true)
161 | when:
162 | strategy.doInfer(project, grgit, locator)
163 | then:
164 | thrown(GradleException)
165 | }
166 |
167 | private def mockScope(String scopeProp) {
168 | (0..1) * project.hasProperty('release.scope') >> (scopeProp as boolean)
169 | (0..1) * project.property('release.scope') >> scopeProp
170 | }
171 |
172 | private def mockStage(String stageProp) {
173 | (0..1) * project.hasProperty('release.stage') >> (stageProp as boolean)
174 | (0..1) * project.property('release.stage') >> stageProp
175 | }
176 |
177 | private def mockRepoClean(boolean isClean) {
178 | Status status = GroovyMock()
179 | (0..2) * status.clean >> isClean
180 | (0..2) * grgit.status() >> status
181 | 0 * status._
182 | }
183 |
184 | private def mockBranchService() {
185 | BranchService branchService = GroovyMock()
186 | (0..1) * branchService.current >> new Branch(fullName: 'refs/heads/master')
187 | (0..2) * grgit.getBranch() >> branchService
188 | 0 * branchService._
189 | }
190 |
191 | private def mockLocator(NearestVersion nearest) {
192 | NearestVersionLocator locator = Mock()
193 | locator.locate(grgit) >> nearest
194 | return locator
195 | }
196 |
197 | private def mockStrategy(String scope, String stage, NearestVersion nearest, boolean createTag, boolean enforcePrecedence) {
198 | PartialSemVerStrategy normal = Mock()
199 | PartialSemVerStrategy preRelease = Mock()
200 | PartialSemVerStrategy buildMetadata = Mock()
201 | SemVerStrategyState initial = new SemVerStrategyState([
202 | scopeFromProp: scope?.toUpperCase(),
203 | stageFromProp: stage ?: 'and',
204 | currentHead: null,
205 | currentBranch: new Branch(fullName: 'refs/heads/master'),
206 | repoDirty: true,
207 | nearestVersion: nearest])
208 | SemVerStrategyState afterNormal = initial.copyWith(inferredNormal: '1.2.3')
209 | SemVerStrategyState afterPreRelease = afterNormal.copyWith(inferredPreRelease: 'beta.1')
210 | SemVerStrategyState afterBuildMetadata = afterPreRelease.copyWith(inferredBuildMetadata: 'abc123')
211 |
212 | 1 * normal.infer(initial) >> afterNormal
213 | 1 * preRelease.infer(afterNormal) >> afterPreRelease
214 | 1 * buildMetadata.infer(afterPreRelease) >> afterBuildMetadata
215 | 0 * normal._
216 | 0 * preRelease._
217 | 0 * buildMetadata._
218 |
219 | return new SemVerStrategy(
220 | stages: ['one', 'and'] as SortedSet,
221 | normalStrategy: normal,
222 | preReleaseStrategy: preRelease,
223 | buildMetadataStrategy: buildMetadata,
224 | createTag: createTag,
225 | enforcePrecedence: enforcePrecedence
226 | )
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/src/test/groovy/org/ajoberstar/gradle/git/release/semver/StrategyUtilSpec.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012-2017 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.ajoberstar.gradle.git.release.semver
17 |
18 | import spock.lang.Specification
19 |
20 | class StrategyUtilSpec extends Specification {
21 | SemVerStrategyState initialState = new SemVerStrategyState([:])
22 |
23 | def 'closure backed uses behavior passed in'() {
24 | expect:
25 | stringReplace('a').infer(initialState) == new SemVerStrategyState(inferredPreRelease: 'a')
26 | }
27 |
28 | def 'choose one returns the first that changes state'() {
29 | given:
30 | def chain = StrategyUtil.one(nothing(), stringReplace('a'), stringReplace('b'), nothing())
31 | expect:
32 | chain.infer(initialState) == new SemVerStrategyState(inferredPreRelease: 'a')
33 | }
34 |
35 | def 'apply all uses all strategies in order'() {
36 | given:
37 | def chain = StrategyUtil.all(stringAppend('a'), stringAppend('b'), stringAppend('c'))
38 | expect:
39 | chain.infer(initialState) == new SemVerStrategyState(inferredPreRelease: 'a.b.c')
40 | }
41 |
42 | private def nothing() {
43 | return StrategyUtil.closure { state -> state }
44 | }
45 |
46 | private def stringReplace(String str) {
47 | return StrategyUtil.closure { state -> state.copyWith(inferredPreRelease: str) }
48 | }
49 |
50 | private def stringAppend(String str) {
51 | return StrategyUtil.closure { state ->
52 | def preRelease = state.inferredPreRelease ? "${state.inferredPreRelease}.${str}" : str
53 | return state.copyWith(inferredPreRelease: preRelease)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------