Skip to content

Commit ee3564b

Browse files
jmongardJoel Mongård
andauthored
feat: Added the option to use two digit version (#81)
* build: Kotlin JVM toolchain config * feat: added possibility to output two digit version format * docs(README): improved the documentation * refactor: improve code --------- Co-authored-by: Joel Mongård <joel.mongard@idainfront.se>
1 parent 6c68649 commit ee3564b

File tree

11 files changed

+186
-63
lines changed

11 files changed

+186
-63
lines changed

README.md

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# Semantic versioning for Gradle using Git
1+
# Git Semantic Versioning Plugin for Gradle
22
[![Gradle Build](https://github.com/jmongard/Git.SemVersioning.Gradle/workflows/Gradle%20Build/badge.svg)](https://github.com/jmongard/Git.SemVersioning.Gradle/actions/workflows/gradle-push.yml)
33
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=jmongard_Git.SemVersioning.Gradle&metric=alert_status)](https://sonarcloud.io/dashboard?id=jmongard_Git.SemVersioning.Gradle)
44
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=jmongard_Git.SemVersioning.Gradle&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=jmongard_Git.SemVersioning.Gradle)
55
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=jmongard_Git.SemVersioning.Gradle&metric=coverage)](https://sonarcloud.io/summary/new_code?id=jmongard_Git.SemVersioning.Gradle)
66
[![GitHub tag (with filter)](https://img.shields.io/github/v/tag/jmongard/Git.SemVersioning.Gradle?logo=gradle&label=Release)](https://plugins.gradle.org/plugin/com.github.jmongard.git-semver-plugin)
77

8-
Gradle plugin for automatically versioning a project using semantic versioning and conventional commits with change log support based on git commit messages.
8+
A Gradle plugin that automatically versions your project using semantic versioning and conventional commits. It analyzes Git commit messages to determine version increments and generates change logs based on your commit history.
99

1010

1111
## Usage
@@ -38,18 +38,13 @@ The plugin requires Gradle 8 and Java version 17 to run.
3838

3939
## Versioning
4040

41-
The versioning system is designed to follow semantic versioning as described by https://semver.org/.
41+
The versioning system follows semantic versioning as described at [semver.org](https://semver.org/).
4242

43-
It works by recursively traversing the commit tree until it finds a version tag or release commit and then calculating
44-
the new version using from there using commit messages.
43+
The plugin works by traversing the Git commit history backwards from HEAD until it finds a version tag or release commit, then calculates the new version based on conventional commit messages since that point.
4544

46-
The plugin will look for [conventional commit](https://www.conventionalcommits.org/) messages (`fix:`, `feat:`, `refactor!:`, ...)
47-
and will increase the corresponding version number.
45+
The plugin recognizes [conventional commit](https://www.conventionalcommits.org/) messages (`fix:`, `feat:`, `refactor!:`, etc.) and increments the corresponding version number accordingly.
4846

49-
The plugin has the opinion that you want to group several fixes/features or breaking changes into a single release.
50-
Therefore, the major, minor or patch number will be increases by at most one compared to the previous release that is
51-
not a pre-release version. Set property `groupVersionIncrements = false` if you don't want the version changes to be combined.
52-
(See [Configuration](#Configuration) reference below.)
47+
By default, the plugin groups multiple fixes/features or breaking changes into a single release. This means the major, minor, or patch number will increase by at most one compared to the previous release (excluding pre-releases). Set `groupVersionIncrements = false` if you prefer each commit to increment the version individually.
5348

5449
### Releases
5550

@@ -62,12 +57,11 @@ The version number should consist of three numbers separated by a dot e.g. `1.0.
6257
be at the start of the message e.g. `release: v1.2.3` will be matched.
6358

6459

65-
### Uncommited changes or non release commits
60+
### Uncommitted Changes
6661

67-
If no version changed has been triggered by any commit messages since the last release
68-
then the patch number will be increased by one.
62+
If no version increment has been triggered by conventional commit messages since the last release, the patch number will be increased by one to indicate development progress.
6963

70-
If the current version is not a pre-release then `-SNAPSHOT` will be added.
64+
If the current version is not already a pre-release, `-SNAPSHOT` will be appended to indicate this is a development version.
7165

7266

7367
## Version format
@@ -97,6 +91,46 @@ string will not be semver compliant.
9791
* semver.infoVersion == semver.semVersion.toInfoVersionString("%03d", 0, true, false)
9892
* semver.semVersion.toString() == semver.semVersion.toInfoVersionString("%03d", 7, true, false)
9993

94+
### Two-Digit Versioning
95+
96+
The plugin supports 2-digit versioning (major.minor) in addition to the standard 3-digit semantic versioning (major.minor.patch).
97+
This can be useful for projects that follow a simpler versioning scheme.
98+
99+
To enable 2-digit versioning, set `useTwoDigitVersion = true` in your configuration:
100+
101+
```groovy
102+
semver {
103+
useTwoDigitVersion = true
104+
}
105+
```
106+
107+
#### How 2-Digit Versioning Works
108+
109+
When `useTwoDigitVersion` is enabled:
110+
111+
* **Version Format**: Versions are formatted as `major.minor` (e.g., `5.2` instead of `5.2.0`)
112+
* **Patch Changes**: Fix commits (`fix:`) are treated as minor version changes instead of patch changes
113+
* **Version Bumping**: When no specific version changes are triggered, the minor version is incremented instead of the patch version
114+
* **Pre-releases**: Pre-release versions work the same way (e.g., `5.2-alpha.1`)
115+
116+
#### Example with 2-Digit Versioning
117+
118+
| Command | Commit Text | Calculated version |
119+
| --------------------------------------------- | ------------------------- | ------------------- |
120+
| git commit -m "Initial commit" | Initial commit | 0.1-SNAPSHOT+001 |
121+
| git commit -m "fix: a bug fix" | fix: a bug fix | 0.2-SNAPSHOT+001 |
122+
| gradle releaseVersion | release: v0.2 | 0.2 |
123+
| git commit -m "feat: new feature" | feat: new feature | 0.3-SNAPSHOT+001 |
124+
| git commit -m "feat!: breaking change" | feat!: breaking change | 1.0-SNAPSHOT+002 |
125+
| gradle releaseVersion --preRelease="alpha.1" | release: v1.0-alpha.1 | 1.0-alpha.1 |
126+
127+
#### Accessing 2-Digit Versions
128+
129+
When `useTwoDigitVersion` is enabled, the standard version properties automatically use the 2-digit format:
130+
131+
* `semver.version` - Returns the 2-digit version (e.g., `5.2`)
132+
* `semver.infoVersion` - Returns the 2-digit version with commit count (e.g., `5.2+001`)
133+
* `semver.semVersion.toString()` - Returns the 2-digit version with SHA (e.g., `5.2+001.sha.1c792d5`)
100134

101135
## Tasks
102136

@@ -124,7 +158,7 @@ $ gradlew printInfoVersion
124158

125159
## `printSemVersion`
126160
This plugin adds a printSemVersion task, which will echo the project's calculated version
127-
to standard-out includning commit count and sha.
161+
to standard-out including commit count and SHA.
128162

129163
````shell
130164
$ gradlew printSemVersion
@@ -134,13 +168,12 @@ $ gradlew printSemVersion
134168
````
135169

136170
## `printChangeLog`
137-
This plugin adds a printChangeLog task, which will format the commit message for the current version
138-
and output them to standard-out. To avoid enoding problem in the console the change log can be outputed
139-
to an UTF-8 encoded file using `--file <filename>` e.g. `./gradlew printChangeLog --file build/changelog.md`
140-
Note: Use an absolute path for filename as the working directory might not be the one you expect if running
141-
using gradle deamon.
171+
This plugin adds a printChangeLog task, which will format the commit messages for the current version
172+
and output them to standard-out. To avoid encoding problems in the console, the change log can be output
173+
to a UTF-8 encoded file using `--file <filename>`, e.g. `./gradlew printChangeLog --file build/changelog.md`.
142174

143-
Note: The `printChangeLog` task is currently only registered on the root project given that the plugin is applied there.
175+
**Note:** Use an absolute path for the filename as the working directory might not be what you expect when running
176+
using the Gradle daemon. The `printChangeLog` task is currently only registered on the root project when the plugin is applied there.
144177

145178
````shell
146179
$ gradlew printChangeLog
@@ -158,19 +191,17 @@ $ gradlew printChangeLog
158191
[Configuring the changelog](/ChangeLog.md)
159192

160193
## `releaseVersion`
161-
The `releaseVersion` task will by default create both a release commit, and a release tag. The releaseVersion task will
162-
fail with an error if there exists local modification. It is possible to change this behaviour with the following options:
194+
The `releaseVersion` task creates both a release commit and a release tag by default. The task will fail with an error if there are uncommitted local modifications. You can modify this behavior using the following options:
163195

164-
* **--no-tag**: skip creating a tag. (Can also be set in settings using `createReleaseTag=false`.)
165-
* **--tag**: create a tag (If this has been disabled by the `createReleaseTag=false` option otherwise this is the default.)
166-
* **--no-commit**: skip creating a commit. (Can also be set in settings using `createReleaseCommit=false`.)
167-
* **--commit**: create a commit (If this has been disabled by the `createReleaseCommit=false` option otherwise this is the default.)
168-
* **--no-dirty**: skip dirty check. (Can also be set in settings using `noDirtyCheck=true`.)
169-
* **--message**="a message": Add a message text to the tag and/or commit
170-
* **--preRelease**="pre-release": Change the current pre-release e.g. `--preRelease=alpha.1`.
171-
Set the pre-release to "-" e.g. `--preRelease=-` to promote a pre-release to a release.
196+
* **--no-tag**: Skip creating a tag (can also be set in settings using `createReleaseTag=false`)
197+
* **--tag**: Create a tag (default behavior, unless disabled in settings)
198+
* **--no-commit**: Skip creating a commit (can also be set in settings using `createReleaseCommit=false`)
199+
* **--commit**: Create a commit (default behavior, unless disabled in settings)
200+
* **--no-dirty**: Skip the dirty working directory check (can also be set in settings using `noDirtyCheck=true`)
201+
* **--message**="message": Add a custom message to the tag and/or commit
202+
* **--preRelease**="version": Set the pre-release identifier (e.g., `--preRelease=alpha.1`). Use `--preRelease=-` to promote a pre-release to a full release
172203

173-
Note: The `releaseVersion` task is currently only registered on the root project given that the plugin is applied there.
204+
**Note:** The `releaseVersion` task is currently only registered on the root project when the plugin is applied there.
174205

175206
## Example of how version is calculated
176207
With setting: `groupVersionIncrements = true` (default)
@@ -260,6 +291,7 @@ semver {
260291
createReleaseCommit = true
261292
createReleaseTag = true
262293
metaSeparator = '+'
294+
useTwoDigitVersion = false
263295
}
264296
265297
//Remember to retrieve the version after plugin has been configured
@@ -276,7 +308,7 @@ version = semver.version
276308
version tags with "v".
277309
* **groupVersionIncrements**: Used to disable grouping of version increments so that each commit message counts.
278310
* **noDirtyCheck**: Can be used to ignore all local modifications when calculating the version.
279-
Disabling dirty check can also be donne from the command line e.g. `gradlew -PnoDirtyCheck=true someOtherTask`.
311+
Disabling dirty check can also be done from the command line e.g. `gradlew -PnoDirtyCheck=true someOtherTask`.
280312
* **noAutoBump**: If set only commits matching majorPattern, minorPattern or patchPattern will increase the version.
281313
The default behaviour for the plugin is to assume you have begun the work on the next release for any commit you do
282314
after the last release. The patch level (or pre-release level, if the last release was a pre-release) of the version
@@ -288,6 +320,7 @@ version = semver.version
288320
* **createReleaseCommit**: If a release commit should be created when running the release task. Setting this to false
289321
has the same effect as the --no-commit flag.
290322
* **metaSeparator**: The character to use to separate build metadata from the version when printing info version.
323+
* **useTwoDigitVersion**: If the version should be two digits instead of three.
291324

292325
Patterns is matched using [java regular expressions](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html)
293326
with IGNORE_CASE and MULTILINE options enabled.
@@ -296,7 +329,7 @@ with IGNORE_CASE and MULTILINE options enabled.
296329

297330
This plugin has been tested on Gradle 7.x and 8.x. (Version 0.4.3 and older should work on gradle 6.x and probably 5.x)
298331

299-
## Continues Integration
332+
## Continuous Integration
300333
The plugin calculates the version using the commit tree. Make sure you check out all commits relevant and not just
301334
a shallow copy.
302335

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ plugins {
1313
id("jacoco")
1414
}
1515

16+
kotlin {
17+
jvmToolchain(17)
18+
}
19+
1620
semver {
1721
releaseTagNameFormat = "v%s"
1822
createReleaseTag = false

src/main/kotlin/git/semver/plugin/gradle/GitSemverPluginExtension.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ abstract class GitSemverPluginExtension(project: Project, providerFactory: Provi
104104
/**
105105
* The semantic version for the project with commit info excluding sha as a string e.g. "1.2.3-Alpha.4+005"
106106
*/
107-
val infoVersion: String by lazy { semVersion.toInfoVersionString(metaSeparator = metaSeparator) }
107+
val infoVersion: String by lazy { semVersion.toInfoVersionString(
108+
metaSeparator = metaSeparator,
109+
useTwoDigitVersion = useTwoDigitVersion) }
108110

109111
/**
110112
* The semantic version for the project e.g. 1.2.3-Alpha.4
@@ -115,7 +117,7 @@ abstract class GitSemverPluginExtension(project: Project, providerFactory: Provi
115117
/**
116118
* The semantic version for the project as a string e.g. "1.2.3-Alpha.4"
117119
*/
118-
val version: String by lazy { versionValue.toString() }
120+
val version: String by lazy { versionValue.toString(useTwoDigitVersion) }
119121

120122
private var semInfoVersionValueSource = project.providers.of(SemInfoVersionValueSource::class.java) {
121123
it.parameters.getGitDir().set(gitDirectory);

src/main/kotlin/git/semver/plugin/gradle/PrintTask.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ open class PrintTask @Inject constructor(private val printout: () -> Any, desc:
1717
init {
1818
group = GitSemverPlugin.VERSIONING_GROUP
1919
description = desc
20-
if (!reason.isEmpty()) {
20+
if (reason.isNotEmpty()) {
2121
notCompatibleWithConfigurationCache(reason)
2222
}
2323
}

src/main/kotlin/git/semver/plugin/scm/GitProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ internal class GitProvider(private val settings: SemverSettings) {
7070
getHeadCommit(it.repository),
7171
params.preRelease?.trimStart('-')
7272
)
73-
val versionString = version.toInfoVersionString(metaSeparator = settings.metaSeparator)
73+
val versionString = version.toInfoVersionString(
74+
metaSeparator = settings.metaSeparator,
75+
useTwoDigitVersion = settings.useTwoDigitVersion)
7476
logger.info("Saving new version: {}", versionString)
7577

7678
val isCommit = isFormatEnabled(params.commit, settings.releaseCommitTextFormat)

src/main/kotlin/git/semver/plugin/semver/BaseSettings.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ abstract class BaseSettings(
1414
var noDirtyCheck: Boolean = false,
1515
var noAutoBump: Boolean = false,
1616
var gitSigning: Boolean? = null, // null means use the jgit default
17-
var metaSeparator: Char = '+'
17+
var metaSeparator: Char = '+',
18+
var useTwoDigitVersion: Boolean = false // Enable 2-digit versioning (major.minor) instead of 3-digit (major.minor.patch)
1819
) : Serializable {
1920
constructor(settings: BaseSettings) : this(
2021
settings.defaultPreRelease, settings.releasePattern, settings.patchPattern, settings.minorPattern,
2122
settings.majorPattern, settings.releaseCommitTextFormat, settings.releaseTagNameFormat,
2223
settings.groupVersionIncrements, settings.noDirtyCheck, settings.noAutoBump,
23-
settings.gitSigning, settings.metaSeparator
24+
settings.gitSigning, settings.metaSeparator, settings.useTwoDigitVersion
2425
)
2526
}

src/main/kotlin/git/semver/plugin/semver/MutableSemVersion.kt

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,21 @@ internal class MutableSemVersion(
113113
) {
114114
when {
115115
major > 0 && settings.majorRegex.containsMatchIn(text) ->
116-
if (!isPreRelease || major == initialVersion.major) {
117-
bumpMajor += 1
118-
bumpMinor = 0
119-
bumpPatch = 0
120-
bumpPre = 0
116+
bumpMajor()
117+
118+
settings.useTwoDigitVersion ->
119+
if (settings.minorRegex.containsMatchIn(text) ||
120+
settings.patchRegex.containsMatchIn(text)
121+
) {
122+
if (preRelease.number == null) {
123+
bumpMinor()
124+
} else {
125+
bumpPre += 1
126+
}
121127
}
122128

123129
settings.minorRegex.containsMatchIn(text) ->
124-
if (!isPreRelease || major == initialVersion.major && minor == initialVersion.minor) {
125-
bumpMinor += 1
126-
bumpPatch = 0
127-
bumpPre = 0
128-
}
130+
bumpMinor()
129131

130132
settings.patchRegex.containsMatchIn(text) ->
131133
if (preRelease.number == null) {
@@ -137,7 +139,24 @@ internal class MutableSemVersion(
137139
}
138140
}
139141

140-
internal fun applyPendingChanges(forceBumpIfNoChanges: Boolean, groupChanges: Boolean): Boolean {
142+
private fun bumpMajor() {
143+
if (!isPreRelease || major == initialVersion.major) {
144+
bumpMajor += 1
145+
bumpMinor = 0
146+
bumpPatch = 0
147+
bumpPre = 0
148+
}
149+
}
150+
151+
private fun bumpMinor() {
152+
if (!isPreRelease || major == initialVersion.major && minor == initialVersion.minor) {
153+
bumpMinor += 1
154+
bumpPatch = 0
155+
bumpPre = 0
156+
}
157+
}
158+
159+
internal fun applyPendingChanges(forceBumpIfNoChanges: Boolean, groupChanges: Boolean, useTwoDigitVersion: Boolean = false): Boolean {
141160
if (hasPendingChanges) {
142161
if (groupChanges) {
143162
applyChangesGrouped()
@@ -148,17 +167,22 @@ internal class MutableSemVersion(
148167
return true
149168
}
150169

151-
if (!forceBumpIfNoChanges) {
152-
return false
170+
if (forceBumpIfNoChanges) {
171+
forceBump(useTwoDigitVersion)
172+
return true
153173
}
174+
return false
175+
}
154176

177+
private fun forceBump(useTwoDigitVersion: Boolean) {
155178
val preReleaseNumber = preRelease.number
156179
if (preReleaseNumber != null) {
157180
preRelease = preRelease.copy(number = preReleaseNumber + 1)
181+
} else if (useTwoDigitVersion) {
182+
minor += 1
158183
} else {
159184
patch += 1
160185
}
161-
return true
162186
}
163187

164188
private fun applyChangesNotGrouped() {

0 commit comments

Comments
 (0)