Microsoft MVP Logo

Over the last few months I've been serving as a coordinators for the ngOfficeUIFabric project where we are building directives for Angular 1.x for the Office UI Fabric controls. We have adopted some practices and patterns, most borrowed from other projects that have helped us in managing the project. This post is one of a few in a How We Do It series of posts to share some of our guidelines that may help others.

Those contributing to the project are doing so on their own time. Anyone is welcome to contribute and not be saddled with the burden of a lot of rules. My goal has always been to put things in place that make it easy for new people to get involved, to make sure the management of the project and tedious stuff is stuff contributors don't have to worry about.

Check out the first post in this series: ngOfficeUIFabric - How We Do It - Keeping a Clean Commit Log

Check out the first post in this series: ngOfficeUIFabric - How We Do It - Handling Pull Requests


We want our library, ngOfficeUIFabric, to be available to anyone using their preferred packaging framework. This includes NPM, Bower, NuGet as well as a CDN. The process of cutting a new release & pushing to these different distribution targets is tedious and includes multiple steps. If you miss one, you can really screw up things for one of your users. I wanted to put in place a process, inspired by the Angular Material project, that was as automated as possible.

How automated is it? All we have to do is bump the version number + update the change log in the master branch. Once I push it to the main ngOfficeIUFabric repo on GitHub the deployment to all four of the package distribution services mentioned above happens automatically. The best part: it's fully automated & transparent so you can see how we did it... so let's get to it...

Overview of the ngOfficeUIFabric CD Process

Before I jump into the weeds, let me explain the process from a high level. This sequence diagram may look complicated, but in reality is quite simple:

The orange boxes {} & {} represent the package distribution targets we have to factor into the process with every release. Before I mentioned we also use a CDN & Bower... in the next section I'll explain why we don't have to concern ourselves with those two with every release.

GitHub Repo's Supporting the Release Distributions

Our entire code hosting & distribution process is comprised of three repos in our GitHub organization ngOfficeUIFabric:

  • / ng-OfficeUIFabric - This is the main repository where we keep the source for the library. There are two primary branches here:
    • master: The master branch is an exact copy of the source that is currently released. Therefore if you are currently using our library using any of the distributions and you want to see what the code looks like for the implementation, you can go to this library and see exactly that (or jump to previous versions using the tags / releases in GitHub).
    • dev: The dev branch is our staging... think of it as the nightly build with the most current stuff in it. Contributors submit PRs to this branch. Eventually master will be rebased on this branch to get the latest stuff.
  • / package-ngofficeuifabric - This repo is used as the primary distribution for the library. It powers NPM & Bower package managers as well as CDNJS.
  • / nuget-ngofficeuifabric - This repo powers the NuGet package manager distribution for the library.

Continuous Integration (CI) Services Facilitating the Release Process

At the present time we are using three different continuous integration (CI) services to handle builds, tests and release distribution. We may move to two in the future eliminating TravisCI, but that's still undecided. For all services we're using the free tiers for open source projects.

  • TravisCI - Travis is a very popular CI service. Every push to master & dev as well as all PR's to the main source repo trigger TravisCI to run. This is primarily used to vet the builds making sure all linting checks & tests pass.

    You can see the status of our builds & history of builds here:

    The builds are controlled by the .travis.yml config file in the root of the project: If anything fails (tests or vets), an error is logged and the build goes red.

  • CircleCI - Circle is like Travis but is a bit more polished and reliable from my experience. We are only using it now on the package-ngofficeuifabric repo. The reason I used CircleCI here instead of TravisCI is because publishing to NPM was very unreliable from Travis.

    At present, all it does is simply publish to NPM... seems like a waste, but it works.

    You can see the status of our builds and history of builds here:

    The builds are controlled by the config file In that file you see references to three environment variables which I've set in a password protected area of the CircleCI config for this project.

  • AppVeyor - The other two CI services above are Linux-based. While I tried to use JavaScript to manually create the Nuget *.nupkg file and upload it, NuGet kept saying the file wasn't valid OpenXML. So I gave up and elected to use the NuGet command line client to build and push the package. The downside is that the CLI client only works on Windows today, so the existing CI services didn't cut it.

    AppVeyor is a Windows-based CI/CD service and it's hooked up to the nuget-ngofficeuifabric repo. The only purpose it has for us is to run the nuget CLI to create the NuGet package and publish it to NuGet.

    You can see the status of our builds here,, all of which are controlled by this config file:

    Ignore the failed builds... right now the CLI fails when it tries to publish to NuGet the same version of a previously deployed package. In the future I'll clean this up but for now, it works. You can look at the history and logs in the builds to see what the error looks like as well as the success messages.

