添加自定义管道任务扩展

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

本文介绍如何在 Azure DevOps 中为组织的自定义生成或发布任务安装扩展。 有关详细信息,请参阅什么是 Azure Pipelines?

注意

本文介绍基于代理的扩展中的代理任务。 有关服务器任务和基于服务器的扩展的详细信息,请参阅 服务器任务创作

先决条件

  • Azure DevOps 中的组织。 创建一个组织

  • 文本编辑器。 对于许多教程,我们使用 Visual Studio Code,它提供 Intellisense 和调试支持。

  • 最新版本的 Node.js

  • npmjs.com 4.0.2 或更高版本。 安装此 TypeScript 编译器的 最新版本

  • 用于打包扩展的 Azure DevOps 跨平台 CLI(tfx-cli)

    • 可以使用 npm(Node.js 的组件)通过运行 npm i -g tfx-cli 安装 tfx-cli。
  • Azure DevOps 扩展 SDK。 安装 azure-devops-extension-sdk 包。

  • 项目的 home 目录。 完成 home 本文中的步骤后,生成或发布任务扩展的目录应具有以下结构:

    |--- README.md    
    |--- images                        
        |--- extension-icon.png  
    |--- buildandreleasetask            // where your task scripts are placed
    |--- vss-extension.json             // extension's manifest
    

重要

开发计算机必须运行 最新版本的 Node ,以确保编写的代码与代理上的生产环境和最新的非预览版 azure-pipelines-task-lib兼容。 请根据以下命令更新您的task.json文件。

"execution": {
   "Node20_1": {
     "target": "index.js"
   }
 }

1.创建自定义任务

buildandreleasetask目录内的home文件夹中执行此过程的每个部分。

注意

此示例演练将 Windows 与 PowerShell 配合使用。 这些步骤适用于所有平台,但用于获取环境变量的语法不同。 如果使用 Mac 或 Linux,请将任何实例 $env:<var>=<val> 替换为 export <var>=<val>

创建任务基架

  1. 为任务创建文件夹结构,并安装所需的库和依赖项。

  2. 打开 PowerShell 命令窗口,转到文件夹 buildandreleasetask ,然后运行以下命令。

    npm init --yes
    

    npm init 创建 package.json 文件。 添加了参数 --yes 以接受所有默认 npm init 选项。

    提示

    代理不会自动安装所需的模块,因为任务文件夹需要包含节点模块。 若要缓解此问题,请将 node_modules 复制到 buildandreleasetask。 随着任务变大,很容易超过 VSIX 文件的大小限制(50 MB)。 在复制节点文件夹之前,可能需要运行 npm install --productionnpm prune --production,也可以编写脚本来生成和打包所有内容。

  3. azure-pipelines-task-lib 添加到您的库中。

    npm install azure-pipelines-task-lib --save
    
  4. 确保为外部依赖项安装了 TypeScript typing。

    npm install @types/node --save-dev
    npm install @types/q --save-dev
    
  5. 创建文件 .gitignore 并向其添加node_modules。 你的构建过程应该执行 npm installtypings install,以便每次构建 node_modules 并且不需要签入。

    echo node_modules > .gitignore
    
  6. Mocha 安装为开发依赖项。

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  7. 选择 TypeScript 版本 2.3.4 或 4.6.3。

    npm install typescript@4.6.3 -g --save-dev
    

    注意

    请确保在开发环境中通过全局方式使用 npm 安装 TypeScript,以保证 tsc 命令可用。 如果跳过此步骤,则默认情况下使用 TypeScript 版本 2.3.4,并且仍需全局安装包,才能让 tsc 命令可用。

  8. 创建 tsconfig.json 编译器选项。 此文件可确保 TypeScript 文件编译为 JavaScript 文件。

    tsc --init --target es2022
    

创建任务

