Nerdbank.gitversioning: The `nuget pack` command doesn't produce packages with expected version info

Created on 26 Nov 2017  Â·  23Comments  Â·  Source: dotnet/Nerdbank.GitVersioning

Quite likely that this is user error, so would appreciate any pointers.

I have a project using NB.GV 2.0.41, building with msbuild 15 from the master branch, packing using NuGet 4.3.0.

My version.json includes:

  "publicReleaseRefSpec": [
    "^refs/heads/master",
    "^refs/tags/v\\d+\\.\\d+" 
  ]

My .nuspec file includes:

<version>$version$</version>

When I do nuget pack -Build, the resulting file is just MyPackage.1.0.4.nupkg. But NuGet gives me a warning:

WARNING: Issue: Package version not supported on legacy clients.
WARNING: Description: The package version '1.0.4+g2b37059da8' uses SemVer 2.0.0 or components of SemVer 1.0.0 that are not supported on legacy clients. This message can be ignored if the package is not intended for older clients.
WARNING: Solution: Change the package version to a SemVer 1.0.0 string. If the version contains a release label it must start with a letter.

And in fact, the embedded version string in the package does include the git commit hash.

I thought that the point of the publicReleaseRefSpec was to prevent the git hash from appearing in the package (although it does still appear in the AssemblyInformationalVersion is the assembly itself).

Am I misunderstanding, or doing something stupid?

question

Most helpful comment

We found a workaround/solution for our build process.

  1. According to the cloud build documentation there are options to have NB.GV emit variables containing the proper version strings.
  2. According to the nuget pack reference there are options to pass a version into nuget pack command which overrides the version information from the .nuspec file. This avoids the problem that NuGet tries to resolve the package version via the AssemblyInfo.

In Azure DevOps we now use nbgv cloud --all-vars to determine the version for the build label and the version variables. We then use a NuGetCommand@2 to perform a pack command with

  • versioningScheme: byEnvVar
  • versionEnvVar: NBGV_NUGETPACKAGEVERSION

You can probably also just use something like nuget pack -Version %NBGV_NUGETPACKAGEVERSION% or whatever is appropriate in your build system to achieve the same result. Also other version strings are available.

All 23 comments

Great question. This was a design point that there was some discussion and research on before it was made in the NB.GV 2.x release. And you can set it to avoid the +g suffix if you want IIRC by setting the nuget package semver version to 1 instead of 2.

What you describe is standard NuGet behavior as far as hiding the build metadata everywhere except inside the nuspec file itself. It's pretty much out of site everywhere (as far as nuget.org and clients) except in the nuspec so that someone who wants to know the git commit that the package came from can do so. We considered that a feature. The publicReleaseRefSpec is designed to control between stable and unstable package versions. If you weren't building out of master or a vX.Y tag spec, you'd get a suffix that starts with a hyphen instead of a plus, and the git commit ID would be obvious to everyone. So in fact that publicReleaseRefSpec is having an effect by hiding that git commit ID and giving you a stable package. And as I said, if the commit ID in the nuspec bothers you, I think you can turn it off with the nuget package semver setting.

Does that help?

Thanks, I understand what you're saying – but I can't seem to get it to work correctly.

If I set nugetPackageVersion.semVer to 1, I still get the +g in the version string. And if I build out of a non-master branch, I do not get the commit ID in the package name. IOW, I seem to be unable to change what I'm getting (clean package name, but +g in the version string).

When you build in a non-master branch, the package ID should stay the same, but the -gCommitId suffix should be added to the version. Is that non happening?

Terminology is a little confusing here! When I say package name, I mean the name of the .nupkg file (MyPkg.1.0.4.nupkg), which I think is what you also mean by package ID. When I say version string, I mean the string that gets put into the .nuspec file inside the .nupkg.

If I'm understanding correctly, publicReleaseRefSpec is supposed to control whether the commit hash appears in the package name (MyPkg.1.0.4-g12345678.nupkg) and nugetPackageVersion.semVer is supposed to control whether the commit hash appears in the version string.

No matter what I do in terms of changing those two settings, and building in master and non-master branches, I only ever seem to get the same result: The commit hash is always in the version string, and never in the package name.

Can you share a diagnostic or detailed msbuild log?

See attached.
nbgv.zip

Built from branch foo with nuget pack -build -verbosity detailed

