Set projects versions based on git tags and following semantic versioning.
Inspired on Reckon but centered on supporting multi-project
versions and combine normal stages with snapshot
stage.
The plugin must be applied individually to each project in order to support configuration cache and project isolation.
plugins {
id("com.javiersc.semver.gradle.plugin")
}
semver {
tagPrefix.set("v") // optional, default is empty
}
Default values:
tagPrefix | |
---|---|
default value | (empty) |
Optional | Yes |
Check documented examples or test examples to understand easily how it works.
There are three project properties which the plugin uses to detect automatically the current version
based on the last tag in the current branch: semver.stage
, semver.scope
and semver.tagPrefix
.
They can be set via CLI, for example:
./gradlew "-Psemver.stage=final" "-Psemver.scope=major" "-Psemver.tagPrefix=v"
semver.stage
indicates the stage to be changed, for example,alpha
semver.scope
indicates the scope to be changed, for example,patch
semver.tagPrefix
is used to know which version is going to be changed based on the tag prefix, for examplev
. If the projects have different tag prefix, it is necessary to disambiguate which version is going to be bumped.
Default values:
stage | scope | tagPrefix | |
---|---|---|---|
default value | auto |
auto |
(empty) |
Optional | Yes* | Yes* | Yes* |
Depends on the use case*
semver {
tagPrefix.set("")
}
It is used to asociate a project version with a tag prefix, and it allows having different versions in multi-project builds.
An example can be setting the extension prefix to v
in a specific project and the last tags in the
last commit are: 1.0.0
and v3.0.1
. The project version is v3.0.1
.
"-Psemver.tagPrefix=v"
semver.tagPrefix=v
If it is necessary to bump the project version, for example to v3.0.2
from v3.0.1
, but at same
time there are more project with different prefixes, the plugin needs to know which tag prefix is
going to be bumped, so semver.tagPrefix
property is the solution to that problem.
To get it working:
./gradlew "-Psemver.scope=patch" "-Psemver.tagPrefix=v"
If all projects are not using a tag prefix, or in other words, the tag prefix is empty, both the property in the extension and the project property via CLI or
gradle.properties
file are irrelevant.
If all projects share a tag prefix, it is easier to set it in the root project
gradle.properties
file instead of passing it constantly via CLI.
- Format:
<major>.<minor>.<patch>
- Example:
1.0.0
- Format:
<major>.<minor>.<patch>-<stage>.<num>
- Example:
1.0.0-alpha.1
- Format:
<major>.<minor>.<patch>-<stage>.<num>.<commits>+<hash or timestamp>
- Examples:
1.0.0.4+2021-11-11T14-22-03-207850300Z
1.0.0.4+26f0484
- Format:
<major>.<minor>.<patch>-SNAPSHOT
- Example:
1.0.0-SNAPSHOT
To change between stages, use the Gradle project property -Psemver.stage=<stage>
There are reserved stages that can be used to create certain versions:
final
: It creates a version without a suffix stage, for example,1.0.1
.auto
: It calculates automatically the next stage based on the previous stage.snapshot
: It generate the next snapshot version, for example,1.0.1-SNAPSHOT
.
# gradle.properties
semver.tagPrefix=v
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.stage=alpha" # v1.0.0-alpha.2
./gradlew "-Psemver.stage=beta" # v1.0.0-beta.1
./gradlew "-Psemver.stage=rc" # v1.0.0-rc.1
./gradlew "-Psemver.stage=snapshot" # v1.0.1-SNAPSHOT (uses the next patch version)
./gradlew "-Psemver.stage=final" # v1.0.0
./gradlew "-Psemver.stage=auto" # v1.0.0-alpha.2
# Last tag = v1.0.0
./gradlew "-Psemver.stage=alpha" # v1.0.1-alpha.1
./gradlew "-Psemver.stage=beta" # v1.0.1-beta.1
./gradlew "-Psemver.stage=rc" # v1.0.1-rc.1
./gradlew "-Psemver.stage=snapshot" # v1.0.1-SNAPSHOT (still uses the same patch version)
./gradlew "-Psemver.stage=final" # v1.0.1
./gradlew "-Psemver.stage=auto" # v1.0.1
To change between scopes, use the Gradle property -Psemver.scope=<scope>
The scope has to be one of major
, minor
, patch
or auto
.
# gradle.properties
semver.tagPrefix=v
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.scope=major" # v2.0.0
./gradlew "-Psemver.scope=minor" # v1.1.0
./gradlew "-Psemver.scope=patch" # v1.0.1
./gradlew "-Psemver.scope=auto" # v1.0.0-alpha.2 (uses the next num version)
# Last tag = v1.0.0
./gradlew "-Psemver.scope=major" # v2.0.0
./gradlew "-Psemver.scope=minor" # v1.1.0
./gradlew "-Psemver.scope=patch" # v1.0.1
./gradlew "-Psemver.scope=auto" # v1.0.1 (uses the next patch version)
# gradle.properties
semver.tagPrefix=v
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=major" # v2.0.0-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=major" # v2.0.0-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=major" # v2.0.0-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=major" # v2.0.0
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=major" # v2.0.0-SNAPSHOT
./gradlew "-Psemver.stage=auto" "-Psemver.scope=auto" # v1.0.0-alpha.2
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=minor" # v1.1.0-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=minor" # v1.1.0-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=minor" # v1.1.0-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=minor" # v1.1.0
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=minor" # v1.1.0-SNAPSHOT
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=patch" # v1.0.1-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=patch" # v1.0.1-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=patch" # v1.0.1-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=patch" # v1.0.1
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=patch" # v1.0.1-SNAPSHOT
# Last tag = v1.0.0-alpha.1
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=auto" # v1.0.0-alpha.2
./gradlew "-Psemver.stage=beta" "-Psemver.scope=auto" # v1.0.1-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=auto" # v1.0.1-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=auto" # v1.0.1
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=auto" # v1.0.1-SNAPSHOT
# Last tag = v1.0.0
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=major" # v2.0.0-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=major" # v2.0.0-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=major" # v2.0.0-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=major" # v2.0.0
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=major" # v2.0.0-SNAPSHOT
./gradlew "-Psemver.stage=auto" "-Psemver.scope=auto" # v1.0.1
# Last tag = v1.0.0
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=minor" # v1.1.0-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=minor" # v1.1.0-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=minor" # v1.1.0-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=minor" # v1.1.0
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=minor" # v1.1.0-SNAPSHOT
# Last tag = v1.0.0
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=patch" # v1.0.1-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=patch" # v1.0.1-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=patch" # v1.0.1-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=patch" # v1.0.1
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=patch" # v1.0.1-SNAPSHOT
# Last tag = v1.0.0
./gradlew "-Psemver.stage=alpha" "-Psemver.scope=auto" # v1.0.1-alpha.1
./gradlew "-Psemver.stage=beta" "-Psemver.scope=auto" # v1.0.1-beta.1
./gradlew "-Psemver.stage=rc" "-Psemver.scope=auto" # v1.0.1-rc.1
./gradlew "-Psemver.stage=final" "-Psemver.scope=auto" # v1.0.1
./gradlew "-Psemver.stage=snapshot" "-Psemver.scope=auto" # v1.0.1-SNAPSHOT
There are three tasks:
printSemver
: Prints the tag in CLI and create a file inbuild/semver/version.txt
which has two lines; the version without the tag and the version including the tag.createSemverTag
. Creates a git tag.pushSemverTag
. Creates and pushes a git tag to the remote.
You can combine them with any semver
project properties to ensure the correct tag version is
printed, created or pushed.
pushSemverTag
can use a specific remote if the Gradle property semver.remote
is set. If it is
not set, origin
is used if it exists, if not, the first remote by name is used. If there is no
remote, the task fails.
Samples:
./gradlew createSemverTag
./gradlew createSemverTag "-Psemver.stage=alpha"
./gradlew pushSemverTag
./gradlew pushSemverTag "-Psemver.stage=alpha"
By default, if the repository status is not clean, the version shows the timestamp but that can be
avoided by setting the Gradle property semver.checkClean
.
For example, if the last tag is 1.0.0
, there are 23 commits between that tag and the last commit
and the repo is not clean:
./gradlew "-Psemver.stage=final" "-Psemver.scope=patch"
semver: 1.0.0.23+2021-12-09T23-46-33-217289300Z
./gradlew "-Psemver.stage=final" "-Psemver.scope=patch" "-Psemver.checkClean=false"
semver: 1.0.1
./gradlew "-Psemver.checkClean=false"
semver: 1.0.0.23+1a2cd5b2 # 1a2cd5b2 is the last commit hash