Setting the version number for .NET Core projects - CSPROJ - not JSON projects
Asked Answered
D

12

148

This question is very similar to Setting the version number for .NET Core projects, but not the same. Using the latest stable version of .NET Core at the time of writing (1.1) and VS2017, .NET Core has switched from JSON based project files to CSPROJ files.

So - what I am trying to do is set up a CI environment where I would like to be able to modify something prior to a build to stamp my builds with the correct version number.

If I use the attributes like this the old (SharedAssemblyInfo.cs trick):

[assembly: AssemblyFileVersion("3.3.3.3")]
[assembly: AssemblyVersion("4.4.4.4")]

somewhere in the project, I get
CS0579 - Duplicate 'System.Reflection.AssemblyFileVersionAttribute'
and
CS0579 - Duplicate 'System.Reflection.AssemblyVersionAttribute'
errors when building.

When digging into it a bit, I find that there is a file which looks like this generated during the build process (it doesn't exist before I build) in \obj\Debug\netcoreapp1.1:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Reflection;

[assembly: System.Reflection.AssemblyCompanyAttribute("TestApplication")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyDescriptionAttribute("Package Description")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.1.99.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.1.99")]
[assembly: System.Reflection.AssemblyProductAttribute("TestApplication")]
[assembly: System.Reflection.AssemblyTitleAttribute("TestApplication")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.1.99.0")]

// Generated by the MSBuild WriteCodeFragment class.

Question - How do I do this bit?
So I can see that this must somehow be generated from the values entered in the project properties 'package page', but I don't know what the right way would be to change these values on my CI machine.

Ideally, I'd like to be able to specify all this information in my (Jenkins) CI script, but I'd settle for just being able to set the version number.

EDIT - More Info
After reading the first answer, I wanted to make it clear that I am creating both services and NuGET packages - and I would prefer to have 1 way of versioning everything, which would be like the old JSON project where I could just update a single file.

UPDATE I am going with scripting a change to the CSPROJ file which in my opinion is rather hacky as the section I need to modify looks like this...

<PropertyGroup>
 <OutputType>Exe</OutputType>
 <TargetFramework>netcoreapp1.1</TargetFramework>
 <Version>1.0.7777.0</Version>
 <AssemblyVersion>1.0.8888.0</AssemblyVersion>
 <FileVersion>1.0.9999.0</FileVersion>
 <Company>MyCompany</Company>
 <Authors>AuthorName</Authors>
 <Product>ProductName</Product>
 <Description />
 <Copyright>Copyright © 2017</Copyright>
</PropertyGroup>

So - the problem here is that there are multiple 'PropertyGroup' elements; the others appear to be labelled - but not knowing how the CSPROJ is put together, I can't say this will always be the case.

I am working on the premise that the package details will always be filled in, otherwise the value tags (above) don't appear in the XML - so then I can use a script to update the values in place. If the value tags were not there, I would have no clear idea which PropertyGroup element to insert the values into (and also which order, as this appears to be important; changing the order stopped me from loading the project in VS2017).

I am still holding out for a better solution than this one!

Update: After someone marking this question as a possible duplicate (Auto Versioning in Visual Studio 2017 (.NET Core)) - I hadn't seen this question before and now reading it seems to be almost the same except that I dont just want to set the version number. Also, the answers to this question do not solve my problem - only asks what I asked in my question. The accepted answer to my question is exactly the answer I need to solve my problem - so while the other question came first and appears the same - it does not help me at all. Maybe a mod can help?

Dewitt answered 7/4, 2017 at 9:13 Comment(2)
Possible duplicate of Auto Versioning in Visual Studio 2017 (.NET Core)Rowney
Updated answer to include explanation - I had not seen that post; it appears to be the same but it does not answer my question. The accepted answer to this post answers my question perfectly.Dewitt
L
150

You can override any property from the command line by passing /p:PropertyName=Value as arguments to dotnet restore, dotnet build and dotnet pack.

Currently, Version composition works as this: If Version is unset, use VersionPrefix (defaults to 1.0.0 if unset) and - if present - append VersionSuffix.

All other versions are then defaulted to whatever Version is.

So for example you can set <VersionPrefix>1.2.3</VersionPrefix> in your .csproj and then call dotnet pack --version-suffix beta1 to produce a YourApp.1.2.3-beta1.nupkg (if you have a project reference that you want the version suffix to be applied to as well, you need to call dotnet restore /p:VersionSuffix=beta1 before that - this is a known bug in the tooling).

Of course, you can use custom variables as well, see this GitHub issue for a few examples.

For a complete reference of supported assembly attributes, i suggest looking at the source code of the build logic here (the values surrounded with $() are the properties used). And since i'm already talking about the source, this is the logic that composes the version and a few other properties.

Lyndsaylyndsey answered 7/4, 2017 at 14:11 Comment(4)
The /p: parameter might be what I'm looking for; I am reading that this is new in the 1.1 version of .NET Core and that it passes the parameters straight to dotnet msbuild (which is also new in .NET Core 1.1). I'll have to play with this for a bit over the weekend to see if it gives me the desired results, but looks promising.Dewitt
Just got round to trying this out, and it was the /p: parameter passed to a dotnet msbuild command which did the trick for me. Thanks!Dewitt
Make sure that if you're calling multiple dotnet commands that you are passing these version parameters to each command. Many commands (like test) will re-build your assemblies. In my situation, I was running build followed by test but not passing my version parameters to the test command so my version was getting wiped out. In my situation, I added the --no-build option to the test command so it didn't rebuild.Drouin
"if you have project reference that you want the version suffix to be applied to as well, you need to call dotnet restore /p:VersionSuffix=beta1 before that - this is a known bug in the tooling" @Martin can you provide a reference to that bug?Galilee
E
124
dotnet build /p:AssemblyVersion=1.2.3.4

Does that work for you?

Eke answered 31/7, 2017 at 22:35 Comment(7)
It does, but how does this answer differ from or add anything to the accepted answer to this question (which was posted 4 months ago)?Dewitt
It's more concise for one--but the above answer does not mention setting the AsemblyVersion property at all. It goes on about VersionPrefix and VersionSuffix and editing csproj files and the like, mentions that you can set any property you want from the command-line, but doesn't give the actual command line that you'd use to accomplish the requested result. I happened across this SO looking for the answer I eventually found--but did not find here.Eke
for core 2.0 using dotnet msbuild /p:AssemblyVersion=1.2.3.4 worksKnurled
Thank you Mr. McKenzie for giving me the clear concise answer I had to scroll down to find! :)Ichor
I love one liners. Thanks!Averyaveryl
And add -p:FileVersion=1.2.3.4 to get the other parameter that shows in the File Explorer -> Properties. Thanks! Upvoted!Psychologism
How do I do this from Visual Studio?Evesham
S
49

In my case, the main key was /property:Version=1.2.3.4. And the following command line did the job:

dotnet build SolutionName.sln -c Release /property:Version=1.2.3.4

This will override Assembly default version.

Update

So in addition just to clarify this in a practical use case of software release.

For instance when you publish a software you can set File version and Product version as mentioned here.

lets take an example:

dotnet publish .\ProjectFolder\Project.csproj -r win-x64 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true --self-contained true -o ReleaseFolderName -c Release /p:AssemblyVersion=1.2.3.4 /p:Version=1.2.3.4-product-version

will publish software, when right click on software and click on Details, you get following picture:

Software Details

Sully answered 15/9, 2018 at 0:36 Comment(3)
But then doesn't your build server always have to know what version it's on? If you compose it from the csproj then devs can just change the version there.Auvergne
@Auvergne that is true, but there are also situations where you need to update your version using command line.Sully
99.999% of the time you do not want developers setting version numbers, you want that done by tools such as a build server that pulls pristine code from source control, generates a SEMVER, and applies it to all assemblies produced by a build.Defoe
L
32

For those looking for a different automated (CI) way to do this, consider using conditionals in the .csproj file based on environment variables. For example, you might normally have a hardcoded version prefix and a timestamp-based suffix. But for proper releases, you would want to replace both with a single version that you set during the CI build. To do this, you can set an environment variable before calling dotnet build: let's say, RELEASE_VERSION.

In the .csproj file, under a <PropertyGroup>, you would have the following:

<Version Condition="'$(RELEASE_VERSION)' != ''">$(RELEASE_VERSION)</Version>
<VersionPrefix Condition="'$(RELEASE_VERSION)' == ''">0.0.1</VersionPrefix>
<VersionSuffix Condition="'$(RELEASE_VERSION)' == ''">$([System.DateTime]::UtcNow.ToString(`yyyyMMdd-HHmm`))</VersionSuffix>

The conditions above are set up such that, if the environment variable RELEASE_VERSION is empty, the normal prefix and suffix tags are used. But if it's not empty, then the signular version tag is used instead.

Lorileelorilyn answered 23/2, 2020 at 16:21 Comment(0)
C
8

To answer your question straight: The new SDK for msbuild is auto generating an assembly info file. You can suppress that using msbuild directives (to see the it by sample: invoke dotnet migrate on a project.json based project).

But let me tell you my handling: I had multiple projects sharing the same version. I added a version.props file which contained a property group including a item named VersionPrefix. This file I included via the csproj file (Include statement). I also removed all AssemblyInfo.cs files and let the SDK generate them for me.

I modify the version.props file during build.

Crinum answered 8/4, 2017 at 13:17 Comment(4)
fyi if you create a file named Directory.build.props in the directory hierarchy, it will be imported automatically and you don't need <Import /> directives.Lyndsaylyndsey
Awesome. Thanks.Crinum
+1: Its a useful answer too, but I have gone with @MartinUllrich's answer because it's even easier (for me) to build up a command line than it is to modify a file as I'm doing this all in a minimal Linux environment.Dewitt
Here is a link that describes Directory.Build.props learn.microsoft.com/en-us/visualstudio/msbuild/…Renelle
F
8

MsBuild 2017 will generate some assembly info if you missing them in Project file.

If you can read the msbuild target file you can look at:

[VS Install Dir] \MSBuild\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.GenerateAssemblyInfo.targets

You will see you can use some property in you project file to disable generated assembly info to prevent duplicated with your generated tools.

  • <GenerateAssemblyInfo> (This property will on/off all generate assembly info)
  • <GenerateAssemblyCompanyAttribute>
  • <GenerateAssemblyConfigurationAttribute>
  • <GenerateAssemblyCopyrightAttribute>
  • <GenerateAssemblyDescriptionAttribute>
  • <GenerateAssemblyFileVersionAttribute>
  • <GenerateAssemblyInformationalVersionAttribute>
  • <GenerateAssemblyProductAttribute>
  • <GenerateAssemblyTitleAttribute>
  • <GenerateAssemblyVersionAttribute>
  • <GenerateNeutralResourcesLanguageAttribute>
Faller answered 19/4, 2017 at 8:44 Comment(0)
T
8

What I do is this in .csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <Deterministic>false</Deterministic>
    <AssemblyVersion>2.0.*</AssemblyVersion>
  </PropertyGroup>
</Project>

Specify the wildcard in AssemblyVersion and then turn off Deterministic flag. Numbers will be added for the wildcard.

Transmogrify answered 23/11, 2019 at 17:49 Comment(3)
Later I get the version with GetType().Assembly.GetName().Version.ToString(). Source: linkPreclinical
in my caese <AssemblyVersion>1.1.*</AssemblyVersion> did not work, but <AssemblyVersion>1.1.0.0</AssemblyVersion> did workQuintan
Doesn't work for NuGet packagesSkive
P
4

I use Jenkins + Octopus for CI, and following worked quite well:

  1. Have a pre-build Powershell script that can take CI build number as a param or default to something preset.
  2. Have a separate nuspec file in the project for CI.
  3. The pre-build script would update nuspec file with the latest build version.
  4. Publish project with Jenkins.
  5. Call Nuget manually with a nuspec file from #2.
  6. Push the nuget package to Octopus.
Philosophical answered 7/4, 2017 at 13:38 Comment(2)
Does the nuspec file just work for packages? I'm not planning on packing everything - I am creating line of business micro-services which will end up getting executed in a Linux environment (probably in a docker container). Anyway, if the nuspec is just labeling NuGet packages, then this isn't what I'm after (though it is useful).Dewitt
No problem, you still taught me something, hence the upvote :)Dewitt
R
1

As I've answered here, I've made a CLI tool called dotnet-setversion which you can use for versioning *.csproj-style .NET Core projects.

During a CI build, you can use GitVersion or another tool to determine a version number for your project and then invoke dotnet-setversion $YOUR_VERSION_STRING in your project root directory.

Rowney answered 10/4, 2017 at 19:5 Comment(9)
It's very clever, but I'm perhaps not understanding why would you use this when you can just pass the /p: parameter to dotnet msbuild which will do the job as suggested in the accepted answer to this question? Looking at the source code, this looks like it only updates the 'Version' element.Dewitt
@Dewitt I didn't even know it existed. The "/p" flag seems to be new in 1.1.0. It doesn't even mention the flag if you run dotnet <command> --help. I've upvoted the accepted answer.Rowney
Also, it only updates the Version element because that's all that's required (you can include the suffix information with it too). If you set Version, you don't need to set VersionSuffix or VersionPrefix.Rowney
Oh no wait, I'm blind haha. At the bottom of the command help: "Additional Arguments: Any extra options that should be passed to MSBuild. See 'dotnet msbuild -h' for available options."Rowney
Yeah, I'm using it to set version, company name, product etc... its really useful :)Dewitt
I kind of get why they switched back to *.csproj format now.Rowney
@Dewitt Recently I've tried using the -p:Version=... instead of my CLI tool, but I'm finding that it creates issues when a project references another project directly (i.e. within the same solution). If I omit the version information in the *.csproj file and run dotnet pack -p:Version=..., each individual package will have the correct version number (e.g. package.0.6.1.nuget), but the "higher-level" project will incorrectly reference v1.0.0 of the lower-level one, as the version information isn't kept in the csproj file itself. Have you encountered this yourself?Rowney
I think so - see this so post #44178857 - doing a dotnet pack requires nuget references; project references do not appear to be packed into the same package, so this may be why... try unpacking your nuget file (it's just a zip file with a defferent extension) - I believe you will not see any of your project references included. hth :)Dewitt
@Dewitt Oh yeah, each project will map to its own individual Nuget package and I'm aware/don't mind that. The issue I have is just with how the higher-level project will determine the required versions of its project-to-project dependencies, if those projects themselves don't keep version information in their *.csproj files (since all version information is determined and configured for the project during the CI build itself). Got time for a quick chat?Rowney
T
1

In my case, I use this command line to build and update the version

dotnet build solution.sln -c release -v -p:Version=$env:APP_VERSION -p:FileVersion=$env:APP_VERSION 

It will update 2 field File version and Product version for dll files.

Torrlow answered 1/2, 2023 at 4:41 Comment(0)
B
0

Passing /p:PropertyName=Value as arguments does not work for me (ASP.Net Core 2.0 Web App). I found the Manifest Versioning Build Tasks on marketpace: https://marketplace.visualstudio.com/items?itemName=richardfennellBM.BM-VSTS-Versioning-Task

Bobolink answered 22/11, 2017 at 12:9 Comment(1)
I'm using exactly this using a .NET Core 2.0 app - and it is working for me. Could you expand your answer to show the actual MSbuild command you are using?Dewitt
A
0

Below is how I finally got mine to work for a .NET 6.0 desktop app. Only the Version.txt is manually updated.

<Project 
  Sdk="Microsoft.NET.Sdk" 
  InitialTargets="PreBuild"
  >
  <!--Have to do the PreBuild target before the IMPORT Directory.Build.props
  , else built version number lags one version behind every build-->

  <!--Using the filename Directory.Build.props will do the import below automatically-->
  <!--https://www.codeproject.com/Tips/1231820/NET-Core-Versioning-Demystified-->
  <!--<Import Project="Common.props" />-->

  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <ReadLinesFromFile File="$(ProjectDir)\Version.txt">
      <Output TaskParameter="Lines" ItemName="AppVersion" />
    </ReadLinesFromFile>
    <Exec Command="&quot;C:\Program Files\Git\bin\git.exe&quot; rev-parse --short HEAD &gt; &quot;$(ProjectDir)\Build.txt&quot;" />
    <ReadLinesFromFile File="$(ProjectDir)\Build.txt">
      <Output TaskParameter="Lines" ItemName="BuildVersion" />
    </ReadLinesFromFile>
    <Exec Command="ECHO|SET /p DUMMY=&quot;&lt;Project&gt;&lt;PropertyGroup&gt;&lt;VersionPrefix&gt;@(Appversion)&lt;/VersionPrefix&gt;&lt;VersionSuffix&gt;@(BuildVersion)&lt;/VersionSuffix&gt;&lt;/PropertyGroup&gt;&lt;/Project&gt;&quot; &gt; &quot;$(ProjectDir)Directory.Build.props&quot;" />
  </Target>
</Project>    
Adriel answered 14/12, 2023 at 8:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.