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.
Implementing Azure DevOps Extension
Based on the design from my previous post, the
task.json invokes the
index.js file. Therefore, I’m going to write it.
In addition to this, TypeScript is required for this implementation. If you don’t have it yet, install it using the command below:
Scaffolding Task Structure
Each task uses node.js, as defined in
task.json. As both
deploy need node.js modules, create the
package.json file under the
src folder. You can create the
package.json file individually for each task if you like.
package.json is created, run the following commands to install
azure-pipelines-task-lib and type definitions.
tsconfig.json by running the following command:
Once created, open it and update the compile option from
ES6. Now we’ve got all scaffolding done for implementation. Your files and folder structure might look like this with
Don’t worry about the content in
package.json for now, because it’s not relevant to our extension itself. We don’t even publish this to npm, anyway.
It’s time for the implementation! First of all, create the
index.ts file under the
install folder and enter the following code:
This is the easiest and simplest boilerplate template. Of course, you can create a more sophisticated one, but this is beyond our topic for now. What it describes is:
- To import all necessary modules,
- To declare the
- To call the
All we need to do is just to put all logics into the
run() function. Let’s write the logic. Enter the following snippet into the
The purpose of this task is to install
netlify-cli in the pipeline so that the next task can run it. More specifically, this task runs the following command through this
As the version number is the only information we need to know for this task, we got the version value from
task.json, according to my previous post. This version number is passed through the function,
getInput('version', false). The first argument,
version is the input field name from
task.json. Both value MUST be the same as each other in both
task.json; otherwise, it will throw an error.
At the last line in the
try block, it invokes the function,
exec('npm', args), which installs the
netlify-cli npm package to the pipeline. The second argument value,
args is an array created a few lines above the
This task only uses
exec()functions, but there are many other functions worth having a look. This document is the official reference.
And finally, if there’s any error occurs, the entire process stops and throws the error back to the pipeline so that the pipeline itself can handle this error, by using the
This time, let’s implement the
deploy task. The overall process is the same as the
install task writing. First of all, create the
index.ts file under the
deploy folder and enter the boilerplate code in it. Then fill up the
run() function with the following:
This task will eventually run the command:
task.json receives many values from the UI and they will be used in this function. You can focus a few functions here in this task snippet –
getInput() for string input,
getPathInput() for path input, and
getBoolInput() for boolean input.
Testing on Local Machine
All implementations have completed! Now it’s time for testing the extension locally. I’m not covering unit test bits and pieces here in this post, but covering to confirm whether the extension works or not. Run the following command at the
src folder, as there is
.ts files have been compiled to
.js files. Check whether both
deploy.index.js exist. By the way, we need to pass the
version value to the
install task. But the
run() function doesn’t accept any parameter. How can we pass the value, then? As the
getInput() function passes the value, we need to prepare the value for it to identify. According to the official document, those values are set to the environment variables. Therefore run the following command, depending on your platform.
Once it’s set, run the following command to run the
Now we can confirm that the
install task has been run in your local machine. Likewise,
deploy/index.js can be run in the same way.
So far, we’ve implemented the Azure DevOps extension. Once everything is done, the folder structure might look like:
As we complete development, we need to publish this extension to Marketplace. Let’s discuss how to register a publisher for the extension in the next post.