Azure Pipeline - Increment build number and display in web app - asp.net-core

I have the following simple build pipeline working in Azure DevOps with releases deployed to a staging slot.
I would like to have a build revision/version string and that is auto-incremented. I then want to display this in my web app so I can tell which version of the software is in production.
Currently I display the version string from the .csproj file. Something like this in a
<Version>1.1.4.7</Version>
And then displayed on a web page using the following code:
Version: #typeof(Startup).Assembly.GetName().Version.ToString()
If I can update the existing version string that would be great but I'm open to changing to whatever's easiest to integrate in the CI process.

Versioning has been simplified in the .Net Core world.
Edit your csproj and modify it as follows:
<PropertyGroup>
<Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">1.0.0.0</Version>
<Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version>
</PropertyGroup>
If your file doesn’t have a version node, add the above.
The above setup will mean debugging locally will give you a version of 1.0.0.0, and in the event, you build in a non-Azure DevOps environment you will also end up with a 1.0.0.0 version. $(BUILD_BUILDNUMBER) is an environment variable set by Team Build and which will be updated at build time by VSTS or TFS.
The .Net version needs to be in the format [major].[minor].[build].[revision] with each segment being a number between 0 and 65000. You can configure the build number format in the Options tab, see here more info about the formatting. See here for helpful steps on configuring the build.

I am using ADO pipeline variables and the counter function like this
The patch variable is $[counter(format('{0}.{1}',variables['Major'], variables['Minor']),0)]
Then I put all of these values directly into a variable as a powershell task.
- task: InlinePowershell#1
displayName: Create Version Number
inputs:
Script: |
param($major, $minor, $patch)
$bv = "$major.$minor.$patch"
Write-Host "##vso[task.setvariable variable=buildVersion]$bv"
Write-Host "Version of net App : $bv"
ScriptArguments: '-major $(Major) -minor $(Minor) -patch $(Patch)'
Now when I do the .net publish I just pass the version
- task: DotNetCoreCLI#2
displayName: Publish Application
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/MyAPIProject.csproj'
arguments: '-r $(buildPlatform) -o $(Build.BinariesDirectory) -c $(buildConfiguration) --self-contained true /p:Version=$(buildVersion)'
zipAfterPublish: false
modifyOutputPath: false
What is really nice about this, is the patch gets seeded at zero, then every follow up build will auto-increment.

You should Use Azure DevOps Pipelines Release (a.k.a. Azure Pipelines release) instead of Azure DevOps Pipelines build (a.k.a. Azure Pipelines build). Azure Pipelines release by default will auto-increment your releases.
Azure Pipelines build has no automatic version numbering by default. Because increasing version after it should be done at release stage, because build should only concerns the continuous integrations, not to be used as versioning a build to be released.
This is the official docs of Azure Pipelines release on auto-increment your release:
https://learn.microsoft.com/en-us/azure/devops/pipelines/release/?view=vsts#numbering

In a .NET Core (.NET 6.0 in particular) app I have set the following code in .csproj:
<PropertyGroup>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
</PropertyGroup>
Then in Azure DevOps I have added the following powershell script task to set version on my build.
Note that I am using the below convention:
Major -> Phase/Product cycle. I am using a variable/parameter to pass this value in the pipeline.
Minor -> Sprint number. I am using a variable/parameter to pass this value in the pipeline.
Revision -> Max limit for this number is 65000 and therefore cannot use Build.BuildNumber. I am using (Date:YY)(DayOfYear).
Build -> Taking the second part of the Build.BuildNumber (increment of the day)
- task: PowerShell#2
displayName: 'Edit AssemblyInfo'
inputs:
targetType: 'inline'
script: |
$Major = "$(Major)"
Write-Host "Major: $Major"
$Minor = "$(Minor)"
Write-Host "Minor: $Minor"
$DayOfYear = (Get-Date).DayOfYear
$Year = (Get-Date –format yy)
$Revision = '' + $Year + $DayOfYear
Write-Host "Revision: $Revision"
$BuildNumber = "$(Build.BuildNumber)"
Write-Host "BuildNumber: $BuildNumber"
$Build = $BuildNumber.Split('.')[1]
Write-Host "Build: $Build"
$pattern = '<AssemblyVersion>(.*)</AssemblyVersion>'
Write-Host "Will search in paths.."
$csProjFiles = Get-ChildItem .\*.csproj -Recurse
Write-Host "CsProjFiles found are: "
Write-Host "$csProjFiles"
foreach ($file in $csProjFiles)
{
(Get-Content $file.PSPath) | ForEach-Object{
if($_ -match $pattern){
# We have found the matching line
# Edit the version number and put back.
Write-Host "Matched $file"
$fileVersion = [version]$matches[1]
$newVersion = "{0}.{1}.{2}.{3}" -f $Major, $Minor, $Revision, $Build
'<AssemblyVersion>{0}</AssemblyVersion>' -f $newVersion
} else {
# Output line as is
$_
}
} | Set-Content $file.PSPath
}

Related

