entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

ClosedなGitLabのURLをSlackに貼ったら展開されるようにした

こんにちは、sue445です。今期の嫁は キュアミルキー です。

tl;dr;

GitLabとSlackを使ってる場合は https://github.com/sue445/gitpandaキラやば〜っ☆ なくらい便利なのでみんな使ってください☆

前置き

ピクシブ社内での開発にはGitLab(オンプレ)とGitHub(GHEではない方)が使われています。

コードレビューの依頼などでSlackにPullRequestのURLを貼ることが多いと思うのですが、GitHubのURLをSlackに貼ったら勝手に展開されて便利ですよね。

GitLabにもOGPがあるのでpublicなリポジトリであればSlackに貼った時に展開されます。

しかし業務で利用しているGitLabのリポジトリは外部から容易にアクセスできなかったり、ログインしないといけないページなのでSlackにURLを貼ってもOGPが取得できません。

そこでClosedなGitLabのリポジトリであってもSlackにURLを貼った時にいい感じに展開するツールを作りました。

それが今回紹介するgitpandaです。

下記が実際にgitpandaでGitLabのURLを展開した画像になります。

gitpandaついて

gitpandaはGo製のwebhookアプリです。

https://github.com/sue445/gitpanda

名前の由来は「GitLab URL expander」を略してgitpanda(ギットパンダ)です 🐼

開発期間はだいたい1週間くらいです。

gitpandaはビルド済のバイナリを https://github.com/sue445/gitpanda/releases で配布しているため、ダウンロードするだけで動きます。

実行例

GitLabのアクセストークンとSlackのアクセストークン取得 1 後に下記のようなコマンドを実行してください。

PORT=8000 \
GITLAB_API_ENDPOINT=https://your-gitlab.example.com/api/v4 \
GITLAB_BASE_URL=https://your-gitlab.example.com \
GITLAB_PRIVATE_TOKEN=xxxxxxxxxx \
SLACK_OAUTH_ACCESS_TOKEN=xoxp-0000000000-0000000000-000000000000-00000000000000000000000000000000 \
TRUNCATE_LINES=5 \
./gitpanda

gitpandaの仕組み

SlackにはURLが貼られた時に任意のwebhookを叩く link_shared というイベントがあるのでそれを利用します。

gitpandaは下記の流れで動いています。

  1. GitLabのURLがSlackに貼られたらgitpandaにリクエストが飛ぶ
  2. gitpandaはGitLabのURLを見て、IssueやMergeRequestなどのAPIを叩き分けていい感じにURLの情報を取得する。(ここが一番大変)
  3. 取得した情報を元のメッセージに埋め込む

だいたい下図のような流れになります。

詳しいことは Unfurling links in messages | Slack に書いてあります。

先行事例(参考プロダクト)

gitpandaはFromAtomの Closedなesaの記事URLをSlackに貼ったら展開されるようにした - pixiv inside にインスパイヤされて作成しました!

技術選択の理由

なぜGoで作ったか?

要件的にSlackからのリクエストを受けられてGitLabのAPIを叩けるwebhookアプリがあればよかったので、開発言語はなんでもよかったというのがありました。

僕は RubyKaigiに登壇した くらいなので日常的に書き慣れている言語はRubyなのですが、ウェブアプリケーションとしての配布&利用しやすさを考えた時に今回の場合クロスコンパイル(複数のOSに対応したスタンドアローンバイナリを作る)がやりやすいGoの方が向いていたというのがあります。

その観点でいえばDockerイメージで配布でもよかったのですが、社内環境ではAWS Lambdaを使いたかった(後述)のでGoを採用しました。

なぜAWS Lambdaを採用したか?

大前提としてwebhookアプリはSlackからのリクエストを受けられる場所(httpやhttpsでアクセスできる場所)に置く必要があります。

pixiv TECH SALON でも発表したのですが弊社のGitLabは社外からアクセスできません。(APIも叩けない)

しかし、CI環境として使っているAWSのVPCからであればNAT Gatewayを通してGitLabにアクセスできるため、gitpandaの設置場所として適していたというのがAWS選択の一番大きな理由です。

またwebhookの特性上Slackからのリクエストが飛んできた時だけ起動していればよいので、必然的にLambda + API Gatewayのような構成になりました。

LambdaとAPI Gatewayは無料利用枠がとてつもなく大きいので 実質無料 と言っても差し支えないくらいのコスパも嬉しいところです。

こだわりポイント

gitpandaを作成するにあたっていくつかこだわりポイントがあるので紹介したいと思います。

linux amd64以外でLambda関係のライブラリを含めないようにして、バイナリサイズを削減した

