Automating builds with VSTS saves the teams countless hours of debugging when someone gives the code the “works on my machine” seal of approval. The tests may not run, there will be stale code between developers and it just causes problems.
The VSTS ecosystem provides a wide set of tools that you should be using. If you aren’t head over to Visual Studio Team Services and take a look.
It is easy to add a new CI build that runs unit tests, VSTS has several templates for most of the builds you want to run that include all the build tasks. Sometimes you have requirments for your build that just don’t fit perfectly into the pre-defined VSTS Build Tasks. The best place to start is the VSTS Marketplace where you will find different extensions and build tasks that you can use. You may get lucky and find just what you need but you may need to make something custom and proprietary or something that you will be sharing with the community.
Overview
After going through this article you should be able to do the following:
- Create your very own VSTS Build Task
- Publish your new VSTS Build Task to the marketplace and share it with VSTS instances
- Anyone can publish to the marketplace but to have public Tasks you need to be a verified VSTS Publisher
- Understanding of the VSTS Extension Ecosystem
Download the CLI
To get started you will need to download the CLI using node.js, if you don’t have version 4.0 or greater head over to https://nodejs.org/ and download the latest version.
Execute the following command from your favorite shell/command prompt npm install -g tfx-cli
You should now have the CLI successfully installed and you are ready to start developing your new VSTS Build Task.
Build Scaffolding
There is no quick scaffolding command that I found in the Visual Studio docs but there is some in-depth tutorials that you will find useful. (see useful links at the bottom).
In your root directory create the following structure and files
1
2
3
4
5
6
|- README.md
|- vss-extension.json
|- icon.png
|- buildtasks
|- task.json
|- powershell.ps1
README.md
This is really just for you, it is a README.md
file. You should document what your VSTS Build Task is going to do and it would be cool if you hosted it on GitHub to contribute to the community. Unless you need to keep it in house. Either way document what your task does, it is important.
vss-extension.json
The vss-extension.json
tell the tfx
how to build your extension. This file contains all of your metadata and configuration. Some important sections that we have to fill out:
Property | Description |
---|---|
id | This is the camalCaseName of the task |
version | The current version of the task |
name | The friendly name of the task |
description | The description of the task |
publisher | your publisher ID |
Go to the Visual Studio Market Place publisher to create your publisher profile https://marketplace.visualstudio.com/manage/publishers | |
targets | What type of extension are you building? In our case it should always be Microsoft.VisualStudio.Services for VSTS Build Task |
icons | The icon image you want to represent the build task |
contribution | contains meta information about the task |
files | All the files that compose the build task |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"manifestVersion": 1,
"id": "HelloBuildTask",
"version": "1.0.0",
"name": "Hello World",
"description": "A VSTS Build Task that prints Hello World to the Console",
"publisher": "hoefling-software",
"targets": [
{
"id": "Microsoft.VisualStudio.Services"
}
],
"icons": {
"default": "icon.png"
},
"contributions": [
{
"id": "HelloBuildTask",
"type": "ms.vss-distributed-task.task",
"targets": [
"ms.vss-distributed-task.tasks"
],
"properties": {
"name": "buildtask"
}
}
],
"files": [
{
"path": "buildtask"
}
]
}
icon.png
The icon image file that will be used to identify your build task in the marketplace and wherever it is used inside of VSTS
task.json
The Task metadata file which is located in your build task describes the individual task and what it executes. This includes parameters the user needs to enter and which scripts to execute.
The task.json
configures what the user is going to see when they select your build task in the UI. Here is an example of what a rendered Hello World build task will look like
Here is a simple task.json
file that executes a powershell script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{
"id": "5164728d-cfca-4576-a066-bde89930bf2b",
"name": "HelloWorld",
"friendlyName": "Hello World",
"description": "Prints Hello World to the console",
"helpMarkDown": "",
"category": "Build",
"visibility": [
"Build"
],
"runsOn": [
"Agent",
"DeploymentGroup"
],
"author": "Skye Hoefling",
"version": {
"Major": 0,
"Minor": 0,
"Patch": 16
},
"instanceNameFormat": "Prints Hello World",
"groups": [
{
"name": "advanced",
"displayName": "Advanced",
"isExpanded": false
}
],
"inputs": [
{
"name": "name",
"type": "string",
"label": "Name",
"defaultValue": "",
"required": true,
"helpMarkDown": "Enter your name to print to the console"
}
],
"execution": {
"PowerShell3": {
"target": "powershell.ps1",
"platforms": [
"windows"
],
"workingDirectory": "$(currentDirectory)"
}
}
}
Property | Description |
---|---|
id | A GUID to uniquely identify the build task |
name | The name of the build task |
friendlyName | The friendly name of the build task |
description | The description of the build task |
author | The author of the build task |
version | The version to display in format (x.x.x) |
inputs | The inputs the user will be able to enter data |
execution | The scripts the build task will execute |
Buildtasks Powershell Script
If you examine the end of the task.json
you will notice at the bottom of the file there is an execution
property. This property defines what scripts will execute.
task.json
snippet
1
2
3
4
5
6
7
8
9
"execution": {
"PowerShell3": {
"target": "powershell.ps1",
"platforms": [
"windows"
],
"workingDirectory": "$(currentDirectory)"
}
}
This part of the metadata file tells the build task to execute the powershell.ps1
script in the working directory of build agents current execution.
Powershell Script and Modules
Let’s try creating our Hello World script, all we care about right now is printing “Hello World” to the console. See the powershell script below.
1
2
3
4
5
[CmdletBinding()]
param()
Write-Host "Hello World"
If we take everything we have done and deployed it out to our build enviornment we will get “Hello World” printing to the console. If you would like to attemp this skip ahead to the Publishing and Deployment section below, but be sure to come back after you test it.
…
Now that you have tried a publish and deployment you can’t really do anything with this unless you can get the parameters from the metadata file task.json
. That is where the VstsTaskSdk
Powershell Module comes in. It is a useful module that allows us easy access to the task inputs. When we load the module in you may run into a few issues that we are going to walk through.
Install VstsTaskSdk Powershell Module
- open up powershell
- navigate to the
root/buildtask
of directory of your extension - execute
mkdir ps_modules
and then navigate into the new directory - your pwd should read
root/buildtask/ps_modules
- execute
Save-Module -Name VstsTaskSdk -Path .
which will save the module to disk. - Flatten the directory structure by removing the version number. For example you will have a path of
root/buildtask/ps_modules/VstsTaskSdk/0.10.0/*
which should now readroot/buildtask/ps_modules/VstsTaskSdk/*
When you are installing Powershell Modules if you don’t remove the version folder and flatten it you will run into issues on the build machine when it tries to find the reference.
Now we have access to the APIs to retrieve information from the task.json
and we can make a more powerful powershell script.
1
2
3
4
5
6
7
8
9
10
11
12
[CmdletBinding()]
param()
Trace-VstsEnteringInvocation $MyInvocation
try {
# Get inputs.
$inputName = Get-VstsInput -Name 'name' -Require
Write-Host "Hello $inputName"
} finally {
Trace-VstsLeavingInvocation $MyInvocation
}
This script retrieves the name
property we defined as an input and it will print out “Hello Skye” to the console if you enter the name “Skye” for the name.
Publishing and Deployment
The code is completed and it is now time to publish our extension to the marketplace and deploy it into our next VSTS Build. This is where everything starts coming together and is how you can test your task.
The first thing you have to do is create your publishing profile so you can upload your Visual Studio Extension .vsix
into the marketplace and add it to your VSTS instance. Open a browser and navigate to the marketplace to create your publishing profile
https://marketplace.visualstudio.com/manage/publishers
Once you have created a publishing profile you will need to edit the vss-extension.json
file to use the publisher ID you just created. My publisher id is hoefling-software
so we are going to use that.
Before generating the extenstion file I make sure my version numbers are in sync with my task.json
file and vss-extension.json
. Once you confirm this you can proceed with generating your extension file.
Navigate to the root directory and execute the following command with tfx-cli
1
tfx extension create --manifest-globs vss-extension.json
This will create a .vsix
file which is a Visual Studio Extension file. The name will be something like hoefling-software.HelloBuildTask-1.0.0.vsix
where the name conforms to the standard defined below
Name | Property |
---|---|
hoefling-software | Publisher |
HelloBuildTask | id |
1.0.0 | version |
Now that we done all the hard work we can upload our extension to the marketplace. Navigate to the marketplace
- Click the
Upload new extension
button in green at the top right of the screen - You will see a “upload new item” modal appear where you can drag and drop or browse to your
.vsix
file. Upload the file that we just generated from thetfx-cli
- Click
Upload
You will now see your new extension appearing in the marketplace for your publisher. Until you become a verified publisher you will not be able to see your extension on the public marketplace. We talk about becoming a verified publisher below.
At this point we can share our extension with anyone that wants it. When you hover over your extension you will see the ...
option appear. If you click that and select the share option you can specify the VSTS instance you would like to share your extension with.
Once your extension is shared you can log into that VSTS instance and add the extension via extension page. You will need to be at the root level of VSTS to access extensions for the account. Once you get to the extensions screen you will see a screen like this:
Select the shared extension and install it. Your extension is successfully installed
Testing the Build Task
If you have gotten this far you have now created, published and deployed your custom Build Task and you want to see it in action.
- Open up VSTS and head over to any project and navigate to the Build and Release section
- Create new build, select empty process
- Click add task and search for hello world
- You will see the custom build task, now add it to the build
- Modify the parameter by plugging in your name
- save and queue the new build
When you complete the task your output should contain your build task:
You have now successfully created your first VSTS Build Task and got it working in your VSTS instance.
Verified Publishers and the Public Marketplace
If you want to add your new Build Task to the public marketplace you will need to send an email to microsoft and request permission. They have their own set of rules of who can and can not become a verified publisher.
More information on this topic can be found at the marketplace publisher portal.