Continuous Delivery of Visual Studio Code (@code) Extensions

With the VSCodeVim extension, I recently setup a continuous delivery pipeline to make it easier to push a new release. Here's the lowdown of how we accomplished an automated approach to publishing a new release to the extension marketplace:

Upon a commit to the master branch of our GitHub repo, this will kick off a Travis CI build. Depending on the change, Travis CI will build, test, and release a new version of the extension. Breaking down each step:

Build

We'll need to pull down the extension's dependencies and, if necessary, transpile the TypeScript. For VSCodeVim, we've encapsulated much of the logic using gulp. The default gulp command is configured to download the appropriate TypeScript typings.

The relevant section of our .travis.yml looks like this:

install:  
- npm install;

script:  
- npm install -g gulp;
- gulp;
- npm compile

You'll notice that we are calling npm compile. If you created an extension using the yo generator, by default you should have a couple of scripts defined in your package.json to help you build and test your extension:

"scripts": {
    ...
    "compile": "node ./node_modules/vscode/bin/compile -watch -p ./",
    ...
},

Test

In order to run your tests in a CI environment, this article best describes the changes you'll need to make to .travis.yml.

Release

Upon a successful build and test, we'll arrive at the release portion of the pipeline. We've configured the release portion of the pipeline to run only on tagged builds. As an aside, the reason I decided against creating a release branch is that it would be too much overhead to maintain the state of an additional branch especially considering we are leveraging GitHub releases.

In order to push a new release out to the marketplace, we need to:

  1. Update the package.json version of the extension
  2. Create a git tag
  3. Push the changes to the remote repository

With VSCodeVim, steps (1) and (2) are done using gulp. We have a gulp patch|minor|major command (see gulpfile.js#L16) that will rev the appropriate versioning number, create a git tag using the version as the annotated tag name, and perform a git commit.

Finally, once we git push origin --tags assuming the build and test is successful, the release phase will:

  1. Create a .vsix package
  2. Create a GitHub release and upload the package to said release
  3. Publish the new version to the extension marketplace

The .travis.yml to accomplish this is the following:

env:  
  global:
    secure: [VS_TOKEN=<Visual Studio Auth Token>]

after_success:  
- npm install -g vsce;
- vsce package;

deploy:  
- provider: releases
  api_key: [GITHUB_AUTH_TOKEN]
  file_glob: true
  file: "*.vsix"
  skip_cleanup: true
  on:
    repo: VSCodeVim/Vim
    tags: true
- provider: script
  script: vsce publish -p $VS_TOKEN
  skip_cleanup: true
  on:
    repo: VSCodeVim/Vim
    tags: true

Breaking this down, we have two deploy steps:

  1. releases takes care of creating a GitHub release
  2. script will run vsce publish with a VS_TOKEN that we've defined as a secure global environment variable. To encrypt environment variables, refer to this documentation from Travis CI.

VSCodeVim is open source which means that our .travis.yml is open for all to see if you would like a complete reference.