【2019年11月版】Smee.io で Concourse CI の Webhook をトリガー！

# Smee.io から Concourse CI の Webhook を叩いてみるサンプルです。

はい、本日のConcourse CIネタ第２弾です！
[Github.com の Probot](https://probot.github.io/) に憧れてますが、Github.com を使う案件なんて手元にない(泣)ので、ミニマム過ぎる実験といたしまして、生の smee-client から Concourse CI の Webhook を叩いてみる、ってことをやってみたいと思います。

Concourse CI は[前回に引き続きKeycloakとSSOでログインしっぱなし](https://qiita.com/koinori/items/5f0c21c6bd3dd2072f1b)として進めさせていただきます。
つまり、`fly`コマンドは "keycloakでログイン済み" という状態です。

また開発環境としては `node.js 10` を使用します。

今回は実装メインなのでさっさと行きますよ〜！

## 1. Webhook を受け付けるパイプラインの作成

まず、Webhook を受け付けるパイプラインを作成しましょう。簡易に以下のようにいたしました。

```yml:say-hello.yml
resources:
- name: testApp
  type: git
  source: { uri: "https://github.com/...../......" }
  check_every: 8h
  webhook_token: test_token

jobs:
- name: hello-world
  plan:
  - get: testApp
    trigger: true
  - task: say-hello
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: alpine}
      run:
        path: echo
        args: ["Hello, world!"]
```
こちら、gitの`uri`で指定されたリポジトリをチェックして新しいバージョンがあったら,"Hello, world!"を出力するシンプルなパイプラインです。
実際に動作確認をする際には自身がコミットできるリポジトリを指定してください。

これを以下のコマンドでデプロイいたします。

```shell
$ fly -t hello set-pipeline --pipeline sayHello --config say-hello.yml
...
```
デプロイして良いか確認が出ますので、`y`でデプロイを行ってください。

これで上記のymlから `sayHello` パイプラインが作成されました。

続いてブラウザで開き、パイプラインの実行を開始しておきます。

![sayHello - Concourse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/93a44a1c-5af1-fc18-146e-7db40e88db67.png)

画面の右上のボタンですね。初期時は"再生"ボタンとなってます。ここをクリックして実行させてください。
ただし、今回のリソースは**８時間ごとにgitをチェック**しに行きます。
これを Webhookで即時チェックさせてみたいと思います。

## 2. smee.io でチャンネルの作成

[smee.io](https://smee.io/) にアクセスし、"Start a new channel" をクリックすると新しいWebhook用の URLが生成されます。 ~~(たったこれだけで世界中からWebhookが受信できてしまうのです。。。素晴らしい。。。)~~

このブラウザの画面は立ち上げっぱなしにしておいてください。

## 3. プロジェクトの作成

準備は以上ですので実装に取り掛かりましょう！
まずは `npm init` コマンドでプロジェクト作成です。

```shell
$ npm init
```

続いてプラグインのインストールですが・・・ smee-client はsmee.ioからのイベント受けたらそのまま別のURLにPOSTする、というのがお仕事ですので、smee-clientからの着信を受け付ける `express` と、expressが `POST` されたら Concourse の Webhook に POST する `phin` を追加します。
ついでにURLなどをプログラムに直打ちしないように `dotenv` も入れて・・・

```shell
$ npm i smee-client express phin dotenv 
```
でインストールします。

`.env` ファイルには環境変数として以下のように接続先を設定しておきます。

```:.env
SEEM_CHANNEL=https://smee.io/xxxxxxxxx
CC_WEBHOOK_URL=http://cc.ホストのIP.xip.io:8080/api/v1/teams/myAppDevTeam/pipelines/sayHello/resources/testApp/check/webhook?webhook_token=test_token
```

pakcage.json には `start` スクリプトを追加しておきます。

```json:package.json
...
  "scripts": {
    "start": "node index.js",
...
```

本来は ts を使いたいのですが、今回はちょっとお試しということで js のままで行きます。

## 4. index.js の実装

本体コードは対してボリュームもないのでドーンと載せてしまいます。

```js:index.js
require('dotenv').config()

const SmeeClient = require('smee-client')

const smee = new SmeeClient({
  source: process.env.SEEM_CHANNEL,
  target: 'http://localhost:3000/events',
  logger: console
})

const events = smee.start()

const express = require('express')
const app = express()
const p = require('phin')

app.post('/events', async (req, res) => {
    console.log("called");
    await p({
        url: process.env.CC_WEBHOOK_URL,
        method: 'POST',
        data: { 
            say: "hello"
        }
      })
});

app.listen(3000, () => console.log('Example app listening on port 3000!'))
```

まぁ、最初に述べた通り、smee-client → express → phin → Concourse の流れ、ですね。。。

また、Concourseにサンプルとして`{ say: "hello" }` POSTしてますが、これはダミーです。なくても影響ありません。
というか、Concourse CI のWebhook は純粋にトリガーとして働くだけなので外部から値を渡すことはできない模様です。

[こちらのマニュアル](https://concourse-ci.org/resources.html)にもありますが、

> Note that the request payload sent to this API endpoint is entirely ignored. You should configure the resource as if you're not using web hooks, as the resource config is still the "source of truth."

というわけで、pipelineで設定した値で常に動作しますよ、というポリシーです。

## 5. 動作確認

まず、以下のコマンドで smee.io の Webhoook を待ち受け開始します。

```shell
$ npm start
...
```
express が起動し、smee.ioの接続できた〜などのメッセージが流れてくると思います。

続いて、最初に指定した git のリポジトリに commit & push をして、headを動かしておくのを忘れないでください。

で、以下の curl コマンドで `smee.io` に POST します。

```shell
$ curl -X POST -d '{"sample":1234}' -H 'Content-Type: application/json' https://smee.io/xxxxxxxxxxxxxx
```
ここでデータにJSON文字列を指定してますが何か中身がないと client でエラーメッセージが出る？ので埋めているだけです。これもダミーですね。

smee.io の画面をチェックしてください。
![smee io   Webhook deliveries.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/a9e5741f-12b5-dfd9-84af-b8a196a48892.png)
このように、配信されたデータのログが表示されていればOKです。ちなみに、この画面から"再送"も可能です。便利だ。。。

しばらくすると Concourse CI のタスクが黄金に輝くオーラを纏い、実行されているのがわかります！
![Start job_sayHello - Concourse (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/7e4c7c18-bee9-b372-06dd-8b066bd004c8.png)
オーラが消え、グリーンのラベルになったらクリックすると・・・
![hello-world  4 - Concourse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/dcea5352-1a75-7bfb-b649-740897996fd8.png)
`Hello, world!` とコンソールに表示されていることが確認できました！

無事に smee.io にポストしたイベントが Concourse CI にて受け取れた模様です。

## まとめ

このように smee.io は非常に簡単にWebhookが受け取れるURLを生成してくれて、クライアントでそのURLを待ち受けていれば、グローバルからのアクセスが必須の**外部サービスのWebhookイベント**を**オンプレで構築したローカル環境で受け取れる**という。しかも**無料**！これは革命《レボリューション》ですよ！

Concourse CI の Webhookはリソースのチェックをトリガーするだけなので、直接、ジョブを実行するわけではありません。今回、一番悩んだのがそこでしたね。。。
実際に変化がなければジョブがトリガーされないので、[既存の Resource Type で割と十分](https://github.com/concourse/concourse/wiki/Resource-Types)だし、ダメなら[自分で作ったってよくない？](https://concourse-ci.org/implementing-resource-types.html)と思いましたね。。。というか、どんだけ充実してるんだ Resource Type。

前回の記事の中身も含めて、今回の成果物は以下のリポジトリに上がってます。ご参考にどうぞ！

- [concourse-smee-ci | github.com](https://github.com/Yoshinori-Koide-PRO/concourse-smee-ci)

本日は以上といたします！