现在基架已完成,可以创建自定义任务。

  1. task.json文件夹中创建buildandreleasetask文件。 该 task.json 文件描述了构建/发布任务,并且构建/发布系统使用它来向用户显示配置选项,以及在构建/发布时确定要运行哪些脚本。

  2. 复制以下代码,并用您的任务信息替换 {{placeholders}}。 最重要的占位符是 taskguid,它必须是唯一的。

    {
     "$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
     "id": "{{taskguid}}",
     "name": "{{taskname}}",
     "friendlyName": "{{taskfriendlyname}}",
     "description": "{{taskdescription}}",
     "helpMarkDown": "",
     "category": "Utility",
     "author": "{{taskauthor}}",
     "version": {
         "Major": 0,
         "Minor": 1,
         "Patch": 0
     },
     "instanceNameFormat": "Echo $(samplestring)",
     "inputs": [
         {
             "name": "samplestring",
             "type": "string",
             "label": "Sample String",
             "defaultValue": "",
             "required": true,
             "helpMarkDown": "A sample string"
         }
     ],
     "execution": {
         "Node20_1": {
             "target": "index.js"
         }
     }
     }
    
  3. 使用以下代码作为参考来创建index.ts文件。 此代码在调用任务时运行。

    import tl = require('azure-pipelines-task-lib/task');
    
     async function run() {
         try {
             const inputString: string | undefined = tl.getInput('samplestring', true);
             if (inputString == 'bad') {
                 tl.setResult(tl.TaskResult.Failed, 'Bad input was given');
                 return;
             }
             console.log('Hello', inputString);
         }
         catch (err:any) {
             tl.setResult(tl.TaskResult.Failed, err.message);
         }
     }
    
     run();
    
  4. 若要从 index.js编译 index.ts 文件,请从 tsc 文件夹中输入 buildandreleasetask

task.json 组件

请查看关于文件 task.json 的一些组件的以下说明。

属性 说明
id 任务的唯一 GUID。
name 不带空格的名称。
friendlyName 描述性名称(允许空格)。
description 您的任务执行内容的详细说明。
author 描述开发生成或发布任务的实体的简短字符串,例如: Microsoft Corporation
instanceNameFormat 任务在生成/发布步骤列表中显示的方式。 可以使用 $(variablename)来使用变量值
groups 描述 UI 中任务属性的逻辑分组。
inputs 生成或发布任务运行时要使用的输入。 此任务需要具有名称 samplestring 的输入。
execution 此任务有多个执行选项,包括脚本,例如 NodePowerShellPowerShell3Process
restrictions 对任务可以调用的 GitHub Codespaces 命令 以及任务可设置的变量应用的限制。 建议为新任务指定限制模式。

注意

在 PowerShell 中使用以下命令创建一个 id

(New-Guid).Guid

有关详细信息,请参阅 生成/发布任务参考

运行任务

从 PowerShell 中使用 node index.js 来运行任务。

在以下示例中,任务失败,因为未提供输入(samplestring 是所需的输入)。

 node index.js
 ##vso[task.debug]agent.workFolder=undefined
 ##vso[task.debug]loading inputs and endpoints
 ##vso[task.debug]loaded 0
 ##vso[task.debug]task result: Failed
 ##vso[task.issue type=error;]Input required: samplestring
 ##vso[task.complete result=Failed;]Input required: samplestring

作为解决方案,请设置 samplestring 输入并再次运行任务。

$env:INPUT_SAMPLESTRING="Human"
node index.js
##vso[task.debug]agent.workFolder=undefined
##vso[task.debug]loading inputs and endpoints
##vso[task.debug]loading INPUT_SAMPLESTRING
##vso[task.debug]loaded 1
##vso[task.debug]Agent.ProxyUrl=undefined
##vso[task.debug]Agent.CAInfo=undefined
##vso[task.debug]Agent.ClientCert=undefined
##vso[task.debug]Agent.SkipCertValidation=undefined
##vso[task.debug]samplestring=Human
Hello Human

这次任务成功了,因为提供了 samplestring,并且它正确输出了 Hello Human!