How do I see the logs added to the Xcode Build Phase Scripts in Azure DevOps pipeline?

I have a react native app within an nx monorepo that runs, archives, and builds successfully on my local machine.
I am trying to accomplish the same with Azure DevOps pipeline with the following XCode build task.
The Azure DevOps Xcode build task looks like this...
#Your build pipeline references an undefined variable named ‘Parameters.scheme’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
#Your build pipeline references an undefined variable named ‘Parameters.xcodeVersion’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
#Your build pipeline references an undefined variable named ‘APPLE_CERTIFICATE_SIGNING_IDENTITY’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
#Your build pipeline references an undefined variable named ‘APPLE_PROV_PROFILE_UUID’. Create or edit the build pipeline for this YAML file, define the variable on the Variables tab. See https://go.microsoft.com/fwlink/?linkid=865972
steps:
- task: Xcode#5
displayName: 'Xcode Build to Generate the signed IPA'
inputs:
actions: 'clean build -verbose'
xcWorkspacePath: 'apps/my-app/ios/MyApp.xcworkspace'
scheme: '$(Parameters.scheme)'
xcodeVersion: '$(Parameters.xcodeVersion)'
packageApp: true
exportOptions: specify
exportMethod: 'ad-hoc'
signingOption: manual
signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)'
provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)'
In the pipeline logs, I observed that it runs a task close to this...
xcodebuild -sdk iphoneos -configuration Release -workspace ios/MyApp.xcworkspace -scheme MyApp clean build -verbose
I modified the paths as above and ran the task on local terminal and it builds successfully. It prints the logs I set in Xcode > Target (MyApp) > BuildPhases > Bundle React Native code and images as shown below
echo "\n 0. ⚛️🍀 DEBUG PIPELINE: Bundle React Native code and images \n"
echo "\n 1. ⚛️🍀 cd \$PROJECT_DIR/.."
pwd
ls
cd $PROJECT_DIR/..
export NODE_BINARY=node
./node_modules/react-native/scripts/react-native-xcode.sh
echo "\n 0. 🩸 DEBUG PIPELINE: Bundle React Native code and images::SCRIPT COMPLETED \n"
None of these logs show up in the pipeline. Even when I enable system diagnostics before running the pipeline with...
☑️ Enable system diagnostics
I have seen these related questions and answers and my attempt is at troubleshooting to see the what gets run.
Question: Does the Azure DevOps Xcode build task above use the same phase script? Does it remove the logs? Does it use another build phase script? How can I see the logs added to BuildPhase scripts in the AzurePipe line logs?
Thank you.

How can I trigger a specific stage in azure release pipeline according to the build configuration in build pipeline

In my selenium automation suite I have different config files for different environments. (App.Dev.config, App.QA.config likewise). Currently I have two azure pipelines one build pipeline and a release pipeline. So if I want to run the UI automation tests in QA environment what I do now is change the buildconfiguration variable in build pipeline to 'QA' run the build pipeline and then once it is success run the QA stage in release pipeline manually. Is there a way to trigger this automatically?
Install the trigger release extension: Release Orchestrator :https://marketplace.visualstudio.com/items?itemName=dmitryserbin.release-orchestrator&targetId=ca4e4e67-3099-4c62-9ea9-bef80e0cc70a&utm_source=vstsproduct&utm_medium=ExtHubManageList
Set variable in your build pipeline:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: 'echo "##vso[task.setvariable variable=doThing]QA"'
Use the release Orchestrator task in your build pipeline to trigger the wanted stage in your release pipeline with conditions:
- task: releaseorchestrator#2
inputs:
endpointType: 'integrated'
projectName: '{ProjectName}'
definitionName: 'TestReleaseExtension'
releaseStrategy: 'create'
definitionStage: 'Stage 2'
approvalRetry: 60
updateInterval: 5
condition: eq(variables['doThing'], 'QA')
Make sure the Build Service Account has the permission to create releases.

EF migrations not working in Github action, same command works locally

I can run the following command locally to update my DB using Entity Framework Core migrations:
dotnet ef database update 0 --project src\MyProject\MyProject.csproj --startup-project src\MyStartupProject\MyStartupProject.csproj --context MyDbContext
However when I try to run it in a GitHub action I get an error.
The action:
name: Migrate DB
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout#v3
- name: Migrate DB
shell: pwsh
run: |
dotnet tool install --global dotnet-ef
dotnet tool restore
dotnet ef database update 0 --project src\MyProject\MyProject.csproj --startup-project src\MyStartupProject\MyStartupProject.csproj --context MyDbContext
The error is:
/home/runner/work/MyProject/MyProject/src/MyProject/MyProject.csproj :
error MSB4057: The target "GetEFProjectMetadata" does not exist in the
project. Unable to retrieve project metadata. Ensure it's an SDK-style
project. If you're using a custom BaseIntermediateOutputPath or
MSBuildProjectExtensionsPath values, Use the
--msbuildprojectextensionspath option.
As far as I know I'm not using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values. The projects are a dotnet 6 web project and class library built in C#.
I've tried adding --msbuildprojectextensionspath with the value obj/local as suggested in similar questions but it has had no effect.
instead of adding this command in your github yml file you can migrate database in your program class:
in new program class (ASP .NET 6):
using var scope = app.Services.CreateScope();
using var appContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
appContext.Database.Migrate();
in classic program class:
var host = CreateHostBuilder(args).Build();
using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
await host.RunAsync();
it will migrate if your database it's not up to date.
Bit of a face-palm. It turns out it was because I was not first building the project in the pipeline which meant that the git ignored items were not there. I assume the issue was the missing obj folder.
The solution was to use dotnet-ef instead of dotnet ef which builds the project before attempting migrations (although I assume that simple running dotnet build beforehand may have also worked).
dotnet-ef database update 0 --project src\MyProject\MyProject.csproj --startup-project src\MyStartupProject\MyStartupProject.csproj --context MyDbContext

