An Azure Pipeline for Visual Studio Marketplace
In a previous post I built a custom Azure Pipeline release task and manually uploaded it to the Visual Studio Marketplace. In this post I’ll add new features and have DEV and PROD versions of the extension automatically deploy to the Marketplace on commit.
The source for this project is here and the diff for changes mentioned in this post is here. The extension itself is on the Visual Studio Marketplace here.
Development and Release Versions
Initially I followed a colleague’s post on setting up a pipeline and this devblog from Microsoft in an attempt to have a dev (and prod) version of the release task. However, I later determined that there’s a difference between the extension that is defined and published by vss-extension.json
and the (one or more) tasks that are packaged with the extension and defined by taskName/task.json
. I could successfully push a unique-id extension to my existing publisher, but the deployment would then fail due to the marketplace already having an existing version of the task.
As such, the approach was to name and key both vss-extension.json
(now renamed manifest-*.json
) and task.json
specifically for dev/prod. The core changes to achieve this result in a structure like this:
azure-pipelines-dev.yml # dev pipeline
azure-pipelines-release.yml # release pipeline
manifest-dev.json # dev extension definition
manifest-release.json # release extension definition
cloudflarePurge
|-- task.json # dev & release task definition
NB: Actually manifest-dev.json
and manifest-release.json
could be merged and token-replaced similar to task.json
.
The result is two extensions and their associated tasks:
- Purge Cache for Cloudflare and;
- Purge Cache for Cloudflare (Development)
The first is the existing, already published version, and the second is a brand new instance specifically for development and testing and with public: false
set. The production instance continues to reflect and is now triggered from the master
branch, and the new development instance is triggered from the dev
branch.
Token Replacement
An immediate issue with my proposed pipeline was the difference in how vss-extension.json
and task.json
deal with version
and how JSON variable substitution works within the standard File Transform task.
vss-extension.json
"version": "1.2.3"
task.json
"version": {
"Major": 1,
"Minor": 2,
"Patch": 3
}
The solution proposed by microsoft was to split the two variable transforms across jobs. I’ll give this approach a go in the future, however for now I’ve just used a single job and token replacement.
This same token replacement is used for setting the name
and id
of the extension and task.
Build and deploy
Outside token-replacement the only steps in both dev/prod pipelines are;
- Install node
- Install dependencies
- Compile typescript
- Deploy to marketplace
We’ll use npm
to install the dependencies and use npx
to compile with typescript & deploy with tfx-cli. For the latter we’ll need a PAT from our Azure Devops tenant, instructions can be found here.
Note that before installing npm
dependencies, it’s important to set the working directory to the task directory. Doing so installs dependencies adjacent to the task and ensures they are shipped with tfx-cli extension publish
. Originally I had not done this, such that the extension was building and shipping without the dependencies, meaning users of the extension needed to npm install
themself - my thanks to Patrycjusz Jaskurzyński who reported, and helped me diagnose, this issue.
Further differences
An issue with the previous version of the extension was that the provided icon was only visibile on the marketplace, but once installed into an Azure DevOps tenant, a default icon rather than the one supplied was visible when adding the task to a release pipeline.
Where vss-manifest.json
defines an icons
property, task.json
does not, and instead by convention checks for icon.png
in the same directory. Additionally some documentation indicates an icon size of 32x32 is required - this will however result in a lower quality image once installed as the icon will be scaled up in size. To resolve this supply the icon at 128x128.
Conclusion
The last thing I needed to do was actually install the new Development version of the extension. This is an entirely different extension as far as Visual Studio Marketplace is concerned, but I can have both of them installed on my single Azure Devops tenant. The dev version’s public: false
property in the extension definition and --share-with myUserName
in the pipeline defintion means only my Azure Devops tenant can see the development version. While developing and testing, I commit directly to the dev
branch and the development version of the extension is deployed to the Visual Studio Marketplace and updated on my Azure Devops tenant. Once i’m satisfied, I merge dev
into master
, which triggers the production build & deploy.
As for the extension itself, now you can specify individual URLs instead of purging everything: