45

I'm setting up an Azure Pipelines build that needs to package a C# .NET class library into a NuGet package.

In this documentation, it lists a couple different ways to automatically generate SemVer strings. In particular, I want to implement this one:

$(Major).$(Minor).$(rev:.r), where Major and Minor are two variables defined in the build pipeline. This format will automatically increment the build number and the package version with a new patch number. It will keep the major and minor versions constant, until you change them manually in the build pipeline.

But that's all they say about it, no example is provided. A link to learn more takes you to this documentation, where it says this:

For byBuildNumber, the version will be set to the build number, ensure that your build number is a proper SemVer e.g. 1.0.$(Rev:r). If you select byBuildNumber, the task will extract a dotted version, 1.2.3.4 and use only that, dropping any label. To use the build number as is, you should use byEnvVar as described above, and set the environment variable to BUILD_BUILDNUMBER.

Again, no example is provided. It looks like I want to use versioningScheme: byBuildNumber, but I'm not quite sure how to set the build number, I think it pulls it from the BUILD_BUILDNUMBER environment variable, but I can't find a way to set environment variables, only script variables. Furthermore, am I suppose to just set that to 1.0.$(Rev:r), or to $(Major).$(Minor).$(rev:.r)? I'm afraid that would just interpret it literally.

Googling for the literal string "versioningScheme: byBuildNumber" returns a single result... Does anyone have a working azure-pipelines.yml with this versioning scheme?

aurath
  • 1,347
  • 3
  • 13
  • 19
  • You can do something else: Use GitVersion and then use the for the Build Number Format use something like `$(Build.DefinitionName)-$(GitVersion_FullSemVer)`. Your NuGet packages will automatically get versioned if in the task you use the package option for `Automatic package versioning` set to "`Use an environment variable` and then the env variable you use is `GITVERSION_NUGETVERSIONV2`. – Antebios May 13 '19 at 16:41

9 Answers9

52

Working YAML example for Packaging/Versioning using byBuildNumber

NOTE the second parameter of the counter - it is a seed value, really useful when migrating builds from other build systems like TeamCity; It allows you to set the next build version explicitly upon migration. After the migration and initial build in Azure DevOps, the seed value can be set back to zero or whatever start value (like 100) you may prefer every time majorMinorVersion is changed:

reference: counter expression

name: $(majorMinorVersion).$(semanticVersion) # $(rev:r) # NOTE: rev resets when the default retention period expires

pool: 
  vmImage: 'vs2017-win2016' 

# pipeline variables
variables:
  majorMinorVersion: 1.0
  # semanticVersion counter is automatically incremented by one in each execution of pipeline
  # second parameter is seed value to reset to every time the referenced majorMinorVersion is changed
  semanticVersion: $[counter(variables['majorMinorVersion'], 0)]
  projectName: 'MyProjectName'
  buildConfiguration: 'Release'

# Only run against master
trigger:
- master

# Build
- task: DotNetCoreCLI@2
  displayName: Build
  inputs:
    projects: '**/*.csproj'
    arguments: '--configuration $(BuildConfiguration)'

# Package
- task: DotNetCoreCLI@2
  displayName: 'NuGet pack'
  inputs:
    command: 'pack'
    configuration: $(BuildConfiguration)
    packagesToPack: '**/$(ProjectName)*.csproj'
    packDirectory: '$(build.artifactStagingDirectory)'
    versioningScheme: byBuildNumber # https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/build/dotnet-core-cli?view=azure-devops#yaml-snippet

# Publish
- task: DotNetCoreCLI@2
  displayName: 'Publish'
  inputs:
    command: 'push'
    nuGetFeedType: 'internal'
    packagesToPush: '$(build.artifactStagingDirectory)/$(ProjectName)*.nupkg'
    publishVstsFeed: 'MyPackageFeedName'
