次の方法で共有


カスタム パイプライン タスク拡張機能を追加する

Azure DevOps Services |Azure DevOps Server 2022 および Azure DevOps Server 2019

このガイドでは、カスタム ビルドタスクまたはリリース タスクを Azure DevOps 拡張機能として作成、テスト、発行する手順について説明します。 カスタム パイプライン タスクを使用すると、単純なユーティリティから外部システムとの複雑な統合まで、チームのワークフローに合わせて調整された特殊な機能を使用して Azure DevOps を拡張できます。

次のタスクを実行する方法について説明します。

  • 開発環境とプロジェクト構造を設定する
  • TypeScript と Azure Pipelines タスク ライブラリを使用してタスク ロジックを作成する
  • モック フレームワークを使用して包括的な単体テストを実装する
  • 拡張機能を配布用にパッケージ化する
  • Visual Studio Marketplace に発行する
  • 拡張機能のメンテナンス用に自動化された CI/CD パイプラインを設定する

Azure Pipelines の詳細については、「Azure Pipelines とは」を参照してください。

この記事では、エージェント ベースの拡張機能のエージェント タスクについて説明します。 サーバー タスクとサーバー ベースの拡張機能の詳細については、「 サーバー タスクの作成」を参照してください。

前提条件

開始する前に、次の要件が満たされていることを確認してください。

コンポーネント 要件 説明
Azure DevOps 組織 必須 組織がない場合は作成する
テキスト エディター 推奨 Visual Studio Code for IntelliSense とデバッグのサポート
Node.js 必須 最新バージョンをインストールします (Node.js 20 以降を推奨)
TypeScript コンパイラ 必須 最新バージョン (バージョン 4.6.3 以降) をインストールする
Azure DevOps CLI (tfx-cli) 必須 npm i -g tfx-cliを使用して拡張機能をパッケージ化する
Azure DevOps 拡張機能 SDK 必須 azure-devops-extension-sdk パッケージをインストールする
テスト フレームワーク 必須 単体テスト用 Mocha (セットアップ時にインストール)

プロジェクト構造

プロジェクトの home ディレクトリを作成します。 このチュートリアルを完了すると、拡張機能の構造は次のようになります。

|--- README.md    
|--- images                        
    |--- extension-icon.png  
|--- buildandreleasetask            // Task scripts location
    |--- task.json                  // Task definition
    |--- index.ts                   // Main task logic
    |--- package.json               // Node.js dependencies
    |--- tests/                     // Unit tests
        |--- _suite.ts
        |--- success.ts
        |--- failure.ts
|--- vss-extension.json             // Extension manifest

重要

開発環境との互換性を確保するには、開発用コンピューターで 最新バージョンの Node.js を実行する必要があります。 Node 20 を使用するように task.json ファイルを更新します。

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

1. カスタム タスクを作成する

このセクションでは、カスタム タスクの基本的な構造と実装を作成する手順について説明します。 この手順のすべてのファイルは、プロジェクトのhome ディレクトリ内のbuildandreleasetask フォルダー内に作成する必要があります。

このチュートリアルでは、PowerShell で Windows を使用します。 手順はすべてのプラットフォームで動作しますが、環境変数の構文は異なります。 Mac または Linux では、 $env:<var>=<val>export <var>=<val> に置き換えます。

タスクスキャフォールディングを設定する

