Auto Versioning in Visual Studio 2017 (.NET Core)
Asked Answered
C

21

150

I have spent the better part of a few hours trying to find a way to auto-increment versions in a .NETCoreApp 1.1 (Visual Studio 2017).

I know the the AssemblyInfo.cs is being created dynamically in the folder: obj/Debug/netcoreapp1.1/

It does not accept the old method of: [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]

If I set the project to package I can set versions there but this seems to be used to build the AssemblyInfo.cs file.

My question is, has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.

Cacao answered 25/3, 2017 at 17:48 Comment(4)
I don't know how far you got with this, but looks like I asked almost the same question a different way ( https://mcmap.net/q/157683/-setting-the-version-number-for-net-core-projects-csproj-not-json-projects ) - Maybe the accepted answer to this question will help you out; you can just pass the /p: flag to dotnet msbuild in your build script and set version, company, copyright... all that good stuff.Claypan
Thanks for the info. That just open up additional options.Cacao
Previously * was supported for AssemblyVersion, not for AssemblyFileVersion- see Can I automatically increment the file build version when using Visual Studio?Barraza
FWIW the wildcard in the assembly version is not supported because for these new project, the compiler's "deterministic" mode is active by default. Since auto-increment would break determinism (same input > same output) it is disallowed in that mode. You can set <Deterministic>False</Deterministic> in the csproj to use it. (or use any other MSbuild logic to calculate <VersionPrefix>/<Version>)Cartie
U
30

I have been looking for a version incrementer for a .NET Core app in VS2017 using the csproj configuration format.

I found a project called dotnet bump that worked for the project.json format but struggled to find a solution for the .csproj format. The writer of dotnet bump actually came up with the solution for the .csproj format and it is called MSBump.

There is a project on GitHub for it at:

https://github.com/BalassaMarton/MSBump

where you can see the code and it's available on NuGet too. Just search for MSBump on Nuget.