Emil
  • 2,196
  • 2
  • 25
  • 24
  • 3
    I use a similar solution, but if I don't build again for the next 30 days (default retention period), the latest build disappears from the history, the $(rev:r) is reset to 1 again and NuGet publish fails. – Hein Gustavsen May 15 '19 at 09:37
  • Hmm, very useful to know - I was planning to use this for versioning of nuget packages in a private repo. Obviously most of the packages will not get built for months and if $(rev:r) resets this method is unusable... I am migrating those package builds from TeamCity and now need to revisit my versioning... What is your solution? – Emil May 15 '19 at 13:17
  • we are also migrating from TeamCity, but we have not solved this issue yet. – Hein Gustavsen May 22 '19 at 09:04
  • 3
    @HeinGustavsen We are also migrating builds from TeamCity to Azure DevOps - see my updated sample above; My current solution is to use the counter expression function it replaces the use of $(rev:r). I hope that helps you too... – Emil May 23 '19 at 13:41
  • @Emil thanks ..you just save my day.How can I append suffix to version x.y.z-beta1 like that.. – lazydeveloper Jul 16 '19 at 14:11
  • A bit late to the party, but is there a way to increment the version when merged into master, not prior? – devtoka Mar 06 '20 at 11:30
  • There is easy workaround for retention period, by setting 2nd parameter ot the counter to next following value. – lissajous Jul 14 '20 at 12:07
20

byBuildNumber uses the build number you define in your YAML with the name field.

Ex: name: $(Build.DefinitionName)-$(date:yyyyMMdd)$(rev:.r)

So if you set your build format to name: 1.0.$(rev:.r), it should work as you expect.

Daniel Mann
  • 57,011
  • 13
  • 100
  • 120
  • 3
    `##[error]Could not find version number data in the following environment variable: BUILD_BUILDNUMBER. The value of the variable should contain a substring with or are positive integers.` Looks like it's looking in the environment variable. – aurath Feb 17 '19 at 03:17
  • 5
    I'll mark this as the answer, as it was close and got me the rest of the way. I was missing that the `name` field goes by itself at the root of the yaml, not in the variables section or the nuget section. I put it as the first line in the yaml file. Pay attention to Leo though, he has a good point. – aurath Feb 18 '19 at 17:11
  • 1.0.$(rev:.r) doesnt work because it ends up like 1.0..1 you should use 1.0.$(rev:r) instead – SyLuS Apr 07 '22 at 06:33
10

I had the similar issue and now let me make it clear.

Firstly, what is the definition of Build Number?

By the official document of Azure Pipeline YAML scheme, it is

name: string  # build numbering format
resources:
  containers: [ containerResource ]
  repositories: [ repositoryResource ]
variables: { string: string } | [ variable | templateReference ]
trigger: trigger
pr: pr
stages: [ stage | templateReference ]

Look at the first line:

name: string  # build numbering format

Yes, that's it!

So you could define it like

name: 1.0.$(Rev:r)

if you prefer to Semantic Versioning. Then

Secondly, what's the meaning of versioningScheme: 'byBuildNumber' in task NuGetCommand@2?

It's really straightforward: just use the format defined by name!

Last but not least

The official document on Package Versioning and Pack NuGet packages don't make it clear that what a build number really is and how to define it. It's really misleading. And I'm so sad as an MS employee as I'd to resort to external resource to make all that clear.

Robert
  • 1,964
  • 1
  • 22
  • 22
8

Issues

My issues:

  • when trying the answer by @Emil, my first package started at 2.0 (I did no further testing to investigate)
  • when trying the answer by @Leo Liu-MSFT, I was unable to find the matching "Options" tab.

I therefore used this solution by @LanceMcCarthy.

Fix

Set the variables:

variables:
  major: '1'
  minor: '0'
  revision: $[counter(variables['minor'], 1)] # This will get reset every time minor gets bumped.
  nugetVersion: '$(major).$(minor).$(revision)'

then use nugetVersion as an environment variable when packing:

- task: NuGetCommand@2
  inputs:
    command: 'pack'
    packagesToPack: '**/*.csproj'
    packDestination: '$(Build.ArtifactStagingDirectory)'
    versionEnvVar: 'nugetVersion'
    versioningScheme: 'byEnvVar'
SharpC
  • 6,974
  • 4
  • 45
  • 40
  • As far as I can see the pipeline run name will not match up with nuget version number in this case, but, admitted, that's a minor ;) – noontz Jan 26 '23 at 21:15