提示

有关各种任务运行程序以及如何在 task.json 中包括最新节点版本的信息,请参阅面向 Azure Pipelines 任务作者的 Node 运行器更新指南

2.对任务脚本进行单元测试

执行单元测试以快速测试任务脚本,而不是它调用的外部工具。 测试成功路径和失败路径的各个方面。

  1. 安装测试工具。 在此过程中,我们使用 Mocha 作为测试驱动程序。

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  2. 创建一个tests文件夹,其中包含一个_suite.ts文件,文件内容如下:

    import * as path from 'path';
    import * as assert from 'assert';
    import * as ttm from 'azure-pipelines-task-lib/mock-test';
    
    describe('Sample task tests', function () {
    
        before( function() {
    
        });
    
        after(() => {
    
        });
    
        it('should succeed with simple inputs', function(done: Mocha.Done) {
            // Add success test here
        });
    
        it('it should fail if tool returns 1', function(done: Mocha.Done) {
            // Add failure test here
        });    
    });
    

    提示

    测试文件夹应位于 buildandreleasetask 该文件夹中。 如果收到同步请求错误,可以通过使用命令buildandreleasetask将同步请求添加到npm i --save-dev sync-request文件夹来解决此问题。

  3. success.ts在测试目录中创建包含以下内容的文件。 此文件创建模拟运行任务并模拟对外部方法的所有调用。

    import ma = require('azure-pipelines-task-lib/mock-answer');
    import tmrm = require('azure-pipelines-task-lib/mock-run');
    import path = require('path');
    
    let taskPath = path.join(__dirname, '..', 'index.js');
    let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
    
    tmr.setInput('samplestring', 'human');
    
    tmr.run();
    

    成功测试验证了在适当的输入下,测试是否成功且没有错误或警告并返回正确的输出。

  4. 若要运行任务模拟运行程序,请将以下示例成功测试添加到 _suite.ts 文件。

         it('should succeed with simple inputs', function(done: Mocha.Done) {
         this.timeout(1000);
    
         let tp: string = path.join(__dirname, 'success.js');
         let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
    
         // tr.run(); //current, old function.
         tr.runAsync().then(() => {
             console.log(tr.succeeded);
             assert.equal(tr.succeeded, true, 'should have succeeded');
             assert.equal(tr.warningIssues.length, 0, "should have no warnings");
             assert.equal(tr.errorIssues.length, 0, "should have no errors");
             console.log(tr.stdout);
             assert.equal(tr.stdout.indexOf('Hello human') >= 0, true, "should display Hello human");
             done();
         }).catch((error) => {
             done(error); // Ensure the test case fails if there's an error
         });
     });
    
  5. 在测试目录中创建一个 failure.ts 文件作为任务模拟运行程序,其中包含以下内容:

    import ma = require('azure-pipelines-task-lib/mock-answer');
    import tmrm = require('azure-pipelines-task-lib/mock-run');
    import path = require('path');
    
    let taskPath = path.join(__dirname, '..', 'index.js');
    let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
    
    tmr.setInput('samplestring', 'bad');
    
    tmr.run();
    

    失败测试验证当工具输入不正确或不完整时,它会以预期方式失败,并得到有用的输出。

  6. 若要运行任务模拟运行程序,请将以下代码添加到 _suite.ts 文件。

    it('should fail if tool returns 1', function(done: Mocha.Done) {
        this.timeout(1000);
    
        const tp = path.join(__dirname, 'failure.js');
        const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
    
        tr.runAsync().then(() => {
            console.log(tr.succeeded);
            assert.equal(tr.succeeded, false, 'should have failed');
            assert.equal(tr.warningIssues.length, 0, 'should have no warnings');
            assert.equal(tr.errorIssues.length, 1, 'should have 1 error issue');
            assert.equal(tr.errorIssues[0], 'Bad input was given', 'error issue output');
            assert.equal(tr.stdout.indexOf('Hello bad'), -1, 'Should not display Hello bad');
            done();
        });
    });
    
  7. 运行测试。

    tsc
    mocha tests/_suite.js
    

    这两个测试都应通过。 如果要使用更详细的输出(在生成控制台中看到的内容)运行测试,请设置环境变量: TASK_TEST_TRACE=1

    $env:TASK_TEST_TRACE=1
    

