octahedron

LemとSKKとCommon Lispでたたかうプログラマのブログ

Niko --- GitHub上のメンションをSlackでお知らせ

Niko

 NikoというGitHub上のメンションをSlackで教えてくれるソフトウェアをつくりました。
 もちろんCommon Lisp製です😎

github.com

 NikoはGitHubのWebhookとして動作し、GitHub上のissueやプルリクエストの本文や、それらに対するコメントやレビューの中のメンション(@usernameの文字列)を検出して、Slackで通知してくれます。GitHubとSlackそれぞれのユーザ名は、事前に管理画面より登録しておく必要があります。

 Webアプリケーションとしてはかなり小さい(3画面+1APIパス)ものですが、ちょっとだけ特徴があって、それは、深町さんがつくっているフルスタックWebアプリケーションフレームワークUtopianを利用していることです。

github.com

 使ってるという話を(深町さん以外では)耳にしないので、もしかしたら使っているプロジェクトの10番台くらいはいけているかも…?

Nikoの導入から起動まで

 このソフトウェアは会社で絶賛稼動中で、個人的にはとっても重宝しています。ここでは、Nikoのデプロイ方法を記します。

 Nikoをサーバにデプロイする場合、NikoとUtopianはQuicklispでは公開されていないので、Common Lispの処理系マネージャRoswellでインストールするか、GitHubからクローンしてくる必要があります。Roswellスクリプトを利用する作業もあるUtopianはRoswell経由で、そうではなく純粋に起動するのみのNikoはGitHubよりクローンして、手元に導入します。

 まずは、Utopianの導入から。

$ ros install qlot  # 依存ライブラリのバージョン固定
$ ros install lake  # タスクランナー
$ ros install clack  # Webアプリケーション環境
$ ros install fukamachi/utopian

 Nikoのほうは、GitHubからクローンして好きなディレクトリに置きます。

$ cd /path/to/dir
$ git clone https://github.com/t-sin/niko
$ cd niko

 UtopianはPythonでいうところのvenv環境のように、qlotで切り離された環境を前提とします。まずはその環境を初期化して、依存ライブラリをインストールします。

$ qlot install

 これで準備完了です。起動しましょう。

$ export GITHUB_TOKEN=xxxxxxxx
$ export SLACK_TOKEN=yyyyyyyy
$ export SLACK_CHANNEL=niko-channel
$ qlot exec clackup app.lisp

 起動すると、デフォルトではポート5000番で起動します。http://localhost:5000/にアクセスして、こんな画面が表示されたら成功です。

f:id:t-sin:20180723225841p:plain

Nikoの使い方

 Nikoはユーザ情報管理用の管理画面とWebhook用のAPIからなります。管理画面はトップ画面(上の画像)以外に、

  • ユーザ追加画面 /users/add
  • ユーザ一覧画面 /users/lists

があります。読んで字の如くなんですが、GitHubユーザとSlackユーザのマッピングを追加する画面と、一覧を表示する画面です。ちなみに、削除画面はまだないんですが、まあすぐに追加できると思うので、あとで付けます。

 あとは、NikoをGitHubの好きなリポジトリや組織のwebhookに設定するだけです。したがって、外部からアクセスできる必要があるので、ngrok等を利用して、起動したNikoを公開しましょう。
 Webhookとして動作するAPIのパスは/api/github/webhookです。

あまり真似してほしくない部分

 Utopianを使ったソフトウェアであるところのNikoなんですが、セキュリティ上ちょっとよくないことをしているので、真似しないでほしいなーという部分があります。

 UtopianはRuby on Railsのような、画面がパスに対応するようなアプリケーションの作成を意図してつくられています。Nikoはひとつのプログラムで管理画面としても、GitHubのWebhookとしても動くようにつくりました。そのため、Utopianが標準で行っているクロスサイトリクエストフォージェリ対策のセッション値チェックをNikoでは外しています

# niko/app.lisp
 (builder
  (:static
   :path "/public/"
   :root (project-path #P"public/"))
  :accesslog
  (unless (productionp)
    :clack-errors)
  (when (config :error-log)
    `(:backtrace :output ,(config :error-log)))
  :session
- :csrf
  *app*)

 おそらくこういう場合は画面をUtopianアプリとして、webhookはningleアプリとして、同時に起動するようにするのがいいかと思います。

 それと、はじめてデプロイするとき、herokuのpostgresを利用して運用しようとしたのですが、cl-postgresがherokuの自己証明書による接続を許可しない(CL+SSLの証明書検査無視オプションを公開してない、気がした)ことでDB接続ができませんでした。早く運用したかったのでその場ではとりあえず諦め、AWS EC2に置いてsqlite3でDBをつくる運用としました。

 ちょっとカッコわるいのでこの2点は真似しないでください。


 ところで、Utopianはまだ開発中ということもありあまりドキュメント化されていません。DBIやORMやテンプレートエンジンなど、複数のライブラリが組み合わさっているので、ソースコードを参照しながら動きを理解しました。

 Utopianを覚えようとして挫折した方を見ているので、ソースコードを読みながら得たことを記事にしてみると、いいかもしれないですね。