Setting the version number for .NET Core projects

Viewed 33255

What are the options for setting a project version with .NET Core / ASP.NET Core projects?

Found so far:

  • Set the version property in project.json. Source: DNX Overview, Working with DNX projects. This seems to set the AssemblyVersion, AssemblyFileVersion and AssemblyInformationalVersion unless overridden by an attribute (see next point).

  • Setting the AssemblyVersion, AssemblyFileVersion, AssemblyInformationalVersion attributes also seems to work and override the version property specified in project.json.

    For example, including 'version':'4.1.1-*' in project.json and setting [assembly:AssemblyFileVersion("4.3.5.0")] in a .cs file will result in AssemblyVersion=4.1.1.0, AssemblyInformationalVersion=4.1.1.0 and AssemblyFileVersion=4.3.5.0

Is setting the version number via attributes, e.g. AssemblyFileVersion, still supported?

Have I missed something - are there other ways?

Context

The scenario I'm looking at is sharing a single version number between multiple related projects. Some of the projects are using .NET Core (project.json), others are using the full .NET Framework (.csproj). All are logically part of a single system and versioned together.

The strategy we used up until now is having a SharedAssemblyInfo.cs file at the root of our solution with the AssemblyVersion and AssemblyFileVersion attributes. The projects include a link to the file.

I'm looking for ways to achieve the same result with .NET Core projects, i.e. have a single file to modify.

6 Answers

You can create a Directory.Build.props file in the root/parent folder of your projects and set the version information there.

However, now you can add a new property to every project in one step by defining it in a single file called Directory.Build.props in the root folder that contains your source. When MSBuild runs, Microsoft.Common.props searches your directory structure for the Directory.Build.props file (and Microsoft.Common.targets looks for Directory.Build.targets). If it finds one, it imports the property. Directory.Build.props is a user-defined file that provides customizations to projects under a directory.

For example:

<Project>
  <PropertyGroup>
    <Version>0.0.0.0</Version>
    <FileVersion>0.0.0.0</FileVersion>
    <InformationalVersion>0.0.0.0.myversion</InformationalVersion>
  </PropertyGroup>
</Project>

Another option for setting version info when calling build or publish is to use the undocumented /p option.

dotnet command internally passes these flags to MSBuild.

Example:

dotnet publish ./MyProject.csproj /p:Version="1.2.3" /p:InformationalVersion="1.2.3-qa"

See here for more information: https://github.com/dotnet/docs/issues/7568

Why not just change the value in the project.json file. Using CakeBuild you could do something like this (optimizations probably possible)

Task("Bump").Does(() => {
    var files = GetFiles(config.SrcDir + "**/project.json");
    foreach(var file in files)
    {
        Information("Processing: {0}", file);

        var path = file.ToString();
        var trg = new StringBuilder();
        var regExVersion = new System.Text.RegularExpressions.Regex("\"version\":(\\s)?\"0.0.0-\\*\",");
        using (var src = System.IO.File.OpenRead(path))
        {
            using (var reader = new StreamReader(src))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    if(line == null)
                        continue;

                    line = regExVersion.Replace(line, string.Format("\"version\": \"{0}\",", config.SemVer));

                    trg.AppendLine(line);
                }
            }
        }

        System.IO.File.WriteAllText(path, trg.ToString());
    }
});

Then if you have e.g. a UnitTest project that takes a dependency on the project, use "*" for dependency resolution.

Also, do the bump before doing dotnet restore. My order is as follows:

Task("Default")
  .IsDependentOn("InitOutDir")
  .IsDependentOn("Bump")
  .IsDependentOn("Restore")
  .IsDependentOn("Build")
  .IsDependentOn("UnitTest");

Task("CI")
  .IsDependentOn("Default")
  .IsDependentOn("Pack");

Link to full build script: https://github.com/danielwertheim/Ensure.That/blob/3a278f05d940d9994f0fde9266c6f2c41900a884/build.cake

The actual values, e.g. the version is coming from importing a separate build.config file, in the build script:

#load "./buildconfig.cake"

var config = BuildConfig.Create(Context, BuildSystem);

The config file looks like this (taken from https://github.com/danielwertheim/Ensure.That/blob/3a278f05d940d9994f0fde9266c6f2c41900a884/buildconfig.cake):

public class BuildConfig
{
    private const string Version = "5.0.0";

    public readonly string SrcDir = "./src/";
    public readonly string OutDir = "./build/";    

    public string Target { get; private set; }
    public string Branch { get; private set; }
    public string SemVer { get; private set; }
    public string BuildProfile { get; private set; }
    public bool IsTeamCityBuild { get; private set; }

    public static BuildConfig Create(
        ICakeContext context,
        BuildSystem buildSystem)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var target = context.Argument("target", "Default");
        var branch = context.Argument("branch", string.Empty);
        var branchIsRelease = branch.ToLower() == "release";
        var buildRevision = context.Argument("buildrevision", "0");

        return new BuildConfig
        {
            Target = target,
            Branch = branch,
            SemVer = Version + (branchIsRelease ? string.Empty : "-b" + buildRevision),
            BuildProfile = context.Argument("configuration", "Release"),
            IsTeamCityBuild = buildSystem.TeamCity.IsRunningOnTeamCity
        };
    }
}
Related