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);