Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Populate new csproj format properties from Azure DevOps build pipeline task #1611

Closed
roryprimrose opened this issue Mar 1, 2019 · 20 comments
Closed

Comments

@roryprimrose
Copy link

@roryprimrose roryprimrose commented Mar 1, 2019

The existing build tasks for Azure DevOps have long had the option to update AssemblyInfo.cs before running a build task (which is really sweet BTW). Is there going to be support in the future for updating the version/package metadata properties in the new csproj format from the build task?

I've previously used a powershell script to take the output of the GitVersion variables to manually go into all the csproj files and update the properties before running dotnet build. It would be great if a future version of the build task could do this OOTB.

I'll try to find the powershell script I use which may better explain what I'm trying to achieve.

@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Mar 1, 2019

This is the powershell script I am using between running the GitVersion build task and the dotnet build task.

$buildVersion = $env:GitVersion_NuGetVersionV2 
$sourcesDirectory = $env:BUILD_SOURCESDIRECTORY

Write-Host "Setting version $buildVersion. Searching for projects"

# Find all the csproj files
if ($buildVersion -eq $null) {
    Write-Error ("GitVersion_NuGetVersionV2 environment variable is missing.")
    exit 1
}

if ($sourcesDirectory -eq $null) {
    Write-Error ("BUILD_SOURCESDIRECTORY environment variable is missing.")
    exit 1
}

# This code snippet gets all the files in $Path that end in ".csproj" and any subdirectories.
Get-ChildItem -Path $sourcesDirectory -Filter "*.csproj" -Recurse -File | 
    ForEach-Object { 
        $projectPath = $_.FullName
        $project = Select-Xml $projectPath -XPath "//Project"
        
        $version = $project.Node.SelectSingleNode("PropertyGroup/Version")

        if ($version -eq $null) {
            Write-Host "Adding Version element to $projectPath"

            $group = $project.Node.SelectSingleNode("PropertyGroup")
            $version = $group.OwnerDocument.CreateElement("Version")
            $group.AppendChild($version) | Out-Null
        }

        $version.InnerText = $env:GitVersion_NuGetVersionV2
        $version.OwnerDocument.Save($projectPath)

        Write-Host "Saved version $($env:GitVersion_NuGetVersionV2) to $projectPath"
    }

Loading

@arturcic
Copy link
Member

@arturcic arturcic commented Mar 1, 2019

You could also create/update the Directory.Build.props file with the properties you're interested in.

That would be a good addition to the Build Task, for example create Directory.Build.props for new sdk based projects and keep AssemblyInfo.cs for older project types.

You can have a look how we use it in GitVersion
https://github.com/GitTools/GitVersion/blob/master/src/Directory.Build.props

Loading

@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Mar 5, 2019