基本的なプロジェクト構造を作成し、必要な依存関係をインストールします。

  1. Node.js プロジェクトを初期化するには、PowerShell を開き、 buildandreleasetask フォルダーに移動して、次のコマンドを実行します。

    npm init --yes
    

    package.json ファイルは、既定の設定で作成されます。 --yes フラグは、すべての既定のオプションを自動的に受け入れます。

    ヒント

    Azure Pipelines エージェントは、タスク フォルダーにノード モジュールが含まれると想定しています。 node_modulesbuildandreleasetask フォルダーにコピーします。 VSIX ファイル サイズ (50 MB の制限) を管理するには、パッケージ化する前に npm install --production または npm prune --production を実行することを検討してください。

  2. Azure Pipelines タスク ライブラリをインストールします。

    npm install azure-pipelines-task-lib --save
    
  3. TypeScript 型定義をインストールします。

    npm install @types/node --save-dev
    npm install @types/q --save-dev
    
  4. バージョン 管理の除外を設定する

    echo node_modules > .gitignore
    

    ビルド プロセスは、毎回node_modulesを再構築するために npm install を実行する必要があります。

  5. テストの依存関係をインストールします。

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  6. TypeScript コンパイラをインストールします。

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

    typeScript をグローバルにインストールして、 tsc コマンドを使用できるようにします。 これを使用しない場合、TypeScript 2.3.4 が既定で使用されます。

  7. TypeScript コンパイルを構成します。

    tsc --init --target es2022
    

    tsconfig.json ファイルは、ES2022 ターゲット設定で作成されます。

タスク ロジックを実装する

スキャフォールディングが完了したら、機能とメタデータを定義するコア タスク ファイルを作成します。

  1. タスク定義ファイルを作成する: buildandreleasetask フォルダーにtask.jsonを作成します。 このファイルは、Azure Pipelines システムへのタスクを記述し、入力、実行設定、UI プレゼンテーションを定義します。

    {
     "$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"
         }
     }
     }
    

    {{placeholders}}をタスクの実際の情報に置き換えます。 taskguid は一意である必要があります。 PowerShell を使用して生成します。 (New-Guid).Guid

  2. タスク ロジックを実装するには、タスクの主な機能を使用して 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();
    
  3. TypeScript を JavaScript にコンパイルする:

    tsc
    

    index.js ファイルは、TypeScript ソースから作成されます。

task.json コンポーネントについて

task.jsonファイルは、タスク定義の中心です。 主なプロパティを次に示します。

プロパティ 説明
id タスクの一意の GUID 識別子 を使用して生成されます。 (New-Guid).Guid
name スペースのないタスク名 (内部的に使用) MyCustomTask
friendlyName UI に表示される表示名 My Custom Task
description タスク機能の詳細な説明 Performs custom operations on files
author 発行元または作成者の名前 My Company
instanceNameFormat パイプラインステップでのタスクの表示方法 Process $(inputFile)
inputs 入力パラメーターの配列 次の入力の種類を参照してください
execution 実行環境の仕様 Node20_1PowerShell3 などです。
restrictions コマンドと変数のセキュリティ制限 新しいタスクに推奨

セキュリティ制限

運用タスクの場合は、コマンドの使用と変数アクセスを制限するセキュリティ制限を追加します。

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

制限モード では、次のコマンドのみが許可されます。

  • logdetaillogissuecompletesetprogress
  • setsecretsetvariabledebugsettaskvariable
  • prependpathpublish

変数許可リスト は、 setvariable または prependpathを使用して設定できる変数を制御します。 基本的な正規表現パターンをサポートします。

この機能には 、エージェント バージョン 2.182.1 以降が 必要です。

入力の種類と例

タスク パラメーターの一般的な入力の種類:

"inputs": [
    {
        "name": "stringInput",
        "type": "string",
        "label": "Text Input",
        "defaultValue": "",
        "required": true,
        "helpMarkDown": "Enter a text value"
    },
    {
        "name": "boolInput",
        "type": "boolean",
        "label": "Enable Feature",
        "defaultValue": "false",
        "required": false
    },
    {
        "name": "picklistInput",
        "type": "pickList",
        "label": "Select Option",
        "options": {
            "option1": "First Option",
            "option2": "Second Option"
        },
        "defaultValue": "option1"
    },
    {
        "name": "fileInput",
        "type": "filePath",
        "label": "Input File",
        "required": true,
        "helpMarkDown": "Path to the input file"
    }
]

タスクをローカルでテストする

