# HG changeset patch # User cin # Date 2022-03-21 00:11:00 # Node ID 193d6f38df6e1510f7e239dad24399c54e1246b3 # Parent 47abdf72678542dc865605a8a02c4e1e9c139fe7 added preReleasePolicy, stagingBookmark, releaseBookmark to the extension, added documentation. SemVersion made read-only record, convenient method names. diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +group=org.implab.gradle +version=1.0.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b GIT binary patch literal 58702 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/mercurial/build.gradle b/mercurial/build.gradle --- a/mercurial/build.gradle +++ b/mercurial/build.gradle @@ -1,17 +1,26 @@ plugins { id "java-gradle-plugin" + id "com.gradle.plugin-publish" version "0.16.0" } - repositories { mavenCentral() } gradlePlugin { plugins { - containerPlugin { + mercurialPlugin { id = 'org.implab.gradle-mercurial' implementationClass = 'org.implab.gradle.mercurial.MercurialPlugin' + displayName = "Integrates mercurial into the build script" + description = 'Automatically calculates version using tags. Simple wrapper around cli.' } } } + +pluginBundle { + website = 'https://code.implab.org/implab/gradle-mercurial-plugin' + vcsUrl = 'https://code.implab.org/implab/gradle-mercurial-plugin' + + tags = ['hg', 'mercurial'] +} diff --git a/mercurial/src/main/java/org/implab/gradle/Utils.java b/mercurial/src/main/java/org/implab/gradle/Utils.java --- a/mercurial/src/main/java/org/implab/gradle/Utils.java +++ b/mercurial/src/main/java/org/implab/gradle/Utils.java @@ -18,42 +18,47 @@ import groovy.lang.Closure; public final class Utils { public static void redirectIO(final InputStream src, final Action consumer) { - new Thread(() -> { - try (Scanner sc = new Scanner(src)) { - while (sc.hasNextLine()) { - consumer.execute(sc.nextLine()); - } - } - }).start(); - } + new Thread(() -> { + try (Scanner sc = new Scanner(src)) { + while (sc.hasNextLine()) { + consumer.execute(sc.nextLine()); + } + } + }).start(); + } - public static String execCmd(List args, String charset) throws IOException, InterruptedException, Exception { + public static String execCmd(List args, String charset) { if (args == null || args.size() == 0) throw new IllegalArgumentException(); ProcessBuilder builder = new ProcessBuilder(args); - Process p = builder.start(); + Process p; + try { + p = builder.start(); - String output = readAll(p.getInputStream(), charset); + String output = readAll(p.getInputStream(), charset); - int code = p.waitFor(); - if (code != 0) - throw new Exception(String.format("The process `%s` failed with code: %s", args.get(0), code)); + int code = p.waitFor(); + if (code != 0) + throw new RuntimeException(String.format("The process `%s` failed with code: %s", args.get(0), code)); - return output; - + return output; + } catch (IOException | InterruptedException e) { + throw new RuntimeException(String.format("Failed to execute `%s`", args.get(0)), e); + } + } public static void redirectIO(final InputStream src, final File file) { - new Thread(() -> { + new Thread(() -> { try (OutputStream out = new FileOutputStream(file)) { src.transferTo(out); - } catch(Exception e) { + } catch (Exception e) { // silence! } - }).start(); - } + }).start(); + } public static String readAll(final InputStream src) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -67,24 +72,25 @@ public final class Utils { return out.toString(charset); } - public static JsonGenerator createDefaultJsonGenerator() { + public static JsonGenerator createDefaultJsonGenerator() { return new JsonGenerator.Options() - .excludeNulls() - .addConverter(new Converter() { - public boolean handles(Class type) { - return (File.class == type); - } - public Object convert(Object value, String key) { - return ((File)value).getPath(); - } - }) - .build(); + .excludeNulls() + .addConverter(new Converter() { + public boolean handles(Class type) { + return (File.class == type); + } + + public Object convert(Object value, String key) { + return ((File) value).getPath(); + } + }) + .build(); } public static void closeSilent(Closeable handle) { try { handle.close(); - } catch(Exception e) { + } catch (Exception e) { // silence! } } diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialExtension.java b/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialExtension.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialExtension.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialExtension.java @@ -6,12 +6,15 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.gradle.api.Project; import org.implab.gradle.Utils; +import groovy.lang.Closure; + public class MercurialExtension { private static final String COMMIT_INFO_TEMPLATE = "{latesttag('re:^v') % '{ifeq(tag,'null','',tag)}:{distance}'}:{branch}:{node|short}"; @@ -25,15 +28,21 @@ public class MercurialExtension { private String hgCmd = "hg"; + private String stagingBookmark = "staging"; + + private String releaseBookmark = "prod"; + private SemVersion defaultWorkspaceVersion = new SemVersion(0, 0, 1, "dev", null); + private Function preReleasePolicy; + public MercurialExtension(Project project) { Objects.requireNonNull(project); this.project = project; } - public String hg(String... args) throws Exception { + public String hg(String... args) { List commandLine = new ArrayList<>(); commandLine.add(hgCmd); Collections.addAll(commandLine, args); @@ -41,6 +50,26 @@ public class MercurialExtension { return Utils.execCmd(commandLine, outputCharset.name()); } + public String getReleaseBookmark() { + return releaseBookmark; + } + + public void setReleaseBookmark(String value) { + if(value == null) + throw new IllegalArgumentException(); + releaseBookmark = value; + } + + public String getStagingBookmark() { + return stagingBookmark; + } + + public void setStagingBookmark(String value) { + if(value == null) + throw new IllegalArgumentException(); + stagingBookmark = value; + } + public Charset getOutputCharset() { return outputCharset; } @@ -62,42 +91,71 @@ public class MercurialExtension { this.hgCmd = hgCmd; } - public WorkspaceInfo getWorkspaceInfo() throws Exception { + public WorkspaceInfo getWorkspaceInfo() { if (workspaceInfo == null) workspaceInfo = loadWorkspaceInfo(); return workspaceInfo; } - public SemVersion getWorkspaceVersion() throws Exception { + public SemVersion getWorkspaceVersion() { return tryParseVersion(getWorkspaceInfo().getVersionTag()).orElse(defaultWorkspaceVersion); } - public void applyVersioningPolicy() throws Exception { + private SemVersion defaultPreReleasePolicy(SemVersion version) { + String changeset = getWorkspaceInfo().getChangeset(); + return version.addPatch(1).withPreRelease("snapshot").withMeta(changeset); + } + + public void preReleasePolicy(Closure policy) { + if (policy == null) + throw new IllegalArgumentException(); + + this.preReleasePolicy = x -> { + policy.setDelegate(getWorkspaceInfo()); + policy.setResolveStrategy(Closure.DELEGATE_FIRST); + return policy.call(x); + }; + } + + public void preReleasePolicy(Function policy) { + if (policy == null) + throw new IllegalArgumentException(); + this.preReleasePolicy = policy; + } + + public void applyVersioningPolicy() { Object version = project.getVersion(); - if (version == null || version.toString().isBlank() || version.toString().equalsIgnoreCase(Project.DEFAULT_VERSION)) { + if (version == null || version.toString().isBlank() + || version.toString().equalsIgnoreCase(Project.DEFAULT_VERSION)) { + // if the version isn't specified int distance = getWorkspaceInfo().getVersionDistance(); - String changeset = getWorkspaceInfo().getChangeset(); - project.setVersion( - distance> 0 ? - getWorkspaceVersion().addPatch(distance).suffix("snapshot").meta(changeset) : - getWorkspaceVersion() - ); + + // the current workspace is not tagged with the version + if (distance > 0) { + SemVersion preReleaseVersion = Optional.ofNullable(preReleasePolicy) + .orElse(this::defaultPreReleasePolicy) + .apply(getWorkspaceVersion()); + + project.setVersion(preReleaseVersion); + } else { + project.setVersion(getWorkspaceVersion()); + } } else { project.setVersion(SemVersion.toSemVersion(version, false)); } } - String[] queryLogInfo() throws Exception { + String[] queryLogInfo() { return hg("log", "-r", ".", "--template", COMMIT_INFO_TEMPLATE).split(":"); } - WorkspaceInfo loadWorkspaceInfo() throws Exception { + WorkspaceInfo loadWorkspaceInfo() { boolean isDirty = checkIsDirty(); String[] info = queryLogInfo(); return new WorkspaceInfo(info[0], Integer.parseInt(info[1]), info[2], info[3], isDirty); } - boolean checkIsDirty() throws Exception { + boolean checkIsDirty() { return !hg("status", "-mard").isBlank(); } @@ -111,5 +169,4 @@ public class MercurialExtension { return isMatch ? SemVersion.tryParseLax(m.group(1)) : Optional.empty(); } - } diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialPlugin.java b/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialPlugin.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialPlugin.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/MercurialPlugin.java @@ -29,6 +29,7 @@ public class MercurialPlugin implements project.getTasks().register("printVersion", t -> { t.doLast(t2 -> { + t2.getLogger().quiet("workspace: {}", mercurialExtension.getWorkspaceInfo()); t2.getLogger().quiet("version: {}", project.getVersion()); }); }); @@ -50,11 +51,11 @@ public class MercurialPlugin implements throw new Exception("The workspace is dirty, you need to commit changes first"); SemVersion version = (SemVersion) project.getVersion(); - SemVersion nextVersion = version.release(); + SemVersion nextVersion = version.withNoSuffix(); - self.getLogger().quiet("release {} -> {}", version, nextVersion); + self.getLogger().quiet("{} {} -> {}", mercurialExtension.getReleaseBookmark(), version, nextVersion); - hgBookmarkTask.get().getBookmarks().add("prod"); + hgBookmarkTask.get().getBookmarks().add(mercurialExtension.getReleaseBookmark()); hgTagTask.get().getTags().add("v" + nextVersion.toString()); } catch (Exception e) { throw new TaskExecutionException(self, e); @@ -70,11 +71,11 @@ public class MercurialPlugin implements throw new Exception("The workspace is dirty, you need to commit changes first"); SemVersion version = (SemVersion) project.getVersion(); - SemVersion nextVersion = version.release().suffix("rc"); + SemVersion nextVersion = version.withNoSuffix().withPreRelease("rc"); - self.getLogger().quiet("staging {} -> {}", version, nextVersion); + self.getLogger().quiet("{} {} -> {}", mercurialExtension.getStagingBookmark(), version, nextVersion); - hgBookmarkTask.get().getBookmarks().add("staging"); + hgBookmarkTask.get().getBookmarks().add(mercurialExtension.getStagingBookmark()); hgTagTask.get().getTags().add("v" + nextVersion.toString()); } catch (Exception e) { throw new TaskExecutionException(self, e); diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/SemVersion.java b/mercurial/src/main/java/org/implab/gradle/mercurial/SemVersion.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/SemVersion.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/SemVersion.java @@ -8,6 +8,14 @@ import java.util.regex.Pattern; import javax.annotation.Nonnull; +/** + * Semantic version object. + * + *

