dotnet / Nerdbank.GitVersioning

Stamp your assemblies, packages and more with a unique version generated from a single, simple version.json file and include git commit IDs for non-official builds.
https://www.nuget.org/packages/Nerdbank.GitVersioning
MIT License
1.36k stars 166 forks source link

Support Calendar versioning #1047

Closed KnapSac closed 5 months ago

KnapSac commented 5 months ago

We're currently using Calendar versioning as it aligns better with our deployment model than semantic versioning. We currently accomplish this by manually incrementing the version in version.json each month, which works fine, but requires manual action each month to keep the version up to date.

Would it be possible to support Calendar versioning out-of-the-box, determining the version automatically based on the current date and time? Perhaps adding something like these properties to version.json:

{
    "useCalendarVersioning": true,
    "calendarVersioningFormat": "YY.MM"
}
AArnott commented 5 months ago

I'm afraid that falls decidedly outside the scope of a specifically git-based versioning system. I don't know what overlap would even exist between calendar-based and git history based versioning systems such that it would make sense for one tool to perform both functions.

KnapSac commented 5 months ago

We use a combination of the 2, the major and minor versions are determined using Calendar versioning, while the rest of the version number is based on the git height and the first 2 bytes of the commit hash.

AArnott commented 5 months ago

Well, what you might do in that case is author an msbuild target that overrides the major.minor version components that are computed and change the msbuild properties that will end up influencing the versions stamped on packages, assemblies, etc. You should be able to achieve what you want that way. Details here: https://github.com/dotnet/Nerdbank.GitVersioning/blob/main/doc/msbuild.md

KnapSac commented 5 months ago

I've added the following to Directory.Build.props, and it works great:

<!-- Replace the major and minor parts of the generated version numbers by the current year and month -->
<Target Name="AdjustVersion" DependsOnTargets="GetBuildVersion" AfterTargets="GetBuildVersion">
  <PropertyGroup>
    <MajorMinor>$([System.DateTime]::Now.ToString(yy.M))</MajorMinor>
    <AssemblyVersion>$(MajorMinor).$(AssemblyVersion.Split('.', 3)[2])</AssemblyVersion>
    <AssemblyFileVersion>$(MajorMinor).$(AssemblyFileVersion.Split('.', 3)[2])</AssemblyFileVersion>
    <AssemblyInformationalVersion>$(MajorMinor).$(AssemblyInformationalVersion.Split('.', 3)[2])</AssemblyInformationalVersion>
  </PropertyGroup>
</Target>
KalleOlaviNiemitalo commented 5 months ago

For reproducible builds, it might be better to use the commit date than the build date.

KnapSac commented 5 months ago

For reproducible builds, it might be better to use the commit date than the build date.

That's a great point, thanks!

Any ideas on how to create a DateTime instance from the GitCommitDateTicks property inside Msbuild? I tried the constructor using <MajorMinor>$([System.DateTime]::new($(GitCommitDateTicks), [System.DateTimeKind]::Utc).ToString(yy.M))</MajorMinor>, but then I get an Attempted to access missing method exception. And e.g. Convert.ToDateTime always throws an exception for the long argument, so that doesn't work either.

KalleOlaviNiemitalo commented 5 months ago
<Project>
 <Target Name="Demo">
   <Message Text="$([System.DateTime]::new(638504286055825472, 'DateTimeKind.Utc').ToString('O'))"
            Importance="high"/>
 </Target>
</Project>

output: 2024-05-04T14:10:05.5825472Z

KalleOlaviNiemitalo commented 5 months ago

The part of MSBuild that coerces a string to an enum type is based on String.Replace and pretty lenient. For example, it accepts DateTimeKind.UtcSystem.DateTimeKind. as well. I wouldn't recommended using such a bogus syntax in MSBuild projects, though.

KnapSac commented 5 months ago

Too bad there isn't a nicer way to use enums, but this gets the job done:

<MajorMinor>$([System.DateTime]::new($(GitCommitDateTicks), 'DateTimeKind.Utc').ToString("yy.M"))</MajorMinor>

I noticed MSBuild also didn't care about the missing quotes in the ToString(yy.M) call before either, is that also based on string.Replace?

KalleOlaviNiemitalo commented 5 months ago

Quotation marks around arguments are handled here:

Note that AddArgument treats unquoted null as different from quoted "null" or 'null'. I imagine the MSBuild team would be very hesitant to introduce any new keywords like that.