Reputation: 2392
I develop all our task extensions in PowerShell
, now I start to translate my first extension into TypeScript
. The extension is a small task which should run in build or release pipelines. The task should get deployed to a Azure DevOps Server 2020.1 (on prem).
- Visual Studio Code
- Node (v14.15.4)
- TypeScript (Version 4.1.3)
- ts-node (v9.1.1)
- mocha (8.2.0)
- ts-mocha (8.0.0)
- azure-pipelines-task-lib (2.12.0)
{
"version": "0.2.0",
"configurations": [
{
"args": ["task/index.ts", "--Template", "Custom"],
"internalConsoleOptions": "openOnSessionStart",
"name": "Run TypeScript",
"request": "launch",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
"skipFiles": ["<node_internals>/**"],
"type": "pwa-node"
}
]
}
Start up command:
node.exe --nolazy -r ts-node/register/transpile-only task/index.ts --Template Custom
At runtime, when the tl.getInput
function with required true
get executed, the debugging stop immediately without any response (no error, no output).
App.ts
:
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Test: "Here",
Template: tl.getInput("Template", true),
}
}
Index.ts
(entry point):
import { App } from "./app";
function run() {
console.log("Hello");
console.log(App.Param.Test);
}
run();
Output (just nothing):
Index.ts
(modified):
import { App } from "./app";
function run() {
console.log("Hello");
// console.log(App.Param.Test);
}
run();
Output (modified):
Hello
obviously it stops because the required variable Template
get not passed to the application.
tl.getInput
?It is totally clear that running azure-pipelines-task-lib
without a Azure DevOps environment run into issues. But I was hoping that it is possible to mockup the required pipeline variables and run this library locally. If using azure-pipelines-task-lib
means that you have to deploy the extension and run it in a pipeline to test, it get kind of komplex to develop tasks with it, or?
I found the deprecated repository about vsts-task-lib. In azure-pipelines-tasks/docs/debugging.md
is manual to debug that library. The author of Debugging TypeScript Tasks in VS Code describe an example launch.json
configuration and I modify it for my usecase:
{
"name": "Launch tar.gz",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/dist/task/index.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}/task",
"preLaunchTask": "tsc: build - tsconfig.json",
"runtimeExecutable": null,
"runtimeArgs": ["--nolazy"],
"env": {
"NODE_ENV": "development",
"INPUT_Separator": ";",
"BUILD_SOURCESDIRECTORY": "C:\\agents\\latest\\_work\\21\\s"
},
"sourceMaps": true,
"outFiles": ["${workspaceRoot}/dist"]
}
I can confirm that it is possible to start up debugging and the tl.getInput("Separator")
will return ;
.
Upvotes: 3
Views: 3347
Reputation: 2392
With the help of Debugging TypeScript Tasks in VS Code I was able to do the following things:
tl.getInput
from import tl = require("azure-pipelines-task-lib/task")
tl.getVariable
from import tl = require("azure-pipelines-task-lib/task")
new azdev.WebApi
from import * as azdev from "azure-devops-node-api"
getBuildApi
from import * as ba from "azure-devops-node-api/BuildApi"
{
"name": "Run TypeScript",
"type": "pwa-node",
"request": "launch",
"internalConsoleOptions": "openOnSessionStart",
"stopOnEntry": false,
// path to your ts file
"args": ["index.ts"],
"cwd": "${workspaceRoot}/task",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
"env": {
"NODE_ENV": "development",
// param (enter your input params here!)
"INPUT_WebhookUrl": "MyVariables",
"INPUT_Template": "Empty",
"INPUT_Json": "{\"text\":\"I am a test message\",\"attachments\":[{\"text\":\"And here’s an attachment!\"}]}",
"INPUT_Separator": ";",
// env
"AGENT_JOBSTATUS": "Succeeded",
"AGENT_NAME": "MyAgent",
"BUILD_BUILDID": "5",
"BUILD_BUILDNUMBER": "20210108.1",
"BUILD_REASON": "Scheduled",
"BUILD_REPOSITORY_NAME": "MyRepo",
"BUILD_SOURCEBRANCHNAME": "master",
"BUILD_SOURCEVERSION": "122a24f",
"BUILDCONFIGURATION": "Debug",
"BUILDPLATFORM": "Any CPU",
"SYSTEM_ACCESSTOKEN": "",
"SYSTEM_DEFINITIONNAME": "MyDefinitionName",
"SYSTEM_TEAMFOUNDATIONSERVERURI": "https://myurl.de/mycollection/",
"SYSTEM_TEAMPROJECT": "PSItraffic",
// debug
"DEBUG_PAT": "my debug pat"
},
"skipFiles": ["<node_internals>/**"]
}
Read param & env: app.ts
import tl = require("azure-pipelines-task-lib/task");
export const App = {
// ------------------------------------------------------------ param
Param: {
WebhookUrl: tl.getDelimitedInput("WebhookUrl", "\n", true),
Template: tl.getInput("Template", true)
},
// ------------------------------------------------------------ env
Env: {
Agent: {
Jobstatus: getVariable("AGENT_JOBSTATUS"),
Name: getVariable("AGENT_NAME"),
},
...
System: {
AccessToken: getVariable("SYSTEM_ACCESSTOKEN"),
DefinitionName: getVariable("SYSTEM_DEFINITIONNAME"),
TeamFoundationServerUri: getVariable("SYSTEM_TEAMFOUNDATIONSERVERURI"),
TeamProject: getVariable("SYSTEM_TEAMPROJECT"),
},
// ------------------------------------------------------------ debug
Debug: {
Pat: getVariable("DEBUG_PAT"),
},
}
function getVariable(name: string): string {
// get variable
let v = tl.getVariable(name);
if (v === undefined) return "";
return v;
}
connect to azure devops server: rest.ts
import { App } from "./app";
import * as azdev from "azure-devops-node-api";
import * as ba from "azure-devops-node-api/BuildApi";
export class Rest {
static AuthHanlder: IRequestHandler = Rest.Auth();
static Connection: azdev.WebApi = new azdev.WebApi(App.Env.System.TeamFoundationServerUri, Rest.AuthHanlder);
static Auth(): IRequestHandler {
// auth
if (App.Env.System.AccessToken === "") return azdev.getPersonalAccessTokenHandler(App.Debug.Pat);
// no sure if this works on production
return azdev.getBearerHandler(App.Env.System.AccessToken);
}
}
Upvotes: 3
Reputation: 13479
is there a way to debug an azure devops task extension?
Yes, According to the Step 1 in the article "Add a custom pipelines task extension", after installing all the required libraries and dependencies and adding all the required task implementation files, you can compile and run the task with PowerShell
or other shells. By default, the task is run with debugging mode. See the example I share below.
is it possible to pass parameter and load them via tl.getInput?
Sure, you can pass the value of tl.getInput
as an parameter. See the example I share below.
is there a state of the art or a complete guideline how to develop azure devops task extension?
Currently, the Microsoft Docs about DevOps extensions is the best guide for us to develop DevOps extensions.
Follow your case, I also test on my side, below are the main source code I use:
{
"$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
"id": "dc7322d8-6c98-4be7-91c9-dcbf7f4df7dd",
"name": "buildAndReleaseTask",
"friendlyName": "Build and Release task",
"description": "Test create a Build and Release task.",
"helpMarkDown": "",
"category": "Utility",
"author": "Bright Ran",
"version": {
"Major": 0,
"Minor": 1,
"Patch": 0
},
"instanceNameFormat": "Echo $(UserName)",
"inputs": [
{
"name": "UserName",
"type": "string",
"label": "User name",
"defaultValue": "",
"required": true,
"helpMarkDown": "An user name"
}
],
"execution": {
"Node10": {
"target": "index.js"
}
}
}
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Here: "Here",
UserName: tl.getInput("UserName", true),
}
}
import { App } from "./App";
function run() {
console.log("Hello,", App.Param.UserName);
console.log("Look", App.Param.Here);
}
run();
tsc
$env:INPUT_USERNAME="xxxx"
node index.js
From the result, you can see the two parameters can be passed normally.
Upvotes: 1