Walkthrough: use Azure DevOps with SPFx, GitFlow and GitVersion

Walkthrough: use Azure DevOps with SPFx, GitFlow and GitVersion


Here is the scenario: as a developer, you start a new SharePoint Framework development project for a customer and this one has no processes, tools or development practices to manage the source code, deploy it automatically across multiple environments or manage versions. The purpose of this post is to provide you and end-to-end development pipeline in such a situation. Of course, this is an extreme scenario and you could likely reuse a part of the pipeline to adapt to your own context. Also, the following is very generic and not specifically tied to SPFx projects. For the recipe, I will use the following tools/technologies:

  • SharePoint Framework, as the source code to deploy
  • Azure DevOps, for the pipeline definition and automation
  • GitHub for the repository (could be anything else)
  • GitFlow, for the source code management
  • GitVersion, for version management

The base build and release definitions are available in the following git repository:

https://github.com/aequos-solutions/sp-solutions/tree/master/boilerplates/azure-devops-gitflow

Manage your source code lifecycle efficiently: the GitFlow workflow

GitFlow is a generic branching workflow for Git (not the same as GitHub Flow). I often use this workflow for my projects because it is quite easy to understand and very convenient, even if you are the only developer on your project. This worklow can be used with any Git repository (GitHub, BitBucket, built-in AzureDevOps repository, etc) and simply define best practices about branches management. As a best practice, this does not technically constrain you so you can still do whatever you want with your branches… To help you with this workflow and if, like me, you’re not a Git command line afficionado, you can use the Git client ‘SourceTree‘ which already integrate all GitFlow actions:

When you work with GitFlow, branches typically represent a ‘stage’ in your code lifecycle and follow a specific naming convention:

  • develop: Integrates all features from developers. With GitFlow, commits whithin this branch should always be a merge commit (from a feature, release or hotfix) branch. It means that, as a developer, you shouldn’t commit directly in it (but, again, you can) and the develop branch should always be ‘stable’ (i.e. no deployment or build errors).
  • master: the latest stable version ready for production and deployable at any time. Commits in this branch should always come from release or hotfix branches
  • release/{version}: final validation before the production deployment. For instance, adjust the documentation, write the changelog, resolve minor bug fixes etc. You don’t develop any new feature here. When it is done, the update is merged to develop and master to reflect the latest version of the code.
  • feature/{feature name}: represent a new feature for you product. You can start many features in parallel if you want.
  • hotfix/{version}: used to fix a bug encountered in production that you need to fix right now. That’s why you always start an hotfix from the master branch. Like the release branch, when it is done, the update is merged to both develop and master.