AutoIncrementing Build with Core and DevOps Pipeline

I'm trying to setup an auto-incrementing version number for my ASP.NET Core 3.1 web app built using Azure Pipelines. I've tried various snippets and pipeline tasks and got as far as generating the version number but my build fails with the message
The specified version string does not conform to the required format - major[.minor[.build[.revision]
I'm using the below snippet of yml to generate the version number:
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
version.MajorMinor: '1' # Manually adjust the version number as needed for semantic versioning. Revision is auto-incremented.
version.Revision: $[counter(variables['version.MajorMinor'], 0)]
versionNumber: '$(version.MajorMinor).$(version.Revision)'
steps:
- task: PowerShell#2
displayName: Set the name of the build (i.e. the Build.BuildNumber)
inputs:
targetType: 'inline'
script: |
$doy = (Get-Date).DayofYear
Write-Host "##vso[task.setvariable variable=DayOfYear]$doy"
$rev = $env:BUILD_BUILDNUMBER.Split('.')[-1]
Write-Host "##vso[task.setvariable variable=Revision]$rev
Write-Host "set Revision to $rev"
[string] $buildName = "$(versionNumber).$doy.$rev"
Write-Host "Setting the name of the build to '$buildName'."
Write-Host "##vso[build.updatebuildnumber]$buildName"
This does generate a version number (1.26.199.50), however when I get the the MSBuild task:
task: VSBuild#1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site" /p:Version=$(buildName)'
I get the version error. I've even tried replacing the version with 1.0.199.50 but I get the same error. I understand that each segment has to be less than 65534 as that's the max value for a UInt so I don't understand why it's failing.
After much frustration, I did this with a Powershell script in the release pipeline instead. I've got a Powershell task containing the below which is before the File Transform task. Steps to get it working.
Add a release pipeline variable called "MajorVersion". This is the first part of the version string
Add a property to appsettings.json called AppConfig.Version, set it to whetever for local testing.
Add a powershell task. The order should be Powershell Task->File Transform->App Service Deploy:
$doy = $(Get-Date).DayofYear
[string] $buildName = "$Env:MajorVersion.$doy.$Env:BUILD_BUILDNUMBER"
Write-Host "##vso[task.setvariable variable=AppConfig.Version;]$buildName"
Access the variable from your app settings using your preferred method. In core, probably via Configuration.GetSection("blah")
The assembly isn't stamped with the version, but you can get to it to display on the UI. I feel like this needs to be simplified by MS as auto-incrementing a version shouldn't be so difficult.

Effecting the environment flag from within the build

I have an Aurelia CLI app that uses the CLI in conjunction with Gulp to build. I was asked to enable a feature where we pass the name of the branch we're building and determine an environment from that. I was hoping to do this within my gulp tasks. I think I can achieve it through our Continuous Integration, but, I'd like to do it from within gulp if possible. Is this possible?
You can pass any custom arguments to Aurelia CLI and grab them from within your build tasks.
Say you call au run --branch master
You can get the value from within a task like so:
import { CLIOptions } from "aurelia-cli";
const branch = CLIOptions.getFlagValue('branch')
Is that what you need?
We use Visual Studio Team Services for Continuous Integration. I added a Powershell Script build step to our definition, which, depending on the name of the passed branch, would write a variable with the right environment name. Then, I'm planning to add an additional build step to my definition, to only run when the master branch is being built - to rebuild my source without the testing framework.
The powershell script we use to write the variables is as follows:
if ($env:BUILD_SOURCEBRANCHNAME -eq "qa"){
Write-Output ("##vso[task.setvariable variable=auenv]" + "stage")
}
elseif ($env:BUILD_SOURCEBRANCHNAME -eq "master")
{
Write-Output ("##vso[task.setvariable variable=auenv]" + "prod")
}
else
{
Write-Output ("##vso[task.setvariable variable=auenv]" + "dev")
}
Then, when it comes time to use it:
au build --env $(auenv) --version $(Build.BuildNumber) --testable
Finally, we build without the test framework
au build --env $(auenv) --version $(Build.BuildNumber)
I recognize my solution is out of scope relative to the audience I asked it for. Sorry about that.