🔀Rust クロスコンパイル
Docker for Mac内のRustでmacOS向けのプログラムを書けるようにした。
Rustのクロスコンパイルの設定ができたので、Docker for MacでmacOS用のプログラムが書けるようになってきた
— mzp (@mzp) 2017年5月2日
⭐️要約
以下のDockerfireでクロスコンパイルのできるイメージを作成できる。
FROM multiarch/crossbuild # reinstall osx cross compiler to link with SDK 10.7 ENV DARWIN_OSX_VERSION_MIN="10.7" RUN rm -rf /usr/osxcross \ && mkdir -p "/tmp/osxcross" \ && cd "/tmp/osxcross" \ && curl -sLo osxcross.tar.gz "https://codeload.github.com/${OSXCROSS_REPO}/tar.gz/${OSXCROSS_REVISION}" \ && tar --strip=1 -xzf osxcross.tar.gz \ && rm -f osxcross.tar.gz \ && curl -sLo tarballs/MacOSX${DARWIN_SDK_VERSION}.sdk.tar.xz \ "${DARWIN_SDK_URL}" \ && yes "" | SDK_VERSION="${DARWIN_SDK_VERSION}" OSX_VERSION_MIN="${DARWIN_OSX_VERSION_MIN}" ./build.sh \ && mv target /usr/osxcross \ && mv tools /usr/osxcross/ \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/omp \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-macports \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-mp \ && rm -rf /tmp/osxcross \ && rm -rf "/usr/osxcross/SDK/MacOSX${DARWIN_SDK_VERSION}.sdk/usr/share/man" # install toolchain RUN curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain stable -y ENV PATH=/root/.cargo/bin:$PATH # install target RUN rustup target add x86_64-apple-darwin WORKDIR /work
カレントディレクトリに hello.rs
を配置した上で、以下のコマンドを実行すると、macOS向けのバイナリが生成される。
docker build -t rust-cross-compile . docker run -v $PWD:/work -it -e CROSS_TRIPLE=x86_64-apple-darwin rust-cross-compile rustc --target=x86_64-apple-darwin hello.rs
📝調べたこと
上記のDockerfileを書くまでに調べたことをメモしておく。
標準ライブラリ
macOS向けにビルドされた標準ライブラリをインストールする。 これは rustup
から行なえる。
$ rustup target add x86_64-apple-darwin info: downloading component 'rust-std' for 'x86_64-apple-darwin' info: installing component 'rust-std' for 'x86_64-apple-darwin'
リンカ
リンカがMach-O形式を理解しないので、ビルドできずエラーになる。
$ rustc --target=x86_64-apple-darwin hello.rs error: linking with `cc` failed: exit code: 1 | (snip) = note: hello.0.o: file not recognized: File format not recognized collect2: error: ld returned 1 exit status error: aborting due to previous error
multiarch/crossbuildが各ターゲットのツールチェインを含んでいるのでこれを使うようにする。 このイメージには crossbuild
というツールチェインを切り替えるシェルスクリプトが含まれているので、これを使うようにする。
CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs
macOS向けの設定
標準ライブラリは OSX 10.7 向けにビルドされているが、multiarch/crossbuild に含まれるリンカは 10.6向けのものなので、うまくビルドできない。
CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs error: linking with `cc` failed: exit code: 1 | (snip) ld: warning: object file (...) was built for newer OSX version (10.7) than being linked (10.6) ld: targeted OS version does not support use of thread local variables in __ZN4core6result13unwrap_failed17h374a94f6b7a942a9E for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) error: aborting due to previous error
そのため、macOS向けのツールチェインを再ビルドする。
export DARWIN_OSX_VERSION_MIN="10.7" rm -rf /usr/osxcross \ && mkdir -p "/tmp/osxcross" \ && cd "/tmp/osxcross" \ && curl -sLo osxcross.tar.gz "https://codeload.github.com/${OSXCROSS_REPO}/tar.gz/${OSXCROSS_REVISION}" \ && tar --strip=1 -xzf osxcross.tar.gz \ && rm -f osxcross.tar.gz \ && curl -sLo tarballs/MacOSX${DARWIN_SDK_VERSION}.sdk.tar.xz \ "${DARWIN_SDK_URL}" \ && yes "" | SDK_VERSION="${DARWIN_SDK_VERSION}" OSX_VERSION_MIN="${DARWIN_OSX_VERSION_MIN}" ./build.sh \ && mv target /usr/osxcross \ && mv tools /usr/osxcross/ \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/omp \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-macports \ && ln -sf ../tools/osxcross-macports /usr/osxcross/bin/osxcross-mp \ && rm -rf /tmp/osxcross \ && rm -rf "/usr/osxcross/SDK/MacOSX${DARWIN_SDK_VERSION}.sdk/usr/share/man"
ビルド
これでビルドできるる。
$ CROSS_TRIPLE=x86_64-apple-darwin crossbuild rustc --target=x86_64-apple-darwin hello.rs $ file hello hello: Mach-O 64-bit x86_64 executable
複数列Slack
Slack のデスクトップアプリは、1つのチャンネルしか表示できないため一覧性が悪い。 そこで、複数のチャンネルを一度に表示できるアプリを作った。
📦ダウンロード
https://github.com/mzp/SlackStack
😫チャンネル切り替え作業の増加
Slack のデスクトップアプリは、チャンネルを1つしか表示できない。
そのため、参加するチャンネルに比例し、切り替え作業が増えていく。 さらに切り替え作業中に未読が増え、延々とチャンネルを切り替え続けることになる。
また同様の理由でメッセージを見落すことも増え、チャットのレスポンスも悪化していく。
📑Slack☆Stack
そこで一画面で複数チャンネルの内容を確認するためのアプリを作成した。 オフィスでは縦置きのモニタを使っているので、横方向にも縦方向にも重ねれるようになっている。
🔧開発の様子
ブラウザを並べる
Slackを開いたブラウザを複数並べて、複数のチャンネルを見れるようにした。 Magnet – Window manager for Macでウインドウを整列した上で、Slack: Hide sidebar when window is narrow | Userstyles.orgでチャンネルリストを非表示にした。
Slackで複数チャンネルの様子をうかがうのつらいんですが、みなさんどうしてるんですか。
— mzp (@mzp) 2017年4月21日
とりあえずブラウザの別ウインドウで開きまくって横に並べてる。
Slackアプリ
WebViewだけはりつけたら、あっというまにslackアプリになってしまった pic.twitter.com/aINOAxPgsh
— mzp (@mzp) 2017年4月29日
WebViewを貼りつけただけのアプリを作ったら、それなりに動くようになった。 Slackはすごい。
複数行化
複数チャンネルSlack pic.twitter.com/G4IJ9vV6UE
— mzp (@mzp) 2017年4月29日
そのまま横に並べて複数行化した。 また、読み込み時にカスタムCSSを読み込ませ、チャンネルリストを非表示にしている。
チャンネルの追加、削除に対応
メニューを実装し、チャンネルの追加・削除をできるようにした。
NSStackView
NSStackViewを使い、何個でも横に並べれるようにした。
何個でも横に並べれるようになった pic.twitter.com/bReSsfaEDF
— mzp (@mzp) 2017年4月29日
行の追加
複数行、複数列モード pic.twitter.com/ame7S4nJSG
— mzp (@mzp) 2017年4月30日
NSStackView自体をNSStackViewで重ねるようにし、縦方向にも重ねれるようにした。
fastlane
fastlane/gym at master · fastlane/fastlane · GitHubを使って、ビルド・署名するようにした。 LoveLiverのGymfileを参考にしている。
アイコン
StackのアイコンとYosemite風のアイコンが簡単に作れるツール作った - Qiitaを組合せて、アイコンを作った。
配布用zip
Github releases にアップロードするためにzipファイルを作成したら、なぜか署名が破損した。 Finderから圧縮するようにしたら破損しなくなった。
macOSのアプリをzipしてunzipしたら、署名部分が破損した..
— mzp (@mzp) 2017年4月30日
finderで圧縮すれば大丈夫
— mzp (@mzp) 2017年4月30日
🐬Docker for Mac without qcow2
Docker for MacのファイルIOが遅い。VirtualBox上で動いているDockerと比較しても遅い。
これにはいくつかの原因があるが、その1つとしてディスクイメージのフォーマットとしてqcow2を用いていることがあげられる。
qcow2は書き込み時に必要な容量を確保するcopy on write方式のディスクイメージだが、これを事前に必要な容量をすべて確保するrawファーマットに差し替えたところ、パフォーマンスが改善した。
使い方
既存のDockerイメージは消えます
https://github.com/mzp/docker.img
curl https://raw.githubusercontent.com/mzp/Docker.img/master/install.sh | bash
ベンチマーク
fioを用いてベンチマークを行なったところ、Docker Machine(VirtualBox)と同程度のパフォーマンスはでるようになった。*1
読み込み
Average(MiB/s) | Min(MiB/s) | Max(MiB/s) | |
---|---|---|---|
native | 361 | 361 | 361 |
docker machine | 105 | 105 | 105 |
qcow2 | 46 | 46 | 46 |
raw | 147 | 147 | 147 |
書き込み
Average(MiB/s) | Min(MiB/s) | Max(MiB/s) | |
---|---|---|---|
native | 487 | 487 | 487 |
docker machine | 51 | 51 | 51 |
qcow2 | 29 | 29 | 29 |
raw | 45 | 45 | 45 |
やっていること
Docker for Macはhyperkitを使い、macOS上でLinuxを動かし、その上でDockerを動かしている。
hyperkitはrawフォーマットのイメージに対応しているので、Docker for Macが起動する際にhyperkitに渡す引数を変更することで、ディスクイメージをqcow2からrawに差し替えている。
ただ起動時に渡す引数を設定で変更することができないので、hyperkitのバイナリをシェルスクリプトに差し替えて、無理矢理変更している。
*1:実行したコマンド等は https://github.com/mzp/docker.img に書いた
ぺろぺろ - Github pull request bot framework -
名古屋Ruby会議03で発表した。
発表資料
関連記事
原稿
導入
自己紹介
こんにちは、mzpです。
大須はたまにくるので、この大須演芸場も気になってはいたんですが、なかなか入る機会がありませんでした。 まさかこっち側に立つのが最初だとは思っていませんでした。
よろしくお願いします。
会社紹介
仕事では、Misocaという会社でMisocaというWebサービスを作っています。この名古屋Ruby会議のスポンサーでもあるそうです。すごいですね。
Github
会社としてのMisocaはおいといて、サービスとしてのMisocaはGithubのプライベートレポジトリ上で開発を進めています。そのためGithubが開発の中心になります。
プルリクエスト
さらに言えばプルリクエストが中心になります。そのためプルリクエストにまつわるさまざまなルールや運用が生まれます。例えば「コンフリクトに気付いたら声をかけてあげようね」ですとか「githubでコメントしたらSlackでも教えてあげよう」などなどなどです。 最初はこれでもよいのですが、だんだんと面倒になっていきますよね。
bot
そこでbotを作り、こういった運用を自動化しました。
このbotは機能を固定したものでなく、gemによって拡張できるbot frameworkとして作りました。 今日はそのbot frameworkを紹介したいと思います。
使い方
まずは簡単に使い方を話します。
Deploy to heroku
レポジトリに deploy to herokuボタンがあるので、押せばherokuにデプロイできます。
Webhookの設定
あとはGithubでWebhookの設定をすれば動きます。
カスタマイズ
デプロイされたgitレポジトリをcloneしてきてGemfileを更新すれば挙動を拡張できます。 例えば、コンフリクトしているプルリクエストにラベルを自動でつけるようにしたい場合は、Gemfileに gem ‘prpr-conflict_label’ と追記してpushすればOKです。
既存プラグインの紹介
次は各gemでできることを紹介していきます。
prpr-checklist
各プルリクエストに自動でチェックリストを投稿するgemです。
機能を更新したけどヘルプを更新するのを忘れた、とか、バリデーションルールを追加したら既存データと矛盾するようになってしまった、みたいな単純な失敗をしてしまうことありますよね。 そういうときは古来よりチェックリストを使うとよいと言われているので、それです。
チェックリストの内容
チェックリストの内容は、レポジトリ上にあるCHECKLIST.mdという名前のファイルを使うようにしているので、変更も容易ですし、バージョン管理もできます。
余談: これを作ったときにはなかったんですが、今のGithubにはプルリクエスト テンプレートがあるので、そっちつかってもいいかもしれませんね。
prpr-mention_comment
コメント中に「@xxx 」といった文字列が含まれていた場合は、そのままSlackに転送するgemです。
Githubの通知をSlackに流している方も多いと思うんですが、GithubコメントでメンションしてもSlackではメンションにはならないんですよね。 これだとなかなか気づけませんし、気付いてもらえません。
Slackの通知は気付いてくれる人が多いので、これで解決です。
アカウント名の対応
工夫ポイントとしては、Slackのユーザ名とGithubのユーザ名が違う人もいるので、ユーザ名の変換もやっていることです。 これはレポジトリのルートに以下のようなファイルを置くことでカスタマイズできるようになっています。
prpr-gemfile
gemGemfile.lockのdiffを確認して、メジャーバージョンアップとマイナーバージョンアップをしている箇所にコメントをいれるgemです。
定期的にbundle updateをしてプルリクエストを送るbotを飼っているんですが、毎回Gemfile.lockの差分を見ながら「ふむふむこれはbugfixだけだし、いれていいでしょ」「う、こっちはメジャーバージョンがあがってる。ちゃんと調べなきゃ」というのをやるのは面倒なので自動化しています。
prpr-conflict_label
何本もプルリクエストが並行して動いてる状況だと、ちょいちょいコンフリクトが発生します。ただ、Githubのプルリクエスト一覧ではどれがコンフリクトしているかがわからず、個別のプルリクエスト画面をひらいて初めてコンフリクトしていることがわかります。
マージイベントが発生するたびに全プルリクエストを確認し、コンフリクトラベルのつけはずしをするようにしましたgemです。
これで一覧をみるだけでどれがコンフリクトしているかが分かるようになります。
まとめ
以上が、標準的なprprのgemです。 たぶんだいたいのシチュエーションで便利だと思います。 ボクも自分のプライベートプロジェクトで使っていたりします。
Misoca開発フローとの統合
これらとは別にMisocaでの開発フローにあわせて作ったgemもあります。 次はこういったgemについて紹介していきたいと思います。
開発フロー
まずは、我々の開発スタイルについて簡単に説明します。
- 各開発者がプルリクエストを作る。このときはまだWIP(work in progress; 作業中)というラベルをつけておき、まだレビューしなくてもいいよ、ということを明示します。
- プルリクエストが完成したら、レビューを依頼する。 指摘がついたら対応する。
- 最低1人、できれば2人がOKを出したあとでマージする。
- いくつかマージされたプルリクエストがたまってきたらデプロイする。
その他の特徴
補足情報としては、
- レビュー担当者などは特に固定せず、互いにプルリクエストレビューを行なう
- 基本的にはSlackを使ってやりとりする
といった特徴もあります。
prpr-review_label
レビュー依頼部分を支援するgemです。 このgemはREVIEWというラベルがついた場合は、Slackに「レビュー待ちにしました」という通知を流します。
この通知にはラベルをつけた人の名前とアイコンを使うようにしているので、誰が依頼をしたかがすぐ分かるようになっています。
prpr-lgtm
次は「マージする」という部分を支援するgemです。最初は雰囲気でやっていたのですが、
そこで、 * OKの数をラベルとして表現する * 二人がOKをした場合は、プルリクエストにassignされている人にメンションを飛ばす というbotを作りました。
一覧をみるだけで何人がOKをだしているかがわかる、二人がOKを出いた瞬間に気づけるようになり、テンポよくマージしていけるようになりました。
自己
余談ですが、このgemを作る過程でうっかり暴走させてしまい、同僚に大量のメンションを飛してしまい、たいへん申し分けない感じになりました。ごめん。 これは自分のコメントに反応してさらにコメントをつけてしまい、それに反応して…という図です。
prpr-merged
「マージする」という部分を支援するgemはもう一つあります。
「マージする」という行為は影響が大きいので、できるだけみんなに把握しておいてほしいです。 そのため、マージが発生するとslackに通知されるgemを作りました。
もちろんslackのgithub連携機能でも同じ情報は流れてきますが、よりマージを目立たせるという効果があります。
デプロイ
最後はデプロイの支援です。
Misocaのデプロイはチャット経由でできるようになっています。
- チャットで「@bot デプロイしたい」と発言する
- botがデプロイ用の内容をまとめたプルリクエストを作る。 これはマージ先がmasterではなくリリース用ブランチになっています。
- デプロイ用のプルリクエストをマージすると、デプロイプロセスがはじまる
- デプロイされる
という流れです。
このうち、3の「デプロイ用のプルリクエストをマージすると、デプロイプロセスがはじまる」の部分はprprがやっています。
prpr-code_deploy
Misocaのデプロイ作業はAWSのCodedeployを使っています。 codedeployにコミットIDを送ると、デプロイしてくれます。
そこで、デプロイ用のブランチにマージされた場合は、AWS codedeployにそのコミットIDを送り、デプロイプロセスがはじまるようにしています。
まとめ
以上がMisoca開発におけるprprの使い方です。
あとはこのprprを作ったときの話をしていきたいと思います。
先行事例
作るときに参考ににしたものや、あとから見つけたやつなどがあるのでそれを紹介します。
クックパッド
チェックリストの投稿などはクックパッド開発ブログで紹介されています。 ただこれは自分たちで作っていこうな、という趣旨の記事なので、汎用のbotがあるわけではありません。
trailing space bot
Github上で動作するbotというのもいくつかあります。 たとえば、末尾のスペース(trainling space)を検知して修正するプルリクエストを作成するbotもいます。
これはそいつが送ってくるプルリクエストです。
しかしこれは挙動が固定されているため自分たちにあわせて機能を追加・削除していくのは困難です。
拡張可能なbot
プラグインで挙動を拡張できるbotは多数存在しており、開発関係だとhubotやrubotyなどが有名な気がします。
ただこれはチャット経由でなにかする、というものがほとんどで、プルリクエストに反応して何かをするものは見付けれませんでした。
設計
目標
自由に拡張できるgithubのbotフレームワークはないことがわかったので、自分で作ることにしました。 作成時に標榜した目標は以下の通りです。
gemにより挙動を拡張できる。 コア部分と、自分たちの開発を便利にするための機能を切り離す。
botの設定変更は誰でもできるようにする。 これは先ほど紹介したチェックリスト投稿が分かりやすいと思うんですが、チェックリストの内容変更は管理者だけができる、ということになると、チェックリストへの追加作業のハードルがあがってしまいます。そこで、レポジトリ上のファイルが設定ファイルになるようにし、レポジトリにコッミトできる人間なら誰でも設定変更できるようにしました。 設定変更もgitで履歴管理されるようになるのもメリットです。
herokuで動く。 自分でちゃんとデプロイするの大変ですしね。
すばやく作る。 とりあえず日々の運用を楽にするのが先決なので、わりとさっさと作りたかったです。
構成
構成としては以下のようになっています。
- どのイベントに反応するかを決めるHandler
- 挙動を決めるAction
- 環境変数やレポジトリ上のファイルから設定を読み込むsettings
- Slack等に通知するpublisher
といった部分で構成されています。 またイベントを受け取る部分は通常のwebhookの他にテスト用のCLIもあります。
各プラグインがやるのがhandler/actionあたりです。
例: Handler
マージ通知をするgemを例に、もうちょっとだけ具体的に説明します。
マージだけに反応するればいいので、handlerは以下のようになっておりプルリクエストのcloseに反応するようになっています。
例: Action(½)
Actionはこのようになっております。
- 本当にマージされたかどうかの判定
- publisher経由で通知する
例: Action(2/2)
送信するメッセージは、このように環境変数から読むようになっています。
やらなかったこと
prprは最初のバージョンを早めに出して、プルリクエストにまつわる運用を楽にしたかったので、割り切って作った部分もいくつかあります。
WebUIを作らない。 設定画面等をつけようかとも思ったんですが、認証とかを考えると面倒そうだったのと、Webhook受け取るだけでとりあえず動くだろ、ということでWebUIをつけるのはやめました。
Bitbucketのことは忘れる。 最初はGithubとbitbucketの両対応をしようかと思ったんですが、抽象レイヤーを作るのが面倒そうだったのと、bitbucketを使う予定がないので忘れることにしました。
命名
次は名前の話ですね。
コードを書きはじめる前に名前を決める派なので、けっこう初期からこの名前でした。 プルリクエスト=PRに反応するbotつくりたいけどなにがいいかなー?って相談しました。 よくみると夜中の12時くらいに決めてるので、まあそういうことだよなって気持ちになりました。
ヲタクっぽくて気にいっています。
よかったところ
あとは感想です。
- gem化したのでどこでからがプラグインかが明確になって、クラス設計等がしやくなった
- CLIでテストできるようにしたのでデバッグが楽
- 外部のAPIを叩く部分をすべてプラグインにしたので、本体のspecでモックを作るのが簡単だった。
苦労したところ
- githubの新機能(Approve/Project/…)などの機能がなかなかAPIにおりてこない。 ReviewのApproveのAPIがくるまで一ヶ月ほど待ちました。
- PreviewAPIはoctokitにはいってこないので、わりと無理矢理呼ぶことになる。 こんな感じです。
- gemに分割したのでgemspecを書いたり、レポジトリを大量に作るのが面倒だった。
まとめ
まとめます。
今朝書いたシェルスクリプト
こんなシェルスクリプトをいくつか書いてレポジトリにコミットした。
./bin/docker/bundle
#!/bin/sh docker-compose run --rm app bundle "$@"
./bin/docker/rails
#!/bin/sh docker-compose run --rm app bundle exec rails "$@"
できること
./bin/docker/bundle install
とか
./bin/docker/rails db:migrate
とかができるようになって便利。
今までは docker-compose run app bash
してその中で実行していたが、履歴が残らず不便だったのが解消された。 docker-compose run app ....
を毎回打ってもいいんだけど、長くてダルい。
全部
⛄️飛騨・高山旅行
雪と合唱造りの組合せが見たかったので行ってきた。
道中
向う途中でひるがの高原に寄ったが、冗談みたいな量の雪が積もっていた。
古い町並み(昼)
高山市内の古い町並みは予想通りいい感じだった。
謎の器具で雪を落している。
寿司はうまい。
団子もうまい。
牛串も当然うまい。
古い町並み(夜)
夜になるとさらにいい感じになる。
⛄️
ものすごく寒いのでうっかりラーメンを食べる。めっちゃうまい。
合掌造り
本当は白川郷に行くつもりが雪が怖くて断念した。かわりに、高山駅近くの飛騨の里に行った。
立ち入り禁止の札が埋まってて迫力がある。
日が落ちるとライトアップがはじまる。
めちゃくちゃ寒いので火に集まる。
氷菓 舞台巡り
氷菓の舞台巡りをしたが「劇中だとこんなに雪が降ってなかったよな?」という疑問がついて回った。
いろいろな箇所に貼ってあったポスター。
図書館。
劇中で二人で閉じ込められていた神社。ここに放置されたら死ぬのでは?
君の名は 舞台巡り
君の名はの舞台になった飛騨古川も見てきた。
アピールしている。
「君の名はを見て来ました」と言ったら、伝票に書かれた。
近くの美術館で君の名は展をやっていたので見てきた。
等身大パネルより背景の雪の量が気になる。
感想
- 雪が大量にあってたのしい
- 寒い。痛いくらい寒い。
- 日がたつにつれどんどん体調が悪化していき、帰ってきてから風邪を引いた