GitFlow workflow (https://nvie.com/posts/a-successful-git-branching-model/)

Relationship with ‘physical’ environments

In a real world scenario, these branches can often be associated with one or more « physical » environments according to the lifecycle, for instance ‘Staging’,’Production’,’User Acceptance Testing’, etc. In a SharePoint/Office 365 world, these can be separate tenants or site collections depending of what development you make. Here are common environments I often see/use according to GitFlow branches (of course, it can be different in your context):

Environment

Associated GitFlow branch(es)

Used for

Development

feature/*

Used by developers to test a feature inside a sandbox environment isolated form other currently developed features. It is often a dedicated site collection or tenant.

Staging, QA (Quality Assurance)

develop

Used by developers and testers to integrate all features and perform components testing (ex: UI manipulations, etc.).

UAT, Pre-production

release/*,hotfix/*

Typically used by a small group of ‘beta tester’ end users to validate features from a functional point of view. This environment is generally identical to the production one in terms of configuration.

Production

master

Final version for all end users.

Manage versions with GitFlow: GitVersion

Honestly, managing versions has always been a pain point in many of my projects. Most of the time, when you have an existing continous integration system, the version often means ‘build number’ and it is automatically set by the system and triggered from a ‘master‘ branch commit. Actually, I realized not so many developers, including me until recently, really care about this number and its meaning from a functional point of view. That’s why regarding SPFx and GitFlow, I had a lot of questions on how to improve this versioning process. For instance, when to set a new version? What format? Can I automate something regarding the version, GitFlow and deployments? How do I update these versions in my SPFx? etc.

Hopefully, I found GitVersion to help me with all these questions and, luckily, it has a default integration the GitFlow workflow (among other worfklows). With GitVersion, version numbers are not about build numbers but about commits. It makes an huge difference regarding the way you manage your versions and your release pipeline. Basically, the tool is able to look at your Git commits history and deduce versions numbers accordingly. But make no mistake, the process is only semi-automated and you still have to perform manual increments. For this part, GitVersion relies on the Semantic Versioning (SemVer) best practice. Instead of a dummy build number, the concept of semantic versioning allows you undestrand quickly the functional implications of a version. Here is the quick summary from https://semver.org.

Given a version number MAJOR.MINOR.PATCH, increment the:

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards-compatible manner, and
  3. PATCH version when you make backwards-compatible bug fixes.

Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.

So when you release a new version, it is up to you to increment these numbers according the functional implications using different strategies (commit tag, branch name, commit message. etc,). GitVersion will only use these information to help you in the process of getting a meaningful SemVer version number. Here is a concrete example illustrating version increments according to the GitFlow branches and the default GitVersion configuration (‘0.1.0‘ version is the default version). The start is from bottom 😉

Pay attention to the ‘ContinousDeployment‘ and ‘ContinousDelivery, modes. These are very important to understand to be able to predict version numbers. GitVersion allows you to change these modes for each branch (among plenty of parameters) using a .yml configuration file, but honestly, for common scenarios, the default settings do the job perfectly. From a technical point of view, GitVersion is simply a CLI calculating the current version number based on your repo Git history. It means to see the current version you have to execute it from the root repo folder. It will give you the following output:

Example of GitVersion output for a specific branch

Versioning in SPFx

To make the link with SPFx, let’s see how versions work there. The versioning in an SPFx solution takes place in multiple files:

  • package-solution.json: corresponds to the version of the SharePoint .sppkg and is visible through the SharePoint App Catalog (tenant or site collection). This version number format is not SemVer compliant and follow the SharePoint Add-In standard with 4 digits.
  • package.json and Web Part *.manifest.json files: These numbers follow the SemVer standard and are mainly used for upgrade scenarios. By default, Web Parts versions follow the  package.json version using the special token ‘*‘.
This version is then available in the dataVersion property in the SPFx main Web Part class allows you to run custom upgrade logic if desired:
As a best practice, they usually all follow the same version number in the solution.

Integrate SPFx, GitFlow and GitVersion with Azure DevOps

Now we have a standardized way to manage branches and versions, we have to integrate all of this into an automated Azure Dev Ops build and release pipelines. From here, the main challenges are :

  • Handle the deployment in multiple environments according to GitFlow branches.
  • Version SPFx package according to GitVersion SemVer.
  • Handle multiple configurations per environments (ex: Web Parts configuration, app catalog URL, environment credentials, etc.).
  • Get an unique and reusable build and release definition for all environments.

Build pipeline configuration

1. Configure repository connection

In my case, I use a GitHub repository. Nothing special here, except you may have to generate personal token with appropriate permissions to connect to your repository (especially for webhooks).

2. Configure triggers

The second thing to do is to configure the triggers for the build according to GitFlow. The following configuration means the build will start if a commit is made on these branch:

You may notice the ‘feature/*’ branches are not here. In my opinon, this is not necessary since when developers work on feature branches, they often use their own environment (tenant or site collection with dedicated app actalog) and likely deploy ‘manually’ their SPFx packages for a time saving purpose. At least, this is my case.

3. Configure build name

I configure the build name to reflect the current version generated by GitVersion (this number will be determined later in a specific build task). I also add the build id to get an unique build name.

4. Configure build tasks

The complete sequence looks like this. I mainly reused build definition from the sp-dev-build-extensions SharePoint repository thanks to @baywet.

Build pipeline tasks

Task

Description

Use Node 8.x

Prerequisite for SPFx

GitVersion

Available through the marketplace, this task gets the current version according to the triggered branch. It does the same as if you run the GitVersion cmd on your local repo for the targeted branch before merging/pushing. All version related variables are then available in the other tasks using $(GitVersion.<variable_name>) (typically the variables from the console output above). If you don’t manually set a build name, this one will be automatically set to the GitVersion output version.

npm install

Install all npm packages from package.json.

gulp update-properties

Updates Web Part manifest configuration according to current branch. In certain scenarios, you may have different configurations for you Web Part properties according to the targeted environment. For instance, I’m thinking about back-end service URL (like Azure Functions, Flows, etc.) pointing to different environments as well. Because GitFlow branches are associated with a physical environment, I use a dedicated gulp task to choose the right configuration according to the branch.

In addition, I use the corresponding structure for configuration files per environment for each Web Parts. It is one solution among others to deal with multiple configurations.

gulp build –ship

Build sources (TypeScript/Sass) for SPFx.

gulp bundle –ship

Bundle SPFx sources (Webpack)

npm version

Update the version determined by GitVersion in the package.json file. You can use $(GitVersion.SemVer) or $(GitVersion.MajorMinorPatch) variable here. Notice the ‘–no-git-tag-version‘ flag to avoid tagging branches (because tags are handled by GitFlow).

gulp update-version

Update the SPFx version in the package-solution.json file using a specific gulp task. Because SPFx package solutions rely on the old SharePoint Add-In model (4 digits format), the version is not SemVer compliant so I can’t include pre-release tags from GitVersion here. Anyway, in my case and for convenience, I use the MajorMinorPatch version number + the unique build id to identify precisely the current deployed build for an environment just by looking the SPFx app package version in the site contents. You could leave the last digit to ‘.0’ or choose something else.

gulp update-version task

Generated build number

Allows you to identify this build uniquely

The gulp task is inspired by the one from Stefan Bauer blog post.

gulp package-solution –ship

Generate the SPFx .sppkg package.

Copy Files

Just copy the generated package to the default ‘drop‘ artifact location. This location will be used by the release pipeline to get the package.

Publish artifact

Publish the artifcat to be consumed by the release pipeline.

Release pipeline configuration

The release pipeline will be used to deploy the SPFx package into different environments according to the GitFlow code lifecycle. For instance, when a commit will be made on ‘develop’, the package will be deployed automatically to the ‘Staging’ environment. For ‘master’, it will be the ‘Production’ environment and so on… The complete release pipeline looks like this. It does not cover gates, approvals etc. that you can add afterwards.

 

Azure Dev Ops GitFlow release pipeline

Each stage represents a physical environment (here it will be different SharePoint site collections within the same tenant).

1. Setup the release name

To match the release with the build, I use the build number for the release name:

2. Configure artifact

The artifcat comes directly from the build pipeline and ‘starts’ a new release. The configuration and triggers look like this:

As you can see and like the build pipeline, the triggers correspond to the GitFlow branches. They will be used to determine the right stage (i.e. environment).

3. Configure stages for environments

To deploy on the right environment, I use ‘pre-deployment‘ conditions filtering on the triggered Git branch from the artifact. Here are the settings for the all 3 environments:

Staging  = develop

Pre Production = release/*,hotfix/*

Production = master

 

Release pipeline tasks

The tasks are the same for all stages. For example, ‘Pre-Production‘, ‘Production‘ are just clones from ‘Staging‘ and only differ about their pre-deployment conditions. This way you could add other environments pretty easily. Again I mainly reused release definition from the sp-dev-build-extensions SharePoint repository.

Task

Description

Use Node 8.x

Prerequisite for Office 365 CLI

npm install office 365 cli

Install the Office 365 cli package

spo login

Login to the SharePoint environment (i.e. the site collection).

spo app add (site collection)

Add the solution to the site collection app catalog (need to be activated before).

spo deploy app (site collection)

Deploy the package to the site collection.

As you can see in the tasks, I use variables for URLs and credentials. To be able to distinguish multiple deployment URLs (i.e. site collections in this case), I use release piepline variables and the ‘scope‘ attribute. It means the variable will be only available in corresponding stage so you can set the same name for all stages:

Test the pipeline

To test the whole pipeline, simply start pushing your commits in configured branches according to the GitFlow workflow and you will see builds and releases triggered automatically and your package deployed to the right environment:

Triggered builds according to GitFlow

Triggered releases

Conclusion

DevOps is a indeed very large topic and I’m definetely not an expert. However, for my day-to-day SPFx requirements, coupled with GitFlow, this Azure DevOps pipeline does the job in the case there is nothing in place for my new projects. I would happy to hear your thoughts about how you manage your versions and deployments with SPFx. Also free to improve this pipeline 😉

Useful links

2 Comments

Add yours

+ Leave a Comment

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.