VS Code extension: building auto CI/CD with GitHub Actions
Tags: vscode, devops, github, kubernetes
Published: 2020-04-06 18:49:57

# tl;dr

This article is about building a full CI/CD pipeline for Visual Studio Code extensions using only GitHub Actions.

My basic requirement was to build an automatic CI/CD that will allow me to do the following upon pushing a new commit to `master` branch:

- Test: run the tests on Mac, Windows and Linux

- Release: create a new GitHub release with automatic release notes based on [Angular Commit Message Conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines)

- Publish: publish the new version to Visual Studio’s Marketplace

One last thing: I wanted to do all of that based solely on GitHub Actions :) So I need nothing but the GitHub repository to configure and run everything.

# Motivation

It all began when I wanted to write a small VS Code extension that will allow me to right-click on a Yaml file and apply/delete it from my local Kubernetes cluster.
Very soon I realized that VS Code’s [documentation to how to write an extension](https://code.visualstudio.com/api/get-started/your-first-extension) is really good — I found myself writing and locally-debugging my new Typescript extension in no time, but then it occurred to me that the full CI/CD for it that Microsoft suggests in their docs is a bit lacking:

#### Tests

This part is actually relatively good in the [formal docs](https://code.visualstudio.com/api/working-with-extensions/continuous-integration), they specifically mention a GitHub[ Action](https://code.visualstudio.com/api/working-with-extensions/continuous-integration#github-actions) that runs the tests on Mac, Windows and Linux, and I definitely used it.

#### Release & Publish

The entire release and publish process is built on the [_vsce_](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#vsce) tool.
The first things you need to do to set the ground is to follow the [publishing docs](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) and especially do the following:

- [get a personal access token](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#get-a-personal-access-token) from Azure DevOps

- create a [publisher](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#get-a-personal-access-token) (the publisher name has to match the *publisher* section in package.json)

Once you have all that you can package, publish and unpublish your extension using vsce.
Those are manual steps needed to be taken, less than ideal!

In addition there is also a gap in how the extension version is set during publishing. there are a few ways to set that:

- change the _version_ field in package.json and call `vsce publish`

- use `vsce publish minor` for example (or major/patch accordingly) to automatically increment the version

- use `vsce publish 2.0.1` to mention a specific version

This is really not handy, I want the version to be set according to my [commit conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) automatically, with automatic release notes generation. Spoiler: I will use [semantic-release](https://github.com/semantic-release/semantic-release) for that :)

# Let’s start!

I’ll follow the steps I went through and we’ll build our CI/CD pipeline step by step.

#### Create an extension

I followed the [formal docs](https://code.visualstudio.com/api/get-started/your-first-extension) and relatively easily created[ a new repository](https://github.com/shaimendel/vscode-plugin-cicd-github-actions) with my fresh templated Typescript extension.
I then modified the functionality so right clicking a YAML file allows you to apply or delete it from my local Kubernetes cluster. super cool and useful to me!

#### Create an empty GitHub Actions workflow

Create an empty yaml file at `.github/workflows/<workflow name>.yaml`. I called it `ci.yaml`

#### Mention that the workflow should happen on push to master

Change you yaml workflow to be:

{% gist https://gist.github.com/shaimendel/50d79172fa348293b860e4172f38ae92 %}

The upper paragraph tells GitHub to run that action when a push to master happens.
The lower part is the jobs that should be run, we will fill them soon.

#### Auto tests

As mentioned above I would like the tests to run on Mac, Windows and Linux. and the docs actually mention a specific action I will use now by adding a dedicated job:

{% gist https://gist.github.com/shaimendel/fed2d2527e198f7eb917a7b1385e6ba7 %}

As can be seen this adds a job called `Test`, that will run on Mac, Windows and Ubuntu.
this job contains 4 steps:

- Checkout: use the builtin action to checkout the repo

- Setup Node.js: use the builtin action to set Node 12

- Install dependencies: run `npm install`

- Run headless test: runs the tests using the [GabrielBB/xvfb-action@v1.0](https://github.com/GabrielBB/xvfb-action) action, as recommended by [the formal docs](https://code.visualstudio.com/api/working-with-extensions/continuous-integration#github-actions) (plus some improvements)

After this step I saw that on the Actions tab:

![](https://cdn-images-1.medium.com/max/1180/1*Y_BfVywUfmsM9rtTHClUuw.png)

where each test had the following steps:

![](https://cdn-images-1.medium.com/max/864/1*dyKw1S9u3slbLY5qmIZK-A.png)

#### Publish and release

Let’s add a new step, which looks like the following:

{% gist https://gist.github.com/shaimendel/7feb6e4b696f3be175d5b20e48a17263 %}

We’ll go slowly through this, don’t worry!
So we begin with the previous steps of checking out the code, setting up Node 12 and running `npm install`. The next two steps are the most important ones, and deserve sections of their own.

**Release**

The purpose here is to create and publish a new GitHub release with proper release notes.
In order to do so I basically just call `semantic-release` :) This action runs through the commit messages, builds release notes and creates a new GitHub release with a semver version corresponding to the commits I added.
It also sets that release version in the package.json, a useful fact for next phase.

In order to properly configure `semantic-release` you need to:

- pass the`GITHUB_TOKEN` env var (as GitHub secret): your personal GitHub token, with minimal `repo` permissions

![](https://cdn-images-1.medium.com/max/1078/1*Z18MvnZBIp7esbFMtdorTg.png)

- pass the`NPM_TOKEN`env var (as GitHub secret): your npm token to release your npm package. In my case I didn’t want to release an npm package. In order to do so you need to configure `"private": true` in your package.json. after you do so you don’t have to pass this env var (found this fact [here](https://github.com/semantic-release/npm#options)).

- install `@semantic-release/github` as a dev dependency

- configure the following in your package.json:

{% gist https://gist.github.com/shaimendel/dbefc2b7be5159ebd6d2f1059d957c7d %}

Once this step was done I could start seeing proper GitHub releases

![](https://cdn-images-1.medium.com/max/2536/1*MTUHzQ1uDTmeYEpx2W04fg.png)

**Publish**

The purpose here is to upload a new version of the extension to the VS Code marketplace with the correct version.
One of the benefits of the previous Release section is that the package.json now contains the right package version. all needed to do no is to call `vsce publish -p <my token>`.
I found an existing GitHub action to release a plugin, [JCofman/vscodeaction](https://github.com/marketplace/actions/vscode-plugin-release-action). all you need to do is to pass it your `PUBLISHER_TOKEN` as an env var (again, using GitHub secrets).

After this step you’ll see a new `Release and publish` job in the Actions tab that contains

![](https://cdn-images-1.medium.com/max/1428/1*pqz2xLFLLhtoa2BPV-RL6Q.png)

# Result

![](https://cdn-images-1.medium.com/max/2582/1*K7Bg21RLqNPAj-Gm_ppKnw.png)

You can now find my new extension in [the marketplace](https://marketplace.visualstudio.com/items?itemName=ShaiMendel.kubernetesapply) and in VS Code extensions search!

You can find my repository at [https://github.com/shaimendel/vscode-plugin-cicd-github-actions](https://github.com/shaimendel/vscode-plugin-cicd-github-actions), with the workflow configured at [ci.yaml](https://github.com/shaimendel/vscode-plugin-cicd-github-actions/blob/master/.github/workflows/ci.yaml).

# Summary

We just build a fully automatic CI/CD for a new VS Code extension.
every time a new PR is merged to `master` we:

- test the code in multiple operating systems

- release a new GitHub release with super nice release notes

- publish a new extension to the marketplace

and we did all of it purely using GitHub Actions!

Great success :)

![](https://cdn-images-1.medium.com/max/1736/1*Ai5wAGW4Vw5YKIuBSTNrrQ.png)

Shai Mendel, Engineering team lead at [Snyk](https://snyk.io/)