パッケージ化する前に、タスクをテストして、正しく動作することを確認します。

  1. 入力が不足しているテスト (失敗する必要があります):

    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
    
  2. 有効な入力でテストする (成功する必要があります):

    $env:INPUT_SAMPLESTRING="World"
    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]samplestring=World
    Hello World
    
  3. テスト エラー処理:

    $env:INPUT_SAMPLESTRING="bad"
    node index.js
    

    このアクションにより、コード内のエラー処理パスがトリガーされます。

    ヒント

    タスク ランナーと Node.js バージョンの詳細については、 ノード ランナーの更新ガイダンスを参照してください。

詳細については、 ビルド/リリース タスクリファレンスを参照してください

2. 包括的な単体テストを実施する

タスクを徹底的にテストすることで信頼性が確保され、運用パイプラインにデプロイする前に問題をキャッチするのに役立ちます。

テストの依存関係をインストールする

必要なテスト ツールをインストールします。

npm install mocha --save-dev -g
npm install sync-request --save-dev
npm install @types/mocha --save-dev

テストの作成

  1. _suite.ts ファイルを含むtests フォルダーをタスク ディレクトリに作成します。

     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() {
             // Setup before tests
         });
    
         after(() => {
             // Cleanup after tests
         });
    
         it('should succeed with simple inputs', function(done: Mocha.Done) {
             // Success test implementation
         });
    
         it('should fail if tool returns 1', function(done: Mocha.Done) {
             // Failure test implementation
         });    
       });
    

    ヒント

    テスト フォルダーはタスク フォルダーに配置する必要があります (たとえば、 buildandreleasetask)。 同期要求エラーが発生した場合は、タスク フォルダー ( npm i --save-dev sync-request) にインストールします。

  2. テスト ディレクトリに 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);
    
     // Set valid input for success scenario
     tmr.setInput('samplestring', 'human');
    
     tmr.run();
    
  3. _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.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
         });
     });
    
  4. テスト ディレクトリに 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);
    
    // Set invalid input to trigger failure
    tmr.setInput('samplestring', 'bad');
    
    tmr.run();
    
  5. _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();
         });
     });
    

テストの実行

テスト スイートを実行します。

# Compile TypeScript
tsc

# Run tests
mocha tests/_suite.js

両方のテストに合格する必要があります。 詳細出力 (ビルド コンソール出力と同様) の場合は、トレース環境変数を設定します。

$env:TASK_TEST_TRACE=1
mocha tests/_suite.js

テスト カバレッジのベスト プラクティス

  • すべての入力の組み合わせをテストする: 有効な入力、無効な入力、必要な入力がありません
  • テスト エラー シナリオ: ネットワークエラー、ファイル システム エラー、アクセス許可の問題
  • 外部依存関係をモックする: 単体テストで外部サービスに依存しない
  • 出力の検証: コンソール出力、タスクの結果、生成された成果物を確認する
  • パフォーマンス テスト: 大きなファイルを処理するタスクのテストを追加することを検討する

セキュリティのベスト プラクティス

  • 入力の検証: 常に入力を検証してサニタイズする
  • シークレットの処理: 機密データに setSecret を使用する
  • コマンドの制限: 運用タスクのコマンド制限を実装する
  • 最小限のアクセス許可: 必要なアクセス許可のみを要求する
  • 定期的な更新: 依存関係と Node.js バージョンを最新の状態に保つ

タスクをローカルでテストし、包括的な単体テストを実装したら、Azure DevOps の拡張機能にパッケージ化します。

パッケージ ツールをインストールする

クロス プラットフォーム コマンド ライン インターフェイス (tfx-cli) をインストールします。

npm install -g tfx-cli

拡張機能マニフェストを作成する