3. 创建扩展清单文件

扩展清单包含有关扩展的所有信息。 它包括文件的链接,其中包括任务文件夹和图像文件夹。 确保已创建一个包含extension-icon.png的图片文件夹。 以下示例是包含生成或发布任务的扩展清单。

将以下 .json 代码复制并保存为 vss-extension.json 目录中的 home 文件。

请勿在 buildandreleasetask 文件夹中创建此文件。

{
    "manifestVersion": 1,
    "id": "build-release-task",
    "name": "Fabrikam Build and Release Tools",
    "version": "0.0.1",
    "publisher": "fabrikam",
    "targets": [
        {
            "id": "Microsoft.VisualStudio.Services"
        }
    ],    
    "description": "Tools for building/releasing with Fabrikam. Includes one build/release task.",
    "categories": [
        "Azure Pipelines"
    ],
    "icons": {
        "default": "images/extension-icon.png"        
    },
    "files": [
        {
            "path": "buildandreleasetask"
        }
    ],
    "contributions": [
        {
            "id": "custom-build-release-task",
            "type": "ms.vss-distributed-task.task",
            "targets": [
                "ms.vss-distributed-task.tasks"
            ],
            "properties": {
                "name": "buildandreleasetask"
            }
        }
    ]
}

注意

请将发布者更改为你的发布者名称。 有关详细信息,请参阅 “创建发布者”。

贡献

属性 说明
id 贡献的标识符。 在扩展中必须唯一。 不需要与生成或发布任务的名称匹配。 通常,生成或发布任务名称位于贡献项的 ID 中。
type 贡献的类型。 应为 ms.vss-distributed-task.task
targets 该贡献所针对的贡献。 应为 ms.vss-distributed-task.tasks
properties.name 任务的名称。 此名称必须与相应的自包含生成或发布管道任务的文件夹名称匹配。

文件

属性 说明
path 相对于 home 目录的文件或文件夹的路径。

有关扩展清单文件(例如其属性及其用途)的详细信息,请参阅 扩展清单参考

4.打包扩展

将所有文件打包在一起,将扩展引入 Visual Studio 市场。 所有扩展都打包为 VSIX 2.0 兼容的 .vsix 文件。 Microsoft提供了用于打包扩展的跨平台命令行接口(CLI)。

获取 tfx-cli 后,转到扩展的主目录,并运行以下命令:

tfx extension create --manifest-globs vss-extension.json

注意

每次更新时,都必须递增扩展或集成版本。 更新现有扩展时,请更新清单中的版本或传递 --rev-version 命令行开关。 这会递增你的扩展的补丁版本号,并将新版本保存到清单中。 必须同时更新任务版本和扩展版本才能进行更新。 tfx extension create --manifest-globs vss-extension.json --rev-version 仅更新扩展版本,而不是任务版本。 有关详细信息,请参阅 GitHub 中的生成任务。

打包的扩展名位于 .vsix 文件中后,即可将扩展发布到市场。

5.发布扩展

若要发布扩展,请先 创建发布者,然后 上传扩展,最后 共享该扩展。

创建发布者

所有扩展(包括 Microsoft 的扩展)都标识为由发布者提供。 如果你还不是现有发布方的成员,你应该创建一个新的发布方。

  1. 登录到 Visual Studio Marketplace 发布门户

  2. 如果还没有现有发布者的成员,系统会提示你创建发布者。 如果未提示创建发布者,请向下滚动到页面底部,然后在“相关网站”下选择“发布扩展”。

    • 为发布者指定标识符,例如: mycompany-myteam
      • 此标识符用作扩展清单文件中 publisher 属性的值。
    • 为发布者指定显示名称,例如: My Team
  3. 查看市场发布者协议,然后选择“创建”。

