调试 Azure DevOps 任务扩展(TypeScript)

3

我使用PowerShell编写所有任务扩展,现在我开始将我的第一个扩展翻译成TypeScript。该扩展是一个小任务,应在构建或发布管道中运行。该任务应部署到Azure DevOps Server 2020.1(本地)。


准备工作

教程

系统设置

- 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


问题

运行时,当执行带有必需参数truetl.getInput函数时,调试立即停止,没有任何响应(无错误,无输出)。

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 DevOps环境的情况下运行azure-pipelines-task-lib会遇到问题。但我希望能够模拟所需的管道变量并在本地运行此库。 如果使用azure-pipelines-task-lib意味着您必须部署扩展并在管道中运行它以进行测试,则使用它开发任务会变得复杂,对吗?


编辑1:

我找到了关于vsts-task-lib的弃用存储库。在azure-pipelines-tasks/docs/debugging.md中,有关于调试该库的手册。在VS Code中调试TypeScript任务的作者描述了一个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")会返回;
2个回答

1

有没有办法调试 Azure DevOps 任务扩展?

是的,根据文章 "Add a custom pipelines task extension" 中的 第一步,在安装所有必需的库和依赖项并添加所有必需的任务实现文件后,您可以使用 PowerShell 或其他 shell 编译和运行任务。默认情况下,任务以调试模式运行。请参见我分享的以下示例。

是否可以通过 tl.getInput 传递参数并加载它们?

当然可以,您可以将 tl.getInput 的值作为参数传递。请参见我分享的以下示例。

有没有一份最先进或完整的指南,告诉我们如何开发 Azure DevOps 任务扩展?

目前,Microsoft Docs 关于 DevOps 扩展是我们开发 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

enter image description here

从结果中可以看到,这两个参数可以正常传递。

1
借助 在 VS Code 中调试 TypeScript 任务 的帮助,我能够做到以下几点:
  • 使用 tl.getInputimport tl = require("azure-pipelines-task-lib/task") 读取输入参数
  • 使用 tl.getVariableimport tl = require("azure-pipelines-task-lib/task") 读取环境变量
  • 使用 new azdev.WebApiimport * as azdev from "azure-devops-node-api" 连接到 Azure DevOps Server
  • 使用 getBuildApiimport * as ba from "azure-devops-node-api/BuildApi" 发送构建 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);
  }
}

嗨@MarTin,很高兴你解决了这个问题,并感谢你分享你的经验。你能否将你的答案标记为此主题的解决方案?这对于正在寻找类似问题解决方案的人们可能非常有帮助。 - Bright Ran-MSFT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接