调试 Azure DevOps 任务扩展 (TypeScript) 教程Launch.json问题问题编辑 1:launch.json用例

问题描述

我在 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


问题

在运行时,当需要 tl.getInputtrue 函数被执行时,调试立即停止,没有任何响应(无错误,无输出)。

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 中是调试该库的手册。 Debugging TypeScript Tasks in VS Code 的作者描述了一个示例 launch.json 配置,我针对我的用例对其进行了修改

{
      "name": "Launch tar.gz","type": "node","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") 将返回 ;

解决方法

有没有办法调试 azure devops 任务扩展?

是的,根据“Step 1”一文中的Add a custom pipelines task extension,在安装好所有需要的库和依赖并添加所有需要的任务实现文件后,可以compile and run the taskPowerShell 或其他外壳。默认情况下,任务以调试模式运行。请参阅我在下面分享的示例。

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

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

是否有最先进的技术或完整的指南来开发 azure devops 任务扩展?

目前,关于 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

enter image description here

从结果可以看出两个参数都可以正常传递。

,

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.WebApiimport * as azdev from "azure-devops-node-api" 连接到 Azure DevOps 服务器
  • 使用来自 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",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);
  }
}