拡張機能マニフェスト (vss-extension.json) には、タスク フォルダーやイメージへの参照など、拡張機能に関するすべての情報が含まれます。

  1. extension-icon.png ファイルを含むイメージ フォルダーを作成する

  2. 拡張機能のルート ディレクトリ (タスク フォルダー内ではなく) に vss-extension.json を作成します。

    {
     "manifestVersion": 1,
     "id": "my-custom-tasks",
     "name": "My Custom Tasks",
     "version": "1.0.0",
     "publisher": "your-publisher-id",
     "targets": [
         {
             "id": "Microsoft.VisualStudio.Services"
         }
     ],
     "description": "Custom build and release tasks for Azure DevOps",
     "categories": [
         "Azure Pipelines"
     ],
     "icons": {
         "default": "images/extension-icon.png"
     },
     "files": [
         {
             "path": "MyCustomTask"
         }
     ],
     "contributions": [
         {
             "id": "my-custom-task",
             "type": "ms.vss-distributed-task.task",
             "targets": [
                 "ms.vss-distributed-task.tasks"
             ],
             "properties": {
                 "name": "MyCustomTask"
             }
         }
     ]
    }
    

主なマニフェスト プロパティ

プロパティ 説明
publisher マーケットプレースの発行元識別子
contributions.id 拡張機能内の一意の識別子
contributions.properties.name タスク フォルダー名と一致する必要があります
files.path マニフェストを基準としたタスク フォルダーへのパス

発行元の値を 発行元 名に変更します。 パブリッシャーの作成の詳細については、「パブリッシャーの 作成」を参照してください

拡張機能をパッケージ化する

拡張機能を .vsix ファイルにパッケージ化します。

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

バージョン管理

  • 拡張機能のバージョン: 更新プログラムごとにバージョンを vss-extension.json 単位でインクリメントする
  • タスクのバージョン: タスクの更新ごとに task.json のバージョンをインクリメントする
  • 自動インクリメント: --rev-version を使用してパッチ バージョンを自動的にインクリメントする
tfx extension create --manifest-globs vss-extension.json --rev-version

重要

Azure DevOps で変更を有効にするには、タスクのバージョンと拡張機能のバージョンの両方を更新する必要があります。

バージョン管理戦略

タスクの更新に関するセマンティック バージョン管理の原則に従います。

  • メジャー バージョン: 入力/出力に対する破壊的変更
  • マイナー バージョン: 新機能、下位互換性
  • パッチ バージョン: バグ修正のみ

更新プロセス:

  1. task.json バージョンを更新する
  2. vss-extension.json バージョンを更新する
  3. テスト組織で徹底的にテストする
  4. 発行して問題を監視する

Visual Studio Marketplace に発行する

1. 発行元を作成する

  1. Visual Studio Marketplace 発行ポータルにサインインする
  2. メッセージが表示されたら、新しいパブリッシャーを作成します。
    • 発行元識別子: 拡張機能マニフェストで使用されます (例: mycompany-myteam)
    • 表示名: マーケットプレースに表示されるパブリック名 (例: My Team)
  3. Marketplace パブリッシャー契約を確認して同意する

2. 拡張機能をアップロードする

Web インターフェイス メソッド:

  1. [新しい拡張機能のアップロード] を選択する
  2. パッケージ化された .vsix ファイルを選択する
  3. [アップロード] を選択します。

コマンド ライン メソッド:

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

3. 拡張機能を共有する

  1. マーケットプレースで拡張機能を右クリックする
  2. 共有 を選択します
  3. 組織名を入力する
  4. 必要に応じて組織を追加する

重要

公開されている拡張機能を共有するには、発行元を確認する必要があります。 詳細については、「 Package/Publish/Install」を参照してください。

4. 組織にインストールする

共有後、拡張機能を Azure DevOps 組織にインストールします。

  1. 組織の設定>Extensions に移動します
  2. 拡張機能を参照する
  3. [ 無料で入手 してインストールする] を選択する

3. 拡張機能をパッケージ化して公開する

拡張機能を確認する

インストール後、タスクが正しく動作することを確認します。

  1. パイプラインを作成または編集します。
  2. カスタム タスクを追加します。
    • パイプライン エディターで [タスクの追加] を選択する
    • 名前でカスタム タスクを検索する
    • パイプラインに追加する
  3. タスク パラメーターを構成します。
    • 必要な入力を設定する
    • オプション設定を構成する
  4. パイプラインを実行して機能をテストする
  5. 実行の監視:
    • タスク ログで適切な実行を確認する
    • 予想される出力を確認する
    • エラーや警告がないことを確認する