你的发布者已定义。 在将来的版本中,可以授予查看和管理发布者扩展的权限。 在通用发布者下发布扩展更容易且更安全,无需跨用户共享一组凭据。

上传扩展

找到“上传新扩展”按钮,转到打包的 .vsix 文件,然后选择“上传”。

可以通过命令行接口(CLI)使用 tfx extension publish 命令,而不是使用 tfx extension create,一步完成扩展的打包和发布。 可以选择使用 --share-with 在扩展发布之后与一个或多个帐户共享你的扩展。

tfx extension publish --manifest-globs your-manifest.json --share-with yourOrganization

分享扩展

上传扩展后,扩展将位于市场中,但没有人可以看到它。 与组织共享它,以便你可以安装和测试它。

右键单击扩展并选择“共享,然后输入组织信息。 你也可以与想要访问你扩展的其他帐户共享它。

重要

必须验证发布者才能公开共享扩展。 有关详细信息,请参阅 包/发布/安装

在市场中共享扩展后,任何想要使用它的人都必须安装它。

6. 创建生成和发布管道以将扩展发布到市场

若要在市场中维护自定义任务,请在 Azure DevOps 上创建生成和发布管道。

发布先决条件

  • Azure DevOps 项目。 创建项目

  • Azure DevOps 扩展任务插件。 在组织中免费安装它

  • 管道库变量组。 创建管道库变量组以保存管道使用的变量。 有关详细信息,请参阅 “添加和使用变量组”。 可以从 Azure DevOps 库选项卡或通过 CLI 生成变量组。 在管道中使用此组中的变量 。 此外,在变量组中声明以下变量:

    • publisherId:市场发布者的 ID
    • extensionId:扩展的 ID,如 vss-extension.json 文件中声明的那样
    • extensionName:扩展的名称,如 vss-extension.json 文件中声明的那样
    • artifactName:为 VSIX 文件创建的项目的名称
  • 服务连接。 创建新的市场服务连接并为所有管道授予访问权限。

  • YAML 管道。 使用以下示例通过 YAML 创建新管道。 有关详细信息,请参阅 创建第一个管道YAML 架构

        trigger: 
        - main
        pool:
          vmImage: "ubuntu-latest"
        variables:
          - group: variable-group # Rename to whatever you named your variable group in the prerequisite stage of step 6
        stages:
          - stage: Run_and_publish_unit_tests
            jobs:
              - job:
                steps:
                  - task: TfxInstaller@4
                    inputs:
                      version: "v0.x"
                  - task: Npm@1
                    inputs:
                      command: 'install'
                      workingDir: '/TaskDirectory' # Update to the name of the directory of your task
                  - task: Bash@3
                    displayName: Compile Javascript
                    inputs:
                      targetType: "inline"
                      script: |
                        cd TaskDirectory # Update to the name of the directory of your task
                        tsc
                  - task: Npm@1
                    inputs:
                      command: 'custom'
                      workingDir: '/TestsDirectory' # Update to the name of the directory of your task's tests
                      customCommand: 'testScript' # See the definition in the explanation section below - it may be called test
                  - task: PublishTestResults@2
                    inputs:
                      testResultsFormat: 'JUnit'
                      testResultsFiles: '**/ResultsFile.xml'
          - stage: Package_extension_and_publish_build_artifacts
            jobs:
              - job:
                steps:
                  - task: TfxInstaller@4
                    inputs:
                      version: "0.x"
                  - task: Npm@1
                    inputs:
                      command: 'install'
                      workingDir: '/TaskDirectory' # Update to the name of the directory of your task
                  - task: Bash@3
                    displayName: Compile Javascript
                    inputs:
                      targetType: "inline"
                      script: |
                        cd TaskDirectory # Update to the name of the directory of your task
                        tsc
                  - task: QueryAzureDevOpsExtensionVersion@4
                    name: QueryVersion
                    inputs:
                      connectTo: 'VsTeam'
                      connectedServiceName: 'ServiceConnection' # Change to whatever you named the service connection
                      publisherId: '$(PublisherID)'
                      extensionId: '$(ExtensionID)'
                      versionAction: 'Patch'
                  - task: PackageAzureDevOpsExtension@4
                    inputs:
                      rootFolder: '$(System.DefaultWorkingDirectory)'
                      publisherId: '$(PublisherID)'
                      extensionId: '$(ExtensionID)'
                      extensionName: '$(ExtensionName)'
                      extensionVersion: '$(QueryVersion.Extension.Version)'
                      updateTasksVersion: true
                      updateTasksVersionType: 'patch'
                      extensionVisibility: 'private' # Change to public if you're publishing to the marketplace
                      extensionPricing: 'free'
                  - task: CopyFiles@2
                    displayName: "Copy Files to: $(Build.ArtifactStagingDirectory)"
                    inputs:
                      Contents: "**/*.vsix"
                      TargetFolder: "$(Build.ArtifactStagingDirectory)"
                  - task: PublishBuildArtifacts@1
                    inputs:
                      PathtoPublish: '$(Build.ArtifactStagingDirectory)'
                      ArtifactName: '$(ArtifactName)'
                      publishLocation: 'Container'
          - stage: Download_build_artifacts_and_publish_the_extension
            jobs:
              - job:
                steps:
                  - task: TfxInstaller@4
                    inputs:
                      version: "v0.x"
                  - task: DownloadBuildArtifacts@0
                    inputs:
                      buildType: "current"
                      downloadType: "single"
                      artifactName: "$(ArtifactName)"
                      downloadPath: "$(System.DefaultWorkingDirectory)"
                  - task: PublishAzureDevOpsExtension@4
                    inputs:
                      connectTo: 'VsTeam'
                      connectedServiceName: 'ServiceConnection' # Change to whatever you named the service connection
                      fileType: 'vsix'
                      vsixFile: '$(PublisherID).$(ExtensionName)/$(PublisherID)..vsix'
                      publisherId: '$(PublisherID)'
                      extensionId: '$(ExtensionID)'
                      extensionName: '$(ExtensionName)'
                      updateTasksVersion: false
                      extensionVisibility: 'private' # Change to public if you're publishing to the marketplace
                      extensionPricing: 'free'
    

