Table of Contents
This series consists of those six posts:
- Building Azure DevOps Extension - Design
- Building Azure DevOps Extension - Implementation
- Building Azure DevOps Extension - Publisher Registration
- Building Azure DevOps Extension - Manual Publish
- Building Azure DevOps Extension - Automated Publish 1
- Building Azure DevOps Extension - Automated Publish 2
Use Case Scenario
I'm interested in using a static website generator, called Hugo, to publish a website. There's an extension already published in the marketplace so that I'm able to install it for my Azure DevOps organisation. To publish this static website, I wanted to use Netlify. However, it doesn't yet exist, unfortunately. Therefore, I'm going to build an extension for Netlify and at the end of this series, you will be able to write an extension like what I did.
Actually, this Netlify extension has already been published, which you can use it straight away. This series of posts is a sort of reflection that I fell into situations – some are from the official documents but the others are not, but very important to know during the development. The source code of this extension can be found at this GitHub repository.
Extension MUST be packaged before it is published to the marketplace. It has
.vsix file extension, which is basically another type of
.zip file. To generate this package, we need to create a manifest file,
vss-extension.json. Let's start from there.
The manifest file,
vss-extension.json MUST be placed in the root folder of the extension. Once it is created, the folder structure might look like:
Fill in the file like below:
It looks overwhelming, but it's not that complicated. Let's have a look at each attribute.
id: Extension ID. This MUST be unique across the whole marketplace. Only alphanumeric letters and hyphen are allowed.
version: Extension version. It doesn't need to be the same version as individual tasks in the extension.
name: Extension name
publisher: Publisher ID that the extension belongs.
description: Brief description of the extension.
targets: List of areas the extension is in charge. It SHOULD always be
Microsoft.VisualStudio.Servicesas this is the Azure DevOps extension.
categories: List of services in Azure DevOps. Set
Azure Pipelines, as this extension is for Azure Pipelines.
tags: List of tags for search in the marketplace.
galleryFlags: List of flags how the extension is published. Possible values are
Preview. Of course,
Paidcan't come together, and neither does
icons: Path and name of the icon. It can be any location and name like
images/my-icon.png. But it is recommended using the root folder and fixed name of
content: Path and name of the content that describes the extension. Like the
icons, it can be any location and name like
docs/readme.md. But it is recommended using the root folder and fixed name of
files: List of files that consist of this extension.
- If the
README.mdneeds some images, that images themselves or folder containing the images can be included. In this case, the files or folder MUST have the
true, to be accessible from the Internet.
- The current folder structure shows all the tasks are placed under the
src/deploy. However, the package expects all the task folders MUST be under the root folder. Therefore,
packagePathattribute is used to adjust the location.
- Each task expects
node_modulesfolder for proper invocation. Therefore, the current
src/node_modulesfolder MUST be copied to
- If the
links: List of external URL links for more information. It generally includes
repository: Repository URL, if the extension is open-sourced.
badges: Build/release status badge URL.
contributions: Each task MUST have its corresponding contribution.
It's really a brief, but if you need more details, refer to this page.
Now, we've got the manifest file. Let's create the package. It requires to install Azure DevOps Extension CLI (
tfx-cli). Enter the following command to install
tfx-cli on your local machine.
After installing CLI, run the following command at the location where the
Obviously, there are many more commands on
tfx-cli, but we only need this command above. If you need to more about
tfx-cli, refer to this page.
There might be naming confusion around many CLIs. This
tfx-cliis for Azure DevOps Extension related. If you want Azure DevOps itself through CLI, use Azure DevOps CLI Extension, which is one of the extensions of Azure CLI.
Now, we've got the package file! The package file name always looks like
[Publisher ID].[Extension ID]-[Version].vsix.
It's time to publish. We're going to publish it through the publisher of
When you go into the publisher manager page,
aliencube-dev has nothing published yet. Click the
+ New Extension button to publish a new extension.
You'll be asked to upload the package file. As we just created the package, upload it.
Oops! Upload failure! How come? As the error message says, we create the package for the publisher of
aliencube, which is not correct for now. In other words, we need to update the manifest file,
vss-extension.json to declare the correct publisher.
vss-extension.json file with correct publisher ID, package it again with
tfx-cli and upload it. Hmmm, another error occurred. What does this mean this time?
aliencube-dev publisher hasn't officially verified, it can't publish any public-facing extension. To sort this out, either the publisher is verified by Microsoft, or upload a private package. The intention of using
aliencube-dev is only to deal with private extensions. So, just update
vss-extension.json to create a private extension. Change the
galleryFlags attribute values from
Private, package it and upload it.
Now, it's all good!
The extension uploaded is private, which is not publicly shown. Unless it's visible, we can't download and use it. Therefore, we can share the private extension with designated Azure DevOps organisations. Click the three dots button then click
It shows to enter the Azure DevOps organisation to whare this private extension. I've got an Azure DevOps organisation only for the extension testing only,
It's now shared. Open the Azure DevOps organisation and go to the organisation settings page. In the settings, open the
Extensions tab then click the
Shared tab in the middle.
Now you can find out the shared extension. Install it and you'll be able to see the following screen.
Now, the extension has been installed. Let's run the task in a pipeline.
Running Task in Pipeline
In a release pipeline, search
netlify and you'll be able to find two tasks that we've built.
So far, we've created a package of Azure DevOps extension that we built, published, shared, installed and used it. All these steps were manual, by the way.
Let's think about the errors we've met during the package publishing. We had to modify the manifest file for testing. If we publish it publicly, we have to modify the manifest file again, which is not nice. I'm not sure this is efficient. Is there any other way to minimise manual human intervention?
The next post will discuss how to automate the entire publishing process through CI/CD pipelines using Azure DevOps.