Visual Studio Onlineのビルドタスクを作成する

Visual Studio Online (VSO)のビルドタスクを自作してみる
====

VSOでRubyで実行したテスト結果をテストレポートに表示したかったので、カスタムのVSOタスク作成を実施してみることにしました。

、、、と思っていたのですが、実はすでにこれを実行できる「Publish Test Results」というタスクがあることが途中でわかりました。
ですので、この記事は、カスタムのタスクを作りたいときのコマンドやリンクについて簡単ですが整理したいと思います。

カスタムビルドタスクは[tfs-cli](https://github.com/Microsoft/tfs-cli)というものが必要であり、そのためには、node/npmが必要です。筆者の環境では、カスタムビルドの環境をWindows10で作成してみました。もちろんLinuxやMacでも環境構築は簡単で、node/npmが動作すればOKです。


VSOのタスクを作るのはかなり簡単です。JSONとJavascriptもしくは、PowerShellが書ければよいみたいです。私がVSOのデモをする時がありますが、たまにPreviewのタスク、例えばDockerとかChefのタスクがあります。それらが羨ましいなと思った人もいるかもしれません。

ご安心を。もうあなたはそれを使えますし、なければ自分で書けますよ！

開発基盤としては、Node/npmがインストールされていたら準備完了です。Windows環境でなくても動作します。では、ビルドタスクのハックを楽しんでください！

1. Node/npmのインストール
---
私は[chocolatey](https://chocolatey.org)を使ってnode/npmをインストールしました。管理者権限でコマンドラインを起動し、次のコマンドを実行します。その後新しいコマンドプロンプトを起動するとnode/npmが使用可能になっているでしょう。（筆者はGitBashのシェルを使っています）

```
> choco install nodejs.install
```
新しいウィンドウで動作確認

```
$ which node
/c/Program Files/nodejs/node
$ which npm
/c/Program Files/nodejs/npm
```

2. tfs-cliのインストール
---

tfs-cliは[TFS Cross Platform Command Line](https://github.com/Microsoft/tfs-cli)の略です。

私の今回の目的はカスタムのビルドタスクづくりでしたが、それいがいの機能ももっている、TFS/VSOのコマンドラインクライアントです。

インストールするにはコマンドラインで下記のコマンドを実行しましょう。

```
$ npm install -g tfx-cli
$ tfx
Copyright Microsoft Corporation

tfx <command> [<subcommand(s)> ...] [<args>] [--version] [--help] [--json]

                        fTfs
                      fSSSSSSSs
                    fSSSSSSSSSS
     TSSf         fSSSSSSSSSSSS
     SSSSSF     fSSSSSSST SSSSS
     SSfSSSSSsfSSSSSSSt   SSSSS
     SS  tSSSSSSSSSs      SSSSS
     SS   fSSSSSSST       SSSSS
     SS fSSSSSFSSSSSSf    SSSSS
     SSSSSST    FSSSSSSFt SSSSS
     SSSSt        FSSSSSSSSSSSS
                    FSSSSSSSSSS
                       FSSSSSSs
                        FSFs    (TM)
```

簡単ですね。

3. tfx-cliでログイン実行
---

　実際に使うためには、下記のコマンドでVSOへログインを実行します。Enter collectionには、あなたがお使いのVSOのプロジェクトのURLのうち、Collectionのところまでを記述すればよいです。

　また、Personal access tokenはVSOにアクセスするための個人用トークンです。VSOのログインしている右上の自分の名前の部分をクリックすると、DETAILSというページが表示されます。そのDETAILS > Security > Personal access tokensで発行できます。

デフォルトで９０日有効なトークンが作成できます。そのトークンをtfx loginコマンドで求められたときにコピー＆ペーストするとログイン可能になります。

```
$ tfx login
Enter collection > https://<ここにVSOのアカウント名を書く>.visualstudio.com/DefaultCollection
Enter personal access token > xxxxxxxx
```

4. テンプレートの生成
---
さて、カスタムコマンドを作るのは簡単です、次のコマンドを発行すると、テンプレートを生成してくれます。タスクの名前を聞かれますので、適当にうめてください。
筆者の環境では、task nameが短いと、friendly nameが入力できないという問題が発生しました。そのケースでは、長めのtask nameを入力すると動作するようです。

ちなみにこれは、バッドノウハウであり、なぜなのかが、まだわかっていません（すみません、コードも読んだのですが、、、）

```
$ tfx build tasks create
Enter short task name > junitstyletestreport
Enter friendly name > JUnit style test report
Enter description > A task for testing and reporting
Enter author > Tsuyoshi Ushio
```

無事生成されたら、次のようなファイルができています。 task.jsonがマニフェストと呼ばれる定義ファイルになっており、ここをいじると、タスクの画面を作成することができます。

```
$ dir
2015/10/28  12:22             2,581 icon.png
2015/10/28  12:22               484 sample.js
2015/10/28  12:22               543 sample.ps1
2015/10/28  12:57             1,877 task.json
```

後はこれらのjson, javascript, powershellをコーディングして、VSOにアップロードすれば、完了です。これらのファイルはgit等バージョン管理ツールの配下においておいて、いつでも戻せるようにしておくとよいでしょう。

残念ながらエラーメッセージがわかりにくいので注意しましょう。私はいきなりまとめてコーディングをしてuploadしました。すると、Bad Request(400)とか、Failed Request: Conflict(409)等のエラーが発生しました。これは相当わかりにくいメッセージです。
　私のケースではBad Request(400)はjsonの中身が問題がある場合、Conflict(409)は既にタスクをアップロードしているのに、overwriteを指定せずにアップロードするケースで出るようです。

```
$ tfx build tasks upload .\<ここにディレクトリ名>
```

でアップロード可能です。２回目以降は

```
$ tfx build tasks upload .\<ここにディレクトリ名> --overwrite=true
```

もしくは

```
$ tfx build tasks delete <task.jsonの中のID>
```
で一旦削除してから、tfx build tasks upload で反映されます。

5. コーディング
---

さて簡単にお試しレベルでコーディングしてみましょう。私はJUnit 形式のレポートをだしたかったので、MicrosoftのビルドタスクのGitHubの中の[Gradleのタスク](https://github.com/Microsoft/vso-agent-tasks/tree/master/Tasks/Gradle)を参考にしてみました。

task.jsonを書き換えて下記のようにしました。idの部分は、tfx build tasks createの時点で自動で発行されます。

Junit系の部分を試しに追記しています。日本語も出るようですが、このtask自体が多言語対応していて将来はVSOも対応しそうなので本当は正しいローカライズのファイルを書いた方がよさそうです。

```
{
  "id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "junitstyletestreport",
  "friendlyName": "JUnit style test report",
  "description": "A task for testing and reporting",
  "author": "Tsuyoshi Ushio",
  "helpMarkDown": "Replace with markdown to show in help",
  "category": "Utility",
  "visibility": [
    "Build",
    "Release"
  ],
  "demands": [],
  "version": {
    "Major": "0",
    "Minor": "1",
    "Patch": "0"
  },
  "minimumAgentVersion": "1.83.0",
  "instanceNameFormat": "junitstyletestreport $(message)",
  "groups": [
    {
        "name":"junitTestResults",
        "displayName":"JUnit Test Results",
        "isExpanded":true
    },
    {
        "name": "codeCoverage",
        "displayName": "Code Coverage",
        "isExpanded":true
    }
],
  "inputs": [
    {
      "name": "cwd",
      "type": "filePath",
      "label": "Working Directory",
      "defaultValue": "",
      "required": false,
      "helpMarkDown": "Current working directory when junitstyletestreport is run."
    },
    {
      "name": "msg",
      "type": "string",
      "label": "Message",
      "defaultValue": "Hello World",
      "required": true,
      "helpMarkDown": "Message to echo out"
    },
    {
    "name":"publishJUnitResults",
    "type":"boolean",
    "label":"日本Publish to VSO/TFS",
    "required":true,
    "defaultValue":"true",
    "groupName" : "junitTestResults",
    "helpMarkDown":"日本語Select this option to publish JUnit Test results produced by the Gradle build to VSO/TFS. Each test result file matching `Test Results Files` will be published as a test run in VSO/TFS."
    }
  ],
  "execution": {
    "Node": {
      "target": "sample.js",
      "argumentFormat": ""
    },
    "PowerShell": {
      "target": "$(currentDirectory)\\sample.ps1",
      "argumentFormat": "",
      "workingDirectory": "$(currentDirectory)"
    }
  }
}

```
これをtfx build tasks uploadすると、次のようになります。もちろんアイコンも自由自在！

<img width="539" alt="CustomTask.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/3470/524d2a12-5507-ac24-edef-6c930373bdfa.jpeg">

<img width="960" alt="CustomTask2.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/3470/3459b4bb-6632-fd04-7ccd-3144a96a2c32.jpeg">

ちょっとだけ気持ちいい。

あとは、javascriptもしくはpowershellのコードですが、githubに[Microsoft/vso-agent-tasks](https://github.com/Microsoft/vso-agent-tasks)がありますので、ここで参考になるのを見ながら書き方を覚えましょう。

PowerShellのライブラリは見つけられませんでしたが、javascript
はこちらにライブラリのコードがありました。[Microsoft/vso-task-lib](https://github.com/Microsoft/vso-task-lib)これさえあれば、どんなタスクでも自作できそうです。

既存のタスクを取り込む
---

ちなみにお察しのよい皆さんはお分かりかもしれませんが、まだVSOの標準設定で未公開のタスクも[Microsoft/vso-agent-tasks/Tasks](https://github.com/Microsoft/vso-agent-tasks/tree/master/Tasks)にあるものは自分でとってきて、上記のと同じような手順で自分の環境に組み込めるということです。 :)

是非皆さんでも自分のほしいVSOビルドタスクを作っちゃってGitHubにあげていただき、VSOをもっと便利にすることに貢献してくださると大変うれしいです！


参考
---
[Developing a Custom Build vNext Task: Part 1](http://www.colinsalmcorner.com/post/developing-a-custom-build-vnext-task-part-1)
[Developing a Custom Build vNext Task: Part 2](http://colinsalmcorner.com/post/developing-a-custom-build-vnext-task-part-2)