My thoughts on blog
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.
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
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.
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).
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
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
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.
The steps needed to get this working:
Create a build & release:
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.