+ * This is a readonly version record which follows general rules of + * semantic versioning. The object is serializable and can be used as + * input property value for build tasks. + */ public class SemVersion implements Serializable, Comparable { private static final long serialVersionUID = 1L; @@ -18,77 +26,61 @@ public class SemVersion implements Seria private static Pattern semVerLaxPattern = Pattern.compile( "^(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?)?(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"); - private int major; + private final int major; - private int minor; + private final int minor; - private int patch; + private final int patch; - private String suffix; + private final String preRelease; + + private final String meta; - private String meta; - + /** Creates the new and empty version */ public SemVersion() { + this(0, 0, 0, null, null); } - public SemVersion(int major, int minor, int patch, String suffix, String meta) { + /** Creates the version object with the specified values */ + public SemVersion(int major, int minor, int patch, String preRelease, String meta) { this.major = major; this.minor = minor; this.patch = patch; - this.suffix = suffix; + this.preRelease = preRelease; this.meta = meta; } + /** Creates the version object with the specified values */ public SemVersion(int major, int minor, int patch) { this(major, minor, patch, null, null); } - public SemVersion(SemVersion src) { - this(src.major, src.minor, src.patch, src.suffix, src.meta); - } - + /** Returns the major part of the version */ public int getMajor() { return major; } - public void setMajor(int major) { - this.major = major; - } - + /** Returns the minor part of the version */ public int getMinor() { return minor; } - public void setMinor(int minor) { - this.minor = minor; - } - + /** Returns the patch part of the version */ public int getPatch() { return patch; } - public void setPatch(int patch) { - this.patch = patch; - } - - public Optional getSuffix() { - return Optional.ofNullable(suffix); - } - - public void setSuffix(String suffix) { - this.suffix = suffix; + /** Gets the optional pre-release version part */ + public Optional getPreRelease() { + return Optional.ofNullable(preRelease); } public Optional getMeta() { return Optional.ofNullable(meta); } - public void setMeta(String meta) { - this.meta = meta; - } - public boolean isRelease() { - return getSuffix().isEmpty(); + return getPreRelease().isEmpty(); } @Override @@ -96,7 +88,7 @@ public class SemVersion implements Seria StringBuilder sb = new StringBuilder(); sb.append(major).append(".").append(minor).append(".").append(patch); - getSuffix().ifPresent(s -> sb.append("-").append(s)); + getPreRelease().ifPresent(s -> sb.append("-").append(s)); getMeta().ifPresent(m -> sb.append("+").append(m)); return sb.toString(); @@ -112,67 +104,92 @@ public class SemVersion implements Seria return res; res = Integer.compare(patch, other.patch); - return getSuffix() - // if we have suffix, chech another suffix, if there is no another suffix - // then another version is bigger: 1.0.0-rc < 1.0.0 - .map(s1 -> other.getSuffix().map(s2 -> s1.compareTo(s2)).orElse(-1)) + return getPreRelease() + // if we have suffix, check the other version suffix, + // if the other has no suffix then it's greater. + // 1.0.0-rc < 1.0.0 + .map(s1 -> other.getPreRelease().map(s2 -> s1.compareTo(s2)).orElse(-1)) // if we don't have the suffix - .orElse(other.getSuffix().isPresent() ? 1 : 0); + .orElse(other.getPreRelease().isPresent() ? 1 : 0); } - public SemVersion release() { + /** Removes the tag (pre-release and meta information) */ + public SemVersion withNoSuffix() { return new SemVersion(major, minor, patch, null, null); } - public SemVersion suffix(String suffix) { - return new SemVersion(major, minor, patch, suffix, meta); + /** Sets the specified suffix leaving other parts unchanged */ + public SemVersion withPreRelease(String preRelease) { + return new SemVersion(major, minor, patch, preRelease, meta); } - public SemVersion suffix() { + /** Removes pre-release information leaving other parts unchanged */ + public SemVersion withNoPreRelease() { return new SemVersion(major, minor, patch, null, meta); } - public SemVersion meta(String meta) { - return new SemVersion(major, minor, patch, suffix, meta); + /** Sets the specified build metadata */ + public SemVersion withMeta(String meta) { + return new SemVersion(major, minor, patch, preRelease, meta); } - public SemVersion meta() { - return new SemVersion(major, minor, patch, suffix, null); + /** Removes the build metadata from the version */ + public SemVersion withNoMeta() { + return new SemVersion(major, minor, patch, preRelease, null); } - public SemVersion patch(int patch) { - return new SemVersion(major, minor, patch, suffix, meta); + /** Sets the specified patch version */ + public SemVersion withPatch(int patch) { + return new SemVersion(major, minor, patch, preRelease, meta); } - public SemVersion minor(int minor) { - return new SemVersion(major, minor, patch, suffix, meta); + /** Sets the specified minor version */ + public SemVersion withMinor(int minor) { + return new SemVersion(major, minor, patch, preRelease, meta); } - public SemVersion major(int major) { - return new SemVersion(major, minor, patch, suffix, meta); + /** Sets the specified major version */ + public SemVersion withMajor(int major) { + return new SemVersion(major, minor, patch, preRelease, meta); } + /** Adds the specified number to the patch version */ public SemVersion addPatch(int number) { - return new SemVersion(major, minor, patch + number, suffix, meta); + return new SemVersion(major, minor, patch + number, preRelease, meta); } + /** Adds the specified number to the minor version */ public SemVersion addMinor(int number) { - return new SemVersion(major, minor + number, patch, suffix, meta); + return new SemVersion(major, minor + number, patch, preRelease, meta); } + /** Adds the specified number to the major version */ public SemVersion addMajor(int number) { - return new SemVersion(major + number, minor, patch, suffix, meta); + return new SemVersion(major + number, minor, patch, preRelease, meta); } + /** Generated the next minor version by incrementing the minor version, + * setting the patch version to 0 and removing the pre-release information. + * The build metadata is left unchanged. + * + *

Use this method to publish next minor version. + */ public SemVersion nextMinor() { return new SemVersion(major, minor + 1, 0, null, meta); } + /** Generated the next minor version by incrementing the major version, + * setting the minor version and the patch to 0 and removing the + * pre-release information. The build metadata is left unchanged. + * + *

Use this method to publish next major version. + */ public SemVersion nextMajor() { return new SemVersion(major + 1, 0, 0, null, meta); } + /** Parses the version string. */ public static Optional tryParse(@Nonnull String str) { Objects.requireNonNull(str); @@ -183,6 +200,7 @@ public class SemVersion implements Seria : Optional.empty(); } + /** Parses the version string. When */ public static Optional tryParseLax(@Nonnull String str) { Objects.requireNonNull(str); @@ -198,10 +216,11 @@ public class SemVersion implements Seria if (value == null) { return new SemVersion(); } else if (value instanceof SemVersion) { - return new SemVersion((SemVersion) value); + return (SemVersion) value; } else { Optional semver = strict ? tryParse(value.toString()) : tryParseLax(value.toString()); - return semver.orElseThrow(() -> new IllegalArgumentException(String.format("Failed to parse semver '%s', strict=%s", value, strict))); + return semver.orElseThrow(() -> new IllegalArgumentException( + String.format("Failed to parse semver '%s', strict=%s", value, strict))); } } diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgBookmark.java b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgBookmark.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgBookmark.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgBookmark.java @@ -10,12 +10,11 @@ import org.gradle.api.DefaultTask; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.implab.gradle.Utils; import org.implab.gradle.mercurial.MercurialExtension; -import groovy.transform.Internal; - public class HgBookmark extends DefaultTask { private final Property cliCmd; @@ -28,6 +27,7 @@ public class HgBookmark extends DefaultT private String commandOutput; + @Internal protected MercurialExtension getMercurialExtension() { return getProject().getExtensions().getByType(MercurialExtension.class); } diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTag.java b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTag.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTag.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTag.java @@ -10,14 +10,11 @@ import org.gradle.api.DefaultTask; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.SkipWhenEmpty; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.implab.gradle.Utils; import org.implab.gradle.mercurial.MercurialExtension; -import groovy.transform.Internal; - public class HgTag extends DefaultTask { private final Property cliCmd; @@ -28,6 +25,7 @@ public class HgTag extends DefaultTask { private String commandOutput; + @Internal protected MercurialExtension getMercurialExtension() { return getProject().getExtensions().getByType(MercurialExtension.class); } @@ -52,8 +50,7 @@ public class HgTag extends DefaultTask { return commandOutput; } - @Input - @SkipWhenEmpty + @Internal public ListProperty getTags() { return tags; } diff --git a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTask.java b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTask.java --- a/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTask.java +++ b/mercurial/src/main/java/org/implab/gradle/mercurial/tasks/HgTask.java @@ -7,6 +7,7 @@ import java.util.List; import org.gradle.api.DefaultTask; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.implab.gradle.Utils; import org.implab.gradle.mercurial.MercurialExtension; @@ -19,6 +20,7 @@ public class HgTask extends DefaultTask private String commandOutput; + @Internal protected MercurialExtension getMercurialExtension() { return getProject().getExtensions().getByType(MercurialExtension.class); } @@ -29,6 +31,7 @@ public class HgTask extends DefaultTask } + @Internal public String getCommandOutput() { return commandOutput; } diff --git a/readme.md b/readme.md new file mode 100644 --- /dev/null +++ b/readme.md @@ -0,0 +1,120 @@ +# Mercurial build integration + +## SYNOPSIS + +```groovy + +plugins { + id 'org.implab.gradle-mercurial' version '1.0' +} + +mercurial { + releaseBookmark = 'prod' + + preReleasePolicy { it + // this is the default behaviour + .addPatch(1) + .withPreRelease('snapshot') + .withMeta('changeset') + } + + // get version from repository and apply it to the project + applyVersioningPolicy() +} + +``` + +## DESCRIPTION + +### `semver(versionString)` + +Parses the specified version string and returns the semantic version object + +### `mercurial(Closure configure)` + +The extension added by the plugin to the project. Apply this plugin to the +root project and use the `mercurial { ... }` section to configure the +versioning. + +See the description below for more details about settings. + +#### `hgCmd` + +The command which will be executed to perform the mercurial commands. +The default value is `hg`. + +#### `outputCharset` + +The charset for the `hgCmd` output, this property defaults +to the default charset of the operating system. + +#### `hg(...args)` + +Executes the mercurial command, returns the command output as text. + +#### `workspaceInfo` (read-only) + +Returns the information about current workspace, see description +below for details. + +#### `workspaceVersion` + +Returns the semantic version obtained from the repository. Querying +this property will lead querying the workspace info. + +#### `applyVersioningPolicy()` + +Reads the workspace information and sets the version of the project +according to settings. + +If the version was specified using properties, then the specified +version will take preceding over the calculated one. + +#### `preReleasePolicy(Closure config)` + +The closure which is used to calculate the version of the project +when the current commit isn't marked with a version tag. + +The default behaviour is to increment the patch version and +to set the pre-release suffix to `'snapshot'`. + +### `WorkspaceInfo` + +This class encapsulates information about the current workspace. +All properties are read-only. + +#### `versionTag` + +The original version tag from the repository before parsing. + +#### `changeSet` + +The changeset id of the current workspace. + +#### `versionDistance` + +The distance in commits to the versionTag + +#### `branch` + +The name of the current sources branch. + +#### `isDirty` + +This flag indicates uncommited changes to the workspace. This flag is useful +to control some pipelines which run locally on developer machines. + +## TASKS + +### `release` + +Marks this commit with current version and pre-release suffix removed and +moves the `release` bookmark to it. + +### `staging` + +Marks this commit with current version and moves the `staging` bookmark to it. + +### `printVersion` + +Prints the current version.