Ubald answered 8/8, 2017 at 13:0 Comment(6)
I recommend using the latest 2.1.0 release of MSBump, It supports switching configurations better, and also sets the version for the current build, not the next one (like the previous version).Retract
I see it now also supports MSBuild whereas before it required visual studio.Ubald
Yes, and it also supports multi-targeting projects.Retract
Consider using GitVersioning. It may be suitable to run on your CI environment. github.com/AArnott/Nerdbank.GitVersioningFlatboat
MSBump increments version on every build even if you didn't change anything, this causes a lot of problems in a long term. And sometimes versions go out of sync and one version is behind the other.Mekong
Visual Studio 16.5 just broke this for me. :(Tronna
B
80

Add <Deterministic>False</Deterministic> inside a <PropertyGroup> section  of .csproj

The workaround to make AssemblyVersion * working is described in “Confusing error message for wildcard in [AssemblyVersion] on .Net Core #22660”

Wildcards are only allowed if the build is not deterministic, which is the default for .Net Core projects. Adding <Deterministic>False</Deterministic> to csproj fixes the issue.

The reasons why .Net Core Developers consider Deterministic Builds beneficial described in http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html and Compilers should be deterministic: same inputs generate same outputs #372

However if you are using TeamCity, TFS or other CI/CD tool, it's probably better to keep the version number controlled and incremented by them and pass to build as a parameter (as it was suggested in other answers) , e.g.

msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber

Package number for NuGet packages

msbuild /t:pack /p:Version=YourVersionNumber   
Barraza answered 28/10, 2017 at 2:19 Comment(3)
Thank you! I knew there was a hidden lever for opening the treasure room! I am migrating an old project to the new .NET SDK, and I really wanted to do this fast, without the hassle of finding automated version increment solutions. In fact, the more compatible to the old ways the better for my case.Gaspard
This is the best answer IMO. It allows for the build tooling to work properly. At least I can use an external mechanism to feed the number into the build now.Aultman
Also, make sure you don't have <Version>1.0.0</Version> in your .csprojSubjunction
W
68

If you're using Visual Studio Team Services/TFS or some other CI build process to have versioning built-in, you can utilize msbuild's Condition attribute, for example:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">0.0.1-local</Version>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
  </ItemGroup>

</Project>

This will tell the .NET Core compiler to use whatever is in the BUILD_BUILDNUMBER environment variable if it's present, or fallback to 0.0.1-local if you're doing a build on your local machine.

Wordsmith answered 17/8, 2017 at 15:6 Comment(6)
Nice, I like this approach because the enviornment variables can just be set at build server while these conditionals deteremine the assembly set in the binaries.Roland
Doesn't seem to work on TFS 2010, but hopefully we are moving off that soon!Luong
Not a bad solution, though could be a bit of work if the solution has a lot of projects.Batholith
Good solution. I did get a Build exception though. I had to change the config slightly to fix it. https://mcmap.net/q/157240/-auto-versioning-in-visual-studio-2017-net-coreSuperfamily
This works great with .NET Core 2.1.2 and TFS2017U3Insecticide
One of the simplest approaches, no scripts and (unfortunately) still relevant with dotnet 5Jacklyn
U
30

I have been looking for a version incrementer for a .NET Core app in VS2017 using the csproj configuration format.

I found a project called dotnet bump that worked for the project.json format but struggled to find a solution for the .csproj format. The writer of dotnet bump actually came up with the solution for the .csproj format and it is called MSBump.

There is a project on GitHub for it at:

https://github.com/BalassaMarton/MSBump

where you can see the code and it's available on NuGet too. Just search for MSBump on Nuget.

Ubald answered 8/8, 2017 at 13:0 Comment(6)
I recommend using the latest 2.1.0 release of MSBump, It supports switching configurations better, and also sets the version for the current build, not the next one (like the previous version).Retract
I see it now also supports MSBuild whereas before it required visual studio.Ubald
Yes, and it also supports multi-targeting projects.Retract
Consider using GitVersioning. It may be suitable to run on your CI environment. github.com/AArnott/Nerdbank.GitVersioningFlatboat
MSBump increments version on every build even if you didn't change anything, this causes a lot of problems in a long term. And sometimes versions go out of sync and one version is behind the other.Mekong
Visual Studio 16.5 just broke this for me. :(Tronna
M
22

You can use a MSBuild property function to set the version suffix based on current date:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <VersionSuffix>pre$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm))</VersionSuffix>
</PropertyGroup>

This will output a package with a name like: PackageName.1.0.0-pre20180807-1711.nupkg.

More details about MSBuild property functions: https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions

The Version is formed from the combination of VersionPrefix and VersionSuffix, or if VersionSuffix is blank, VersionPrefix only.

<PropertyGroup>
  <VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
Mariquilla answered 7/8, 2018 at 21:8 Comment(0)
D
16

I came up with a solution that worked almost the same as old AssemblyVersion attribute with star (*) - AssemblyVersion("1.0.*")

Values for AssemblyVersion and AssemblyFileVersion is in MSBuild project .csproj file (not in AssemblyInfo.cs) as property FileVersion (generates AssemblyFileVersionAttribute) and AssemblyVersion (generates AssemblyVersionAttribute). In MSBuild process we use our custom MSBuild task to generate version numbers and then we override values of these FileVersion and AssemblyVersion properties with new values from task.

So first, we create our custom MSBuild task GetCurrentBuildVersion:

public class GetCurrentBuildVersion : Task
{
    [Output]
    public string Version { get; set; }
 
    public string BaseVersion { get; set; }
 
    public override bool Execute()
    {
        var originalVersion = System.Version.Parse(this.BaseVersion ?? "1.0.0");
 
        this.Version = GetCurrentBuildVersionString(originalVersion);
 
        return true;
    }
 
    private static string GetCurrentBuildVersionString(Version baseVersion)
    {
        DateTime d = DateTime.Now;
        return new Version(baseVersion.Major, baseVersion.Minor,
            (DateTime.Today - new DateTime(2000, 1, 1)).Days,
            ((int)new TimeSpan(d.Hour, d.Minute, d.Second).TotalSeconds) / 2).ToString();
    }
}

