自前CIサーバをスクラッチで書いた

依存するgitリポジトリが数十あり、Mac上でしか動作しないビルドを動かしている、Jenkinsの移行をすることになった。

Jenkinsについては詳しくない。もちろん勉強すりゃいいという話なのだが、アラフォーで覚えが悪いので、今回は自前で簡単なCIサーバを書いてみました。今回のプロジェクトはRubyメインなので、Rubyで。

ジェンキンスさんと、佐渡島にて

JenkinsはMovable Typeであり、Wordpressである

僕は、JenkinsをMovable Type/Wordpressのようなソフトウェアだと捉えています。

Movable Typeは「ブログ」というコンテンツ形式を広めたソフトウェアです。Wordpressは、Webの管理画面経由で簡単にプラグインを導入することができ、CMSとしてエコシステムが確立しています。

JenkinsのおかげでContinuous Integrationの概念は広まったし、Jenkinsは豊富なプラグインをはじめとしてエコシステムが確立しています。

このブログのバックエンドシステムは、WordpressからJekyllに移行しました。性能上有利だったこと、記事のデータがテキストでGit管理できたこと、Wordpressが持つ高度なユーザ管理機能などをほとんど使っていなかったことなどが移行の理由です。

同じように、今べつにJenkinsを特殊な用途に合わせていじるより、軽く小さなCIサーバを書いてもいいんじゃないか、と考えました。

個人的には、CIサーバを書いたという感覚ではありません。CIの設定がRubyのコードに、プラグインがRubyGemsになる。そういうイメージです。

要件

  • GithubからのWebhookによりビルドスクリプトがキックされる
  • ビルドが失敗したらSlackに通知する
  • ビルドが失敗から成功に戻ったらSlackに通知する
  • ビルド時のstdout/stderrはどっかファイルに取っておいて、HTTPで配信
  • ビルドの多重度は1とし、新しいビルド要求が来たら、前回のビルドはキャンセルする

できたもの

kinkinと名前つけました。

まあまあ単純で、Gemの依存も少なく書けた。git Gem使ったけど、これくらいの処理内容だと必要ないかな。

読み返したら

  • CIConfigをグローバルに参照している
  • ロガーが適当
  • githubのURL生成はGithubクラスにあるべきだよな

という気もしたけど、まあプログラムじゃなくて設定ファイルということで許してちょ。

Sinatraの"set :lock, true"はWebhookの処理をするのに便利。あたまわるい書き方しても破綻しない。ただデカイログ閲覧するとブロックするんで気いつけや。

Q&A

  • Q. もうちょい複雑なジョブのワークフローを実現したいのだが

    • A. makeやDigdagなどを中で呼んであげるといいのでは。
  • Q. ビルドキャンセルはやだ

    • A. cloneやlogとるディレクトリ変えて、並列に走らせてもよいのは。
  • Q. 環境変数がUSERとHOME以外渡ってないんだけど

    • A. なんか悪影響あるといけないので消してますが、必要なんだったら渡すなり、env -iを削除するなりするとよさげ。
  • Q. 複数のプロジェクト扱うにはどうするの?

    • A. Listenするポート分けて複数起動するといいのでは。
  • Q. 特定のブランチだけビルドしたいのだが

    • A. WebhookでやってくるJSON見て頑張って!
  • Q. DockerでJenkins環境固定したら移行しやすいとちゃうん?

    • A. 今回の対象プロジェクトはMac OS X上でしかビルドできなかったんす。
  • Q. このソフトウェアメンテナンスするの?

    • A. 「CIサーバ」というよりは、あくまで「Rubyで書かれた設定ファイルのサンプル」という位置づけです。適当にコピってきて、大胆に書き換えるといいと思います。小さいし。

まとめ

  • Jenkinsわからないおじさん
  • 簡単なCIサーバをRubyで書いた
    • むしろ、CIの設定をRubyのスクリプトにした、というイメージ