Skip to content

Commit

Permalink
Release by Condition, closes #112
Browse files Browse the repository at this point in the history
  • Loading branch information
dmfs committed May 25, 2024
1 parent 4da3ac3 commit fc6671e
Show file tree
Hide file tree
Showing 22 changed files with 779 additions and 140 deletions.
78 changes: 62 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ gver {
branch not(matches(~/main/))
}
}
// releases can only be made on the main branch, on other branches the `gitRelease` task will fail
releaseBranchPattern ~/main$/ // defaults to ~/(main|master)$/
// releases can only be made on the main branch, on other branches the `gitTagRelease` task will fail
tag {
with release when {
branch matches(~/main|release.*/)
}
with preRelease when {
branch matches(~/^(feature|bug).*/)
}
with nothing otherwise
}
}
```

Expand All @@ -89,7 +97,8 @@ When practising semantic versioning, the most important step is to understand th
gver provides
a DSL to describe how to derive the kind of change based on commit message or referenced issues.

The top-level element is `changes` which takes a closure describing when a change is considered a major, minor or bugfix change.
The top-level element is `changes` which takes a closure describing when a change is considered a major, minor or bugfix
change.
The list of conditions is evaluated top to bottom until the first one matches.

```groovy
Expand Down Expand Up @@ -129,7 +138,8 @@ gver {
```

Note that all conditions inside a change type closure must match in order to apply the change type. If you need to
express a logical `or`, describe the same change type with the other condition underneath the first one or use `anyOf` described below.
express a logical `or`, describe the same change type with the other condition underneath the first one or use `anyOf`
described below.

The `otherwise` case should be last in the list as it catches all cases that didn't match any of the other conditions.
The default is to
Expand Down Expand Up @@ -211,7 +221,8 @@ gver {

This identifies major changes by the presence of the `#breaking` hashtag in the commit message.

Another common pattern is to consider a change a bugfix when it contains one of "fixes", "fixed", "resolves" or "resolved" followed by a `#` and
Another common pattern is to consider a change a bugfix when it contains one of "fixes", "fixed", "resolves" or "
resolved" followed by a `#` and
a numeric issue identifier.

```groovy
Expand Down Expand Up @@ -251,7 +262,8 @@ gver {

#### affects

This condition allows you to determine a change type based on the files that have been affected by a commit. It takes a `Predicate` of a `Set<String>`
This condition allows you to determine a change type based on the files that have been affected by a commit. It takes
a `Predicate` of a `Set<String>`
like `anyThat`, `noneThat` or `only`.

Example:
Expand Down Expand Up @@ -342,7 +354,8 @@ gver {
}
```

In the closure passed to `use` you can use any groups declared in your regular expression. The resulting pre-release version will automatically
In the closure passed to `use` you can use any groups declared in your regular expression. The resulting pre-release
version will automatically
be sanitized to comply with SemVer syntax.

When your pre-release doesn't end with a numeric segment, the next pre-relase will automatically append `.1` and
Expand Down Expand Up @@ -418,26 +431,58 @@ gver {

### Tagging releases

gver can tag your current Git head with the current version or the next release version.
gver can tag your current Git head with the current pre-release or the next release version.

The task `gitTag` creates a tag with the current pre-release version. The task `gitTagRelease` creates a tag with the next release version (unless
the current commit already is a release version).
By default, tags can only be created on `main` or `master` branches.
Tagging can be configured with conditions like above. The following tag types are currently supported

To prevent accidental release version tags on non-release branches, you can provide a pattern matching your release branch names.
* `release`
* `preRelease`
* `none`

With **none** causing any tag task to fail when the condition matches.

Example:

```groovy
gver {
...
releaseBranchPattern ~/(main|release\/.*)$/
tag {
with release when {
branch matches(~/^release\/.*/)
}
with preRelease when {
branch matches(~/^(feature|bugfix)\/.*/)
}
with none otherwise
}
}
```

This will cause the `gitTagRelease` task to fail on any branch not matching that pattern. The default
is `~/(main|master)$/`
*Note*: the `releaseBranchPattern` directive is deprecated and will be removed.

The following tasks are available to tag the current Git head:

#### `gitTagRelease`

Adds a release version tag to the current Git head, if allowed by the `tag` configuration.

#### `gitTagPreRelease`

Adds a pre-release version tag to the current Git head, if allowed by the `tag` configuration.

#### `gitTag`

Adds a version tag to the current Git head. The release type is determined by the `tag` configuration. The first
entry that matches the current head to be precise.

⚠️ This behavior has changed since 0.35.0, when this always created a pre-release version tag. To create a pre-release
version tag use `gitTagPreRelease` now.

## Issue trackers

gver can determine the type of change by checking the issues referred to in the commit message. At present, it supports two issue trackers,
gver can determine the type of change by checking the issues referred to in the commit message. At present, it supports
two issue trackers,
GitHub and Gitea.

### GitHub
Expand Down Expand Up @@ -506,7 +551,8 @@ gver {

## Dirty working trees

At present a dirty working tree always results in a pre-release version. Depending on the last tag, either the pre-release or the minor version
At present a dirty working tree always results in a pre-release version. Depending on the last tag, either the
pre-release or the minor version
is incremented. This prevents accidental release builds after a file has been changed.

## License
Expand Down
28 changes: 14 additions & 14 deletions gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ repositories {

dependencies {
implementation project(":gver-dsl")
implementation "org.dmfs:semver:0.2.0"
implementation "org.dmfs:semver:1.0.0"
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r'
implementation 'org.dmfs:srcless-annotations:0.3.0'
implementation "org.dmfs:jems2:2.19.0"
annotationProcessor 'org.dmfs:srcless-processors:0.3.0'

testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'
testImplementation "org.dmfs:jems2-testing:2.19.0"
implementation 'org.dmfs:srcless-annotations:0.6.0'
implementation 'org.dmfs:jems2:2.23.1'
annotationProcessor 'org.dmfs:srcless-processors:0.6.0'

testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.0-M2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.0-M2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'
testImplementation 'org.dmfs:jems2-testing:2.23.1'
testImplementation "org.hamcrest:hamcrest:2.2"
testImplementation "org.dmfs:jems2-confidence:2.19.0"
testImplementation 'org.dmfs:jems2-confidence:2.23.1'
testImplementation "org.hamcrest:hamcrest:2.2"
testImplementation "org.saynotobugs:confidence-core:0.28.0"
testImplementation "org.saynotobugs:confidence-incubator:0.28.0"
testImplementation "org.saynotobugs:confidence-test:0.28.0"
testAnnotationProcessor 'org.dmfs:srcless-processors:0.3.0'
testImplementation 'org.saynotobugs:confidence-core:0.45.1'
testImplementation 'org.saynotobugs:confidence-incubator:0.45.1'
testImplementation 'org.saynotobugs:confidence-test:0.45.1'
testAnnotationProcessor 'org.dmfs:srcless-processors:0.6.0'
}

test {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.dmfs.gradle.gver;

import org.dmfs.gradle.gver.tasks.TagPreReleaseTask;
import org.dmfs.gradle.gver.tasks.TagReleaseTask;
import org.dmfs.gradle.gver.tasks.TagTask;
import org.dmfs.gradle.gver.tasks.VersionTask;
import org.dmfs.gradle.gver.utils.ProjectRepositoryFunction;
import org.dmfs.gver.dsl.GitVersionConfig;
import org.dmfs.gver.git.GitVersion;
import org.dmfs.gver.git.changetypefacories.FirstOf;
import org.dmfs.gradle.gver.tasks.VersionTask;
import org.dmfs.jems2.Single;
import org.dmfs.jems2.single.Frozen;
import org.dmfs.semver.VersionSequence;
Expand Down Expand Up @@ -73,6 +74,7 @@ public String toString()

project.getTasks().register("gitTag", TagTask.class);
project.getTasks().register("gitTagRelease", TagReleaseTask.class);
project.getTasks().register("gitTagPreRelease", TagPreReleaseTask.class);
project.getTasks().register("gitVersion", VersionTask.class);

project.subprojects(subproject -> subproject.setVersion(project.getVersion()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.dmfs.gradle.gver.tasks;

import org.dmfs.gradle.gver.utils.ProjectRepositoryFunction;
import org.dmfs.gver.dsl.GitVersionConfig;
import org.dmfs.gver.dsl.procedure.CreateTag;
import org.dmfs.semver.StrictParser;
import org.eclipse.jgit.lib.Repository;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskAction;

import java.io.IOException;

import static org.dmfs.gver.dsl.ReleaseType.PRERELEASE;


/**
* A Gradle {@link Task} that tags the current HEAD with a new pre-release version.
* <p>
* This task will throw an {@link IllegalStateException} when the current HEAD is not on a pre-release branch.
*/
public class TagPreReleaseTask extends DefaultTask
{
public TagPreReleaseTask()
{
setGroup("gver");
setDescription("Tags the HEAD commit with a new pre-release version.");
}

@TaskAction
public void perform() throws IOException
{
try (Repository repository = ProjectRepositoryFunction.INSTANCE.value(getProject()))
{
new CreateTag(
getLogger()::lifecycle,
PRERELEASE::equals,
(GitVersionConfig) getProject().getExtensions().getByName("gver"),
new StrictParser().parse(getProject().getVersion().toString()))
.process(repository);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@

import org.dmfs.gradle.gver.utils.ProjectRepositoryFunction;
import org.dmfs.gver.dsl.GitVersionConfig;
import org.dmfs.jems2.iterable.Mapped;
import org.dmfs.jems2.optional.First;
import org.dmfs.semver.Release;
import org.dmfs.gver.dsl.procedure.CreateTag;
import org.dmfs.semver.StrictParser;
import org.dmfs.semver.VersionSequence;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskAction;

import java.io.IOException;

import static org.eclipse.jgit.lib.Constants.R_TAGS;
import static org.dmfs.gver.dsl.ReleaseType.RELEASE;


/**
* A Gradle {@link Task} that tags the current HEAD with a new release version.
* Note, that this task will throw an {@link IllegalStateException} when the current HEAD is not on a release branch.
* <p>
* This task will throw an {@link IllegalStateException} when the current HEAD is not on a release branch.
*/
public class TagReleaseTask extends DefaultTask
{
Expand All @@ -33,36 +28,16 @@ public TagReleaseTask()
}

@TaskAction
public void perform() throws IOException, GitAPIException
public void perform() throws IOException
{
try (Repository r = ProjectRepositoryFunction.INSTANCE.value(getProject()))
try (Repository repository = ProjectRepositoryFunction.INSTANCE.value(getProject()))
{
Git git = new Git(r);
GitVersionConfig config = (GitVersionConfig) getProject().getExtensions().getByName("gver");
ObjectId head = r.resolve("HEAD");

if (!new First<>(branch -> config.mReleaseBranchPattern.matcher(branch).lookingAt(), git.nameRev()
.addPrefix("refs/heads")
.add(head).call().values()).isPresent())
{
throw new IllegalStateException("Not adding a release tag on non-release branch");
}

String version = new VersionSequence(new Release(new StrictParser().parse(getProject().getVersion().toString()))).toString();

if (!new First<>(version::equals,
new Mapped<>(tag -> tag.getName().startsWith(R_TAGS) ? tag.getName().substring(R_TAGS.length()) : tag.getName(),
git.tagList().call())).isPresent())
{
git.tag()
.setObjectId(r.parseCommit(head))
.setName(version)
.call();
}
else
{
getLogger().lifecycle("Tag {} already exists. Not adding tag.", version);
}
new CreateTag(
getLogger()::lifecycle,
RELEASE::equals,
(GitVersionConfig) getProject().getExtensions().getByName("gver"),
new StrictParser().parse(getProject().getVersion().toString()))
.process(repository);
}
}
}
26 changes: 14 additions & 12 deletions gradle-plugin/src/main/java/org/dmfs/gradle/gver/tasks/TagTask.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package org.dmfs.gradle.gver.tasks;

import org.dmfs.gradle.gver.utils.ProjectRepositoryFunction;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.dmfs.gver.dsl.GitVersionConfig;
import org.dmfs.gver.dsl.procedure.CreateTag;
import org.dmfs.jems2.predicate.Anything;
import org.dmfs.semver.StrictParser;
import org.eclipse.jgit.lib.Repository;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Task;
import org.gradle.api.tasks.TaskAction;

Expand All @@ -14,6 +15,8 @@

/**
* A {@link Task} that adds a tag with the current version to the current HEAD.
* <p>
* The decision whether the tag is a release or pre-release version depends on the {@code tag} configuration.
*/
public class TagTask extends DefaultTask
{
Expand All @@ -24,17 +27,16 @@ public TagTask()
}

@TaskAction
public void perform() throws IOException, GitAPIException
public void perform() throws IOException
{
try (Repository r = ProjectRepositoryFunction.INSTANCE.value(getProject()))
try (Repository repository = ProjectRepositoryFunction.INSTANCE.value(getProject()))
{
Git git = new Git(r);
if (git.status().call().hasUncommittedChanges()) {
throw new GradleException(
"The Git working tree must not be dirty. Please commit any uncommitted changes."
);
}
git.tag().setName(getProject().getVersion().toString()).call();
new CreateTag(
getLogger()::lifecycle,
new Anything<>(),
(GitVersionConfig) getProject().getExtensions().getByName("gver"),
new StrictParser().parse(getProject().getVersion().toString()))
.process(repository);
}
}
}
Loading

0 comments on commit fc6671e

Please sign in to comment.