Task class inherit from Microsoft.Build.Utilities.Task class from Microsoft.Build.Utilities.Core NuGet package. It takes BaseVersion property (optional) on input and returns generated version in Version output property. The logic to get version numbers is same as .NET automatic versioning (Build number is days count since 1/1/2000 and Revision is half seconds since midnight).

To build this MSBuild task, we use .NET Standard 1.3 class library project type with this class.

.csproj file can looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <AssemblyName>DC.Build.Tasks</AssemblyName>
    <RootNamespace>DC.Build.Tasks</RootNamespace>
    <PackageId>DC.Build.Tasks</PackageId>
    <AssemblyTitle>DC.Build.Tasks</AssemblyTitle>
  </PropertyGroup>
 
  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Framework" Version="15.1.1012" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.1012" />
  </ItemGroup>
</Project>

This task project is also available in my GitHub holajan/DC.Build.Tasks

Now we setup MSBuild to use this task and set FileVersion and AssemblyVersion properties. In .csproj file, it looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <UsingTask TaskName="GetCurrentBuildVersion" AssemblyFile="$(MSBuildThisFileFullPath)\..\..\DC.Build.Tasks.dll" />
 
  <PropertyGroup>
    ...
    <AssemblyVersion>1.0.0.0</AssemblyVersion>
    <FileVersion>1.0.0.0</FileVersion>
  </PropertyGroup>
 
  ...
 
  <Target Name="BeforeBuildActionsProject1" BeforeTargets="BeforeBuild">
    <GetCurrentBuildVersion BaseVersion="$(FileVersion)">
      <Output TaskParameter="Version" PropertyName="FileVersion" />
    </GetCurrentBuildVersion>
    <PropertyGroup>
      <AssemblyVersion>$(FileVersion)</AssemblyVersion>
    </PropertyGroup>
  </Target>
 
</Project>

Important things here:

  • Mentioned UsingTask imports GetCurrentBuildVersion task from DC.Build.Tasks.dll. It assumes that this dll file is located on parent directory from your .csproj file.
  • Our BeforeBuildActionsProject1 Target that calls task must have unique name per project in case we have more projects in the solution which calls GetCurrentBuildVersion task.

The advantage of this solution is that it works not only from builds on build server, but also in manual builds from dotnet build or Visual Studio.

Disapprobation answered 15/4, 2017 at 13:0 Comment(3)
I'd recommend to use DateTime.UtcNow instead of DateTime.Now in method GetCurrentBuildVersionString() in particular if the code is executed on automated build machines. They may run at 2 am or 3 am in the morning when your computer switches to / from daylight savings time. With DateTime.Now in that scenario you might be going backwards in terms of version. Admittedly, this is a corner case and I also admit that I'm picky. :-) Also, the problem goes also away if you configure the same timezone on all build machines and not to adjust to daylight savings time.Doublecross
Is there a NuGet package for this yet?Depredate
@Jonathan Allen No, I have no plan for nuget package, because of different name in each project. You can download compiled build task assemby in github.com/holajan/DC.Build.Tasks/tree/master/dist folderDisapprobation
I
16

You could do it like below, within the csproj file. I didn't figure out the math. I found that somewhere else on Stack Overflow, but this works and will give you something similiar to 1.0.* for version.

<PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <FileVersion>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays).$([System.Math]::Floor($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds), 1.32))))</FileVersion>
    <Version>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays)</Version>
