diff --git a/NetRevisionTask/Api.cs b/NetRevisionTask/Api.cs
index b109054..1d2b57e 100644
--- a/NetRevisionTask/Api.cs
+++ b/NetRevisionTask/Api.cs
@@ -13,13 +13,16 @@ public static string GetVersion(
string revisionFormat = null,
string tagMatch = "v[0-9]*",
bool removeTagV = true,
- string copyright = null)
+ string copyright = null,
+ string configurationName = null,
+ string errorOnModifiedRepoPattern = null)
{
if (string.IsNullOrEmpty(projectDir))
projectDir = Directory.GetCurrentDirectory();
var logger = new ConsoleLogger();
- var (success, _, informationalVersion, _) = Common.GetVersion(projectDir, requiredVcs, revisionFormat, tagMatch, removeTagV, copyright ?? "", logger, true);
+ var (success, _, informationalVersion, _) = Common.GetVersion(projectDir, requiredVcs, revisionFormat, tagMatch, removeTagV, copyright ?? "", logger, true,
+ configurationName, errorOnModifiedRepoPattern);
if (!success)
{
return null;
@@ -33,13 +36,16 @@ public static string GetShortVersion(
string revisionFormat = null,
string tagMatch = "v[0-9]*",
bool removeTagV = true,
- string copyright = null)
+ string copyright = null,
+ string configurationName = null,
+ string errorOnModifiedRepoPattern = null)
{
if (string.IsNullOrEmpty(projectDir))
projectDir = Directory.GetCurrentDirectory();
var logger = new ConsoleLogger();
- var (success, version, _, _) = Common.GetVersion(projectDir, requiredVcs, revisionFormat, tagMatch, removeTagV, copyright ?? "", logger, true);
+ var (success, version, _, _) = Common.GetVersion(projectDir, requiredVcs, revisionFormat, tagMatch, removeTagV, copyright ?? "", logger, true,
+ configurationName, errorOnModifiedRepoPattern);
if (!success)
{
return null;
diff --git a/NetRevisionTask/AssemblyInfoHelper.cs b/NetRevisionTask/AssemblyInfoHelper.cs
index 9ea7573..7cfbce7 100644
--- a/NetRevisionTask/AssemblyInfoHelper.cs
+++ b/NetRevisionTask/AssemblyInfoHelper.cs
@@ -78,6 +78,7 @@ public AssemblyInfoHelper(string projectDir, bool throwOnMissingFile, ILogger lo
/// Indicates whether only the last number is replaced by the revision number.
/// Indicates whether the copyright year is replaced.
/// Indicates whether the final informational version string is displayed.
+ /// Indicates whether the AssemblyMetadata attribute is processed.
/// The name of the patched AssemblyInfo file.
public string PatchFile(
string patchedFileDir,
@@ -87,7 +88,8 @@ public string PatchFile(
bool informationalAttribute,
bool revOnly,
bool copyrightAttribute,
- bool echo)
+ bool echo,
+ bool metadataAttribute)
{
logger?.Trace($@"Patching file ""{fileName}""...");
ReadFileLines(FullFileName);
@@ -111,7 +113,7 @@ public string PatchFile(
}
// Process all lines in the file
- ResolveAllLines(rf, simpleAttributes, informationalAttribute, revOnly, copyrightAttribute, echo);
+ ResolveAllLines(rf, simpleAttributes, informationalAttribute, revOnly, copyrightAttribute, echo, metadataAttribute);
// Write all lines to the file
string patchedFileName = Path.Combine(patchedFileDir, "Nrt" + Path.GetFileName(fileName));
@@ -250,7 +252,8 @@ private void WriteFileLines(string patchedFileName)
/// Indicates whether only the last number is replaced by the revision number.
/// Indicates whether the copyright year is replaced.
/// Indicates whether the final informational version string is displayed.
- private void ResolveAllLines(RevisionFormatter rf, bool simpleAttributes, bool informationalAttribute, bool revOnly, bool copyrightAttribute, bool echo)
+ /// Indicates whether the AssemblyMetadata attribute is processed.
+ private void ResolveAllLines(RevisionFormatter rf, bool simpleAttributes, bool informationalAttribute, bool revOnly, bool copyrightAttribute, bool echo, bool metadataAttribute)
{
// Preparing a truncated dotted-numeric version if we may need it
string truncVersion = null;
@@ -385,6 +388,23 @@ private void ResolveAllLines(RevisionFormatter rf, bool simpleAttributes, bool i
logger?.Trace($@" Replaced ""{match.Groups[2].Value}"" with ""{copyrightText}"".");
}
}
+
+ if (metadataAttribute)
+ {
+ // Replace the value part of AssemblyMetadata with the resolved string of what
+ // was already there. Format: [assembly: AssemblyMetadata("Key", "Value")]
+ match = Regex.Match(
+ lines[i],
+ @"^(\s*\" + attrStart + @"\s*assembly\s*:\s*AssemblyMetadata\s*\(\s*"")(.*?)(""\s*,\s*"")(.*?)(""\s*\)\s*\" + attrEnd + @".*)$",
+ RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ string metadataText = rf.Resolve(match.Groups[4].Value);
+ lines[i] = match.Groups[1].Value + match.Groups[2].Value + match.Groups[3].Value + metadataText + match.Groups[5].Value;
+ logger?.Success("Found AssemblyMetadata attribute.");
+ logger?.Trace($@" Replaced [{match.Groups[2].Value}] => ""{match.Groups[4].Value}"" with ""{metadataText}"".");
+ }
+ }
}
}
diff --git a/NetRevisionTask/Common.cs b/NetRevisionTask/Common.cs
index dc0b961..55de667 100644
--- a/NetRevisionTask/Common.cs
+++ b/NetRevisionTask/Common.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
+using System.Text.RegularExpressions;
using NetRevisionTask.VcsProviders;
namespace NetRevisionTask
@@ -8,7 +9,8 @@ namespace NetRevisionTask
internal class Common
{
public static (bool Success, string Version, string InformationalVersion, string Copyright)
- GetVersion(string projectDir, string requiredVcs, string revisionFormat, string tagMatch, bool removeTagV, string copyright, ILogger logger, bool warnOnMissing)
+ GetVersion(string projectDir, string requiredVcs, string revisionFormat, string tagMatch, bool removeTagV, string copyright, ILogger logger, bool warnOnMissing,
+ string configurationName, string configurationNameErrorPattern)
{
// Analyse working directory
RevisionData data = ProcessDirectory(projectDir, requiredVcs, tagMatch, logger);
@@ -17,6 +19,10 @@ public static (bool Success, string Version, string InformationalVersion, string
logger.Error($@"The required version control system ""{requiredVcs}"" is not available or not used in the project directory.");
return (false, null, null, null);
}
+ if (TriggerErrorIfRepoModified(logger, data, configurationNameErrorPattern, configurationName))
+ {
+ return (false, null, null, null);
+ }
if (string.IsNullOrEmpty(revisionFormat))
{
revisionFormat = GetRevisionFormat(projectDir, logger, warnOnMissing);
@@ -26,7 +32,7 @@ public static (bool Success, string Version, string InformationalVersion, string
revisionFormat = data.GetDefaultRevisionFormat(logger);
}
- var rf = new RevisionFormatter { RevisionData = data, RemoveTagV = removeTagV };
+ var rf = new RevisionFormatter { RevisionData = data, RemoveTagV = removeTagV, BuildTime = DateTimeOffset.Now, ConfigurationName = configurationName };
try
{
return (true, rf.ResolveShort(revisionFormat), rf.Resolve(revisionFormat), rf.Resolve(copyright));
@@ -145,5 +151,30 @@ public static string GetRevisionFormat(string projectDir, ILogger logger, bool w
}
return revisionFormat;
}
+
+ ///
+ /// Determines if a modified repository that matches the given pattern triggers a build error.
+ ///
+ /// The data about the current revision of the source directory.
+ /// The match pattern that shall trigger the error.
+ /// The name of the build configuration.
+ /// True if an error shall be triggered, false otherwise.
+ public static bool TriggerErrorIfRepoModified(ILogger logger, RevisionData data, string cfgNameMatchPattern, string cfgName)
+ {
+ if (!string.IsNullOrEmpty(cfgNameMatchPattern) && !string.IsNullOrEmpty(cfgName))
+ {
+ if (data.IsModified)
+ {
+ var match = Regex.Match(cfgName, $@"^{cfgNameMatchPattern}$", RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ logger.Error($@"The ""{cfgName}"" configuration does not allow builds with a modified {data.VcsProvider.Name} repository.");
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/NetRevisionTask/RevisionFormatter.cs b/NetRevisionTask/RevisionFormatter.cs
index e74f364..2d4b220 100644
--- a/NetRevisionTask/RevisionFormatter.cs
+++ b/NetRevisionTask/RevisionFormatter.cs
@@ -11,7 +11,7 @@ internal class RevisionFormatter
{
#region Static data
- private static readonly DateTimeOffset buildTime = DateTimeOffset.Now;
+ private static DateTimeOffset buildTime = DateTimeOffset.Now;
///
/// Alphabet for the base-28 encoding. This uses the digits 0–9 and all characters a–z that
@@ -50,9 +50,18 @@ internal class RevisionFormatter
public bool RemoveTagV { get; set; }
///
- /// Gets the build time.
+ /// Gets or sets the build time.
///
- public DateTimeOffset BuildTime => buildTime;
+ public DateTimeOffset BuildTime
+ {
+ get => buildTime;
+ set => buildTime = value;
+ }
+
+ ///
+ /// Gets the value of the build configuration name.
+ ///
+ public string ConfigurationName { get; set; }
#endregion Data properties
@@ -102,6 +111,11 @@ public string Resolve(string format)
}
string tagName = RevisionData.Tag;
+ if (string.IsNullOrEmpty(tagName))
+ {
+ // default value when no tag exists in repository
+ tagName = "v0.0.0.0";
+ }
if (RemoveTagV && Regex.IsMatch(tagName, "^v[0-9]"))
{
tagName = tagName.Substring(1);
@@ -142,6 +156,16 @@ public string Resolve(string format)
format = format.Replace("{copyright}", copyright);
format = Regex.Replace(format, @"\{copyright:([0-9]+?)-?\}", m => (m.Groups[1].Value != copyright ? m.Groups[1].Value + "-" : "") + copyright);
+ // Build Configuration
+ if (ConfigurationName == null)
+ {
+ ConfigurationName = string.Empty;
+ }
+ format = format.Replace("{bconf}", ConfigurationName);
+ format = format.Replace("{BCONF}", ConfigurationName.ToUpperInvariant());
+ format = Regex.Replace(format, @"\{bconf:(.*?):(.+?)\}", m => !Regex.IsMatch(ConfigurationName, $@"^{m.Groups[2].Value}$", RegexOptions.IgnoreCase) ? m.Groups[1].Value + ConfigurationName : "");
+ format = Regex.Replace(format, @"\{BCONF:(.*?):(.+?)\}", m => !Regex.IsMatch(ConfigurationName, $@"^{m.Groups[2].Value}$", RegexOptions.IgnoreCase) ? m.Groups[1].Value + ConfigurationName.ToUpperInvariant() : "");
+
// Return revision ID
return format;
}
diff --git a/NetRevisionTask/Tasks/PatchAssemblyInfo.cs b/NetRevisionTask/Tasks/PatchAssemblyInfo.cs
index 154be36..082224a 100644
--- a/NetRevisionTask/Tasks/PatchAssemblyInfo.cs
+++ b/NetRevisionTask/Tasks/PatchAssemblyInfo.cs
@@ -93,6 +93,22 @@ public class PatchAssemblyInfo : MSBuildTask
///
public bool ShowRevision { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the AssemblyMetadata attribute is processed.
+ ///
+ public bool ResolveMetadata { get; set; }
+
+ ///
+ /// Gets or sets the value of the build configuration name.
+ ///
+ public string ConfigurationName { get; set; }
+
+ ///
+ /// Gets or sets the value of the build configuration RegEx pattern that triggers an error
+ /// on match if the repository is modified.
+ ///
+ public string ErrorOnModifiedRepoPattern { get; set; }
+
#endregion Properties
#region Task output properties
@@ -144,7 +160,14 @@ public override bool Execute()
RevisionFormat = data.GetDefaultRevisionFormat(logger);
}
- var rf = new RevisionFormatter { RevisionData = data, RemoveTagV = RemoveTagV };
+ // check whether a modified repository triggers a build error
+ if (Common.TriggerErrorIfRepoModified(logger, data, ErrorOnModifiedRepoPattern, ConfigurationName))
+ {
+ return false;
+ }
+
+ var rf = new RevisionFormatter { RevisionData = data, RemoveTagV = RemoveTagV,
+ ConfigurationName = ConfigurationName };
try
{
var aih = new AssemblyInfoHelper(ProjectDir, true, logger);
@@ -157,7 +180,8 @@ public override bool Execute()
ResolveInformationalAttribute,
RevisionNumberOnly,
ResolveCopyright,
- ShowRevision);
+ ShowRevision,
+ ResolveMetadata);
}
catch (FormatException ex)
{
diff --git a/NetRevisionTask/Tasks/SetVersion.cs b/NetRevisionTask/Tasks/SetVersion.cs
index ed450ac..806a8da 100644
--- a/NetRevisionTask/Tasks/SetVersion.cs
+++ b/NetRevisionTask/Tasks/SetVersion.cs
@@ -75,6 +75,17 @@ public class SetVersion : MSBuildTask
///
public bool ShowRevision { get; set; }
+ ///
+ /// Gets the value of the build configuration name.
+ ///
+ public string ConfigurationName { get; set; }
+
+ ///
+ /// Gets or sets the value of the build configuration RegEx pattern that triggers an error
+ /// on match if the repository is modified.
+ ///
+ public string ErrorOnModifiedRepoPattern { get; set; }
+
#endregion Properties
#region Task output properties
@@ -117,7 +128,8 @@ public override bool Execute()
logger.Trace($"NetRevisionTask: SetVersion ({targetFramework})");
bool warnOnMissing = !GenerateAssemblyInfo && (NuGetPackOutput == null || NuGetPackOutput.Length == 0);
- var result = Common.GetVersion(ProjectDir, RequiredVcs, RevisionFormat, TagMatch, RemoveTagV, Copyright ?? "", logger, warnOnMissing);
+ var result = Common.GetVersion(ProjectDir, RequiredVcs, RevisionFormat, TagMatch, RemoveTagV, Copyright ?? "", logger, warnOnMissing,
+ ConfigurationName, ErrorOnModifiedRepoPattern);
if (!result.Success)
{
return false;
diff --git a/NetRevisionTask/build/Unclassified.NetRevisionTask.targets b/NetRevisionTask/build/Unclassified.NetRevisionTask.targets
index 6c0130c..38a20f7 100644
--- a/NetRevisionTask/build/Unclassified.NetRevisionTask.targets
+++ b/NetRevisionTask/build/Unclassified.NetRevisionTask.targets
@@ -13,6 +13,7 @@
true
true
true
+ true
v[0-9]*
true
$(MSBuildProjectDirectory)
@@ -37,7 +38,9 @@
RemoveTagV="$(NrtRemoveTagV)"
ResolveCopyright="$(NrtResolveCopyright)"
Copyright="$(Copyright)"
- ShowRevision="$(NrtShowRevision)">
+ ShowRevision="$(NrtShowRevision)"
+ ConfigurationName="$(ConfigurationName)"
+ ErrorOnModifiedRepoPattern="$(NrtErrorOnModifiedRepoPattern)">
@@ -72,7 +75,10 @@
ResolveInformationalAttribute="$(NrtResolveInformationalAttribute)"
RevisionNumberOnly="$(NrtRevisionNumberOnly)"
ResolveCopyright="$(NrtResolveCopyright)"
- ShowRevision="$(NrtShowRevision)">
+ ShowRevision="$(NrtShowRevision)"
+ ResolveMetadata="$(NrtResolveMetadata)"
+ ConfigurationName="$(ConfigurationName)"
+ ErrorOnModifiedRepoPattern="$(NrtErrorOnModifiedRepoPattern)">
diff --git a/README.md b/README.md
index ed53277..e2d11da 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,8 @@ Example:
git
true
$(MSBuildProjectDirectory)
+ true
+ .*Release.*
The following MSBuild properties are supported:
@@ -121,6 +123,14 @@ Specifies whether the determined revision ID is printed during the build with hi
Sets the directory where NRT starts searching for the VCS files. This is helpful if NRT is added to a project that is a submodule of another repository and should observe the parent repository.
+**NrtResolveMetadata**: boolean, default: true.
+
+Specifies whether the value component of the `AssemblyMetadata` (`AssemblyMetadataAttribute`) is resolved.
+
+**NrtErrorOnModifiedRepoPattern**: string, default: “”.
+
+Specifies a case-insensitive RegEx pattern string matching the build configuration string to trigger a build error if the repository contains modifications. If the string is empty, the functionality is disabled.
+
### Revision format
You can customise the format of the resulting version with a revision format string that defines how information about the commit or revision is formatted into the final revision ID. It is a plain string that contains placeholders in `{curly braces}`. Each placeholder is a simple data field or encodes a time value using a scheme and optional configuration arguments.
@@ -183,6 +193,12 @@ The following data field placeholders are supported:
**`{copyright:-}`**: Abbreviation for the copyright year range, starting at ``. The following dash is optional but recommended for clearer understanding.
+**`{bconf}`**: Build configuration.
+
+**`{BCONF}`**: Build configuration, in upper case.
+
+**`{bconf::[}`, `{BCONF::][}`**: Build configuration, if not matching case-insensitive RegEx `][` pattern, separated by ``, otherwise empty.
+
Schemes convert a commit or build time to a compact string representation. They can be used to assign incrementing versions if no revision number is provided by the VCS. First, select from the build, commit or authoring time with `{b:…}`, `{c:…}` or `{a:…}`. This is followed by the scheme name. There are 4 types of schemes.
The following time schemes are supported:
]