Go製のアプリをLambdaで動かすためには github.com/aws/aws-lambda-go/lambda をimportする必要があります。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html

しかしこのライブラリをimportするとビルド後のバイナリのサイズが3MBほど増えます。(gitpandaではLambdaで秘匿値を扱う関係でParameter Storeを使っているため aws-sdk-goもimportしてるためもうちょっとバイナリサイズ増えてます)

必要なライブラリをimportするのはいいのですが、Lambdaの実行環境(AmazonLinux = linux amd64)以外のバイナリ(例:MacやWindowsなど)にもLambdaの依存ライブラリが含まれるのは無駄です。

そのため、Build Constraints という仕組みを使ってlinux amd64のビルドの時だけLambdaの依存を含めるようなことをしています。

Goではファイル名の末尾を _linux_amd64.go のようにしておくことでOSがlinuxかつarchがamd64のビルドの時だけ使われるファイルにすることができます。

また、ファイルの先頭に

// +build !linux !amd64

のように書くことでさっき(linux AND amd64)以外の条件でビルドされるファイルを作ることができます。 2

CI上でバイナリをビルドするようにした

gox を使えばGoのプログラムをコマンド一発で並列でクロスコンパイルできるのですが、バイナリの種類が多いとローカルPCでビルドするのが大変です。

そこで、ローカルからgitのtagをpushした時にCI上でgoxのビルドを実行しつつ、ghr でGitHub ReleasesにバイナリをアップロードするところまでCircleCI上で全自動で行うようにしました。

  • goxで20種類のバイナリをビルド
  • ビルドしたバイナリをzip圧縮
  • バイナリとzipをghrでGitHub Releasesにアップロード

の一連の流れが2分弱で実現できています。

詳しくは下記の設定ファイルを読んでください。

https://github.com/sue445/gitpanda/blob/v0.3.4/.circleci/config.yml#L123-L154

LambdaをSAMでデプロイする

Lambdaにデプロイするバイナリだけあってもそれを毎回手でzipをアップロードするのは大変なので、何らかのツールを使って自動デプロイをするのが普通でしょう。

僕はこれまでLambdaをデプロイするのに TerraformApexServerless Framework などを使ってきましたが、今回は自分の経験値を貯めるために敢えて今まで使ったことのなかったSAMを使ってLambdaをデプロイしてみました。

SAMとは?

AWS公式のサーバレスフレームワークです。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/serverless_app.html

Lambda以外にもAPI GatewayやIAMなどの周辺リソースもセットでプロビジョニングできる点ではServerless Frameworkに近いですが、Serverless Frameworkがサードパーティ製でAWSやGCPなど複数クラウドに対応しているのに対してSAMが(当たり前ですが)AWSのみ対応という差異はあります。

AWSでServerless FrameworkとSAMを普通に使う分には大して機能差異はないので、どちらを使うかは好みの問題だと思います。

両方ともバックエンドにCloudFormation使っている関係でちょっと凝ったことをやりたい時やプロビジョニングでエラーになった時はCloudFormationでググることになるので学習コストは同じくらいという所感です。

gitpandaのリポジトリにSAM用のテンプレートを置いているのでLambdaで動かしたい時にはご利用ください。

https://github.com/sue445/gitpanda/blob/master/examples/aws_sam_template.yaml

また、こちらがGitLab CIでgitpandaをデプロイするための設定のサンプルになります。(バケット名とスタック名以外はピクシブ社内で使っているものと同一です)

masterブランチで .gitlab-ci.ymlGITPANDA_VERSION を書き換えるだけで全部デプロイするようになっています。

Dockerイメージが使えればCircleCIなどでもちょっと改変するだけでいけると思うのでご活用ください。

社内の喜びの声(リアクション)

まとめ

gitpandaはGitLabとSlackを使っている場合にはマジでライフチェンジングなツールなので是非ご活用ください!


  1. Closedなesaの記事URLをSlackに貼ったら展開されるようにした - pixiv inside 参照 

  2. Build Constraintsだと空白はORなので「!linux !amd64」=「(NOT linux) OR (NOT amd64)」=「NOT (linux AND amd64)」という感じです。2つ目の等号がどうして成り立つか分からない人は「ド・モルガンの法則」でググってください。 

20191219021925
sue445
2018年7月に中途入社。CIおじさん。好きな言葉は「全ての手作業を自動化」。 好きなアニメは女児アニメ全般(プリキュア・プリティーシリーズ・アイカツ)とサザエさん。 多数のgemをOSSで公開 https://rubygems.org/profiles/sue445 。代表作はプリキュアのRuby実装のrubicure (https://github.com/sue445/rubicure)