</PropertyGroup>
Iridissa answered 6/3, 2020 at 20:49 Comment(5)
Doesn't appear to work for netstandard2.0 in my env. I'm using .NET Core SDK 3.1.403. Is something else need to make this work? When run with dotnet build version number doesn't change from default.Doublecross
Thank, short and works. – @Manfred: Maybe use AssemblyVersion or something similar instead. That was the problem for me. FileVersion was set. but AssemblyVersion wasn't. Also: <Version> should not be used anymore (if I'm nit mistaken).Derive
Also: Instead of <FileVersion>1.0. … </FileVersion> I used <FileVersion>$(VersionPrefix). … </FileVersion> instead, and then I can set Version-Number correctly in VersionPefix property.Derive
FYI: to match what .NET does with "1.0.*", I changed from using "UtcNow" to "Now" and from "1.32" to "2", although to me UtcNow makes more sense to use.Fortdefrance
does this change everyday? or just once on deployment?Seleucia
C
15

I accepted the above answer because @Gigi is correct (as of now) but I was annoyed and came up with the following PowerShell Scripts.

First I have the script in my solution folder (UpdateBuildVersion.ps1):

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Revision
$avBuild = [Convert]::ToInt32($avBuild,10)+1
$fvBuild = [Convert]::ToInt32($fvBuild,10)+1

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)

I added this to csproj file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <AssemblyVersion>0.0.1</AssemblyVersion>
    <FileVersion>0.0.1</FileVersion>
    <PreBuildEvent>powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -command "& {$(SolutionDir)UpdateBuildVersion.ps1}"</PreBuildEvent>
  </PropertyGroup>
</Project>

Even through its set to be a PreBuildEvent, the fact is the version numbers do not get updated until AFTER the file has been loaded into memory so the version number will not reflect until the next build. In fact, you could change it to a PostBuildEvent and it would have the same effect.

I also created the following two scripts: (UpdateMinorVersion.ps1)

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Minor Version - Will reset all sub nodes
$avMinor = [Convert]::ToInt32($avMinor,10)+1
$fvMinor = [Convert]::ToInt32($fvMinor,10)+1
$avBuild = 0
$fvBuild = 0

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)

(UpdateMajorVersion.ps1)

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Major Version - Will reset all sub nodes
$avMajor = [Convert]::ToInt32($avMajor,10)+1
$fvMajor = [Convert]::ToInt32($fvMajor,10)+1
$avMinor = 0
$fvMinor = 0
$avBuild = 0
$fvBuild = 0

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)
Cacao answered 25/3, 2017 at 20:52 Comment(0)
M
11

These values are now set in the .csproj file:

<PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <AssemblyVersion>1.0.6.0</AssemblyVersion>
    <FileVersion>1.0.6.0</FileVersion>
    <Version>1.0.1</Version>
</PropertyGroup>

These are the same values you see if you go in the Package tab in the project settings. While I don't think you can use * to autoincrement the version, what you can do is introduce a post-processing step that replaces the versions for you (e.g. as part of your continuous integration).

