小さなエンドウ豆

まだまだいろいろ勉強中

Slack(Bolt)から Github Actions を実行

Github Actions を Slack から実行

背景

先日 Github Actions を使って Nuxt アプリケーションを S3 にアップロードし CloudFront で配信するワークフローを作りました。

h-piiice16.hatenablog.com

ただ、このワークフローではブランチが固定になっており、固定の内容しかデプロイすることが出来ません。
これだと検証用のブランチをステージング環境にデプロイしたいというシチュエーションで困ります。

ブランチの分だけワークフローを書くのも一つの手ですが、できればワークフロー実行時にブランチ名を指定したい…
と探してたところ、ワークフロー実行のトリガーを deployment にすることでブランチを可変にできるという記事を見つけました。

qiita.com

この記事によると Github API の Create a deployment を叩くとイベントが発火し、ワークフローを実行することができます。
また、Create a deployment のリクエスト時にブランチ名をパラメータに渡すこととで、そのブランチの内容でワークフローが実行されるそう。

これで万事解決するのだが、Slack アプリと組み合わせてより簡単にワークフローを実行できないかと思いつきやってみることにしました。

Slack Bolt

Bolt とは Slack アプリを作るためのフレームワークです。
開発言語は Node.js で Bot や自動返信、Slack コマンドなどを作ることが出来ます。

Bolt の入門ガイドが秀逸で、簡単なアプリを 1 時間もかからずに作れるようになります。

slack.dev

今回作るアプリは Nuxt アプリをデプロイするためのアプリです。
流れとしては以下のような感じです。

  1. /deploy という Slack のコマンドをアプリに向けて打つ
  2. 完成イメージにあったようなセレクトボックスが出くる
  3. ブランチを選ぶと Github API にリクエストする
  4. ワークフローが実行されデプロイ

f:id:h-piiice16:20200125222904p:plain
完成イメージ

Bolt 側のソースコードが以下です。

const { App } = require('@slack/bolt')
const https = require('https')

const githubToken = process.env.GITHUB_TOKEN
const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  githubToken: githubToken
})

const url = 'https://api.github.com/repos/hiracky16/nuxt-app-test/branches'
const options = { headers: { 'User-Agent': 'Mozilla/5.0' } }

const deployUrl = "https://api.github.com/repos/hiracky16/nuxt-app-test/deployments"
const deployOptions = {
  method: 'POST',
  headers: { 'User-Agent': 'Mozilla/5.0', 'Authorization': `token ${githubToken}` }
}

app.command('/deploy', async ({ command, ack, say }) => {
  ack()
  https.get(url, options, (response) => {
    let body = '';
    console.log('STATUS: ' + response.statusCode);
    response.setEncoding('utf8');
    response.on('data', (chunk) => { body += chunk })

    response.on('end', (res) => {
      res = JSON.parse(body);
      const branches = res.map(r => { text: r.name, value: r.name })
      say({
        text: 'どのブランチをデプロイしますか?',
        response_type: 'in_channel',
        attachments: [{
          text: 'ブランチを選んでください',
          attachment_type: 'default',
          callback_id: 'select_branch',
          actions: [{
            name: 'branch_list',
            text: 'branches',
            type: 'select',
            options: branches
          }]
        }]
      })
    })
  }).on('error', (e) => {
    say({text: 'ブランチが取得できませんでした。'})
  })
})

app.action({ callback_id: 'select_branch' }, ({ body, ack, say }) => {
  const value = body.actions[0].selected_options[0].value
  ack()
  say(`${value} でデプロイします!`)
  const postData = JSON.stringify({"ref": value})
  const request = https.request(deployUrl, deployOptions, (response) => {
    response.on('data', (chunk) => { console.log(`BODY: ${chunk}`) });
    response.on('end', () => say({text: `${value} でデプロイはじめました。`}));
  }).on('error', (e) => say({text: 'デプロイに失敗しました。'}))
  request.write(postData)
  request.end()
});

(async () => {
  // Start your app
  await app.start(process.env.PORT || 3000);

  console.log('⚡️ Bolt app is running!');
})();

メッセージの形式は以下のサイトにテンプレートがたくさんあるので参考にしました。

https://api.slack.com/tools/block-kit-builder

まずはブランチ一覧をリモートリポジトリから取得します。
それをセレクトボックスにセットし、選ばせます。
選ぶと app.action() が実行され、deployment にリクエストが行き、Github Actions が実行されます。

GITHUB_TOKEN は API を使うために必要だったため「Settings > Developer settings > Personal access tokens」から作りました。
API を叩く際に User Agent を設定しないとエラーになっていしまったためブランチ取得時、Deploy 時どちらも設定しています。

Slack のコマンドを作る際は Slack App の設定画面へ行き「Slack Command」で作ることが出来ます。

f:id:h-piiice16:20200125231319p:plain
コマンド作成画面

余談ですが、入門ガイドでもあった ngrok で localhost:3000 を外部公開するやり方だと立ち上げ直すたびに URL が変わってしまい、
コマンドの Webhooks に加えて Event Subscriptions や Interactive Components のリクエスト URL も変えなければならないため大変でした。。

これで完成イメージのようなアプリが出来上がります。

Github Actions 側の修正

これは Qiita を参考に on 句をpush から deployment に変更しました。

on:  on:
+ deployment:
-  push:      deployment:
-    branches:  
-      - master

これにより Slack 側で /deploy コマンドを実行するだけで好きなブランチの内容でデプロイが可能になりました。

まとめ

今回は Slack コマンドから Github Actions を呼び出して Nuxt アプリをデプロイすることに成功しました。
Slack コマンドから Github Actions が呼び出せるということはデプロイだけでなくテストや他のことを Slack を通じて実行できることがわかります。
また Bolt は他のことにも大いに応用できるので今後もネタが付きないと思われます。