I haven't used props files before so I've been reading up on this file and I think I get how this might work. For example, this file could be created/updated to have something like the following (I'm totally guessing here).

<PropertyGroup>
    <AssemblyVersion>$(GitVersion_AssemblySemVer)</AssemblyVersion>
    <FileVersion>$(GitVersion_MajorMinorPatch)</FileVersion>
    <InformationalVersion>$(GitVersion_InformationalVersion )</InformationalVersion>
</PropertyGroup>

In order to use Directory.Build.props for this purpose I can think of the following scenarios that would need to be handled:

  • No Directory.Build.props file exists under source control
    • Create the file with the above properties
    • Probably easy
  • A single Directory.Build.props exists somewhere and does not contain the required properties
    • Add the above properties to the existing file
    • Kinda easy, probably...
  • A single Directory.Build.props exists somewhere and does contain the required properties
    • Add missing properties
    • Who wins on the conflicting properties?
    • This starts to suck
  • Multiple existing Directory.Build.props files exist in any folder between the project and solution root

If you can safely figure out how to handle existing Directory.Build.props files then this may work on Azure DevOps because creating/updating a Directory.Build.props file on the build agent repo wouldn't matter as the change would be cleared out after the build. This wouldn't work for the nuget package on a project because a create or update to a Directory.Build.props file would create a change on the file under source control on the developers machine.

Have I totally missed the mark here?

Loading

@arturcic
Copy link
Member

@arturcic arturcic commented Mar 5, 2019

That's a good description of the the possible scenarios.

As far as I understand, the AssemblyInfo can still be used for setting the version number for the sdk style projects.

You can also pass them as build parameters similar to
dotnet build -p:Version=$(GitVersion_AssemblySemVer) (not tested) reference

Loading

@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Mar 5, 2019

Yep, you could push those three properties into the build task. That does require a manual configuration of the build step though. The thing I like about the AssemblyInfo option on the existing build task (at least for full framework msbuild builds) is that it is a zero config effort on the build workflow.

Loading

@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Mar 6, 2019

I've been thinking about this a bit more. The PowerShell script above is really just doing a similar action to what the AssemblyInfo logic does in the existing build task. It would be easier in the case of the new proj format though because it is xml rather than parsing the AssemblyInfo text file.

This process still wouldn't work for the GitVersionTask NuGet package because it would change what is under source control.

FYI, I've updated my script to include the other properties.

$nuGetVersion = $env:GitVersion_NuGetVersionV2
$sourcesDirectory = $env:BUILD_SOURCESDIRECTORY

Write-Host "Searching for projects under $($sourcesDirectory)"

# Find all the csproj files
if ($nuGetVersion -eq $null) {
    Write-Error ("GitVersion_NuGetVersionV2 environment variable is missing.")
    exit 1
}

if ($env:GitVersion_AssemblySemVer -eq $null) {
    Write-Error ("GitVersion_AssemblySemVer environment variable is missing.")
    exit 1
}

if ($env:GitVersion_MajorMinorPatch -eq $null) {
    Write-Error ("GitVersion_MajorMinorPatch environment variable is missing.")
    exit 1
}

if ($env:GitVersion_InformationalVersion -eq $null) {
    Write-Error ("GitVersion_InformationalVersion environment variable is missing.")
    exit 1
}

if ($sourcesDirectory -eq $null) {
    Write-Error ("BUILD_SOURCESDIRECTORY environment variable is missing.")
    exit 1
}

Function Set-NodeValue($rootNode, [string]$nodeName, [string]$value)
{   
    $nodePath = "PropertyGroup/$($nodeName)"
    
    $node = $rootNode.Node.SelectSingleNode($nodePath)

    if ($node -eq $null) {
        Write-Host "Adding $($nodeName) element to existing PropertyGroup"

        $group = $rootNode.Node.SelectSingleNode("PropertyGroup")
        $node = $group.OwnerDocument.CreateElement($nodeName)
        $group.AppendChild($node) | Out-Null
    }

    $node.InnerText = $value

    Write-Host "Set $($nodeName) to $($value)"
}

# This code snippet gets all the files in $Path that end in ".csproj" and any subdirectories.
Get-ChildItem -Path $sourcesDirectory -Filter "*.csproj" -Recurse -File | 
    ForEach-Object { 
        
        Write-Host "Found project at $($_.FullName)"

        $projectPath = $_.FullName
        $project = Select-Xml $projectPath -XPath "//Project"
        
        Set-NodeValue $project "Version" $nuGetVersion
        Set-NodeValue $project "AssemblyVersion" $env:GitVersion_AssemblySemVer
        Set-NodeValue $project "FileVersion" $env:GitVersion_MajorMinorPatch
        Set-NodeValue $project "InformationalVersion" $env:GitVersion_InformationalVersion 

        $document = $project.Node.OwnerDocument
        $document.PreserveWhitespace = $true

        $document.Save($projectPath)

        Write-Host ""
    }

Write-Host "##vso[build.updatebuildnumber]$($nuGetVersion)"

This works fine as a custom DevOps build task but it would be nice if GitVersion did this OOTB :)

Loading

@stale
Copy link

@stale stale bot commented Jun 29, 2019

This issue has been automatically marked as stale because it has not had recent activity. After 30 days from now, it will be closed if no further activity occurs. Thank you for your contributions.

Loading

@stale stale bot added the stale label Jun 29, 2019
@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Jun 30, 2019

I still think this should be addressed

Loading

@stale stale bot removed the stale label Jun 30, 2019
@garfbradaz
Copy link

@garfbradaz garfbradaz commented Aug 13, 2019

Having GitVersion update the .csproj files automatically would be sweet.

Loading

@stale
Copy link

@stale stale bot commented Nov 11, 2019

This issue has been automatically marked as stale because it has not had recent activity. After 30 days from now, it will be closed if no further activity occurs. Thank you for your contributions.

Loading

@stale stale bot added the stale label Nov 11, 2019
@roryprimrose
Copy link
Author

@roryprimrose roryprimrose commented Nov 13, 2019