有关详细信息,请参阅 指定会触发管道的事件

注意

每个作业都使用新的用户代理,并要求安装依赖项。

管道阶段

以下部分可帮助你了解管道阶段的工作原理。

阶段 1:运行和发布单元测试

此阶段运行单元测试并将测试结果发布到 Azure DevOps。

若要运行单元测试,请将自定义脚本添加到 package.json 文件,如以下示例所示。

"scripts": {
    "testScript": "mocha ./TestFile --reporter xunit --reporter-option output=ResultsFile.xml"
},
  1. 添加将 Node CLI 用于 Azure DevOps (tfx-cli) 以将 tfx-cli 安装到生成代理上。

  2. 使用npm命令添加install任务,并将目标指向包含package.json的文件夹。

  3. 添加 Bash 任务以将 TypeScript 编译到 JavaScript 中。

  4. 使用npmcustom命令添加任务,将包含单元测试的文件夹作为目标,并将输入testScript作为命令。 使用以下输入:

    • 命令: custom
    • 包含 package.json的工作文件夹: /TestsDirectory
    • 命令和参数: testScript
  5. 添加 “发布测试结果 ”任务。 如果使用的是 Mocha XUnit 报告器,请确保结果格式是 JUnit 而不是 XUnit。 将搜索文件夹设置为根目录。 使用以下输入:

    • 测试结果格式: JUnit
    • 测试结果文件: **/ResultsFile.xml
    • 搜索文件夹: $(System.DefaultWorkingDirectory)

    发布测试结果后,“测试”选项卡下的输出应如以下示例所示。

    测试结果示例的屏幕截图。

