diff --git a/doc/nbgv-cli.md b/doc/nbgv-cli.md
index 69b295a9..46ae2d10 100644
--- a/doc/nbgv-cli.md
+++ b/doc/nbgv-cli.md
@@ -165,6 +165,44 @@ For each branch, the following properties are provided:
**Note:** When the current branch is already the release branch for the current version, no new branch will be created.
In that case, the `NewBranch` property will be `null`.
+## Creating a version tag
+
+The `tag` command automates the task of tagging a commit with a version.
+
+To create a version tag, run:
+
+```ps1
+nbgv tag
+```
+
+This will:
+
+1. Read version.json to ascertain the version under development, and the naming convention of tag names.
+1. Create a new tag for that version.
+
+You can optionally include a version or commit id to create a new tag for an older version/commit, e.g.:
+
+```ps1
+nbgv tag 1.0.0
+```
+
+### Customizing the behaviour of `tag`
+
+The behaviour of the `tag` command can be customized in `version.json`:
+
+```json
+{
+ "version": "1.0",
+ "release": {
+ "tagName" : "v{version}"
+ }
+}
+```
+
+| Property | Default value | Description |
+|----------|---------------|-------------------------------------------------------------------------------------------------|
+| tagName | `v{version}` | Defines the format of tag names. Format must include a placeholder '{version}' for the version. |
+
## Learn more
There are several more sub-commands and switches to each to help you build and maintain your projects, find a commit that built a particular version later on, create tags, etc.
diff --git a/doc/versionJson.md b/doc/versionJson.md
index c3e14829..a56adb55 100644
--- a/doc/versionJson.md
+++ b/doc/versionJson.md
@@ -59,6 +59,7 @@ The content of the version.json file is a JSON serialized object with these prop
}
},
"release" : {
+ "tagName" : "v{version}",
"branchName" : "v{version}",
"versionIncrement" : "minor",
"firstUnstableTag" : "alpha"
diff --git a/src/NerdBank.GitVersioning/VersionOptions.cs b/src/NerdBank.GitVersioning/VersionOptions.cs
index b631b39e..7e47f06c 100644
--- a/src/NerdBank.GitVersioning/VersionOptions.cs
+++ b/src/NerdBank.GitVersioning/VersionOptions.cs
@@ -1442,7 +1442,7 @@ public int GetHashCode(CloudBuildNumberCommitIdOptions? obj)
}
///
- /// Encapsulates settings for the "prepare-release" command.
+ /// Encapsulates settings for the "prepare-release" and "tag" commands.
///
public class ReleaseOptions : IEquatable
{
@@ -1452,6 +1452,7 @@ public class ReleaseOptions : IEquatable
internal static readonly ReleaseOptions DefaultInstance = new ReleaseOptions()
{
isFrozen = true,
+ tagName = "v{version}",
branchName = "v{version}",
versionIncrement = ReleaseVersionIncrement.Minor,
firstUnstableTag = "alpha",
@@ -1460,6 +1461,9 @@ public class ReleaseOptions : IEquatable
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private bool isFrozen;
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private string? tagName;
+
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string? branchName;
@@ -1482,11 +1486,28 @@ public ReleaseOptions()
/// The existing instance to copy from.
public ReleaseOptions(ReleaseOptions copyFrom)
{
+ this.tagName = copyFrom.tagName;
this.branchName = copyFrom.branchName;
this.versionIncrement = copyFrom.versionIncrement;
this.firstUnstableTag = copyFrom.firstUnstableTag;
}
+ ///
+ /// Gets or sets the tag name template for tagging.
+ ///
+ [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
+ public string? TagName
+ {
+ get => this.tagName;
+ set => this.SetIfNotReadOnly(ref this.tagName, value);
+ }
+
+ ///
+ /// Gets the tag name template for tagging.
+ ///
+ [JsonIgnore]
+ public string TagNameOrDefault => this.TagName ?? DefaultInstance.TagName!;
+
///
/// Gets or sets the branch name template for release branches.
///
@@ -1498,7 +1519,7 @@ public string? BranchName
}
///
- /// Gets the set branch name template for release branches.
+ /// Gets the branch name template for release branches.
///
[JsonIgnore]
public string BranchNameOrDefault => this.BranchName ?? DefaultInstance.BranchName!;
@@ -1593,7 +1614,8 @@ public bool Equals(ReleaseOptions? x, ReleaseOptions? y)
return false;
}
- return StringComparer.Ordinal.Equals(x.BranchNameOrDefault, y.BranchNameOrDefault) &&
+ return StringComparer.Ordinal.Equals(x.TagNameOrDefault, y.TagNameOrDefault) &&
+ StringComparer.Ordinal.Equals(x.BranchNameOrDefault, y.BranchNameOrDefault) &&
x.VersionIncrementOrDefault == y.VersionIncrementOrDefault &&
StringComparer.Ordinal.Equals(x.FirstUnstableTagOrDefault, y.FirstUnstableTagOrDefault);
}
@@ -1608,7 +1630,8 @@ public int GetHashCode(ReleaseOptions? obj)
unchecked
{
- int hash = StringComparer.Ordinal.GetHashCode(obj.BranchNameOrDefault) * 397;
+ int hash = StringComparer.Ordinal.GetHashCode(obj.TagNameOrDefault) * 397;
+ hash ^= StringComparer.Ordinal.GetHashCode(obj.BranchNameOrDefault);
hash ^= (int)obj.VersionIncrementOrDefault;
hash ^= StringComparer.Ordinal.GetHashCode(obj.FirstUnstableTagOrDefault);
return hash;
diff --git a/src/NerdBank.GitVersioning/VersionOptionsContractResolver.cs b/src/NerdBank.GitVersioning/VersionOptionsContractResolver.cs
index 4975daa3..5b0355fa 100644
--- a/src/NerdBank.GitVersioning/VersionOptionsContractResolver.cs
+++ b/src/NerdBank.GitVersioning/VersionOptionsContractResolver.cs
@@ -126,6 +126,11 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ
property.ShouldSerialize = instance => !((VersionOptions)instance).ReleaseOrDefault.IsDefault;
}
+ if (property.DeclaringType == typeof(VersionOptions.ReleaseOptions) && member.Name == nameof(VersionOptions.ReleaseOptions.TagName))
+ {
+ property.ShouldSerialize = instance => ((VersionOptions.ReleaseOptions)instance).TagNameOrDefault != VersionOptions.ReleaseOptions.DefaultInstance.TagName;
+ }
+
if (property.DeclaringType == typeof(VersionOptions.ReleaseOptions) && member.Name == nameof(VersionOptions.ReleaseOptions.BranchName))
{
property.ShouldSerialize = instance => ((VersionOptions.ReleaseOptions)instance).BranchNameOrDefault != VersionOptions.ReleaseOptions.DefaultInstance.BranchName;
diff --git a/src/NerdBank.GitVersioning/version.schema.json b/src/NerdBank.GitVersioning/version.schema.json
index 14863b3c..44a0bb72 100644
--- a/src/NerdBank.GitVersioning/version.schema.json
+++ b/src/NerdBank.GitVersioning/version.schema.json
@@ -119,7 +119,7 @@
},
"publicReleaseRefSpec": {
"type": "array",
- "description": "An array of regular expressions that may match a ref (branch or tag) that should be built with PublicRelease=true as the default value. The ref matched against is in its canonical form (e.g. refs/heads/master)",
+ "description": "An array of regular expressions that may match a ref (branch or tag) that should be built with PublicRelease=true as the default value. The ref matched against is in its canonical form (e.g. refs/heads/master).",
"items": {
"type": "string",
"format": "regex"
@@ -128,12 +128,12 @@
},
"cloudBuild": {
"type": "object",
- "description": "Options that are applicable specifically to cloud builds (e.g. VSTS, AppVeyor, TeamCity)",
+ "description": "Options that are applicable specifically to cloud builds (e.g. VSTS, AppVeyor, TeamCity).",
"properties": {
"setAllVariables": {
"type": "boolean",
"default": false,
- "description": "Elevates all build properties to cloud build variables prefaced with \"NBGV_\""
+ "description": "Elevates all build properties to cloud build variables prefaced with \"NBGV_\"."
},
"setVersionVariables": {
"type": "boolean",
@@ -172,13 +172,19 @@
}
},
"release": {
- "description": "Settings for the prepare-release command",
+ "description": "Settings for the prepare-release and tag commands.",
"type": "object",
"properties": {
+ "tagName": {
+ "description": "Defines the format of tag names. Format must include a placeholder '{version}' for the version.",
+ "type": "string",
+ "pattern": "\\{version\\}",
+ "default": "v{version}"
+ },
"branchName": {
- "description": "Defines the format of release branch names. Format must include a placeholder '{version}' for the version",
+ "description": "Defines the format of release branch names. Format must include a placeholder '{version}' for the version.",
"type": "string",
- "pattern": ".*\\{version\\}.*",
+ "pattern": "\\{version\\}",
"default": "v{version}"
},
"versionIncrement": {
@@ -188,7 +194,7 @@
"default": "minor"
},
"firstUnstableTag": {
- "description": "Specifies the first/default prerelease tag for new versions",
+ "description": "Specifies the first/default prerelease tag for new versions.",
"type": "string",
"default": "alpha"
}
diff --git a/src/nbgv/Program.cs b/src/nbgv/Program.cs
index 03d372b8..19457436 100644
--- a/src/nbgv/Program.cs
+++ b/src/nbgv/Program.cs
@@ -68,6 +68,7 @@ private enum ExitCodes
PackageIdNotFound,
ShallowClone,
InternalError,
+ InvalidTagNameSetting,
}
private static bool AlwaysUseLibGit2 => string.Equals(Environment.GetEnvironmentVariable("NBGV_GitEngine"), "LibGit2", StringComparison.Ordinal);
@@ -545,6 +546,24 @@ private static Task OnTagCommand(string project, string versionOrRef)
return Task.FromResult((int)ExitCodes.NoGitRepo);
}
+ // get tag name format
+ VersionOptions versionOptions = context.VersionFile.GetVersion();
+ if (versionOptions is null)
+ {
+ Console.Error.WriteLine($"Failed to load version file for directory '{searchPath}'.");
+ return Task.FromResult((int)ExitCodes.NoVersionJsonFound);
+ }
+
+ string tagNameFormat = versionOptions.ReleaseOrDefault.TagNameOrDefault;
+
+ // ensure there is a '{version}' placeholder in the tag name
+ if (string.IsNullOrEmpty(tagNameFormat) || !tagNameFormat.Contains("{version}"))
+ {
+ Console.Error.WriteLine($"Invalid 'tagName' setting '{tagNameFormat}'. Missing version placeholder '{{version}}'.");
+ return Task.FromResult((int)ExitCodes.InvalidTagNameSetting);
+ }
+
+ // get commit to tag
LibGit2Sharp.Repository repository = context.Repository;
if (!context.TrySelectCommit(versionOrRef))
{
@@ -585,8 +604,12 @@ private static Task OnTagCommand(string project, string versionOrRef)
return Task.FromResult((int)ExitCodes.NoVersionJsonFound);
}
- oracle.PublicRelease = true; // assume a public release so we don't get a redundant -gCOMMITID in the tag name
- string tagName = $"v{oracle.SemVer2}";
+ // assume a public release so we don't get a redundant -gCOMMITID in the tag name
+ oracle.PublicRelease = true;
+
+ // replace the "{version}" placeholder with the actual version
+ string tagName = tagNameFormat.Replace("{version}", oracle.SemVer2);
+
try
{
context.ApplyTag(tagName);