Understanding the Package Manager / Distribution Options

As I said above, we use four package manager / distribution options to satisfy everyone. Personally I'd recommend using the CDN option for performance & the NPM option as secondary if you want local copies regardless of the project you use, but we provide two more options for completeness.


This is a community-driven CDN. Using a CDN is preferred for performance reasons in your web application.

Getting your library into CDNJS is a piece of cake... you simply fork their repo, add your library (like we did here: 044eab7f) & submit a PR (PR#6760) and you're in provided you meet their requirements.

Better yet, if you add a section to your package.json file, CDNJS will periodically check your GitHub repo's tags for new versions and automatically ingest them into the CDN for you. You can read more about this process in the CDNJS docs here: autoupdate.

Once you have your library setup with CDNJS and configured for auto update, you don't have to think about it anymore provided new versions are tagged in your GitHub repo. Therefore, you won't see this option mentioned in the rest of this post as it's automatic.

Bower -

Another package manager is Bower. It seems to be losing steam in popularity to NPM, but it's still very relevant. Bower has a centralized public registry that library authors can publish to. When you want to include a library from Bower in your project, Bower pulls the code straight from the library's repo.

Therefore, once your library is added to the registry, no need to republish new versions as they will get pulled down automatically from GitHub or whatever code hoster you are using. Therefore, you won't see this option mentioned in the rest of this post as it's automatic.


This is the defacto package manager today. To publish to it you need to have the code in a public location, which we do at package-ngofficeuifabric, and run the NPM CLI... that's it.


This is the Microsoft defacto package manager today. To publish to it you either use the web browser or use the nuget CLI.

ngOfficeUIFabric's CD Process In Depth

Now that covered the repos involved, the targets & services we use & why, let's look at the process in detail. I'm going to go through the diagram in a bit more detail as it was tedious to put together trying to read the code from what other projects did, so hopefully this speeds your ramp up process.

Step 1: Create a New Version

Creating a new release is the simplest part and best of all, it's the only thing we manually do. As explained above, the master branch is what the releases look like. So, once we are ready to cut a release, we go into the dev branch and do two things:

Once that's done in the dev branch, we commit the change with the commit message docs(release): #.#.# and push it to GitHub. Then we jump over to the master branch, rebase it to the tip of dev & push to GitHub (step 1 in the picture above).

# assuming on the dev branch with release changes...
# save changes to dev & commit as a new release, 0.5.1 in this example
git add -A
git commit -m "docs(release): 0.5.1"
git push origin dev
# jump to master branch & update with changes at tip of dev
git checkout master
git rebase dev
git push origin master

This push starts the entire release deployment process.

From this point forward, EVERYTHING is automated, so these steps are not things we have to do, rather it's how it was setup.

Step 2: Start Release Process & Update Main Repo

The push to master in the last step triggers a webhook (#2 in the picture above) that notifies TravisCI. This is where we kick off the automated process by running a shell script.. well... sort of...

OK, I lied a bit... this is an extra step today, but we could easily automate this. Frankly I want to see the process run without issue for a few more releases before I flip the switch. Let me explain what is really happening...

When we are ready to start the release, from my laptop, I hope a prompt and run the following shell script from the root of the locally cloned repo's master branch:

sh build/scripts/ --git-push-dryrun=false

In the future, that line will be added as the last step in the after_script section of the .travis.yml file to make this fully automated.

That will run our release script which is #3 in the picture above. The script is well documented if you take a look, but let me take a minute to explain it with extra comments using this gist:

if you can't see the code, look at the post on my site, not in a feed reader

As you can see this script does a few checks and then creates a new tag for the ng-officeuifabric repo. Once that's done, it runs two other scripts: one that updates the package-ngofficeuifabric repo & one that updates the nuget-ngofficeuifabric repo.

What's with that --git-push-dryrun argument & a reference to it in the init() function? The file that's included at the bottom of the script, uses a bit of trickery. If it detects this --git-push-dryrun argument is set to TRUE (which is also the default if omitted), it replaces the git command with it's own that adds the --dry-run flag to all git push commands. This lets me run the entire release process without making any changes to any repos. Pretty slick eh? It's a nice little saftey to disable the whole process.

Therefore, if you want to really run a release, you have to specify --git-push-dryrun=false to disable this.

Step 3: Update package-ngofficeuifabric & Publish to NPM, Bower & CDNJS

Let automation flow! We saw how #3 above was kicked off in the previous step. Recall that #3 involved running the script which calls

This script (which you can see below) first clones a fresh copy of the pacakge-ngofficeuifabric repo & copies the two compiled ngOfficeUIFabric libraries from the parent script as well as the file into the repo, overwriting the previous versions. It then uses Node.js to execute a JavaScript file that updates the version numbers for the library and all dependencies in the package.json & bower.json files (I found it much easier to use JS to update the JSON than using a shell script).

Now that the repo has been updated to a new version, I then save all changes by running git add -A and commiting, tagging & pushing the changes to the remote package-ngofficeuifabric repo (#4 in the image above). This simple update takes care of updating the Bowser & CDNJS release targets, but we need to publish to NPM, which is a manual command line task.

The package-ngofficeuifabric is hooked up to CircleCI which will do the publish to NPM once the master branch is updated in GitHub. That just happened so taht will trigger a webhook to notify CircleCI (#5 above) which, using the circle.yml file here, will publish the repo to NPM.

Check it out:

if you can't see the code, look at the post on my site, not in a feed reader

Step 4: Update nuget-ngofficeuifabric & Publish to NuGet

Last step, update NuGet. Again, from step 3 above, the script ran two scripts. One updated the package-ngofficeuifabric repo as explained in step 4. The other, does the same thing, but for the nuget-ngofficeuifabric repo. The code is mostly the same, the difference is that we have to update an XML file with the new versions.

Just like in step 4, once that's done the changes to the locally cloned repo are saved, tagged and pushed to GitHub. The repo is hooked up to AppVeyor which is used to build a new NuGet package using the nuget CLI (#7 in the image above) and publish it to the public NuGet registry. That's controlled by the appveyor.yml file here.


And that's it! Usually within a few minutes of starting the release process we have new releases published to NPM, Bower & NuGet. CDNJS will pickup the changes from the new tag in the package-ngofficeuifabric repo within an hour when they run their scan.

The goal here was to make things as automated as possible. Today I am using one manual command to start the publishing process.

Looking Forward

This is how we do releases today, and it works great. There are a few things we are looking to do to extend this process. Here's what's on my backlog, in order of priority:

  • Standardize on CircleCI: I much prefer their interface to TravisCI and they seem a bit more reliable. We already have to use two CI/CD tools in CircleCI & AppVeyor so I'd like to remove Travis from the equation. That requires a bit more testing on my fork before I do that.
  • Fully Automate the Release: I'm waiting to move to CircleCI (unless I realize that Travis is better for us) before adding one more step when we push to master on ng-officeuifabric. This step will kick off the release automatically when we update master... but I don't want to do this in Travis only to have to reapply to CircleCI.
  • Automatic Changelog Creation: Today we manually update it, but it can automated. That's on my list of things to do using the conventional-changelog.
  • Automatic Demo Site Creation: Lots of work to do here, but there's no reason each release can't trigger the update of our demo site. Got some slick goals & ideas here so once I get this done, I'll write about it.
  • Automatic Documentation Creation: Same as the last one... lots of ideas & goals, but more work and research required. I'll write about it when it's done.
Comments powered by Disqus