Multicellular answered 25/3, 2017 at 18:8 Comment(5)
I was afraid this would be the answer. I will see if I can do a pre-build step to increment it.Cacao
As pointed out in another thread, the new csproj format allows you to turn off the auto-generation of the assemblyinfo file and for you to specify your own. I followed the advice of natemcmaster's answer here and used a standard AssemblyInfo.cs file: #42138918Eyeopener
Why did they remove the auto-increment? It worked really well and very simply for me for years. I push master, CI builds and increments, then the version is read directly from the built DLL using some PS script, then use that version as an arg when pushing to NuGet. So simple. Now broken.Ditzel
@LukePuplett: See [“Confusing error message for wildcard in AssemblyVersion on .Net Core #22660”] (github.com/dotnet/roslyn/issues/22660), The reasons why they consider Deterministic Builds beneficial described in blog.paranoidcoding.com/2016/04/05/… and Compilers should be deterministic: same inputs generate same outputs #372<github.com/dotnet/roslyn/issues/372>Barraza
That's pretty awesome and I agree with the idea... but.... why not support a * auto-increment-but-not-unless-the-source-actually-changed feature? #rhetoricalQuestionDitzel
C
9

has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.

Use:

dotnet build /p:AssemblyVersion=1.2.3.4

I found this question trying to solve this problem in the context of a CI build. I wanted to set the assembly version to the CI build number.

Contredanse answered 31/7, 2017 at 22:14 Comment(6)
The title says "Auto Versioning in Visual Studio 2017 (.NET Core)". Where exactly building it manually complies to "Visual Studio 2017"?Nell
I was responding to: "has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects." I found this question trying to solve this problem in the context of a CI build. I wanted to set the assembly version to the CI build number. I'm sorry if you feel this wasn't relevant to the question at hand.Contredanse
It's a helpful component for me thanks. I will use this as part of a CI solutionLuong
@ChrisMcKenzie: your comment should be included in your answer to make your intent clearBarraza
** this does not work for me on netstandard projects when assemblyinfo.cs is not specified and the version is in the csproj...Batholith
Try removing the version from the csproj? I don't think it's necessary. See this ExampleContredanse
N
8

To summaries all up above: your can revert to old AssemblyInfo.cs behavior with this:

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>

But this approach is not recommended, because turning off GenerateAssemblyInfo can lead to problems with infra, for example. More selective approach:

<Deterministic>false</Deterministic>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<AssemblyVersion>1.2.*</AssemblyVersion>

and you don't need AssemblyInfo.cs any more.

Necrosis answered 29/4, 2021 at 7:1 Comment(0)
F
6

I made a simple CLI tool for setting .csproj .NET Core version strings here. You can combine it with tools like GitVersion for automatic version bumping during CI builds, if that's what you're after.

Fallfish answered 3/4, 2017 at 21:28 Comment(1)
goddamn genius. love it!Hotbed
S
6

Thanks to @joelsand for pointing me in the right direction.

I had to change his answer slightly as when the DevOps Build ran, I got the following exception

The specified version string does not conform to the recommended format - major.minor.build.revision

I had to add the $(BUILD_BUILDNUMBER) at the end of major.minor.build section. To de-duplicate the actual version, I also use a version-prefix:

<PropertyGroup>
    <VersionPrefix>1.0.3</VersionPrefix>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">$(VersionPrefix)-local</Version>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(VersionPrefix)-$(BUILD_BUILDNUMBER)</Version>
</PropertyGroup>
Superfamily answered 22/1, 2020 at 10:50 Comment(1)
I had the same exact problem and your answer fixed it. Thank you.Phosphorate
S
6

What worked for me was to define Patch and Revision using a PropertyGroup, then you can just use this variables for version (and prefix if needed). Version numbers must be short numbers so I use YearMonth for Patch, and MinutesOfDay for Revision. Add this lines to your csproj file:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
        <VersionMajor>0</VersionMajor>
        <VersionMinor>9</VersionMinor>
        <VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yyMM"))</VersionPatch>
        <VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalMinutes.ToString("0"))</VersionRevision>
    </PropertyGroup>

    <PropertyGroup>
        <OutputType>...</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <Title>Software Title</Title>
        <Description>...</Description>
        <Authors>...</Authors>
        <Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
    </PropertyGroup>

    ....

</Project>

It can be achive in a generic way making use of Directory.build.props file. More info here: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019

Just add a file with this name in the project folder and place there these lines.


I came across here searching for a solution for shared projects. In my case, I solved it adding a Version.build.props file in my shared project with the structure shown above, and just one new line at any csproj file for projects using my shared code:

<!-- Shared project import -->
<Import Project="..\Shared\Shared.projitems" Label="Shared" /> 
<!-- Version number generator -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Version.Build.props', '$(MSBuildThisFileDirectory)../Shared/'))" />

I'll left this code here just in case someone needs it.

*Solution tested for .Net5 but should works for earlier versions.

Saltigrade answered 23/11, 2020 at 13:4 Comment(0)
S
5

To enable versioning of your .NET Core / .NET Whatever project based on your GIT setup, using the tags/describe functionality of GIT.

I have been using a Prebuild.targets.xml file which is located in the root folder for the project and included in the csproj file like:

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="PreBuild.targets.xml" />
  ...
  <PropertyGroup>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

Use the "GenerateAssembyInfo" tag to disable automatic assembly info generation.