4. CI/CD を使用して拡張機能の公開を自動化する

カスタム タスクを効果的に維持するには、テスト、パッケージ化、発行を処理する自動ビルドおよびリリース パイプラインを作成します。

自動化の前提条件

  • Azure DevOps 拡張機能タスク: 拡張機能を 無料でインストールする
  • 変数グループ: 次の変数を含む パイプライン ライブラリ変数グループ を作成します。
    • publisherId: マーケットプレースの発行元 ID
    • extensionId: vss-extension.json からの拡張 ID
    • extensionName: vss-extension.json からの拡張名
    • artifactName: VSIX アーティファクトの名前
  • サービス接続: パイプライン アクセス許可を持つ Marketplace サービス接続を作成する

CI/CD パイプラインの完了

テスト、パッケージ化、発行のための包括的なステージを含む YAML パイプラインを作成します。

trigger: 
- main

pool:
  vmImage: "ubuntu-latest"

variables:
  - group: extension-variables # Your variable group name

stages:
  - stage: Test_and_validate
    displayName: 'Run Tests and Validate Code'
    jobs:
      - job: RunTests
        displayName: 'Execute unit tests'
        steps:
          - task: TfxInstaller@4
            displayName: 'Install TFX CLI'
            inputs:
              version: "v0.x"
          
          - task: Npm@1
            displayName: 'Install task dependencies'
            inputs:
              command: 'install'
              workingDir: '/MyCustomTask' # Update to your task directory
          
          - task: Bash@3
            displayName: 'Compile TypeScript'
            inputs:
              targetType: "inline"
              script: |
                cd MyCustomTask # Update to your task directory
                tsc
          
          - task: Npm@1
            displayName: 'Run unit tests'
            inputs:
              command: 'custom'
              workingDir: '/MyCustomTask' # Update to your task directory
              customCommand: 'test' # Ensure this script exists in package.json
          
          - task: PublishTestResults@2
            displayName: 'Publish test results'
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/test-results.xml'
              searchFolder: '$(System.DefaultWorkingDirectory)'

  - stage: Package_extension
    displayName: 'Package Extension'
    dependsOn: Test_and_validate
    condition: succeeded()
    jobs:
      - job: PackageExtension
        displayName: 'Create VSIX package'
        steps:
          - task: TfxInstaller@4
            displayName: 'Install TFX CLI'
            inputs:
              version: "v0.x"
          
          - task: Npm@1
            displayName: 'Install dependencies'
            inputs:
              command: 'install'
              workingDir: '/MyCustomTask'
          
          - task: Bash@3
            displayName: 'Compile TypeScript'
            inputs:
              targetType: "inline"
              script: |
                cd MyCustomTask
                tsc
          
          - task: QueryAzureDevOpsExtensionVersion@4
            name: QueryVersion
            displayName: 'Query current extension version'
            inputs:
              connectTo: 'VsTeam'
              connectedServiceName: 'marketplace-connection'
              publisherId: '$(publisherId)'
              extensionId: '$(extensionId)'
              versionAction: 'Patch'
          
          - task: PackageAzureDevOpsExtension@4
            displayName: 'Package extension'
            inputs:
              rootFolder: '$(System.DefaultWorkingDirectory)'
              publisherId: '$(publisherId)'
              extensionId: '$(extensionId)'
              extensionName: '$(extensionName)'
              extensionVersion: '$(QueryVersion.Extension.Version)'
              updateTasksVersion: true
              updateTasksVersionType: 'patch'
              extensionVisibility: 'private'
              extensionPricing: 'free'
          
          - task: PublishBuildArtifacts@1
            displayName: 'Publish VSIX artifact'
            inputs:
              PathtoPublish: '$(System.DefaultWorkingDirectory)/*.vsix'
              ArtifactName: '$(artifactName)'
              publishLocation: 'Container'

  - stage: Publish_to_marketplace
    displayName: 'Publish to Marketplace'
    dependsOn: Package_extension
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: PublishExtension
        displayName: 'Deploy to marketplace'
        environment: 'marketplace-production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: TfxInstaller@4
                  displayName: 'Install TFX CLI'
                  inputs:
                    version: "v0.x"
                
                - task: PublishAzureDevOpsExtension@4
                  displayName: 'Publish to marketplace'
                  inputs:
                    connectTo: 'VsTeam'
                    connectedServiceName: 'marketplace-connection'
                    fileType: 'vsix'
                    vsixFile: '$(Pipeline.Workspace)/$(artifactName)/*.vsix'
                    publisherId: '$(publisherId)'
                    extensionId: '$(extensionId)'
                    extensionName: '$(extensionName)'
                    updateTasksVersion: false
                    extensionVisibility: 'private'
                    extensionPricing: 'free'

