调试 Azure DevOps 任务扩展 (TypeScript)
Debugging a Azure DevOps Task Extension (TypeScript)
我在 PowerShell
开发了所有任务扩展,现在我开始将我的第一个扩展翻译成 TypeScript
。扩展是一个小任务,应该 运行 在构建或发布管道中。该任务应该部署到 Azure DevOps Server 2020.1(本地)。
准备
教程
- 我按照教程 create a custom pipelines task 并使用它构建了一个示例应用程序
- 我克隆了 ansible task extension 并检查了编程风格
系统设置
- 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)
Launch.json
{
"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"
}
]
}
启动命令:
node.exe --nolazy -r ts-node/register/transpile-only task/index.ts --Template Custom
问题
在运行时,当tl.getInput
函数执行到true
时,调试立即停止,没有任何反应(无错误,无输出)。
App.ts
:
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Test: "Here",
Template: tl.getInput("Template", true),
}
}
Index.ts
(入口点):
import { App } from "./app";
function run() {
console.log("Hello");
console.log(App.Param.Test);
}
run();
输出(什么都没有):
Index.ts
(修改):
import { App } from "./app";
function run() {
console.log("Hello");
// console.log(App.Param.Test);
}
run();
输出(修改):
Hello
显然它停止了,因为所需的变量 Template
没有传递给应用程序。
问题
- 是否有调试 azure devops 任务扩展的方法?
- 是否可以通过
tl.getInput
传递参数并加载它们?
- 是否有关于如何开发 azure devops 任务扩展的最新技术或完整指南?
很明显,运行宁 azure-pipelines-task-lib
没有 Azure DevOps 环境 运行 进入问题。但我希望可以在本地模拟所需的管道变量和 运行 这个库。 如果使用 azure-pipelines-task-lib
意味着你必须部署扩展和 运行它在管道中进行测试,用它来开发任务有点复杂,或者?
编辑 1:
我发现关于 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 的已弃用存储库描述了一个示例 launch.json
配置,我针对我的用例对其进行了修改:
{
"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"]
}
我可以确认可以启动调试,tl.getInput("Separator")
将 return ;
。
is there a way to debug an azure devops task extension?
是的,根据 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 和 PowerShell
或其他 shell。默认情况下,任务是 运行 调试模式。请参阅我在下面分享的示例。
is it possible to pass parameter and load them via tl.getInput?
当然,您可以将tl.getInput
的值作为参数传递。请参阅我在下面分享的示例。
is there a state of the art or a complete guideline how to develop azure devops task extension?
目前,关于DevOps扩展的Microsoft Docs是我们开发DevOps扩展的最佳指南。
按照你的情况,我也在我这边测试,下面是我使用的主要源码:
- task.json
{
"$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"
}
}
}
- App.ts(和你的差不多)
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Here: "Here",
UserName: tl.getInput("UserName", true),
}
}
- index.ts(和你的差不多)
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
从结果可以看出两个参数都可以正常传递
在 Debugging TypeScript Tasks in VS Code 的帮助下,我能够做以下事情:
- 使用
tl.getInput
从 import tl = require("azure-pipelines-task-lib/task")
读取输入参数
- 使用
tl.getVariable
从 import tl = require("azure-pipelines-task-lib/task")
读取环境变量
- 使用
new azdev.WebApi
从 import * as azdev from "azure-devops-node-api"
连接到 Azure DevOps 服务器
- 使用来自
import * as ba from "azure-devops-node-api/BuildApi"
的 getBuildApi
进行构建 api 请求
- 运行 并直接使用 TypeScript 调试应用程序而无需 JavaScript 翻译
launch.json
{
"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>/**"]
}
用例
读取参数和环境: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;
}
连接到 azure devops 服务器: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);
}
}
我在 PowerShell
开发了所有任务扩展,现在我开始将我的第一个扩展翻译成 TypeScript
。扩展是一个小任务,应该 运行 在构建或发布管道中。该任务应该部署到 Azure DevOps Server 2020.1(本地)。
准备
教程
- 我按照教程 create a custom pipelines task 并使用它构建了一个示例应用程序
- 我克隆了 ansible task extension 并检查了编程风格
系统设置
- 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)
Launch.json
{
"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"
}
]
}
启动命令:
node.exe --nolazy -r ts-node/register/transpile-only task/index.ts --Template Custom
问题
在运行时,当tl.getInput
函数执行到true
时,调试立即停止,没有任何反应(无错误,无输出)。
App.ts
:
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Test: "Here",
Template: tl.getInput("Template", true),
}
}
Index.ts
(入口点):
import { App } from "./app";
function run() {
console.log("Hello");
console.log(App.Param.Test);
}
run();
输出(什么都没有):
Index.ts
(修改):
import { App } from "./app";
function run() {
console.log("Hello");
// console.log(App.Param.Test);
}
run();
输出(修改):
Hello
显然它停止了,因为所需的变量 Template
没有传递给应用程序。
问题
- 是否有调试 azure devops 任务扩展的方法?
- 是否可以通过
tl.getInput
传递参数并加载它们? - 是否有关于如何开发 azure devops 任务扩展的最新技术或完整指南?
很明显,运行宁 azure-pipelines-task-lib
没有 Azure DevOps 环境 运行 进入问题。但我希望可以在本地模拟所需的管道变量和 运行 这个库。 如果使用 azure-pipelines-task-lib
意味着你必须部署扩展和 运行它在管道中进行测试,用它来开发任务有点复杂,或者?
编辑 1:
我发现关于 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 的已弃用存储库描述了一个示例 launch.json
配置,我针对我的用例对其进行了修改:
{
"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"]
}
我可以确认可以启动调试,tl.getInput("Separator")
将 return ;
。
is there a way to debug an azure devops task extension?
是的,根据 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 和 PowerShell
或其他 shell。默认情况下,任务是 运行 调试模式。请参阅我在下面分享的示例。
is it possible to pass parameter and load them via tl.getInput?
当然,您可以将tl.getInput
的值作为参数传递。请参阅我在下面分享的示例。
is there a state of the art or a complete guideline how to develop azure devops task extension?
目前,关于DevOps扩展的Microsoft Docs是我们开发DevOps扩展的最佳指南。
按照你的情况,我也在我这边测试,下面是我使用的主要源码:
- task.json
{
"$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"
}
}
}
- App.ts(和你的差不多)
import tl = require("azure-pipelines-task-lib/task");
export const App = {
Param: {
Here: "Here",
UserName: tl.getInput("UserName", true),
}
}
- index.ts(和你的差不多)
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
从结果可以看出两个参数都可以正常传递
在 Debugging TypeScript Tasks in VS Code 的帮助下,我能够做以下事情:
- 使用
tl.getInput
从import tl = require("azure-pipelines-task-lib/task")
读取输入参数
- 使用
tl.getVariable
从import tl = require("azure-pipelines-task-lib/task")
读取环境变量
- 使用
new azdev.WebApi
从import * as azdev from "azure-devops-node-api"
连接到 Azure DevOps 服务器
- 使用来自
import * as ba from "azure-devops-node-api/BuildApi"
的 - 运行 并直接使用 TypeScript 调试应用程序而无需 JavaScript 翻译
getBuildApi
进行构建 api 请求
launch.json
{
"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>/**"]
}
用例
读取参数和环境: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;
}
连接到 azure devops 服务器: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);
}
}