6

Azure Pipeline Nuget Package Versioning Scheme, How to Get “1.0.$(Rev:r)”

This should be a issue in the documentation. I reproduced this issue when I set $(Major).$(Minor).$(rev:.r) in the Build number format in the Options of build pipeline:

enter image description here

However, I suddenly noticed that the build number is not correct with that format after many build tests:

enter image description here

There are two points . between 0 and 2 (Open above image in a new tab). Obviously this is very strange. So, I changed the Build number format to:

$(Major).$(Minor)$(rev:.r)

Or

$(Major).$(Minor).$(rev:r)

Now, everything is working fine.

As test, I just set the Build number format to $(rev:.r), and the build number is .x. So, we could confirm that the value of $(rev:.r) including the . by default.

Note: Since where Major and Minor are two variables defined in the build pipeline, so we need defined them in the variables manually.

Hope this helps.

Leo Liu
  • 71,098
  • 10
  • 114
  • 135
  • 1
    Thanks! That's good to know. However, I'm still having trouble figuring out WHERE the build number is set. Daniel above mentions a `name` field, but setting that doesn't work in either the variables section or the nuget task section. You mention " in the Build number format in the Options of build pipeline." but I don't know where you're talking about. Any further clues? – aurath Feb 18 '19 at 16:44
1

I think the issue many of use have is that there is no option menu. I upvoted @SharpC's post as this worked for me.

enter image description here

SharpC
  • 6,974
  • 4
  • 45
  • 40
user1029883
  • 695
  • 7
  • 21
0

I have a workaround to add a suffix (i.e. '-beta'), since it's for some reason ignored by the Nuget pack command when using the Classic editor and setting auto versioning by build number:

  1. Set a new environment variable, set the value as the predefined $(Build.BuildNumber) variable:

    enter image description here

  2. Set the build number:

    enter image description here

  3. Set NuGet pack command to auto-name by environment variable and specify newly added variable name:

    enter image description here

If you're interested in the whole build/release pipeline design and YAML, have a look at my article here

Duck Ling
  • 1,577
  • 13
  • 20
0

The core of the problem is solved in the approved answer and refined in @Emils answer, so this is just another approach to the azure-pipelines.yml that works for us with DevOps artifacts.

name: $(majorMinorVersion).$(semanticVersion)

trigger:
- main

variables:
  majorMinorVersion: 1.0
  semanticVersion: $[counter(variables['majorMinorVersion'], 0)]

pool:
  vmImage: windows-latest

steps:

- task: DotNetCoreCLI@2
  displayName: 'Create Packages'
  inputs:    
    command: pack
    configuration: 'Release'
    packagesToPack: '**/<VS projectname>.csproj'
    versioningScheme: byBuildNumber

- task: NuGetAuthenticate@0
  displayName: 'NuGet Authenticate'

- task: NuGetCommand@2
  displayName: 'NuGet Push to feed'
  inputs:
    command: push
    publishVstsFeed: '<DevOps projectname>/<feed name>'

BTW: Don't forget this little hinch

noontz
  • 1,782
  • 20
  • 29
0

I have a similar issue with the versioning.

variables:
  version: 4.00
  release: 23.8.0
  semVer: $(version)$(Rev:.r)
name: $(version)$(Rev:.r)

The buildByNumber uses the "name" variable in the yaml pipeline. But it seems to skip the extra zero in the "version" variable.

Here is the NuGet Pack Task:

- task: NuGetCommand@2
  displayName: 'NuGet Pack'
  inputs:
    command: pack
    packagesToPack: 'Test.nuspec'
    configuration: Release
    packDestination: '$(Build.ArtifactStagingDirectory)/releases'
    versioningScheme: byBuildNumber

and the package that it generates is "Test.4.0.3.nupkg"

Here is the Build Pipeline:

Build Pipeline

And here is the Release pipeline that is created and triggered after the completion of the build pipeline.

Release Pipeline

I have also tried the minorVersion: "00" input parameter for this task, but it does not work.

David
  • 1