テスト用に package.json を構成する

package.jsonにテスト スクリプトを追加します。

{
  "scripts": {
    "test": "mocha tests/_suite.js --reporter xunit --reporter-option output=test-results.xml",
    "test-verbose": "cross-env TASK_TEST_TRACE=1 npm test"
  }
}

パイプライン ステージの内訳

ステージ 1: テストと検証

  • 目的: コードの品質と機能を確保する
  • アクション: 依存関係のインストール、TypeScript のコンパイル、単体テストの実行、結果の発行
  • 検証: すべてのテストに合格して続行する必要があります

ステージ 2: パッケージ拡張機能

  • 目的: 配置可能な VSIX パッケージを作成する
  • アクション: 現在のバージョンのクエリ、バージョンの増分、パッケージ拡張機能、成果物の発行
  • バージョン管理: バージョンの増分を自動的に処理する

ステージ 3: マーケットプレースに発行する

  • 目的: Visual Studio Marketplace にデプロイする
  • 条件: パッケージ化が成功した後、メイン ブランチでのみ実行されます
  • 環境: 承認ゲートにデプロイ環境を使用する

CI/CD のベスト プラクティス

  • ブランチ保護: メイン/リリース ブランチからのみ発行する
  • 環境ゲート: 運用環境のリリースにデプロイ環境を使用する
  • バージョン管理: 競合を回避するためにバージョンの増分を自動化する
  • テスト カバレッジ: パッケージ化する前に包括的なテスト カバレッジを確保する
  • セキュリティ: ハードコーディングされた資格情報の代わりにサービス接続を使用する
  • 監視: 失敗したデプロイのアラートを設定する

クラシック ビルド パイプラインの場合は、次の手順に従って拡張機能のパッケージ化と発行を設定します。

  1. Bash タスクを追加して、TypeScript を JavaScript にコンパイルします。

  2. 既存のバージョンを照会するには、次の入力を使用して 拡張機能バージョンのクエリ タスクを追加します。

    • 接続先: Visual Studio Marketplace
    • Visual Studio Marketplace (サービス接続): サービス接続
    • 発行元 ID: Visual Studio Marketplace パブリッシャーの ID
    • 拡張子ID: vss-extension.json ファイル内の拡張子のID
    • バージョンを増やす: パッチ
    • 出力変数: Task.Extension.Version
  3. マニフェスト Json に基づいて拡張機能をパッケージ化するには、次の入力を使用して Package Extension タスクを追加します。

    • ルート マニフェスト フォルダー: マニフェスト ファイルを含むルート ディレクトリを指します。 たとえば、ルート ディレクトリ$(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)
    • 拡張機能の可視性: 拡張機能がまだ開発中の場合は、値を private に設定します。 拡張機能をパブリックにリリースするには、値を public に設定します。
  4. パブリッシュされたファイルにコピーするには、次の入力を使用して ファイルのコピー タスクを追加します。

    • 内容: 成果物として発行するためにコピーされるすべてのファイル
    • ターゲット フォルダー: ファイルのコピー先フォルダー
      • 例: $(Build.ArtifactStagingDirectory)
  5. ビルド成果物の発行を追加して、他のジョブまたはパイプラインで使用する成果物を発行します。 次の入力を使用します。

    • 発行するパス: 発行されるファイルを含むフォルダーへのパス
      • 例: $(Build.ArtifactStagingDirectory)
    • 成果物名: 成果物に指定された名前
    • 成果物の発行場所: 将来のジョブで成果物を使用する Azure Pipelines を選択する

