<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Git on Andrew Wilson's Blog</title><link>https://andrewilson.co.uk/tags/git/</link><description>Recent content in Git on Andrew Wilson's Blog</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Wed, 07 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://andrewilson.co.uk/tags/git/index.xml" rel="self" type="application/rss+xml"/><item><title>Automating Semantic Versioning in Azure DevOps CI/CD Pipelines with GitVersion</title><link>https://andrewilson.co.uk/post/2025/05/cicd-and-automatic-semantic-versioning/</link><pubDate>Wed, 07 May 2025 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2025/05/cicd-and-automatic-semantic-versioning/</guid><description>Overview Versioning is the unsung hero of software development—often overlooked but absolutely essential. Imagine trying to manage a project without a clear way to track changes, communicate updates, or ensure compatibility. Chaos, right? That’s where versioning steps in, providing structure and clarity.
In this post, I’ll share how I streamline versioning in my projects by combining the power of Semantic Versioning (SemVer) with GitVersion, an automation tool that eliminates the manual effort of version management.</description><content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>Versioning is the unsung hero of software development—often overlooked but absolutely essential. Imagine trying to manage a project without a clear way to track changes, communicate updates, or ensure compatibility. Chaos, right? That’s where versioning steps in, providing structure and clarity.</p>
<p>In this post, I’ll share how I streamline versioning in my projects by combining the power of <strong>Semantic Versioning (SemVer)</strong> with <strong>GitVersion</strong>, an automation tool that eliminates the manual effort of version management. Whether you&rsquo;re just beginning your journey or tackling the complexities of feature-rich projects, this post will show you how to automate semantic versioning in <strong>Azure DevOps</strong> for consistency, traceability, and peace of mind.</p>
<h2 id="why-version">Why Version?</h2>
<p>Versioning is a fundamental practice in software development that helps us manage change effectively. It provides a structured way to track and communicate updates, ensuring clarity and consistency throughout the development lifecycle.</p>
<h2 id="what-is-semantic-versioning">What is Semantic Versioning?</h2>
<p>Semantic Versioning (often abbreviated as SemVer) is a versioning scheme that provides a standardised way to communicate the nature of changes in a software release. It uses a three-part version number format: <code>MAJOR.MINOR.PATCH</code>, where each part conveys specific information about the release.</p>
<h3 id="breakdown-of-semantic-versioning">Breakdown of Semantic Versioning:</h3>
<ol>
<li>
<p><strong>MAJOR Version</strong>: Incremented when there are breaking changes that are incompatible with previous versions.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>2.0.0</code></li>
</ul>
</li>
<li>
<p><strong>MINOR Version</strong>: Incremented when new features are added in a backward-compatible manner.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>1.1.0</code></li>
</ul>
</li>
<li>
<p><strong>PATCH Version</strong>: Incremented when backward-compatible bug fixes or small improvements are made.</p>
<ul>
<li>Example: <code>1.0.0</code> → <code>1.0.1</code></li>
</ul>
</li>
</ol>
<h3 id="benefits-of-semantic-versioning">Benefits of Semantic Versioning:</h3>
<ul>
<li><strong>Clarity</strong>: It clearly communicates the scope and impact of changes to users and developers.</li>
<li><strong>Predictability</strong>: Helps teams and users understand what to expect from a new release.</li>
<li><strong>Dependency Management</strong>: Makes it easier to specify compatible versions of libraries or APIs.</li>
<li><strong>Automation</strong>: Tools like GitVersion can automate the generation of semantic versions in CI/CD pipelines, ensuring consistency and reducing manual effort.</li>
</ul>
<h2 id="versioning-with-gitversion">Versioning with GitVersion</h2>
<p><a href="https://gitversion.net/docs/">GitVersion</a> is an open source tool that can be used to automate semantic versioning by analysing our Git repository&rsquo;s history. It streamlines the versioning process, ensuring consistency and reducing manual errors.</p>
<p>GitVersion has the following key features:</p>
<ul>
<li><strong>Semantic Versioning (SemVer)</strong>: Automatically calculates version numbers based on Git history, adhering to SemVer principles.</li>
<li><strong>Branching Strategy Support</strong>: Compatible with Continuous Delivery, GitFlow, GitHub Flow, and Mainline development workflows.</li>
<li><strong>Continuous Integration (CI) Friendly</strong>: Integrates seamlessly with CI/CD pipelines, generating version numbers for builds and releases.</li>
<li><strong>Flexible Configuration</strong>: Highly configurable to suit various project needs and versioning schemes.</li>
</ul>
<h3 id="gitversion-configuration">GitVersion Configuration</h3>
<p>GitVersion uses a <a href="https://gitversion.net/docs/reference/configuration">configuration file</a> (GitVersion.yml) to define how version numbers are calculated based on your Git repository&rsquo;s history and branching strategy. This file allows us to customise the behaviour of GitVersion to suit our project&rsquo;s needs.</p>
<h2 id="how-i-branch-and-configure-gitversion-for-projects">How I Branch and Configure GitVersion for Projects</h2>
<p>My projects tend to use the following setup:</p>
<ul>
<li><strong>Continuous Delivery Branch Model</strong>. This model has the following features:
<ul>
<li><strong>Main branch as release-ready</strong>: The <code>main</code> branch is always in a deployable state.</li>
<li><strong>Frequent Deployments</strong>: Changes are deployed to production or staging environments frequently, often automatically.</li>
<li><strong>Short-Lived Feature Branches</strong>: Feature branches are merged into <code>main</code> after review and testing.</li>
<li><strong>Automated Pipelines</strong>: CI/CD pipelines handle building, testing, and deploying changes seamlessly.</li>
</ul>
</li>
<li>SemVer is incremented using the following strategies:
<ul>
<li><strong>TaggedCommit</strong>. This strategy uses Git tags to determine the version. If a commit is tagged with a semantic version (e.g., 1.0.0), GitVersion will use that tag as the base for calculating the next version.</li>
<li><strong>Fallback</strong>. This strategy is used when no other versioning information (e.g., tags or branch-specific rules) is available. It serves as a default versioning mechanism, especially for newly initiated projects.</li>
</ul>
</li>
<li>Two defined branches for GitVersion increments:</li>
</ul>
<table>
<thead>
<tr>
<th><strong>Branch</strong></th>
<th><strong>Increment</strong></th>
<th><strong>When</strong></th>
<th><strong>Prevent Increment If Commit Tagged</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Main</td>
<td>Major (X.0.0)</td>
<td>Manually using tags for significant breaking changes.</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Main</td>
<td>Minor (0.X.0)</td>
<td>Automatically on new commits for backward-compatible features.</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Pull Request</td>
<td>Patch (0.0.X)</td>
<td>Automatically on pull request creation or updates for backward-compatible bug fixes or small improvements.</td>
<td>✅ Yes</td>
</tr>
</tbody>
</table>
<h3 id="gitconfig-setup">GitConfig Setup</h3>
<p>The described GitVersion configuration looks like the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Yaml" data-lang="Yaml"><span style="display:flex;"><span><span style="color:#6272a4">## YAML</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">strategies</span>:
</span></span><span style="display:flex;"><span>- TaggedCommit
</span></span><span style="display:flex;"><span>- Fallback
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">main</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">increment</span>: Minor
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">prevent-increment</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">when-current-commit-tagged</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>   <span style="color:#ff79c6">pull-request</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">increment</span>: Patch
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">prevent-increment</span>:
</span></span><span style="display:flex;"><span>         <span style="color:#ff79c6">when-current-commit-tagged</span>: <span style="color:#ff79c6">true</span>
</span></span></code></pre></div><table>
<thead>
<tr>
<th><strong>Configuration</strong></th>
<th><strong>Details</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Strategies</strong></td>
<td>- <code>TaggedCommit</code>: Uses Git tags to determine the version.</td>
</tr>
<tr>
<td></td>
<td>- <code>Fallback</code>: Provides a default versioning mechanism when no other information is available.</td>
</tr>
<tr>
<td><strong>Branches</strong></td>
<td></td>
</tr>
<tr>
<td><code>main</code></td>
<td>- <strong>Increment</strong>: <code>Minor</code> (0.X.0)</td>
</tr>
<tr>
<td></td>
<td>- <strong>Prevent Increment</strong>: Enabled when the current commit is tagged.</td>
</tr>
<tr>
<td><code>pull-request</code></td>
<td>- <strong>Increment</strong>: <code>Patch</code> (0.0.X)</td>
</tr>
<tr>
<td></td>
<td>- <strong>Prevent Increment</strong>: Enabled when the current commit is tagged.</td>
</tr>
</tbody>
</table>
<h3 id="azure-devops-cicd-pipeline-setup">Azure DevOps CI/CD Pipeline Setup</h3>
<p>The CI/CD pipeline would be configured as shown below:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Yaml" data-lang="Yaml"><span style="display:flex;"><span><span style="color:#6272a4">## YAML</span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">name</span>: $(Build.DefinitionName)_$(GitVersion.FullSemVer)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">trigger</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">include</span>: [ main ] <span style="color:#6272a4"># branch names which will trigger a build</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">pr</span>: <span style="color:#6272a4"># will trigger on PR</span>
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">branches</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">include</span>: [ main ] <span style="color:#6272a4"># branch names which will trigger a build.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">variables</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">name</span>: Source_Branch_Ref
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">value</span>: $[replace(coalesce(variables[&#39;System.PullRequest.SourceBranch&#39;], variables[&#39;Build.SourceBranch&#39;]), &#39;refs/heads/&#39;, &#39;&#39;)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">resources</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">repositories</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ff79c6">repository</span>: Source_Branch
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">type</span>: git
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">name</span>: GitVersionSemVerWithTags
</span></span><span style="display:flex;"><span>      <span style="color:#ff79c6">ref</span>: <span style="color:#f1fa8c">&#34;$(Source_Branch_Ref)&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">stages</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ff79c6">stage</span>: <span style="color:#f1fa8c">&#39;Build_Packages&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">jobs</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Increment_Version&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">condition</span>: and(succeeded(), or(eq(variables[&#39;Build.Reason&#39;], &#39;PullRequest&#39;), and(eq(variables[&#39;Build.SourceBranch&#39;], &#39;refs/heads/main&#39;), ne(variables[&#39;Build.Reason&#39;], &#39;Manual&#39;))))
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">pool</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">vmImage</span>: <span style="color:#f1fa8c">&#39;windows-latest&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: Source_Branch
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/setup@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Get current version of GitVersion&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">versionSpec</span>: <span style="color:#f1fa8c">&#39;6.0.x&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/execute@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Run GitVersion to generate SEMVER&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">targetPath</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">            useConfigFile: true
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">            configFilePath: &#39;</span>$(Build.SourcesDirectory)\GitVersion.yml&#39;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: PowerShell@2
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Increment the Version using Git Tag&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">targetType</span>: <span style="color:#f1fa8c">&#39;inline&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">script</span>: |<span style="color:#f1fa8c">
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              cd &#39;$(Build.SourcesDirectory)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git config --global user.email &#34;$(Build.RequestedForEmail)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git config --global user.name &#34;$(Build.RequestedFor)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git tag -a &#34;$(GitVersion.MajorMinorPatch)&#34; -m &#34;Released by $(Build.RequestedFor)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#f1fa8c">              git push origin tag &#34;$(GitVersion.MajorMinorPatch)&#34;</span>              
</span></span></code></pre></div><h4 id="key-steps-in-the-pipeline">Key Steps in the Pipeline</h4>
<ol>
<li><strong>Pipeline Name</strong>: Combines the build definition name with the full semantic version: <code>$(Build.DefinitionName)_$(GitVersion.FullSemVer)</code>.</li>
<li><strong>Trigger</strong>: Builds are triggered on changes to the <code>main</code> branch.</li>
<li><strong>Pull Request Trigger</strong>: Builds are triggered on pull requests targeting the <code>main</code> branch.</li>
<li><strong>Variables</strong>: Defines <code>Source_Branch_Ref</code> to extract the source branch reference for the build or pull request.</li>
<li><strong>Resources</strong>: Dynamically sets the branch reference for the Git repository (<code>Source_Branch</code>) to <code>$(Source_Branch_Ref)</code>.</li>
<li><strong>Job: Increment_Version</strong>: Executes if the build succeeds and is triggered by a pull request or the <code>main</code> branch but not part of a <code>Manual Trigger</code>.</li>
<li><strong>Checkout</strong>: Checks out the <code>Source_Branch</code> repository with full Git history and tags for versioning.</li>
<li><strong>GitVersion Setup</strong>: Installs GitVersion (<code>6.0.x</code>) to calculate semantic versions.</li>
<li><strong>GitVersion Execute</strong>: Runs GitVersion using the <code>GitVersion.yml</code> configuration file to generate the semantic version.</li>
<li><strong>PowerShell Script</strong>:
<ul>
<li>Configures Git by setting the user email and name to match the build requester.</li>
<li>Creates or updates a Git tag with the calculated version (<code>$(GitVersion.MajorMinorPatch)</code>).</li>
<li>Pushes the tag to the remote repository, ensuring the version is recorded.</li>
</ul>
</li>
</ol>
<blockquote>
<p>⚠️ <strong>Important Note</strong><br>
The <code>Source_Branch_Ref</code> variable and resource in this pipeline are configured as shown above because pull requests (PRs) create their own Git branches which are a merger of the source branch and main. When tags are created during the pipeline execution, they are placed on the PR branch by default, not the source branch where GitVersion calculates automatic increments.<br>
By setting up the <code>Source_Branch_Ref</code> variable and dynamically referencing the source branch in the <code>resources</code> section, the tag is placed on the source branch instead of the PR branch. This ensures that version increments are correctly applied to the source branch, maintaining accurate semantic versioning.<br>
The PR branch should still be used for building and validating solution artifacts.</p>
</blockquote>
<blockquote>
<p>⚠️ <strong>Important Pipeline Permissions</strong><br>
The Azure DevOps [Project] Build Service must have the following Repository Permissions:</p>
<ul>
<li><strong>Contribute</strong>: Permission to push changes to the repository.</li>
<li><strong>Create Tag</strong>: Permission to create and update tags in the repository.</li>
</ul>
</blockquote>
<h3 id="using-gitversion-to-version-artifacts">Using GitVersion to Version Artifacts</h3>
<p>Versioning artifacts involves two phases:</p>
<ol>
<li><strong>Generate</strong> a semantic version to use. This can be achieved by leveraging the GitVersion task.</li>
<li><strong>Applying</strong> the sematic version to artifacts. This can be achieved by using a task such as <a href="https://marketplace.visualstudio.com/items?itemName=richardfennellBM.BM-VSTS-Versioning-Task">VersionJSONFile@3</a>.</li>
</ol>
<p>As an example, when working with ARM templates in Azure, it&rsquo;s important to version your artifacts for traceability and consistency. Using GitVersion in your Azure DevOps pipeline, you can  generate a semantic version and apply it to the <code>contentVersion</code> field of your ARM template. This guarantees that each deployment is uniquely identifiable.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Yaml" data-lang="Yaml"><span style="display:flex;"><span><span style="color:#6272a4">## YAML</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#ff79c6">job</span>: <span style="color:#f1fa8c">&#39;Test_and_VersionBicepTemplates&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">pool</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">vmImage</span>: <span style="color:#f1fa8c">&#39;windows-latest&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff79c6">steps</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: self
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">path</span>: <span style="color:#f1fa8c">&#39;./s/selfBranch/&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">checkout</span>: Source_Branch
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">path</span>: <span style="color:#f1fa8c">&#39;./s/versionBranch/&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">persistCredentials</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchTags</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">fetchDepth</span>: <span style="color:#bd93f9">0</span> <span style="color:#6272a4"># Ensure we fetch all Git history for Semver</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6272a4"># GitVersion task is needed in each job where the variables are referenced</span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/setup@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Get current version of GitVersion&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">versionSpec</span>: <span style="color:#f1fa8c">&#39;6.0.x&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: gitversion/execute@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Run GitVersion to generate SEMVER&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">targetPath</span>: <span style="color:#f1fa8c">&#39;$(System.DefaultWorkingDirectory)/versionBranch/&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">useConfigFile</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">configFilePath</span>: <span style="color:#f1fa8c">&#39;$(System.DefaultWorkingDirectory)/versionBranch/GitVersion.yml&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: BicepInstall@0
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">version</span>: <span style="color:#bd93f9">0.35.1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: BicepBuild@0
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">process</span>: <span style="color:#f1fa8c">&#34;single&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">sourceFile</span>: <span style="color:#f1fa8c">&#39;$(Build.SourcesDirectory)\selfBranch\Deployment\azuredeploy.bicep&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">stdout</span>: <span style="color:#ff79c6">false</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">outputFile</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)\ARMOutput\azuredeploy.json&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: VersionJSONFile@3
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Version stamp ARM templates&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">Path</span>: <span style="color:#f1fa8c">&#39;$(Build.ArtifactStagingDirectory)\ARMOutput&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">recursion</span>: <span style="color:#ff79c6">true</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">VersionNumber</span>: <span style="color:#f1fa8c">&#39;$(GitVersion.AssemblySemFileVer)&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">useBuildNumberDirectly</span>: <span style="color:#ff79c6">False</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">VersionRegex</span>: <span style="color:#f1fa8c">&#39;\d+\.\d+\.\d+\.\d+&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">versionForJSONFileFormat</span>: <span style="color:#f1fa8c">&#39;{1}.{2}.{3}.{4}&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">FilenamePattern</span>: <span style="color:#f1fa8c">&#39;\w+.json&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">Field</span>: <span style="color:#f1fa8c">&#39;contentVersion&#39;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">OutputVersion</span>: <span style="color:#f1fa8c">&#39;OutputedVersion&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        - <span style="color:#ff79c6">task</span>: PublishPipelineArtifact@1
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">displayName</span>: <span style="color:#f1fa8c">&#39;Publish Versioned Solution Templates build artefact&#39;</span>
</span></span><span style="display:flex;"><span>          <span style="color:#ff79c6">inputs</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">targetPath</span>: <span style="color:#f1fa8c">&#34;$(Build.ArtifactStagingDirectory)/ARMOutput&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">publishLocation</span>: <span style="color:#f1fa8c">&#34;pipeline&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff79c6">artifactName</span>: <span style="color:#f1fa8c">&#34;ARM-Templates&#34;</span>
</span></span></code></pre></div><h4 id="key-steps-in-the-pipeline-1">Key Steps in the Pipeline</h4>
<ol>
<li>
<p><strong>Checkout Repositories</strong></p>
<ul>
<li>The pipeline checks out two branches:
<ul>
<li><strong>Self Branch</strong>: Contains the Bicep files that will be compiled into ARM templates.</li>
<li><strong>Source Branch</strong>: Used for versioning and fetching Git history.</li>
</ul>
</li>
<li>Full Git history and tags are fetched (<code>fetchTags: true</code>, <code>fetchDepth: 0</code>) to ensure accurate semantic version calculation.</li>
</ul>
</li>
<li>
<p><strong>Generate Semantic Version with GitVersion</strong></p>
<ul>
<li><strong>GitVersion Setup</strong>: The <code>gitversion/setup@3</code> task installs GitVersion (<code>6.0.x</code>).</li>
<li><strong>GitVersion Execution</strong>: The <code>gitversion/execute@3</code> task calculates the semantic version based on the repository&rsquo;s history and configuration (<code>GitVersion.yml</code>).
<ul>
<li>The version is exposed as <a href="https://gitversion.net/docs/reference/variables">pipeline variables</a>, such as <code>$(GitVersion.AssemblySemFileVer)</code>.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Build ARM Templates</strong></p>
<ul>
<li>The <code>BicepBuild</code> task compiles the Bicep file (<code>azuredeploy.bicep</code>) into an ARM template (<code>azuredeploy.json</code>).</li>
<li>The compiled ARM template is stored in the <code>$(Build.ArtifactStagingDirectory)/ARMOutput</code> directory.</li>
</ul>
</li>
<li>
<p><strong>Version Stamp the ARM Template</strong></p>
<ul>
<li>The <code>VersionJSONFile</code> task updates the <code>contentVersion</code> field in the ARM template (<code>azuredeploy.json</code>) with the semantic version generated by GitVersion.
<ul>
<li><strong>Inputs</strong>:
<ul>
<li><code>VersionNumber</code>: Uses <code>$(GitVersion.AssemblySemFileVer)</code> (Provides a 4-digit version format (<code>MAJOR.MINOR.PATCH.0</code>), which is ideal for ARM template versioning e.g., <code>1.2.3.0</code>).</li>
<li><code>Field</code>: Specifies the <code>contentVersion</code> field in the ARM template to be updated.</li>
<li><code>VersionRegex</code>: Ensures only valid version formats (<code>\d+\.\d+\.\d+\.\d+</code>) are replaced.</li>
</ul>
</li>
<li>This step ensures that the ARM template is uniquely versioned for traceability.</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Publish the Versioned ARM Template</strong></p>
<ul>
<li>The <code>PublishPipelineArtifact</code> task publishes the versioned ARM template as a pipeline artifact.
<ul>
<li>The artifact is stored under the name <code>ARM-Templates</code> and can be used in subsequent deployment stages.</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="summary">Summary</h2>
<p>Automating semantic versioning in Azure DevOps CI/CD pipelines with GitVersion is a game-changer for maintaining consistency, traceability, and efficiency in your development workflow. By leveraging tools like GitVersion, you can eliminate the manual effort of version management, ensure accurate versioning across branches, and streamline the deployment of versioned artifacts like ARM templates.</p>
<p>Whether you&rsquo;re managing simple projects or complex, feature-rich solutions, adopting semantic versioning practices ensures that your team and stakeholders have a clear understanding of changes, compatibility, and release impact. With the strategies and pipeline configurations shared in this post, you’re now equipped to implement a robust versioning system that aligns with industry best practices.</p>
<p>If you found this post useful, consider sharing it with your team or network to help others streamline their versioning workflows. Happy automating!</p>
]]></content:encoded></item><item><title>Azure DevOps Pipeline | Git Shallow Fetch</title><link>https://andrewilson.co.uk/post/2023/01/azure-devops-pipeline-shallow-fetch/</link><pubDate>Mon, 09 Jan 2023 00:00:00 +0000</pubDate><guid>https://andrewilson.co.uk/post/2023/01/azure-devops-pipeline-shallow-fetch/</guid><description>Problem Space I have been recently working on building a new Yaml pipeline in Azure DevOps and wished to use the GitVersion Task, however, upon running the pipeline the task failed with the following error:
ERROR [../../.. ..:..:..:..] An unexpected error occurred: System.NullReferenceException: Object reference not set to an instance of an object. After some digging and a conversation with one of my colleagues, it turns out there has been a change on Azure DevOps pipelines.</description><content:encoded><![CDATA[<h2 id="problem-space">Problem Space</h2>
<p>I have been recently working on building a new Yaml pipeline in Azure DevOps and wished to use the <a href="https://marketplace.visualstudio.com/items?itemName=gittools.gittools">GitVersion Task</a>, however, upon running the pipeline the task failed with the following error:</p>
<pre tabindex="0"><code>ERROR [../../.. ..:..:..:..] An unexpected error occurred:
System.NullReferenceException: Object reference not set to an instance of an object.
</code></pre><p>After some digging and a conversation with one of my colleagues, it turns out there has been a change on Azure DevOps pipelines. By default now when a pipeline is created, the &lsquo;<em>Get Sources</em>&rsquo; <strong>Shallow fetch</strong> setting is enabled and set to a depth of 1.</p>
<p>The <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/repos/pipeline-options-for-git?view=azure-devops&amp;tabs=yaml#shallow-fetch">Shallow fetch</a> setting if enabled will limit how far back in source history to download. Effectively using the following git command <code>git fetch --depth=n</code>.</p>
<p>This in turn causes the error seen above as the GitVersion task requires the whole repo branching model to calculate the SEMVER version.</p>
<h2 id="switching-off-shallow-fetch">Switching off Shallow Fetch</h2>
<p>To switch off <strong>Shallow Fetch</strong> if using a Yaml pipeline:</p>
<ol>
<li>Edit your Pipeline.</li>
<li>Selecting the ellipsis button, select Triggers.</li>
<li>Navigate to the Yaml tab, and select Get Sources.</li>
<li>There is a setting near the very bottom called <em>Shallow fetch</em>, switch the setting off and save.</li>
</ol>
]]></content:encoded></item></channel></rss>