Presumably GitVersionCore could include a custom implementation to support this. Similar to how the AssemblyInfo logic works - https://github.com/GitTools/GitVersion/blob/master/src/GitVersionCore/Extensions/VersionAssemblyInfoResources/AssemblyInfoFileUpdater.cs

Loading

@stale stale bot removed the stale label Nov 13, 2019
@stale
Copy link

@stale stale bot commented Feb 11, 2020

This issue has been automatically marked as stale because it has not had recent activity. After 30 days from now, it will be closed if no further activity occurs. Thank you for your contributions.

Loading

@stale stale bot added the stale label Feb 11, 2020
@asbjornu
Copy link
Member

@asbjornu asbjornu commented Feb 11, 2020

A pull request implementing this with tests will be accepted.

Loading

@stale stale bot removed the stale label Feb 11, 2020
@mortezaalizadeh
Copy link

@mortezaalizadeh mortezaalizadeh commented Apr 1, 2020

We started using GitVersion recently in our AzureDevOps setup. All of our project are either in .Net Core and .Net Standard and none of them have the AssemblyInfo.cs file. We are currently using the PowerShell script provided here as a workaround, but that would be great if GitVersion task on Azure DevOps can update the csproj file and add the version. Thank you.

Loading

@svengeance
Copy link
Contributor

@svengeance svengeance commented May 7, 2020

Looking at taking care of this @asbjornu @arturcic as this is something I personally want for my AzDo pipelines.

Given that users want a minimalistic configuration, I can see a couple of approaches

  • Given the user inputs /updateassemblyinfo, if we're in an SDK-style csproj and <GenerateAssemblyInfo> is either not present or true, edit the csproj properties directly. If we're not in an SDK csproj or <GenerateAssemblyInfo> is set to false, we will look for AssemblyInfo.cs as usual
  • If the user provides a filepath for a .csproj, edit it to set the properties.

Alternatively, we could add a new switch /updateprojectinfo which will similarly set the assembly version information, but in the csproj files. The downside is that there will be additional work in AzDo to provide this UI experience for the checkbox/filename; the upside is that it is more explicit about what it's doing.

I'm comfortable with either approach. I'm going to use https://www.fuget.org/packages/Microsoft.Build/16.5.0 (I think this is the go-to package for reading/writing project metadata) to handle the project files.

Loading

@asbjornu
Copy link
Member

@asbjornu asbjornu commented May 7, 2020

@svengeance, I'm happy you're volunteering to look into this! Just a heads up of where we're heading with v6: We are hoping to introduce a new command-based CLI and also break GitVersion up in more independent parts, where dealing with specific build servers, output formats, serializations, etc., are compartementalized and made independent of each other. An introduction of an MSBuild dependency and more logic around it might therefore be wise to introduce after the groundworks for the new architecture is laid out. What do you think?

Loading

@svengeance
Copy link
Contributor

@svengeance svengeance commented May 7, 2020

@asbjornu

I see, that sounds like a good move. I can see 3 ways forward

  1. This rearchitecture is far enough along that most of the groundwork and patterns you're interested in is finished: the progress can be made public and I can work on PR'ing into the branch
  2. This process isn't very far along, and I can quickly finish what I'm working on so that you can integrate it into the current architecture and you can subsequently move it after the fact
  3. We play the waiting game for 6.0.0 ⏲️

Personally I'd like to get a crack on this, so 1 or 2 are preferable to me, but I understand this is your timeline so let me know how you'd like to move forward

Loading

@asbjornu
Copy link
Member

@asbjornu asbjornu commented May 8, 2020

I can right off the bat say that 1 is nowhere close to true, so if you don't mind doing 2, then let's have a look at it in a PR. Just beware that it may be postponed and in need of a rewrite it if becomes too big and unwieldy.

Loading

@svengeance
Copy link
Contributor

@svengeance svengeance commented May 8, 2020

Fair enough. I will try to implement it in such a way that it doesn't become a pain in the ass to move around. Hopefully I have something soon for you!

Loading

asbjornu added a commit that referenced this issue May 13, 2020
@pfaustinopt
Copy link

@pfaustinopt pfaustinopt commented Jul 22, 2020

@roryprimrose if you apply the GitVersion NuGet package on your project to ensure that the assembly information gets updated, wouldn't this avoid the need to use your PowerShell script?

I've used the GitVersion NuGet package on my .NET Core project in order to get the correct assembly version and on Azure DevOps I have used the extension onlyo to ensure that the build number gets updated.

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

7 participants