ステージ 3: ビルド成果物をダウンロードして拡張機能を発行する

  1. ビルド エージェントに tfx-cli をインストールするには、 Azure DevOps に Node CLI を使用する (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 ファイル内の拡張機能の名前
    • 拡張機能の可視性: プライベートまたはパブリック

省略可能: 拡張機能をインストールしてテストする

拡張機能を発行したら、Azure DevOps 組織にインストールする必要があります。

組織に拡張機能をインストールする

いくつかの手順で共有拡張機能をインストールします。

  1. [組織の設定] に移動し、[拡張機能] を選択します

  2. [自分 と共有する拡張機能 ] セクションで拡張機能を見つけます。

    • 拡張機能のリンクを選択する
    • [無料で入手する] または [インストール] を選択する
  3. インストールされている拡張機能の一覧に拡張機能が表示されていることを確認します。

    • パイプライン タスク ライブラリで使用可能であることを確認する

[ 拡張機能 ] タブが表示されない場合は、プロジェクト レベルではなく、組織の管理レベル (https://dev.azure.com/{organization}/_admin) であることを確認します。

エンドツーエンドのテスト

インストール後、包括的なテストを実行します。

  1. テスト パイプラインを作成します。

    • カスタム タスクを新しいパイプラインに追加する
    • すべての入力パラメーターを構成する
    • さまざまな入力の組み合わせでテストする
  2. 機能を検証します。

    • パイプラインを実行し、実行を監視する
    • タスクの出力とログを確認する
    • 無効な入力でエラー処理を確認する
  3. テスト パフォーマンス:

    • 大きな入力ファイルを使用してテストする (該当する場合)
    • リソースの使用状況を監視する
    • タイムアウト動作を検証する

よく寄せられる質問

Q: タスクの取り消しはどのように処理されますか?

A: パイプライン エージェントは、タスク プロセスに SIGINT および SIGTERM シグナルを送信します。 タスク ライブラリでは明示的なキャンセル処理は提供されませんが、タスクはシグナル ハンドラーを実装できます。 詳細については、 エージェント・ジョブの取り消しを参照してください。

Q: 組織からタスクを削除するにはどうすればよいですか?

A: 自動削除は、既存の パイプラインを中断する可能性があるため、サポートされていません。 その代わりに:

  1. タスクを非推奨にする: タスクを非推奨としてマークする
  2. バージョン管理: タスクのバージョンをバンプする
  3. コミュニケーション: 非推奨のタイムラインについてユーザーに通知する

Q: タスクを最新の Node.js バージョンにアップグレードするにはどうすればよいですか?

A: パフォーマンスとセキュリティを向上させるために 、最新の Node バージョン にアップグレードします。 移行のガイダンスについては、 ノード 20 へのタスクのアップグレードを参照してください。

task.jsonに複数の実行セクションを含めることで、複数の Node バージョンをサポートします。

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

ノード 20 のエージェントは優先バージョンを使用しますが、古いエージェントはノード 10 にフォールバックします。

タスクをアップグレードするには:

  • コードが期待どおりに動作することを確認するには、さまざまな Node ランナー バージョンでタスクをテストします。

  • タスクの実行セクションで、 Node または Node10 から Node16 または Node20に更新します。

  • 以前のバージョンのサーバーをサポートするには、 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-*リリース フィードからインストールされたエージェントで失敗します。