Then the Prebuild.targets.xml will generate a CommonAssemblyInfo.cs file where you can include the version tags you want based on your GIT version

NOTE: I have found the Prebuilds.targets.xml somewhere else, so haven't bothered cleaning it up .)

The Prebuild.targets.xml file:

    <?xml version="1.0" encoding="utf-8" ?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     
      <UsingTask
        TaskName="GetVersion"
        TaskFactory="CodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
          <VersionString ParameterType="System.String" Required="true" />
          <Version ParameterType="System.String" Output="true" />
          <Commit ParameterType="System.String" Output="true" />
          <VersionSuffix ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
          <!--<Reference Include="" />-->
          <Using Namespace="System"/>
          <Using Namespace="System.IO"/>
          <Using Namespace="System.Text.RegularExpressions" />
          <Code Type="Fragment" Language="cs">
            <![CDATA[
              var match = Regex.Match(VersionString, @"^(?<major>\d+)\.(?<minor>\d+)(\.?(?<patch>\d+))?-(?<revision>\d+)-(?<commit>[a-z0-9-]+)$");
              int major, minor, patch, revision;
              Int32.TryParse(match.Groups["major"].Value, out major);
              Int32.TryParse(match.Groups["minor"].Value, out minor);
              Int32.TryParse(match.Groups["patch"].Value, out patch);
              Int32.TryParse(match.Groups["revision"].Value, out revision);
              _Version = new Version(major, minor, patch, revision).ToString();
              _Commit = match.Groups["commit"].Value;
            ]]>
          </Code>
        </Task>
      </UsingTask>
     
      <UsingTask
        TaskName="GitExistsInPath"
        TaskFactory="CodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
          <Exists ParameterType="System.Boolean" Output="true" />
        </ParameterGroup>
        <Task>
          <!--<Reference Include="" />-->
          <Using Namespace="System"/>
          <Using Namespace="System.IO"/>
          <Using Namespace="System.Text.RegularExpressions" />
          <Code Type="Fragment" Language="cs">
            <![CDATA[
            var values = Environment.GetEnvironmentVariable("PATH");
            foreach (var path in values.Split(';')) {
                var exeFullPath = Path.Combine(path, "git.exe");
                if (File.Exists(exeFullPath)) {
                    Exists = true;
                    return true;
                }
                var cmdFullPath = Path.Combine(path, "git.cmd");
                if (File.Exists(cmdFullPath)) {
                    Exists = true;
                    return true;
            }
            }
            Exists = false;
            ]]>
          </Code>
        </Task>
      </UsingTask>
     
      <Target Name="CreateCommonVersionInfo" BeforeTargets="CoreCompile">
        <Message Importance="high" Text="CreateCommonVersionInfo" />
     
        <GitExistsInPath>
          <Output TaskParameter="Exists" PropertyName="GitExists"/>
        </GitExistsInPath>
        <Message Importance="High" Text="git not found!" Condition="!$(GitExists)"/>
          
        <Exec Command="git describe --tags --long --dirty > $(ProjectDir)version.txt" Outputs="$(ProjectDir)version.txt" WorkingDirectory="$(SolutionDir)" IgnoreExitCode="true" Condition="$(GitExists)">
          <Output TaskParameter="ExitCode" PropertyName="ExitCode" />
        </Exec>
        <Message Importance="high" Text="Calling git failed with exit code $(ExitCode)" Condition="$(GitExists) And '$(ExitCode)'!='0'" />
        
        <ReadLinesFromFile File="$(ProjectDir)version.txt" Condition="$(GitExists) And '$(ExitCode)'=='0'">
          <Output TaskParameter="Lines" ItemName="OutputLines"/>
        </ReadLinesFromFile>
        <Message Importance="High" Text="Tags: @(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'"/>

        <Delete Condition="Exists('$(ProjectDir)version.txt')" Files="$(ProjectDir)version.txt"/>
     
        <GetVersion VersionString="@(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'">
          <Output TaskParameter="Version" PropertyName="VersionString"/>
          <Output TaskParameter="Commit" PropertyName="Commit"/>
        </GetVersion>
          
        <PropertyGroup>
          <VersionString Condition="'$(VersionString)'==''">0.0.0.0</VersionString>
        </PropertyGroup>
     
        <Message Importance="High" Text="Creating CommonVersionInfo.cs with version $(VersionString) $(Commit)" />
     
        <WriteLinesToFile Overwrite="true" File="$(ProjectDir)CommonAssemblyInfo.cs" Encoding="UTF-8" Lines='using System.Reflection%3B
     
    // full version: $(VersionString)-$(Commit)
     
    [assembly: AssemblyVersion("$(VersionString)")]
    [assembly: AssemblyInformationalVersion("$(VersionString)")] 
    [assembly: AssemblyFileVersion("$(VersionString)")]' />
        
      </Target>
    </Project>

