Azure DevOps provides an end-to-end service that takes care of the entire ALM (Application Lifecycle Management) process. DevOps itself takes a big part of this ALM journey. While the service naming indicates that only DevOps related features are offered, it comprises all the ALM cycle from request analysis to delivery, via development and test. There's no doubt that it's such a powerful tool. In addition to this, Azure DevOps offers a strong extension model so that anyone can publish extensions to Visual Studio Marketplace. If your Azure DevOps instance doesn't have a necessary extension that you're looking for, you can search it on the marketplace and install it. If you can't find it, then publish it by yourself publicly or privately. Throughout this series of posts, I'm going to discuss how to build an Azure DevOps extension from various perspectives.
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.
Designing Azure DevOps Extension
There are two different SDKs for Azure DevOps extension development. One is for Web Extensions SDK, and the other i for Pipelines Task SDK. As a rule of thumb, the former is generally used for Azure Boards and Azure Repos related extension development, and the latter is generally used for Azure Pipelines related extension development. I'm going to use the latter because my extension will only be used for Azure Pipelines. You can start from this document to understand the basic approach.
To use Netlify CLI to publish your static websites, you might need two tasks – one is to install the
netlify-clifrom npm, and the other is to deploy your website to Netlify through
netlify-cliinstalled. So, I'm going to create two tasks named
Scaffolding Folder Structure
First of all, let's scaffold the folder structure. I've set it up like the image below – there are
test folders, and under the
src folder, I created
deploy folder. These very two folders are the ones I'm working on. I'm going to touch the other folders later in this series.
netlify-cli is installed into the pipeline through this task. The first thing I need to do is to create a
task.json file in the
install folder and put the JSON content in the file:
Here are attributes you need to understand at the first place:
id: A unique GUID value. This website can easily get the GUID value.
name: Name of the task. This only allows alpha-numeric letters. It's recommended to have the same name as the folder name.
friendlyName: The name displayed in the Azure DevOps pipeline UI.
preview: Indicator that this task is a preview. This usually is set to
false, unless it's a preview.
showEnvironmentVariables: Environment variables only scoped to this task. If necessary, set this value to
runsOn: List of agents that the task can run. The possible values are
Server(Azure DevOps Server). In general, set those three values at the same time.
category: Azure DevOps category. As my use case is only responsible for Azure Pipelines, I just set the value here to
instanceNameFormat: The initial task name when the task is brought to the pipeline.
inputs: This is the most crucial part of designing the
task.json. It defines the user input flow. Based on this, the UI of the task is determined. This task only requires the
netlify-cliversion. My intention around this will be to take the version value. If it's omitted, the latest version of
netlify-cliwill be installed.
Based on the design, the actual UI will look like:
If you want to know more details on the
task.jsonstructure, have a look at
task.schema.jsonas a reference.
This task is to deploy websites through
netlify-cli installed from the previous task. If you're not familiar with Netlify command, this will be the command I'm going to use:
If you want to know more about the
netlify-clicommand, visit this official page.
As the command requires several parameters, I need to reflect them into the design. Under the
deploy folder, create a
task.json file and enter the following JSON object:
I'm going to discuss only the
inputs attribute here. As you can see, it needs more parameters than the
authToken: (Required) PAT (Personal Access Token) of your Netlify account.
siteId: (Required) Site ID of your Netlify website, which is unique to every website on Netlify.
sourceDirectory: (Required) The folder location of the static website artifact. Default value is
isValidationOnly: (Optional) If selected, this task only validates deployment. if not selected the task publishes the website.
message: (Optional) A short blurb for logging during the deployment.
functionsDirectory: (Optional) The folder location of AWS Lambda functions, if they exist.
Based on this design, the UI will look like:
Setting up Icon for Task
When you see the Extension Layout page, each task needs its icon to display on the pipeline UI. This is the only information on the official document, but this is not enough to publish. Fortunately, this Stack Overflow page provides more details. To sum up:
- The icon name MUST be
- The icon size MUST be
- The icon MUST be placed at the same location as the
If you can't meet this condition, your extension won't be displaying the task icon properly.
Of course, the extension icon is different from this task icon, which will be discussed in later of this series.
Now, we've completed designing the extension tasks. As a result, you'll be able to see the folder and file structure like below:
In the next post, I'll discuss how to implement the
index.js that is invoked by the
- GUID Online Generator