GAミント至上主義

Web Monomaniacal Developer.

Cloud BuildのSlack通知をFirebase Functions + TypeScriptで作る

Cloud Buildは同じGCP製品だけあって非常にシンプルにFirebase(WEBしかやったことない)のデプロイを行えますが、ビルド成功、失敗時の通知機能はついてません。

cloud.google.com

Circle CIのように標準ではついていないので、下記の公式ドキュメントのように自分でFunctionsの関数作って行うのが一般的のようです。

https://cloud.google.com/cloud-build/docs/configure-third-party-notifications?hl=ja


でも今回はFirebaseを使ったプロジェクトだったため、Firebase Functions + TypeScriptで行いたいのでTypeScriptの勉強も兼ねて作ってみました。

Firebaseのプロジェクトについては省略
Cloud Functions に TypeScript を使用する  |  Firebase

公式のと違う点は、終了時にビルドにかかった時間を知りたかったので追加した点。
メッセージは面倒だったのでただのテキストにしてます。

Webook URL

Slack側でWebhookのURLを発行します。
slack.com


発行したURLはFirebaseなのでprocess.envは使わずconfigを使って格納しています。

詳しくは下記
環境の構成  |  Firebase

事前に下記のコマンドでセットしておきます。

firebase functions:config:set slack.webhook_url="https://hooks.slack.com/services/***/***/***"

必要なパッケージ

slackの他、ビルド時間を計算するためにdate-fnsを使用しているのでインストールします。

npm install @slack/webhook date-fns

date-fns - modern JavaScript date utility library

ソース

まだいろいろ改善の余地があるけど動いたやつ。
WORKINGはQUEUEDの後にすぐ来るのでスキップしてます。
swich文やめようかな。

slack.ts

import { pubsub, config } from 'firebase-functions'
import { IncomingWebhook } from '@slack/webhook'
import { differenceInSeconds, parseISO } from 'date-fns'

const WEBHOOK_URL = config().slack.webhook_url
const webhook = new IncomingWebhook(WEBHOOK_URL)

function createMessage (data: any): string {
  const status = data.status
  const repoName = data.substitutions?.REPO_NAME
  const startTime = data.startTime
  const finishTime = data.finishTime
  const buildSeconds = differenceInSeconds(parseISO(finishTime), parseISO(startTime))
  const logUrl = data.logUrl
  switch (status) {
    case 'QUEUED':
      return `${status}, repo: ${repoName}`
    case 'SUCCESS':
    case 'FAILURE':
    case 'INTERNAL_ERROR':
    case 'TIMEOUT':
    case 'CANCELLED':
      return `${status}, repo: ${repoName}, buildtime: ${Math.floor(buildSeconds / 60)}min ${buildSeconds % 60}sec \n${logUrl}`
    case 'WORKING':
    default:
      return ''
  }
}

export async function notifySlack (message: pubsub.Message): Promise<void> {
  const body = message.data ? Buffer.from(message.data, 'base64').toString() : null
  if (!body) {
    console.log('body is null')
    return
  }
  const data = JSON.parse(body)
  const text = createMessage(data)
  if (!text) {
    console.log(`${data.status} skipped`)
    return
  }
  await webhook.send({
    text: text
  })
}


↑notifySlackをindex.tsで読み込んでonPublish()に渡します。
topic名'cloud-builds'はCloud Buildを有効にすると勝手にできるやつなので変えることはなさそうなので直書きしてます。

index.tsの抜粋

import { pubsub } from 'firebase-functions'
import { notifySlack } from './slack'

//ほかのやつ
export const notifyBuild = pubsub.topic('cloud-builds').onPublish(notifySlack)

できたやつ

こんな感じで通知が来るようになりました
f:id:uyamazak:20200513181023p:plain



プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

  • 作者:Boris Cherny
  • 発売日: 2020/03/16
  • メディア: 単行本(ソフトカバー)