阶段 2:打包扩展并发布生成项目

  1. 添加将 Node CLI 用于 Azure DevOps (tfx-cli) 以将 tfx-cli 安装到生成代理上。

  2. 使用npm命令添加install任务,并将目标指向包含package.json的文件夹。

  3. 添加 Bash 任务以将 TypeScript 编译到 JavaScript 中。

  4. 若要查询现有版本,请使用以下输入添加 查询扩展版本 任务:

    • 连接到:Visual Studio Marketplace
    • Visual Studio Marketplace(服务连接):服务连接
    • 发布者 ID:Visual Studio Marketplace 发布者的 ID
    • 扩展 ID:vss-extension.json 文件中扩展的 ID
    • 版本升级:补丁
    • 输出变量: Task.Extension.Version
  5. 若要基于清单 Json 打包扩展,请使用以下输入添加 包扩展 任务:

    • 根清单文件夹:指向包含清单文件的根目录。 例如,$(System.DefaultWorkingDirectory) 是根目录。
    • 清单文件:vss-extension.json
    • 发布者 ID:Visual Studio Marketplace 发布者的 ID
    • 扩展 ID:vss-extension.json 文件中扩展的 ID
    • 扩展名:vss-extension.json 文件中扩展的名称
    • 扩展版本:$(Task.Extension.Version)
    • 替代任务版本:已选中 (true)
    • 替代类型:仅替换补丁 (1.0.r)
    • 扩展可见性:如果扩展仍在开发中,请将该值设置为 私有。 若要将扩展发布到公共,请将值设置为 公共
  6. 若要复制到已发布的文件,请使用以下输入添加 “复制文件 ”任务:

    • 内容:所有要复制以便发布为工件的文件。
    • 目标文件夹:文件复制到的文件夹
      • 例如:$(Build.ArtifactStagingDirectory)
  7. 添加发布生成项目以发布要在其他作业或管道中使用的项目。 使用以下输入:

    • 发布路径:包含要发布的文件的文件夹的路径
      • 例如:$(Build.ArtifactStagingDirectory)
    • 文物名称:分配给该文物的名称
    • 工件发布路径:选择 Azure Pipelines 以便在后续作业中使用该工件。

阶段 3:下载生成产物并发布扩展程序

  1. 要将 tfx-cli 安装到生成代理上,请添加将 Node CLI 用于 Azure DevOps (tfx-cli)

  2. 若要将项目下载到新作业,请使用以下输入添加 “下载生成项目 ”任务:

    • 下载以下作业生成的工件:如果要在新作业中从同一管道下载工件,请选择当前生成。 如果要在新管道上下载,请选择特定生成
    • 下载类型:选择 特定项目 以下载已发布的所有文件。
    • 项目名称:已发布的项目名称
    • 目标目录:应下载文件的文件夹
  3. 若要获取 发布扩展 任务,请使用以下输入:

    • 连接到:Visual Studio Marketplace
    • Visual Studio Marketplace 连接:ServiceConnection
    • 输入文件类型:VSIX 文件
    • VSIX 文件: /Publisher.*.vsix
    • 发布者 ID:Visual Studio Marketplace 发布者的 ID
    • 扩展 ID:vss-extension.json 文件中扩展的 ID
    • 扩展名:vss-extension.json 文件中扩展的名称
    • 扩展可见性:私有或公共

可选:安装和测试扩展

