{"id":32064104,"url":"https://github.com/dmfs/gver","last_synced_at":"2026-04-08T22:31:06.005Z","repository":{"id":44584375,"uuid":"451926912","full_name":"dmfs/gver","owner":"dmfs","description":"Gradle versioning based on git history.","archived":false,"fork":false,"pushed_at":"2024-06-12T07:28:23.000Z","size":334,"stargazers_count":1,"open_issues_count":13,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-19T13:55:10.124Z","etag":null,"topics":["git","github","gradle","semantic-versioning","semver","tags"],"latest_commit_sha":null,"homepage":"https://plugins.gradle.org/plugin/org.dmfs.gver","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmfs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-25T15:13:43.000Z","updated_at":"2025-10-10T08:18:03.000Z","dependencies_parsed_at":"2024-06-12T09:54:22.425Z","dependency_job_id":null,"html_url":"https://github.com/dmfs/gver","commit_stats":null,"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"purl":"pkg:github/dmfs/gver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmfs%2Fgver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmfs%2Fgver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmfs%2Fgver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmfs%2Fgver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmfs","download_url":"https://codeload.github.com/dmfs/gver/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmfs%2Fgver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31577444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","github","gradle","semantic-versioning","semver","tags"],"created_at":"2025-10-19T01:17:07.520Z","updated_at":"2026-04-08T22:31:05.998Z","avatar_url":"https://github.com/dmfs.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build](https://github.com/dmfs/gver/actions/workflows/main.yml/badge.svg?label=main)](https://github.com/dmfs/gver/actions/workflows/main.yml)  \n[![codecov](https://codecov.io/gh/dmfs/gver/branch/main/graph/badge.svg)](https://codecov.io/gh/dmfs/gver)  \n[![Confidence](https://img.shields.io/badge/Tested_with-Confidence-800000?labelColor=white)](https://saynotobugs.org/confidence)\n\n# gver\n\nA plugin to take care of versioning your code, so you don't have to.\n\ngver provides a domain specific language (DSL) that lets you describe in simple terms how to derive your version numbers\nfrom your\ngit history. This includes expressions to test the names of affected files and to check the type of referenced issues,\nallowing you to\nskip a version update when no code or build file was altered or to derive the type of change (feature vs. bugfix) from\nthe implemented/fixed ticket.\n\n# Example\n\nIn order to use the plugin, it needs to be configured. Currently, there is no default configuration.\n\nThe following example considers commits to be breaking changes when the commit message contains either the\nhashtags `#major` or `#break`. If the commit message\ncontains neither, the commit message is searched for the pattern `#\\d` and, in case one is found, a matching issue is\nlooked up in the Github repository\n\"dmfs/jems\". If an issue is found, the change type is determined by whether the issue has a `bug` tag or not. If no\nGithub issue was found, the change type is\nconsidered to be a minor change when the commit message contains either the pattern `#feature\\b` or\n`(?i)\\b(implement(s|ed)?|close[sd]?) #\\d+\\b`, and a patch when it contains `(?i)\\b(fix(e[sd])?|resolve[sd]?) #\\d+\\b`.\n\n```groovy\ngver {\n    issueTracker GitHub {\n        repo = \"dmfs/jems\" // the name of the GitHub repo that contains the issues for this project\n        if (project.hasProperty(\"GITHUB_API_TOKEN\")) {\n            accessToken = GITHUB_API_TOKEN // should be stored in your global or local gradle properties\n        }\n    }\n    changes {\n        are none when {\n            // trivial changes don't get a new version number\n            commitMessage contains(\"(?i)#trivial\\\\b\")\n        }\n        are major when {\n            commitMessage contains(\"(?i)#major\\\\b\")\n        }\n        are major when {\n            commitMessage contains(\"(?i)#break\\\\b\")\n        }\n        are minor when {\n            commitMessage contains(~/(?i)\\b(implement(s|ed)?|close[sd]?) #(?\u003cissue\u003e\\d+)\\b/) {\n                where(\"issue\") { isIssue { not(labeled \"bug\") } }\n            }\n        }\n        are patch when {\n            commitMessage contains(~/(?i)\\b(fix(e[sd])?|resolve[sd]?) #(?\u003cissue\u003e\\d+)\\b/) {\n                where(\"issue\") { isIssue { labeled \"bug\" } }\n            }\n        }\n        are patch otherwise // every other change always results in a new patch version\n    }\n    preReleases {\n        // pre-release on the main branch are versioned like a.b.c-beta.x\n        on ~/main/ use { \"beta\" }\n\n        // pre-release on the feature branches are versioned like a.b.c-alpha-\u003cfeature-name\u003e.x\n        on ~/feature\\/(?\u003cname\u003e.*)/ use { \"alpha-${group('name')}\" }\n\n        // every other pre-release is versioned like a.b.c-SNAPSHOT.x\n        on ~/.*/ use { \"SHAPSHOT\" }\n    }\n    suffixes {\n        append \".${new Date().format(\"yyyyMMdd'T'HHmmss\")}-SNAPSHOT\" when {\n            branch not(matches(~/main/))\n        }\n    }\n    tag {\n        // releases can only be tagged on the main branch, on other branches the `gitTagRelease` task will fail\n        with release when {\n            branch matches(~/main|release.*/)\n        }\n        // pre-releases can only be tagged on feature and bugfix branches\n        with preRelease when {\n            branch matches(~/^(feature|bugfix).*/)\n        }\n        // other branches can't be tagged\n        with nothing otherwise\n    }\n}\n```\n\n## DSL\n\nThe following sections describe the DSL to specify your versioning scheme. Note that the DSL is not stable yet and may\nchange with every new\nversion.\n\n### describing change types\n\nWhen practising semantic versioning, the most important step is to understand the kind of change (major, minor, bugfix).\ngver provides\na DSL to describe how to derive the kind of change based on commit message or referenced issues.\n\nThe top-level element is `changes` which takes a closure describing when a change is considered a major, minor or bugfix\nchange.\nThe list of conditions is evaluated top to bottom until the first one matches.\n\n```groovy\ngver {\n    changes {\n        are major when {\n            // condition\n        }\n        are minor when {\n            // another condition\n        }\n        // more change types\n    }\n}\n```\n\nA change type can appear multiple times in the list if it's to be applied under multiple conditions.\n\nIn addition to `major`, `minor`, `patch`, gver also knows a `none` type to identify trivial changes that should not\nresult in a new version,\ne.g. typo fixes in documentation files.\n\nThe `invalid` change type can be used to validate commits. As soon as the project version is determined, this will\nthrow an exception if the condition is fulfilled and the build will fail.\n\nExample:\n\n```groovy\ngver {\n    changes {\n        are invalid when {\n            // enforce messages compliant to conventionalcommits.org\n            commitTitle not(matches(~/^[a-z]+(\\([^)]+\\))?!?: .*/))\n        }\n    }\n}\n```\n\nNote that all conditions inside a change type closure must match in order to apply the change type. If you need to\nexpress a logical `or`, describe the same change type with the other condition underneath the first one or use `anyOf`\ndescribed below.\n\nThe `otherwise` case should be last in the list as it catches all cases that didn't match any of the other conditions.\nThe default is to\nincrease any pre-release version or create a new minor pre-release if no pre-release version is present yet.\n\n**Note**, the notation\n\n```groovy\ngver {\n    changes {\n        ...\n        otherwise patch\n    }\n}\n```\n\nhas been deprecated in favour of\n\n```groovy\ngver {\n    changes {\n        ...\n        are patch otherwise\n    }\n}\n```\n\n### using conventions\n\nThere are a couple of existing conventions for semantic commit messages.\nAt present, gver supports applying [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) using the\n`follow` expression like so\n\n```groovy\ngver {\n    changes {\n        follow conventionalCommits\n    }\n}\n```\n\nYou can still add rules with higher or lower priority, for instance:\n\n```groovy\ngver {\n    changes {\n        are major when { commitTitle contains(~/💥/) }\n        are minor when { commitTitle contains(~/🎀/) }\n        are patch when { commitTitle contains(~/🩹/) }\n        follow conventionalCommits\n        are none otherwise\n    }\n}\n```\n\nThere are two flavours `strictConventionalCommits` and `conventionalCommits`. The latter falls through and applies\nthe next or default change rules if a commit doesn't conform to conventional commits, whereas the former will fail and\nbreak the build when a commit doesn't conform to the convention.\n\n### conditions\n\nAt present, the type of change can be determined from the current branch name or the commit history up to the last\ntagged version.\n\n#### `commitMessage`\n\n`commitMessage` tests the entire commit message. You'd typically use it with `contains` or `matches`  to test it with a\nregular expression.\n\n```groovy\ngver {\n    changes {\n        are major when {\n            commitMessage contains(~/#breaking\\b/)\n        }\n    }\n}\n```\n\nThis identifies major changes by the presence of the `#breaking` hashtag in the commit message.\n\nAnother common pattern is to consider a change a bugfix when it contains one of \"fixes\", \"fixed\", \"resolves\" or \"\nresolved\" followed by a `#` and\na numeric issue identifier.\n\n```groovy\ngver {\n    changes {\n        are patch when {\n            commitMessage contains(\"(?i)\\\\b(fix(e[sd])?|resolve[sd]?) #\\\\d+\\\\b\")\n        }\n    }\n}\n```\n\n#### `commitTitle`\n\nThis works almost like `commitMessage` but only takes the first line of a commit message into account.\n\n#### `branch`\n\nThis can be used to match the name of the current Git head.\n\nThe following configuration considers all changes on the main branch as minor changes, whereas changes on `release`\nbranches are considered to be\npatches.\n\n```groovy\ngver {\n    changes {\n        are minor when {\n            branch matches(~/main/)\n        }\n        are patch when {\n            branch matches(~/release\\/.*/)\n        }\n    }\n}\n```\n\n#### affects\n\nThis condition allows you to determine a change type based on the files that have been affected by a commit. It takes\na `Predicate` of a `Set\u003cString\u003e`\nlike `anyThat`, `noneThat` or `only`.\n\nExample:\n\n```groovy\ngver {\n    changes {\n        are none when {\n            affects only(matches(~/.*\\.(md|adoc)/)) // only documentation updated, don't generate new version\n        }\n        ...\n    }\n}\n```\n\n#### envVariable\n\nIn some environments, e.g. Jenkins builds, the plugin may not be able to\ndetermine branch names from the Git repo.\nInstead, you may have to grab the current branch name from\nan environment variable like `BRANCH_NAME`. Typically, you probably\nstill want to combine that with the `branch` condition to support\nlocal builds.\n\nExample:\n\n```groovy\ngver {\n    changes {\n        are minor when {\n            branch matches(~/feature\\/.*/) // for local builds\n        }\n        are minor when {\n            envVariable \"BRANCH_NAME\", matches(~/feature\\/.*/) // for Jenkins builds\n        }\n        are patch when {\n            branch matches(~/bugfix\\/.*/)\n        }\n        are patch when {\n            envVariable \"BRANCH_NAME\", matches(~/bugfix\\/.*/)\n        }\n        ...\n    }\n}\n```\n\n#### anyOf\n\nA change type matches when *all* conditions in the Closure after `when` match. An \"any of\"\nbehavior is achieved by adding more change types underneath each other, with the first one\nmatching being applied.\nSometimes this can become a bit lengthy and more difficult to understand. If all conditions\nhave the same priority, you can put them together into one change type and combine them with\n`anyOf`.\n\n```groovy\ngver {\n    changes {\n        are minor when {\n            anyOf {\n                branch matches(~/feature\\/.*/) // for local builds\n                envVariable \"BRANCH_NAME\", matches(~/feature\\/.*/) // for Jenkins builds\n            }\n        }\n        are patch when {\n            anyOf {\n                branch matches(~/bugfix\\/.*/)\n                envVariable \"BRANCH_NAME\", matches(~/bugfix\\/.*/)\n            }\n        }\n        ...\n    }\n}\n```\n\n### Pre-Releases\n\ngver can apply different pre-release versions, based on the current Git head's name, e.g.\n\n```groovy\ngver {\n    ...\n    preReleases {\n        on ~/main/ use { \"beta\" }\n        on ~/feature\\/(?\u003cname\u003e.*)/ use { \"alpha-${group('name')}\" }\n        on ~/.*/ use { \"SHAPSHOT\" }\n    }\n}\n```\n\nIn the closure passed to `use` you can use any groups declared in your regular expression. The resulting pre-release\nversion will automatically\nbe sanitized to comply with SemVer syntax.\n\nWhen your pre-release doesn't end with a numeric segment, the next pre-relase will automatically append `.1` and\ncontinue counting from that.\nIf the pre-release already ends with a numeric segment, it will be incremented by 1 with every subsequent pre-release.\n\n### Suffixes\n\ngver can append a suffix to pre-release versions. The suffix is always appended verbatim. This is primarily useful to\ncreate\n`SNAPSHOT` releases.\nSuffixes are specified with `append \"\u003csuffix\u003e\" when {}` where the closure contains one of the conditions also used in\nthe `changes` DSL.\n\nExamples\n\n```groovy\ngver {\n    ...\n    suffixes {\n        append \".${new Date().format(\"yyyy-MM-dd\")}-SNAPSHOT\" when {\n            branch not(matches(~/main/))\n        }\n    }\n    ...\n}\n```\n\nAppends a suffix like `2022-12-13-SNAPSHOT` to every pre-release that's not on a main branch.\n\nYou can set a suffix unconditionally by using `otherwise` instead of `when`\n\n```groovy\ngver {\n    ...\n    suffixes {\n        append \".${new Date().format(\"yyyy-MM-dd\")}-SNAPSHOT\" otherwise\n    }\n    ...\n}\n```\n\nUnconditional suffixes always match, hence any `append` clause following an unconditional suffix will never be applied.\n\nIf no suffixes are specified, the suffix `\".\u003ctimestamp\u003e-SNAPSHOT\"` is added to every pre-release where `\u003ctimestamp\u003e` is\neither the date and time\n(in UTC and RFC 5545 notation) of the HEAD commit in case the working tree is clean or the current date and time if the\nworking tree is dirty.\n\nIn order to apply the default suffix conditionally you can use the special suffix `DEFAULT` like\n\n```groovy\ngver {\n    ...\n    suffixes {\n        append DEFAULT when { branch not(matches(~/main/)) }\n    }\n    ...\n}\n```\n\nTo disable any suffix use\n\n```groovy\ngver {\n    ...\n    suffixes {\n        append \"\" otherwise\n    }\n    ...\n}\n```\n\n### Tagging releases\n\ngver can tag your current Git head with the current pre-release or the next release version.\n\nBy default, tags can only be created on `main` or `master` branches.\nTagging can be configured with conditions like above. The following tag types are currently supported\n\n* `release`\n* `preRelease`\n* `none`\n\nWith **none** causing any tag task to fail when the condition matches.\n\nExample:\n\n```groovy\ngver {\n    ...\n    tag {\n        with release when {\n            branch matches(~/^release\\/.*/)\n        }\n        with preRelease when {\n            branch matches(~/^(feature|bugfix)\\/.*/)\n        }\n        with none otherwise\n    }\n}\n```\n\n*Note*: the `releaseBranchPattern` directive is deprecated and will be removed.\n\nThe following tasks are available to tag the current Git head:\n\n#### `gitTagRelease`\n\nAdds a release version tag to the current Git head, if allowed by the `tag` configuration. Fails with an Exception\notherwise.\n\n#### `gitTagPreRelease`\n\nAdds a pre-release version tag to the current Git head, if allowed by the `tag` configuration. Fails with an Exception\notherwise.\n\n#### `gitTag`\n\nAdds a version tag to the current Git head. The release type is determined by the `tag` configuration. The first\nentry that matches the current head to be precise.\n\nIn contrast to `gitTagRelease` and `gitTagPreRelease` this does not throw an Exception when the configuration does not\npermit creation of a tag.\n\n⚠️ This behavior has changed since 0.35.0, when this always created a pre-release version tag. To create a pre-release\nversion tag use `gitTagPreRelease` now.\n\n## Issue trackers\n\ngver can determine the type of change by checking the issues referred to in the commit message. At present, it supports\ntwo issue trackers,\nGitHub and Gitea.\n\n### GitHub\n\nIf your tickets are tracked at GitHub, you can determine the type of change from the labels of an issue.\nFirst you configure gver to check issues on GitHub:\n\n```groovy\ngver {\n    issueTracker GitHub {\n        repo = \"dmfs/gver\"  // account/repo\n        if (project.hasProperty(\"GITHUB_API_TOKEN\")) {\n            // put the api token into your global gradle properties, never under version control\n            accessToken = GITHUB_API_TOKEN\n        }\n    }\n    ...\n}\n```\n\nFor public repos you can omit the API token. Note, however, that GitHub has a quota for unauthenticated API requests.\nWhen you don't\nprovide an API token, the resulting version may be incorrect. Make sure you always provide a valid token in deployment\nenvironments.\n\nNow you can specify change types testing the issues.\n\n```groovy\ngver {\n    ...\n    are minor when {\n        commitMessage contains(~/#(?\u003cissue\u003e\\d+)\\b/) {\n            where(\"issue\") { isIssue { labeled \"enhancement\" } }\n        }\n    }\n    are patch when {\n        commitMessage contains(~/#(?\u003cissue\u003e\\d+)\\b/) {\n            where(\"issue\") { isIssue { not(labeled \"enhancement\") } }\n        }\n    }\n    ...\n}\n```\n\nThis considers a change to be minor when it contains a GitHub issue reference to an issue with the label `enhancement`\nand to be a patch when\nit contains a GitHub issue reference to an issue without the label `enhancement`.\n\n### Gitea\n\nThe Gitea DSL is much like the GitHub DSL, you just need to provide the Gitea host name\n\n```groovy\ngver {\n    issueTracker Gitea {\n        host = \"gitea.example.com\"\n        repo = \"dmfs/gver\"  // account/repo\n        if (project.hasProperty(\"GITEA_API_TOKEN\")) {\n            // put the api token into your global gradle properties, never under version control\n            accessToken = GITEA_API_TOKEN\n        }\n    }\n    ...\n}\n```\n\n## Dirty working trees\n\nAt present a dirty working tree always results in a pre-release version. Depending on the last tag, either the\npre-release or the minor version\nis incremented. This prevents accidental release builds after a file has been changed.\n\n# Maven Plugin\n\n*Note, the Maven Plugin is currently in experimental state and may be subject to change.*\n\nYou can use some of the features with Maven too. There is a plugin that takes the same configuration and provides\na goal to apply the version to the project and create a tag.\n\n## Usage\n\nApply the `gver-maven` plugin and configure it like this:\n\n```xml\n\n\u003cbuild\u003e\n    \u003cplugins\u003e\n        \u003cplugin\u003e\n            \u003cgroupId\u003eorg.dmfs\u003c/groupId\u003e\n            \u003cartifactId\u003egver-maven\u003c/artifactId\u003e\n            \u003cversion\u003e0.37.0\u003c/version\u003e\n            \u003cconfiguration\u003e\n                \u003cconfig\u003eL: {\n                    changes {\n                    are none when {\n                    affects only(matches(~/.*\\.md/))\n                    }\n                    are major when {\n                    commitMessage contains(~/(?i)#(major|break(ing)?)\\b/)\n                    }\n                    are minor when {\n                    commitMessage^ contains(~/(?i)#minor\\b/)\n                    }\n                    otherwise patch\n                    }}\n                \u003c/config\u003e\n            \u003c/configuration\u003e\n        \u003c/plugin\u003e\n    \u003c/plugins\u003e\n\u003c/build\u003e\n```\n\nNote, the `L:` is not a typo, it must be present before the closure.\nThe configuration itself works as described for the Gradle Plugin above, although some options may not be usable with\nMaven.\n\n## Goals\n\n### `gver:tag-and-set-version`\n\nTo update the project to the current version (as determined using the tag configuration) run\n\n```bash\nmvn gver:tag-and-set-version\n```\n\n**Important:** (unless the project version equals the determined version) this modifies the pom files, resulting in a\ndirty working tree. This means, after executing this goal, the next version will be e different every time.\n\n## License\n\nCopyright 2024 dmfs GmbH\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the\nLicense. You may obtain a copy of the\nLicense at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"\nAS IS\" BASIS, WITHOUT WARRANTIES OR\nCONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and\nlimitations under the License.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmfs%2Fgver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmfs%2Fgver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmfs%2Fgver/lists"}