EDIT: If you are building using MSBUILD the

 $(SolutionDir)

Might cause you trouble, use

 $(ProjectDir)

instead

Sisk answered 6/10, 2017 at 9:45 Comment(1)
Nice! Does VersionSuffix end up getting set or used? It doesn't seem to beLuong
P
5
  <PropertyGroup>
    <SecondsSinceEpoch>$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::MinValue)).TotalSeconds)</SecondsSinceEpoch>
    <Revision>$([System.Math]::Truncate($([System.Decimal]::Remainder($(SecondsSinceEpoch), 100000))))</Revision>
    <Version>1.7.0.$(Revision)</Version>
    <AssemblyVersion>$(Version)</AssemblyVersion>
    <FileVersion>$(Version)</FileVersion>
  </PropertyGroup>

My take on setting a decent value via .csproj. Unfortunately if your next rebuild is an interval of 100000 seconds later it will be the same value. Better than MSBump making every Build a Rebuild though.

Can use TotalMinutes, TotalDays, etc. if slow or automated builds.

Phraseograph answered 14/2, 2021 at 14:44 Comment(0)
S
4

As an alternative, you can try fixed major number with a suffix based on current date:

  <PropertyGroup>
    <VersionPrefix>1</VersionPrefix>
    <VersionSuffix>$([System.DateTime]::UtcNow.ToString(yyMM)).$([System.DateTime]::UtcNow.ToString(ddHH)).$([System.DateTime]::UtcNow.ToString(mmss))</VersionSuffix>
    <Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix).0.0.1</Version>
    <Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
  </PropertyGroup>
Stokehold answered 21/8, 2020 at 11:3 Comment(1)
You have a nice solution here. Not everyone likes to use 'DateTime' in their versions though.Cacao
H
2

We can use special parameter for dotnet publish -- version-suffix 1.2.3

For file version:

<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>

For version:

<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>

https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21

--version-suffix <VERSION_SUFFIX>     Defines the value for the $(VersionSuffix) property in the project.
Hydrotherapeutics answered 10/9, 2018 at 15:9 Comment(0)
S
1

I think this Answer from @joelsand is the correct answer for setting version number for dotnet core running on VSTS

To add more information for this answer,

BUILD_BUILDNUMBER is actually a predefined variable.

It turns out there are 2 versions of predefined variable.

One is build.xxxx, the other is BUILD_XXXX.

You can only use Environment Variable Name in cproj.

Stepsister answered 15/2, 2018 at 22:33 Comment(1)
Isn't build.xxxx used on the front end for referencing within a pipeline and BUILD_XXXX is the same value but with slightly modified syntax required for referencing the variable in PS?Melanymelaphyre
N
0

My OSS project "RelaxVersioner" can full automatic insert with the attributes and constatnt literals on git repository only NuGet package installed without any tool-depended operation.

Example for applied information:

sing System.Reflection;
[assembly: AssemblyVersion("1.0.21")]
[assembly: AssemblyFileVersion("2020.12.20.33529")]
[assembly: AssemblyInformationalVersion("1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a")]
[assembly: AssemblyMetadata("Date","Sun, 20 Dec 2020 09:37:39 GMT")]
[assembly: AssemblyMetadata("Branch","master")]
[assembly: AssemblyMetadata("Tags","")]
[assembly: AssemblyMetadata("Author","Kouji Matsui <[email protected]>")]
[assembly: AssemblyMetadata("Committer","Kouji Matsui <[email protected]>")]
[assembly: AssemblyMetadata("Message","Merge branch 'devel'")]
[assembly: AssemblyMetadata("Build","")]
[assembly: AssemblyMetadata("Generated","Sun, 20 Dec 2020 09:37:43 GMT")]
[assembly: AssemblyMetadata("Platform","AnyCPU")]
[assembly: AssemblyMetadata("BuildOn","Unix")]
[assembly: AssemblyMetadata("SdkVersion","5.0.101")]

namespace YourApp
{
  internal static class ThisAssembly
  {
    public const string AssemblyVersion = "1.0.21";
    public const string AssemblyFileVersion = "2020.12.20.33529";
    public const string AssemblyInformationalVersion = "1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a";
    public static class AssemblyMetadata
    {
      public const string Date = "Sun, 20 Dec 2020 09:37:39 GMT";
      public const string Branch = "master";
      public const string Tags = "";
      public const string Author = "Kouji Matsui <[email protected]>";
      public const string Committer = "Kouji Matsui <[email protected]>";
      public const string Message = "Merge branch 'devel'";
      public const string Build = "";
      public const string Generated = "Sun, 20 Dec 2020 09:37:43 GMT";
      public const string Platform = "AnyCPU";
      public const string BuildOn = "Unix";
      public const string SdkVersion = "5.0.101";
    }
  }
}
Niobic answered 27/3, 2021 at 3:12 Comment(0)
S
0

Another alternative with dates, based on Antonio Rodríguez's answer, to avoid repetitions in the numbers

  • Version Patch: (Year in 2 digits)+(Day of Year)
  • VersionRevision: Total number of seconds in the Day
  <PropertyGroup>
    <VersionMajor>1</VersionMajor>
      <VersionMinor>0</VersionMinor>
      <VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yy"))$([System.DateTime]::UtcNow.DayOfYear.ToString("0"))</VersionPatch>
      <VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds.ToString("0"))</VersionRevision>
  </PropertyGroup>

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
  </PropertyGroup>  

Sanborn answered 19/9, 2022 at 13:51 Comment(2)
This was great and I loved it! I even played with it a bit for my liking and learned how to use MSBuild functions and others. It ran well, until I ran into another issue: Visual Studio 2022 constantly kept restoring my projects and their Nuget packages. I thought it was a Nuget issue until I found this: developercommunity.visualstudio.com/t/…. Now I'm looking for a different solution. I understand MS's stance on this and there is a work-around, but that requires manually changing of the versions once again. Still searching...Denigrate
For completeness, here's what the link above says: ...the use of System.DateTime.UtcNow in MSBuild properties is considered a poor practice as it prevents builds from being repeatable and will tend to prevent MSBuild and VS from successfully caching data. In this particular case, the project is different each time VS checks it (because the VersionPrefix/VersionSuffix property is different every time) and VS thinks it need to attempt another restore to bring everything back up to date.Denigrate
A
0

I am working on a .Net Core project (ver .NET7.0), this is what worked for me, other answers returns 0.0.0.0 Apparently there is no way to have an incremental version number in Assembly unless you get the Assembly file version (which somehow can be considered as application version number) In my project .csproj file, I have added the following entries:

<Deterministic>false</Deterministic>
<VersionSuffix>1.0.0.$([System.DateTime]::UtcNow.ToString(mmff))</VersionSuffix>
<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.0.1</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>

then in my cshtml file I read the version number of the assembly file:

@using System.Reflection;
@{
    var assembly = Assembly.GetEntryAssembly();
    var fileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location);
    var fileVersion = fileVersionInfo.FileVersion;
}
    <p>Version: @fileVersion</p>
Audi answered 23/6, 2023 at 6:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.