Given the settings, I would have expected to see the hash in the package name but not in the version string. Actual result was hash in the version string but not in the package name.

Thanks. Somehow I missed that you were literally using nuget pack. That isn't something I've tested, and is likely what's causing you trouble. Is this a .NET SDK project that you can use msbuild /t:pack with instead? That's how I've tested it before so I'd have more confidence that it works for you.

msbuild /t:pack gives me an error: The target "Pack" does not exist in the project.

I've never tried to use that target before, but this is a brand new Framework project, built with VS2017. https://docs.microsoft.com/en-us/nuget/schema/msbuild-targets says I should be able to use that target with MSBuild >= 15.1, and I have 15.4.8.

I can share the git repo, if that would help, or I'm happy to keep troubleshooting from this end. I appreciate your assistance.

AFAIK the Pack target only exists when using the .NET SDK, which is available in recent versions of MSBuild (like yours). That documentation would suggest you're right though. So it's either very misleading, or there's a bug in msbuild since I'm seeing the same thing you are.

As you say the project is brand new, I would suggest you switch to .NET SDK style projects if you're developing a console app or class library. They're so much nicer. And they define a Pack target. :)

Yes, thanks, I can easily switch this project to SDK. But I'm a little concerned about this issue in other (Framework) projects I have which use GV. Not a big issue for me at the moment, but it does seem as though something in GV is not working as intended.

I emailed the NuGet team to point out the issue in the docs.

I've never tested NB.GV with nuget pack. I'm not 100% confident I'll be able to make it work well with nuget pack -- it will be conditional on how flexible it is to read various properties, and whether NB.GV can detect that that is the scenario we're running in.

I'll leave this issue open to track nuget pack behavior.

I may be running into a similar problem. I have this:

  "publicReleaseRefSpec": [
    "^refs/heads/master$",
    "^refs/tags/v\\d+\\.\\d+",
    "^refs/tags/v\\d+(?:\\.\\d+){0,2}$"
  ],

With this git log:

commit 8ca433787bfb87874652c6027dbd3052e24b5418 (HEAD -> ASIASETL-994, tag: v1.0.9)
Author: Lucas Theisen <[email protected]>
Date:   Fri Oct 12 14:34:47 2018 -0400

    try again

Notice that the tag is v1.0.9 which matches either of these:

    "^refs/tags/v\\d+\\.\\d+",
    "^refs/tags/v\\d+(?:\\.\\d+){0,2}$"

But when i run dotnet pack:

PS C:\Users\ltheisen\git\asiasetl-data-consumer-registry> dotnet pack --include-symbols --include-source --configuration Release
Microsoft (R) Build Engine version 15.7.179.6572 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 89.67 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry.Test.Integration\Mitre.Data.ConsumerRegistry.Test.Integration.csproj.
  Restore completed in 53.35 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\Mitre.Data.ConsumerRegistry.csproj.
  Restore completed in 53.35 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry.Test.Integration\Mitre.Data.ConsumerRegistry.Test.Integration.csproj.
  Mitre.Data.ConsumerRegistry -> C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\netstandard2.0\Mitre.Data.ConsumerRegistry.dll
  Successfully created package 'C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\Mitre.Data.ConsumerRegistry.1.0.9-g8ca433787b.nupkg'.
  Successfully created package 'C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\Mitre.Data.ConsumerRegistry.1.0.9-g8ca433787b.symbols.nupkg'.

You can see that the commit hash is still present on the nupkg: Mitre.Data.ConsumerRegistry.1.0.9-g8ca433787b.nupkg. However, if I add my current branch name (followed by commit):

  "publicReleaseRefSpec": [
    "^refs/heads/master$",
    "^refs/heads/ASIASETL-994$",
    "^refs/tags/v\\d+(?:\\.\\d+){1,2}$"
  ],

I get the desired results:

PS C:\Users\ltheisen\git\asiasetl-data-consumer-registry> dotnet pack --include-symbols --include-source --configuration Release
Microsoft (R) Build Engine version 15.7.179.6572 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 52.63 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\Mitre.Data.ConsumerRegistry.csproj.
  Restore completed in 52.97 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry.Test.Integration\Mitre.Data.ConsumerRegistry.Test.Integration.csproj.
  Restore completed in 85.48 ms for C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry.Test.Integration\Mitre.Data.ConsumerRegistry.Test.Integration.csproj.
  Mitre.Data.ConsumerRegistry -> C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\netstandard2.0\Mitre.Data.ConsumerRegistry.dll
  Successfully created package 'C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\Mitre.Data.ConsumerRegistry.1.0.10.nupkg'.
  Successfully created package 'C:\Users\ltheisen\git\asiasetl-data-consumer-registry\Mitre.Data.ConsumerRegistry\bin\Release\Mitre.Data.ConsumerRegistry.1.0.10.symbols.nupkg'.

With Mitre.Data.ConsumerRegistry.1.0.10.nupkg.

This feels like I must be doing something wrong...

@lucastheisen You're hitting something different. In git, tags can't be "checked out" like branches or commits can be. A tag is just an alias for a commit, so checking out a tag just means you're checking out the commit that was tagged.
During the build, NB.GV can't know that you were building the commit for the sake of the tag or not when you're building locally. If you're building on CI, and the CI knows it's building based on the tag, it will typically set an env var that indicates this. NB.GV _will_ pick up on that (as an example, here is the appveyor-compatible check).

So if you're building locally, and want a public release, and it's not based on a branch you have checked out, you need to build with /p:publicrelease=true. Tags only work on CI servers.

@AArnott , excellent information (and _fantastic tool_!). That explains my problem. I only release off of master so its not a _huge_ problem, it just made things more difficult to _test_. Anyway, thank you much!

According to https://docs.microsoft.com/en-us/nuget/reference/nuspec#replacement-tokens nuget pack would use the AssemblyInformationalVersion if present, otherwise AssemblyVersion for the $version$ token. So if AssemblyInformationalVersion is set to the version number including hash, then <version>$version$</version> will cause this version number to determine the package version.

Is there a recommended way to use NB.GV with old style csproj projects to create NuGet packages?

How do you create nuget packages from old style csproj?

Usually by running nuget pack against a .nuspec file, although you can also run it directly against a .csproj file.

I have never researched how we might make that scenario work. It might be easy. If you want you can open an issue proposing how it can work. I would accept a PR that includes tests. But I won't likely have time to implement it myself.

I'm not sure there's anything for NBGV to do in this case -- I don't think it's invoked during a nuget pack operation. It has already done its job by populating the version fields in the assembly during the build process. @AArnott Do you know whether this is a true statement?

If it is, I think you would either need to edit the .nuspec after the project is built and set the version property by hand, or pass it in on the command line. You could write a build script that builds the project, reads the properties of the built assembly, determines the desired version string for the package (e.g., AssemblyInformationalVersion 5.6.3+g92b7290530 -> version 5.6.3), and then calls nuget pack, passing in that string on the command line.

We found a workaround/solution for our build process.

  1. According to the cloud build documentation there are options to have NB.GV emit variables containing the proper version strings.
  2. According to the nuget pack reference there are options to pass a version into nuget pack command which overrides the version information from the .nuspec file. This avoids the problem that NuGet tries to resolve the package version via the AssemblyInfo.

In Azure DevOps we now use nbgv cloud --all-vars to determine the version for the build label and the version variables. We then use a NuGetCommand@2 to perform a pack command with

  • versioningScheme: byEnvVar
  • versionEnvVar: NBGV_NUGETPACKAGEVERSION

You can probably also just use something like nuget pack -Version %NBGV_NUGETPACKAGEVERSION% or whatever is appropriate in your build system to achieve the same result. Also other version strings are available.

You could write a build script that builds the project, reads the properties of the built assembly, determines the desired version string for the package

@asherber I would never go to all _that_ trouble. I would just use the nbgv tool or query the project itself for the version properties. No need to build the dll or reflect over it just to get the version info out. Plus, the version fields in an assembly aren't necessarily what the user wants for a package version, which can be formatted in several ways. Using NB.GV to get the actual package version is the best way to be consistent.

What @michael-x suggests is a very reasonable approach, particularly if the repo has a common version for all projects. If you need a specific per-project version for a specific package, you can query the individual project or use nbgv for that particular project and it will give you the variables for it (optionally without setting cloud variables).

@AArnott I forgot about using the nbgv tool to read version info, since you can't use it to install nbgv for old-style csproj files. You're right that that's a better solution.

Was this page helpful?
0 / 5 - 0 ratings