Job,Task,Resourceの説明をしながら簡単なPipelineを作ってみる

ConcourseのPipelineとは下図のようなものです。
![スクリーンショット 2018-12-03 2.24.47.png](https://qiita-image-store.s3.amazonaws.com/0/84299/8102b410-bb00-d955-a596-3c95a38aab8a.png)

test-prの処理の中身は下図です。
![スクリーンショット 2018-12-03 2.25.26.png](https://qiita-image-store.s3.amazonaws.com/0/84299/2830f176-bfd3-dd19-4fda-27c93dfaa54b.png)


* GitHubのPRを取得する
* 対象のPRのstatusを`pending`にする
* unit-testを実行する
  * (成功) 対象のPRのstatusを`success`にする
  * (失敗) 対象のPRのstatusを`failure`にする

今回のSampleは[cappyzawa/pipelines](https://github.com/cappyzawa/pipelines)	になります。
## Job
Jobはパイプライン中のアクションを定義します。
今回の例でいうと、以下になります。
> * GitHubのPRを取得する
> * 対象のPRのstatusを`pending`にする
> * unit-testを実行する
>   * (成功) 対象のPRのstatusを`success`にする
>   * (失敗) 対象のPRのstatusを`failure`にする

個人的にはJobから記述し始めるのが書きやすいと思います。
以下がPipleine中のJobになります。

```yml:pipeline.yml
jobs:
- name: test-pr
  plan:
  - get: pr
    trigger: true
  - put: pr
    params:
      path: pr
      status: pending
  - task: unit-test
    file: pr/ci/tasks/unit-test.yml
    on_success:
      put: pr
      params:
        path: pr
        status: success
    on_failure:
      put: pr
      params:
        path: pr
        status: failure
```
### name: test-pr
Jobの名前を定義します。
Jobには`plan`が存在し、処理実行の流れを記述していきます。
#### get: pr
pull requestを取得します。
これは後述のResourceです。Resourceの取得は`get`、送信は`put`のように記述します。
#### put: pr
unit-testを実行する前にPRのstatusを`pending`にしておきます。
#### task: unit-test
unit-testを実行します。
これは後述のTaskです。Taskの定義は別ファイルに切り出しておくとパイプラインの定義ファイルがすっきりします。
#### on_success/on_failure
インデントを揃えることでstepの成功時、失敗時の処理を分岐することができます。
今回の場合、`task: unit-test`が成功したらGitHubのPullRequestに成功のステータスを送信し、失敗したら失敗のステータスを送信します。

## Resource
ResourceはJobで利用されるオブジェクトです。
Resourceは振る舞いが抽象化されており、少ない記述量で決められた振る舞いを行うことができます。
今回はPullRequestを取得、ステータスを送信したいので[telia\-oss/github\-pr\-resource: Github pull request resource for Concourse](https://github.com/telia-oss/github-pr-resource)	を利用します。
### resource_types
これはConcourse本体にpackagingされていないResourceですので、`resource_types`ブロックで利用するDockerImageを指定する必要があります。

```yml:pipeline.yml
resource_types:
- name: pull-request
  type: docker-image
  source:
    repository: teliaoss/github-pr-resource
```
上記のように記述すると[teliaoss/github-pr-resource](https://hub.docker.com/r/teliaoss/github-pr-resource/)のDockerImageを`pull-reqeust`というエイリアスをつけて利用することが可能になります。
厳密にいうと、Resourceには`type`が存在し、このように記述すると`pull-request` typeということになります。
### resource
利用するResourceを定義します。

```yml:pipeline.yml
resources:
- name: pr
  type: pull-request
  check_every: 24h
  webhook_token: ((webhook-token))
  source:
    repository: cappyzawa/pipelines
    access_token: ((github-access-token))
```
先ほど定義した`pull-request`のresource typeを利用しています。
#### check_every
Resourceには`put`, `get`の他に`check`という振る舞いがあります。
これは定義されたResourceに更新があるか定期的に確認するという振る舞いになります(Resourceによってはないものもある)。
今回の場合github.comのPullRequestに対し、更新がないか確認しにいくため、limitを考慮して24hとしています。
#### webhook_token
上記の`check_every`ではConcourseが定期的に更新を確認しにいくのですが、対象にwebhookを設定し、能動的にConcourseにresourceの更新を確認させることができます。
今回はGitHubのPullRequestが対象なので、該当するRepositoryにwebhookを設定してみます。
Setting->Webhooks->Add Webhook

|項目|内容|
|:---:|:---|
|Payload URL|http://<your-concourse.com>/api/v1/teams/<your-team>/pipelines/<your-pipeline>/resources/pr/check/webhook?webhook_token=**token**|
|Content type|application/json|
|SSL Verification|どちらでも|
|Which events would you like to trigger this webhook?|Pull requests|

今回は`((webhook-token))`に`token`を注入します。
注入方法は後述します。

これで、PullRequestに何かしらの更新が入ると、`pr` resourceに更新の確認をお願いできるようなります。
#### source
このブロックがResourceの定義になります。
記述の仕方も各Resourceによって異なりますので、対象ResourceのREADMEを見て、適宜記述してください。
今回の`pr` resourceには以下を記述します。

|パラメータ|内容|
|:---:|:---|
|repository|github.comに存在する対象のRepository(ORG/REPO)を記述|
|access_token|Personal Access Tokenを記述|

今回は必須のパラメータしか記述していません。
https://github.com 以外のGitHubホストを利用する場合は、`v3_endpoint`、または、`v4_endpoint`を記述する必要があります。
[Source Configuration](https://github.com/telia-oss/github-pr-resource#source-configuration)	

## Task
TaskではInputに対して任意の処理を実行することができます。
必要であれば、処理結果をOutputとすることも可能です。
今回であれば、unit-testを行うだけで良いのでOutputは利用しません。
Resourceは振る舞いを抽象化することで、利用者は最低限の記述で処理を実行できるようになりますが、このTaskは逆で自分でゴリゴリ処理を書いていくことができます。
任意のDockerImage上で動作するのでできないことというのはあまりないと思います。
Jobにて`task:unit-test`は`pr/ci/tasks/unit-test.yml`を利用すると定義したので対象のファイルを記述します。
`pr`は前のステップで取得したpathであり、`pr`の中にはPullReqeustの対象コードが含まれています。
### unit-test.yml
Taskの設定を記述していきます。

```yml:pipeline.yml
---
platform: linux

image_resource:
  type: docker-image
  source:
    repository: golang
    tag: 1.11-alpine

inputs:
- name: pr

run:
  path: pr/ci/tasks/unit-test.sh
```
#### [platform](https://concourse-ci.org/tasks.html#task-platform)
実行するplatformとしてlinuxを選択します。

#### [image\_resource](https://concourse-ci.org/tasks.html#task-image-resource)
ここでTaskを実行するためのDockerImageを記述します。
今回は[`golang:1.11-alpine`](https://hub.docker.com/_/golang/)を利用します。

#### [inputs](https://concourse-ci.org/tasks.html#task-inputs)
このTaskのinputとして必要なものの名前を記述します。

#### [run](https://concourse-ci.org/tasks.html#task-run)	
実行するコマンドのconfigを設定します。
今回はpathだけを指定しました。
(unit-test.shに実行権限を付与するのを忘れないようにしましょう)

### unit-test.sh
taskの設定ファイル(`unit-test.yml`)から指定された実行ファイルです。

```bash
#!/usr/bin/env sh

mkdir -p ${GOPATH}/src/github.com/cappyzawa/pipelines
cp -r pr/* ${GOPATH}/src/github.com/cappyzawa/pipelines
cd ${GOPATH}/src/github.com/cappyzawa/pipelines

go test ./...
```

## Pipleine
ここまでを終えて、Pipelineの設定ファイル全体は以下のようになりました

```yml:pipeline.yml
resource_types:
- name: pull-request
  type: docker-image
  source:
    repository: teliaoss/github-pr-resource
resources:
- name: pr
  type: pull-request
  check_every: 24h
  webhook_token: ((webhook-token))
  source:
    repository: cappyzawa/pipelines
    access_token: ((github-access-token))
jobs:
- name: test-pr
  plan:
  - get: pr
    trigger: true
  - put: pr
    params:
      path: pr
      status: pending
  - task: unit-test
    file: pr/ci/tasks/unit-test.yml
    on_success:
      put: pr
      params:
        path: pr
        status: success
    on_failure:
      put: pr
      params:
        path: pr
        status: failure
```

このPipelineをsetします。

```bash
$ fly -t <target> set-pipeline -p sample-unit-test -c ci/pipeline.yml -v webhook-token=token -v github-access-token=XXXXXXXXX
```

Pipelineの設定ファイル中にて`(())`と表記したパラメータに`-v`で値を注入します。

|パラメータ|内容|
|:---:|:---|
|`webhook-token`|GitHubのWebhookのpayloadに設定したtoken。今回は`token`として登録している。|
|`github-access-token`|GitHubのPersonalAccessToken|

## まとめ
ConcourseのPipelineを開発する上で知っておくべき以下の概念の説明と簡単な例を示しました。

* Job
* Resource
* Task

今回は継続的にPRのunit-testを行うpipelineを作成しましたので、最後に更新時のgifを添えます。

![qiita-concourse mov](https://user-images.githubusercontent.com/12455284/49343115-4573fa00-f6a7-11e8-8b52-4df6c6c42eb8.gif)

※ 今回はlocalに立てているConcourseであったためWebhookを使えなかったのですが、GitHubからアクセス可能な場合は上図のように動作します