只需执行几个步骤即可安装与你共享的扩展:

  1. 从组织控制面板(https://dev.azure.com/{organization}/_admin)转到项目集合管理页面。
  2. 在“ 扩展 ”选项卡中,在“ 与我共享的扩展 ”组中找到扩展,然后选择扩展链接。
  3. 安装扩展。

如果看不到“ 扩展 ”选项卡,请确保位于控制面板(项目集合级别的管理页), https://dev.azure.com/{organization}/_admin而不是项目的管理页。

如果未看到“ 扩展 ”选项卡,则不会为组织启用扩展。 可以通过加入 Visual Studio 合作伙伴计划来提前访问扩展功能。

若要打包 Azure DevOps Extensions 并将其发布到 Visual Studio Marketplace,可以下载 Azure DevOps 扩展任务

常见问题

有关在 Azure DevOps 的扩展中添加自定义生成或发布任务,请参阅以下常见问题。

问:如何限制在任务中使用 Azure Pipelines 命令的权限?

可以限制按任务设置的 Azure Pipelines 命令使用情况和变量。 此操作可能有助于防止任务执行过程中自定义脚本对变量或 vso 命令的无限制访问。 建议你为新任务进行设置 若要应用,可能需要将以下语句添加到 task.json 文件:

"restrictions": {
  "commands": {
    "mode": "restricted"
  },
  "settableVariables": {
    "allowed": ["variable1", "test*"]
  }
}

如果为 restricted 指定了 mode 值,则只能通过任务执行以下命令:

  • logdetail
  • logissue
  • complete
  • setprogress
  • setsecret
  • setvariable
  • debug
  • settaskvariable
  • prependpath
  • publish

settableVariables 限制允许你传入由 setvariableprependpath 命令设置的变量的允许列表。 它还允许基本正则表达式。 例如,如果允许列表是:['abc', 'test*'],那么将abctesttest1设置为具有任何值的变量,或者将它们添加到路径前面都会成功,但如果您尝试设置一个变量代理,则会发出警告。 空列表意味着任务不会更改任何变量。

如果settableVariables密钥或commands密钥被省略,则不会应用相关限制。

限制功能可从 代理版本 2.182.1 获取。

问:如何通过任务来处理取消信号?

管道代理向相关子进程发送 SIGINTSIGTERM 信号。 任务库中没有显式的方法来处理。 有关详细信息,请参阅 代理作业取消

问:如何从项目集合中删除任务?

我们不支持自动删除任务。 自动删除不安全,会中断已使用此类任务的现有管道。 但是,可以将任务标记为已弃用。 为此,更新任务版本将任务标记为弃用

问:如何将自定义任务升级到最新的节点?

建议升级到 最新的 Node 版本。 有关示例信息,请参阅 将任务升级到 Node 20

Microsoft 托管代理和各种 Azure DevOps Server 版本的生命周期不同,这导致在不同的任务运行位置会安装不同版本的 Node.js 运行程序。 为了确保与不同节点运行程序版本的代理之间的兼容性, task.json 该文件可以包含多个执行部分。 在以下示例中,使用 Node 20 运行程序的 Azure Pipeline 代理默认使用 Node 20,而没有它的代理将回退到 Node 10 实现。

"execution": {
  "Node10": {
    "target": "bash.js",
    "argumentFormat": ""
  },
  "Node20_1": {
    "target": "bash.js",
    "argumentFormat": ""
  }
}

要升级你的任务,请执行以下操作:

  • 若要确保代码按预期方式运行,请在各种 Node 运行程序版本上测试任务。

  • 在任务的执行部分中,将NodeNode10更新为Node16Node20

  • 若要支持较旧的服务器版本,应保留目标 Node/Node10 。 旧版 Azure DevOps Server 可能不包含最新的 Node 运行程序版本。

  • 可以选择共享目标中定义的入口点,或者将目标优化到使用的 Node 版本。

    "execution": {
       "Node10": {
         "target": "bash10.js",
         "argumentFormat": ""
       },
       "Node16": {
         "target": "bash16.js",
         "argumentFormat": ""
       },
       "Node20_1": {
         "target": "bash20.js",
         "argumentFormat": ""
       }
    }
    

重要

如果你没有在自定义任务中添加对 Node 20 运行器的支持,则任务会在从 pipelines-agent-*发布源安装的代理上失败。