Bob Lokerse

My thoughts on blog


GitVersion versioning made easy and dry

03 November 2015

Intro

You just made a new version of your application. Nice.:-)

“Oh, wait, did I update the assembly version?” Ok, another checkin, and the build is running again - triggered by the checkin. Nice.

“Oh, was the build version in the build server updated?” Updated, Re-Build Commit and go!

At least the deploy server is covered; that one takes the release version from the package.

DRY

What would be nice is to set the version once & Don’t Repeat Yourself. Easier and less risk on making mistakes.

The CI setup used is: GitHub - AppVeyor - Octopus Deploy

To accomplish the DRY versioning, I use the version from the git release branch, using the GitFlow branching structure. In the Appveyor build server, this is used to update the build version and patch the Assembly Version in the AssemblyInfo.cs. I call this a git version.

To keep the builds unique, the AppVeyor build nr is added.

For example:

release/v1.2

Becomes 1.2.{build nr}. With build nr 1 that is: 1.2.1

Scripting it

function gitVersion(){
   write-host "This gets the version nr from the end of the git release branch (e.g.: v1.4). And updates the appVersion build version with that (the build nr is appended)."

   $branch=$env:APPVEYOR_REPO_BRANCH

   $posAfterVchar = $branch.LastIndexOf("v") + 1
   $versionLength = $branch.Length - $posAfterVchar
   
   $gitVersion=$branch.substring($posAfterVchar, $versionLength)
   
   $newVersion="$gitVersion.$env:APPVEYOR_BUILD_NUMBER"
   
   write-host "Update appveyor build version to:$newVersion"
   $env:appveyor_build_version="$newVersion"
   appveyor UpdateBuild -Version "$newVersion"
}
gitVersion
  
# Some notes: AppVeyor uses environment variables, which I use to get the branch:

$branch=$env:APPVEYOR_REPO_BRANCH

# and the build nr:

$env:APPVEYOR_BUILD_NUMBER

# which I use to complete the version:

$newVersion="$gitVersion.$env:APPVEYOR_BUILD_NUMBER"

# the build version can be updated with the *UpdateBuild* command:

 appveyor UpdateBuild -Version "$newVersion"

How to call this script, I cover a bit later in this post: The power of yaml.

Patch it

Wait, this is done at the build server. That means that the code is already checked in, together with the Assembly Version in AssemblyInfo.cs.

Indeed. But that can be fixed with AssemblyInfo patching in AppVeyor. If your turn that on, the file will be updated before the build. (You can even adjust it, but for me the default is fine).

Side step - pushing the build output to Octopus

When the build is done, the output needs to pushed to Octopus Deploy.

With the package OctoPack in your project, you can add MsBuild parameters to push a NuGet package generated by OctoPack to Octopus.

nuget restore SolutionName.sln
msbuild SolutionName.sln /p:Configuration=Release /p:RunOctoPack=true /p:OctoPackPublishPackageToHttp=http://YourOctopusDeployServer/nuget/packages /p:OctoPackPublishApiKey=API-key

The power of yaml

There was another challenge, calling the git version script and the AssemblyInfo patching in the right order.

To add the OctoPack parameters to MSBuild, we have to use the build script option in AppVeyor. That means that there is no pre-build script option, in which can update the build version before the AssemblyInfo patching takes place.

Well, there is another way to setup AppVeyor: by adding a appveyor.yml file to your solution, which describes the override actions regarding the gui/website. And in that yaml file, you have the option of an init section, that is ran before the git cloning.

That is pretty powerful. You can put all the settings in that yaml file, so in the AppVeyor gui you only have to create the project, the rest will be done in the appveyor.yml. See the example below:

# appveyor.yml file

 
  branches:
    only:
      - /release.*/
      - /hotfix.*/

  configuration: Release

  # scripts that are called at very beginning, before repo cloning
  init:
  - ps: |
      function gitVersion(){
        write-host "This gets the version nr from the end of the git release branch (e.g.: v1.4). And updates the appVersion build version with that (the build nr is appended)."

        $branch=$env:APPVEYOR_REPO_BRANCH
    
        $posAfterVchar = $branch.LastIndexOf("v") + 1
        $versionLength = $branch.Length - $posAfterVchar
        
        $gitVersion=$branch.substring($posAfterVchar, $versionLength)
        
        $newVersion="$gitVersion.$env:APPVEYOR_BUILD_NUMBER"
        
        write-host "Update appveyor build version to:$newVersion"
        $env:appveyor_build_version="$newVersion"
        appveyor UpdateBuild -Version "$newVersion"
      }
      gitVersion
  
  # patch the assembly version
  assembly_info:
     patch: true
     file: '**\AssemblyInfo.*'
     assembly_version: '{version}'
     assembly_file_version: '{version}'
     assembly_informational_version: '{version}'

  build_script:
    - ps: |
        write-host "Building version:$env:appveyor_build_version"
        msbuild --% /t:build /p:RunOctopack=true /p:OctoPackPublishPackageToHttp=http://youroctopusserverurl/nuget/packages /p:OctoPackPublishApiKey=API-key

  before_build:
    nuget restore

Package it all up

To make this easier to use, I created a NuGet package of it. That has a script to copy the appveyor.yml next to the solution file and add it to the solution in a (virtual) solution folder. The package is hosted in our internal NuGet Server. So a nuget.config is also added to the package, with the url of that NuGet server.

Conclusion

The steps needed to get this working:

  1. Put the application in a GitHub repo
  2. Add OctoPack and the AppVeyorYml package to the application project
  3. Adjust the OctoPack parameters to the real Octopus values
  4. Create an AppVeyor project
  5. Set Octopus Deploy to use the version number from the NuGet package

Create a build & release:

  1. Make a new release branch with the version (vX.Y) and push it
  2. AppVeyor is triggered to build this branch
    • Retrieves the git version
    • Updates the build version
    • Patches the assembly version
  3. OctoPack creates a packages with the version in the filename and sends it to Octopus
  4. Octopus bases the release version on the version of the package

Now there is one place to fill in the version. If in AppVeyor another build of the same check-in is needed, the build nr is increased, so the build version stays unique. If in Octopus an new release is needed, with the same package, the fourth digit can be increased.


Bob Lokerse

Sitecore .net developer @ Tahzoo

Twitter || LinkedIn || Github

Bob Lokerse profile