From 6d730bb4fd32c7c19b85512566330854e0b2cb82 Mon Sep 17 00:00:00 2001 From: Simon Templer Date: Tue, 26 Mar 2024 00:39:08 +0100 Subject: [PATCH 1/5] feat: support to force using release version ...using an environment variable. Set the environment variable `RELEASE` to `true` to force the release version to be used, even if the repository is dirty or no tag exists at the current commit. --- README => README.md | 3 +++ .../gradle/version/VersionPlugin.groovy | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) rename README => README.md (80%) diff --git a/README b/README.md similarity index 80% rename from README rename to README.md index cdafc59..73c92a5 100644 --- a/README +++ b/README.md @@ -17,4 +17,7 @@ The plugin works based on the following assumptions: 3. If the git repository is not clean or HEAD does not point to a tag, the project version is a SNAPSHOT version that increases the minor version compared to the last release version 4. If no release version is configured or the version file is missing, the project version is `1.0.0-SNAPSHOT` +If the environment variable `RELEASE` is set to `true`, the release version is used, even if the repository is not clean or there is no tag. +This is intended for use cases where the release version was set, but the repository is dirty or no tag was created. + By default the version file is assumed to be the file `version.txt` in the root project. diff --git a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy index d1d2423..5265288 100644 --- a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy +++ b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy @@ -44,12 +44,23 @@ class VersionPlugin implements Plugin { String determineVersion(Project project, File versionFile, File gitDir) { // read version from file - if (!versionFile.exists()) { + def releaseVersion = null + if (versionFile.exists()) { + releaseVersion = versionFile.text.trim() + } + + //TODO parse/verify version + + if (!releaseVersion) { // assume initial snapshot - project.logger.info("Version file does not exists, assuming 1.0.0-SNAPSHOT") + project.logger.info("Version file does not exist or contains no version, assuming 1.0.0-SNAPSHOT") return '1.0.0-SNAPSHOT' } - def releaseVersion = versionFile.text.trim() + + if ('true'.equalsIgnoreCase(System.getenv('RELEASE'))) { + // force release version (e.g if repo is dirty during release in CI) + return releaseVersion + } def dirty = false def tagOnCurrentCommit = false From 2d4e865f157231b59beaff7b65a7f10f3d5587bc Mon Sep 17 00:00:00 2001 From: Simon Templer Date: Sat, 30 Mar 2024 00:24:25 +0100 Subject: [PATCH 2/5] ci: use Gradle plugin for version --- .releaserc.yml | 4 ++-- build.gradle | 2 +- version.txt | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 version.txt diff --git a/.releaserc.yml b/.releaserc.yml index cfb5a44..446250b 100644 --- a/.releaserc.yml +++ b/.releaserc.yml @@ -9,9 +9,9 @@ plugins: - "@semantic-release/changelog" - - "@semantic-release/exec" - publishCmd: ./gradlew publishPlugins - # prepareCmd: echo "${nextRelease.version}" > version.txt # TODO set via plugin + prepareCmd: ./gradlew "-PnewVersion=${nextRelease.version}" setReleaseVersion - - "@semantic-release/git" - assets: - CHANGELOG.md - # - version.txt + - version.txt - "@semantic-release/github" diff --git a/build.gradle b/build.gradle index 8e860cf..b8104ca 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java-gradle-plugin' id 'groovy' id 'com.gradle.plugin-publish' version "1.2.1" + id 'to.wetransform.semantic-release-version' version '1.0.0' } repositories { @@ -15,7 +16,6 @@ java { } group = "to.wetransform" -version = "1.0.0" dependencies { implementation 'org.ajoberstar.grgit:grgit-core:5.2.2' diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.0 From be67f51f9dbf4f4a04d566e9004790c8eb1b91cc Mon Sep 17 00:00:00 2001 From: Simon Templer Date: Sat, 30 Mar 2024 00:38:01 +0100 Subject: [PATCH 3/5] feat: add task to verify if version is a release version --- .releaserc.yml | 1 + .../gradle/version/VersionPlugin.groovy | 28 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.releaserc.yml b/.releaserc.yml index 446250b..4920f5b 100644 --- a/.releaserc.yml +++ b/.releaserc.yml @@ -10,6 +10,7 @@ plugins: - - "@semantic-release/exec" - publishCmd: ./gradlew publishPlugins prepareCmd: ./gradlew "-PnewVersion=${nextRelease.version}" setReleaseVersion + verifyReleaseCmd: ./gradlew verifyReleaseVersion - - "@semantic-release/git" - assets: - CHANGELOG.md diff --git a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy index 5265288..7436b87 100644 --- a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy +++ b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy @@ -4,6 +4,9 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; class VersionPlugin implements Plugin { + + private static def SEM_VER_REGEX = /(\d+)\.(\d+)\.(\d+)/ + void apply(Project project) { //XXX not sure how to easiest use the service - instead the repo is opened manually // project.apply(plugin: 'org.ajoberstar.grgit.service') @@ -31,6 +34,19 @@ class VersionPlugin implements Plugin { } } + project.task('verifyReleaseVersion') { + group 'Version' + description 'Check if a release version is configured, otherwise (if the version is a -SNAPSHOT version) fail' + + doLast { + def version = project.version + + assert version + assert !version.endsWith('-SNAPSHOT') + assert version =~ SEM_VER_REGEX + } + } + // set version project.afterEvaluate { @@ -47,9 +63,15 @@ class VersionPlugin implements Plugin { def releaseVersion = null if (versionFile.exists()) { releaseVersion = versionFile.text.trim() - } - //TODO parse/verify version + // verify version + if (releaseVersion) { + def matcher = releaseVersion =~ SEM_VER_REGEX + if (!matcher) { + throw new IllegalStateException("Provided version for last release is not a valid semantic version: $releaseVersion") + } + } + } if (!releaseVersion) { // assume initial snapshot @@ -86,7 +108,7 @@ class VersionPlugin implements Plugin { } else { // build snapshot version with next minor version - def matcher = releaseVersion =~ /(\d+)\.(\d+)\.(\d+)/ + def matcher = releaseVersion =~ SEM_VER_REGEX if (matcher) { project.logger.info("Current commit is not tagged or repository is dirty, using snapshot version based on last release") From 1e56584da3f07f19181f031c261a2a2261fc6425 Mon Sep 17 00:00:00 2001 From: Simon Templer Date: Sat, 30 Mar 2024 00:57:08 +0100 Subject: [PATCH 4/5] feat: verify if tag(s) represent release version --- README.md | 7 +++-- .../gradle/version/VersionExtension.groovy | 20 +++++++++++++ .../gradle/version/VersionPlugin.groovy | 28 ++++++++++++------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 73c92a5..b25dcc1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,10 @@ The plugin works based on the following assumptions: 3. If the git repository is not clean or HEAD does not point to a tag, the project version is a SNAPSHOT version that increases the minor version compared to the last release version 4. If no release version is configured or the version file is missing, the project version is `1.0.0-SNAPSHOT` -If the environment variable `RELEASE` is set to `true`, the release version is used, even if the repository is not clean or there is no tag. -This is intended for use cases where the release version was set, but the repository is dirty or no tag was created. +For a tag to be recognized it needs to match the configured release version, optionally with the prefix `v`, for example `1.0.0` or `v1.0.0`. + +If the environment variable `RELEASE` is set to `true`, the release version is used, even if the repository is not clean. +This is intended for use cases where the release version was set, but the repository is expected to be dirty, e.g. due to other CI tasks. +A cleaner method is avoid the repository being dirty, e.g. by adding additional files that are created during the release process to `.gitignore` if possible. By default the version file is assumed to be the file `version.txt` in the root project. diff --git a/src/main/groovy/to/wetransform/gradle/version/VersionExtension.groovy b/src/main/groovy/to/wetransform/gradle/version/VersionExtension.groovy index 71645a8..8290a26 100644 --- a/src/main/groovy/to/wetransform/gradle/version/VersionExtension.groovy +++ b/src/main/groovy/to/wetransform/gradle/version/VersionExtension.groovy @@ -1,5 +1,6 @@ package to.wetransform.gradle.version +import java.util.function.BiPredicate import org.gradle.api.Project class VersionExtension { @@ -9,8 +10,27 @@ class VersionExtension { this.gitDir = project.rootProject.projectDir } + /** + * Location of the file that holds the last release version, if it exists. + */ File versionFile + /** + * Location of the git repository to check. + */ File gitDir + /** + * Test to verify if a tag is a version tag and if the version matches the provided release version. + */ + BiPredicate verifyTag = { tag, version -> + def matcher = tag =~ /^v?((\d+)\.(\d+)\.(\d+))$/ + if (matcher) { + matcher[0][1] == version + } + else { + false + } + } + } diff --git a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy index 7436b87..c3a6dda 100644 --- a/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy +++ b/src/main/groovy/to/wetransform/gradle/version/VersionPlugin.groovy @@ -1,5 +1,6 @@ package to.wetransform.gradle.version; +import java.util.function.BiPredicate import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -53,12 +54,14 @@ class VersionPlugin implements Plugin { if(it.version != Project.DEFAULT_VERSION) { throw new IllegalStateException("Version may not be configured if version plugin is applied") } else { - it.version = determineVersion(it, it.versionConfig.versionFile, it.versionConfig.gitDir) + it.version = determineVersion( + it, it.versionConfig.versionFile, it.versionConfig.gitDir, it.versionConfig.verifyTag + ) } } } - String determineVersion(Project project, File versionFile, File gitDir) { + String determineVersion(Project project, File versionFile, File gitDir, BiPredicate verifyTag) { // read version from file def releaseVersion = null if (versionFile.exists()) { @@ -79,11 +82,6 @@ class VersionPlugin implements Plugin { return '1.0.0-SNAPSHOT' } - if ('true'.equalsIgnoreCase(System.getenv('RELEASE'))) { - // force release version (e.g if repo is dirty during release in CI) - return releaseVersion - } - def dirty = false def tagOnCurrentCommit = false def grgit @@ -97,12 +95,22 @@ class VersionPlugin implements Plugin { dirty = !grgit.status().isClean() def currentCommit = grgit.head().id tagOnCurrentCommit = grgit.tag.list().findAll { tag -> - tag.commit.id == currentCommit + tag.commit.id == currentCommit && verifyTag.test(tag.name, releaseVersion) + } + } + + if ('true'.equalsIgnoreCase(System.getenv('RELEASE'))) { + // force release version if repo is dirty (e.g. during release in CI) + // but still verify tag + if (tagOnCurrentCommit) { + return releaseVersion + } + else { + throw new IllegalStateException("There is no matching tag for the configured release version $releaseVersion") } } + if (tagOnCurrentCommit && !dirty) { - //TODO check tags? - //XXX for now assume tag represents release project.logger.info("Current commit is tagged and repository clean, using release version specified in file: $releaseVersion") releaseVersion } From 06ab8bf3a62ef3061abdfbac7cb53dc9cd216ad0 Mon Sep 17 00:00:00 2001 From: Simon Templer Date: Sat, 30 Mar 2024 01:21:38 +0100 Subject: [PATCH 5/5] chore: temporarily add version verification task until plugin update --- build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build.gradle b/build.gradle index b8104ca..e015a66 100644 --- a/build.gradle +++ b/build.gradle @@ -66,3 +66,15 @@ tasks.wrapper { distributionType = Wrapper.DistributionType.ALL gradleVersion = '8.6' } + +task('verifyReleaseVersion') { + //FIXME remove task once plugin could be updated + + doLast { + def version = project.version + + assert version + assert !version.endsWith('-SNAPSHOT') + // assert version =~ SEM_VER_REGEX + } +}