コンテンツへスキップ
GitHubリポジトリ フォーラム RSSニュースフィード

Crystal 自動リリース

Brian J. Cardiff

はじめに

2017年末に共有したように、今年は1.0に向けて計画を立てることから始めました。最初のステップはリリースの自動化の改善でした。12月と1月の一部における寄付のおかげで、これに80時間分の作業を投入することができました。いつものようにご協力ありがとうございます!

以前の状況

長い間、Crystalのリリースと配布プロセスはomnibus-crystalを通じて管理していました。プロセスの部分的な自動化はされていたものの、pkg、deb、tar.gzなどを生成するために、さまざまなディストリビューション用の仮想マシンを起動する必要があるなど、多くの手動ステップが必要でした。

このプロセスは、AryとWajの1〜2人だけが言語のリリースを担当していたときは、私たちのニーズには十分でした。しかし、プロジェクトとコアチームが成長するにつれて、プロセスのいくつかの部分が適切に文書化されておらず、彼らの頭の中の情報や彼らのマシンの芸術的な環境に依存しているのではないかと恐れていました。

リリースプロセスのもう1つの重要な部分は、Webサイトにドキュメントを公開することです。これまで、これらはTravisを使って行ってきました。ビルドにタグを付けると、Travisがdocsコマンドの出力をS3に同期していました。その後、ルーティングのリダイレクトロジックを使用して、/api/Array.htmlのような最新リリースドキュメントへのリンクが/api/0.24.1/Array.htmlにリダイレクトされるようにすることで、記事やCrystal本で前者を使用できるようにしていました。

そしてDockerがあります。今日では、公式のDockerイメージを含まないリリースは少しおかしいように思えます。そのため、各プラットフォーム用のパッケージをビルドして公開した後、Dockerイメージがビルドされ、タグ付けされ、https://hub.docker.com/r/crystallang/crystal/に公開されます。しかし、これも自動化されておらず、場合によっては数日忘れてしまうこともありました。

このビルドの混乱における最後のピースは、CIが現在、コンパイラをビルドするためにDockerイメージを使用するLinuxおよびLinux 32ビットの場合はTravisで実行され、Macビルドの場合はCircleCIに依存していることです。余談ですが、CIで使用されるDockerイメージは、リリースビルド中に自動的に更新されません。

これら全ての手動プロセスにもかかわらず、@waj、@asterite、@jhass、@matiasgarciaisaiaは物事を前進させるために素晴らしい仕事をしてきました。

数回のリリース前、@RX14はいくつかのLinuxディストリビューション向けのパッケージを改善するために、新しいビルドプロセスを作成しました。その結果、omnibusを廃止して、可能な場合はmuslと最新のLLVMバージョンでコンパイラを出荷するマルチステージDockerイメージに賛成することにしました。

私たちの目標

私たちの主な目標は次のとおりです。

  1. ビルドされるマシンのプロビジョニングを含め、リリースされたバイナリをビルドするプロセスを完全に自動化します。
  2. より頻繁かつ苦痛なくリリースできるように、リリースプロセスを円滑化します。
  3. 実際のリリース前に、エコシステム(DB、Kemal、Amber、Luckyなどの最も人気のあるshardを含む)の破損を発見するために継続的にテストします。
  4. 統一されたリリーススクリプトを持つこと(私たち自身の健全性のために)。

これらの目標を達成しようとしている間に、CIに関するいくつかの技術的負債にも対処したいと考えました。

  1. すべてのプラットフォームCIを単一のCI環境に移動します。
  2. CI用のDockerイメージの公開のような、手動によるリリース後のステップを回避します。
  3. 可能な場合は、依存関係のバージョンを更新および統一します。

行ったことと発見した「驚き」

こんにちは、CircleCI 2.0

OSXのみをテストするためにCircleCI 1.0で実行されていたCI設定から、Linux32、Linux64、およびOSXビルドを強調するCircleCI 2.0ワークフローに移行しました。クラウドCIの構成を変更するのは常に遅い作業です。メールでスクリプトを送信し、キューで待ち、見逃したくだらない詳細のために(何度も)失敗し、何度も再試行するようなものです。

Linux 64ビットのナイトリー

CIを実行していたスクリプトを移行した後、サポートされているすべてのプラットフォームで最新のコンパイラの自動ナイトリーイメージの作成に取り組み始めました。

Linux 64ビットをビルドするために、現在@RX14によって作成された新しいdistribution-scriptsを使用しています。これへの移行は、0.23.1に基づいて0.24.1コンパイラをリリースするためにこれらのスクリプトを使用したことがあった(もし不思議に思っていた場合は、0.24.0は事実上削除されました)必要があるいくつかの変更を除いて、ほぼ完全に簡単でした。

OSXのナイトリー

OSXコンパイラをビルドするために、omnibus-crystalリポジトリをdistribution-scriptsに移行しました。単一のリポジトリにすることで、最終的に物事が簡素化されます。CircleCIの時間をあまり費やさないように、以前の状況を少し調整する必要がありました1

最終的にはomnibusを完全に取り除くことができましたが、主な目標はパッケージングを変更するのではなく、プロセスを自動化することでした。

Linux 32ビットのナイトリー

distribution-scriptsビルドシステムはx86_64Linuxパッケージを生成します。32ビットを取得するには、いくつかの代替案がありました。

  1. 以前と同じようにomnibusスクリプトを使用しますが、CircleCIが32ビットマシンエグゼキューターを提供しないため、32ビットVMの代わりにやや公式なDocker i386イメージで実行します。omnibusスクリプトを少しの間維持しているため、これは簡単な道のりであるように思われます。
  2. 以前の32ビットコンパイラから32ビットコンパイラをビルドするdistribution-scriptsのバージョンを作成します。
  3. 以前の64ビットコンパイラから32ビットコンパイラをビルドし、途中でクロスコンパイルを実行するdistribution-scriptsのバージョンを作成します。

2または3を行うと、パッケージングが変更され、現在の64ビットバージョンで導入されたパスの変更が調整されます。ただし、オプション1よりも少し多くの労力が必要になります。

そこで、オプション1を試しました。結果は、BoehmのGCのバグに遭遇しました。これは32ビットDockerコンテナに固有のものであり、Omnibusセットアップで使用していたものよりも新しいリリースで修正されました。

したがって、(バグのあるGCバージョンで)Dockerコンテナ内でCrystal 0.24.1を使用できませんでした。その結果、より新しいGCバージョンで、32ビット用のCrystal 0.24.2を手動でビルドしました。これにより、将来、distribution-scriptsを使用して0.25.0をリリースできるようになります。おそらく、オプション1に固執したままです。

結論:0.24.2用の32ビットナイトリーパッケージはありませんが、将来的にはそれらを導入できるはずです。

ドキュメントのナイトリー

プロセスにドキュメントを含めることも素晴らしいことです。これにより、Travisからの自動公開を停止できます(タグとブランチの場合)。これにより、ナイトリー/タグ付きドキュメントをプレビューしたり、古い未公開バージョンにジャンプしたり、最も重要なこととして、パッケージが公開されると同時にドキュメントをメインサイトに公開できるようになります。これは、リポジトリにタグを付けるときだけでなく、過去に混乱を引き起こしました。なぜなら、サイトにアクセスすると何かを言うが、パッケージにはそれらが反映されないためです。

Dockerのナイトリー

さらに一歩進んで、ナイトリービルドがプロジェクトで機能しているかどうかをユーザーが確認する方法を簡素化したいと考えました。そこで、ナイトリーとしてタグ付けされたDockerイメージを公開するためのいくつかの作業を行いました。ビルド中に作成された新しいLinuxパッケージを使用すると、パッケージがAPTリポジトリでまだ公開されていなくても、CircleCIから直接Dockerイメージをインストールしてプッシュできます。これらのイメージにはリリース最適化を備えたコンパイラがあるため、公式リリースとの唯一の違いはバージョン記述になります(もちろん、ナイトリービルドが同じコミットで実行される限り)。

crystallang/crystal:nightlyおよびcrystallang/crystal:nightly-build Dockerイメージを公開する予定です。前者はstdlibを使用するアプリをコンパイルでき、後者はコンパイラのビルドに必要なLLVMと依存関係を含みます。

コンパイラリポジトリに系統を保持します

新しいCrystalコンパイラがリリースされるたびに、通常、次のコンパイラは新しいコンパイラをベースにビルドされます。ディストリビューションとパッケージングのソリューションは通常、変更されません。コンパイラの系統を追跡できるように、コンパイラの同じリポジトリ内で各タグがどのようにビルドされたかを正確に追跡できると非常に便利です。これを実現するために、ディストリビューションスクリプトに、特定の新しいコミットまたはタグをビルドするために既存のどのCrystalコンパイラを使用すべきかを設定するためのパラメータを受け取るようにしました。

Crystalリポジトリには、ナイトリービルドに使用するディストリビューションスクリプトの特定のリリースの参照が含まれるようになりました。以前のコンパイラに関する情報はCI構成で記述されるようになったため、系統は今後はリポジトリ自体で利用できるようになります。さらに、将来的には異なるブランチが「最新」バージョンを使用するように制約されなくなるという利点もあります。

タグ付きリリースビルド

タグ付きコミットをビルドするプロセスはほとんど同じです。それは良いことです!よりスムーズなビルドプロセスの証です。

CircleCIがディストリビューションパッケージとドキュメントをビルドしたら、次の処理が行われます。

  1. CircleCIビルドから成果物をダウンロードします。
  2. パッケージに署名します。
  3. リポジトリに公開します。
  4. ドキュメントはS3にアップロードする必要があり、新しいディレクトリは最新としてフラグが立てられます。基本的に、今までTravisで行っていたのと同じですが、パッケージを公開するときに全体としてトリガーされます。
  5. 署名付きパッケージでDockerイメージをビルドし、アップロードします。

これらの手順は、署名キーを安全に保管するために、手動でcrystal-lang/crystal-distを使用して実行されます。最終的には、このリポジトリをdistribution-scriptsと結合して、すべてのピースを1つの場所に保管するかもしれません。

エコシステムテスト

最近では、Docker、Vagrant、CIなどのリソースの間で、いくつかのスモークテストを自動化することが可能です。0.24.1をリリースする際にこれを行い、配布前にリリースでいくつかの問題を発見しました。しかし、残念ながら、これらの問題はタグ付け後に発見しました。ナイトリーパッケージの利用とタグ付けプロセスの改善により、リリース前にいくつかのスモークテストを実行できると期待しています。これにより、問題をより早く検出し、影響を受けるリポジトリにPR/issueを提出できる可能性があります。コンパイラとstdlibには多くのスペックがありますが、きっと世の中には興味深いシナリオがあるはずです。

今日、これらのエコシステムテストを使用して、(Darwin、Linux、およびLinux32で)いくつかの基本的なコマンドを実行できること、スタックトレースが機能すること、crystal-dbとドライバが機能すること、およびWebフレームワークのAmber、Lucky、およびKemalが動作できることを確認しています。

完璧ではありませんが、少なくとも今後のリリースで何か問題が発生する可能性があることを事前に知るための努力です。

今後、どのように行うか?

毎晩および適切なリリースで何が起こるかを要約しましょう。

マスターブランチでは毎晩、コミットがタグ付けされるたびに、次のワークフローが実行されます。

それは大まかに自動的に

  1. スペックを実行します
  2. ナイトリーブランドのパッケージをビルドします
  3. ドキュメントをビルドします
  4. 署名されていないパッケージからDockerイメージをビルドします
  5. ダウンロードされる成果物のコピーを残します

次のバージョンでコミットをタグ付けする前に、例:0.25.0、タグ付けするコミットが良い状態であることを確認する必要があります。そのコミットでナイトリーが実行された場合は問題ありませんが、フェールセーフなアプローチは0.25.0-pre1をタグ付けして、上記のステップを待つことです。

スモークテストが成功したら、0.25.0-pre1(または-pre{N})の成果物で、0.25.0をタグ付けしても大丈夫です。

パッケージに署名し、単一のコマンドでドキュメントおよびタグ付きDockerイメージとともに公開します。Dockerイメージの場合、ナイトリーの-buildのイメージを模倣するために、新しいcrystal:{version}-buildイメージも公開されます。

保留中の手動ステップが2つあります。

  1. Homebrewのformulaを更新します
  2. CIスクリプトを更新して、リリースされたばかりのコンパイラから次のコンパイラをビルドします。

さて、改善して完了する必要があることがまだいくつかあります。終わりはありません!次のセクションを確認してください。

自動化されたリリースバックログ

  • ディストリビューションスクリプトを更新して、32ビットのバイナリを出力して、バイナリの作成が完全に自動化されるようにします。
  • CIで新しいDocker {version}-buildイメージを使用できるようにする必要があります。これにより、今までCIを実行していたjhassのcrystal build imagesの余分な依存関係がなくなります。
  • 署名付きパッケージを使用したDockerイメージの公開と、ドキュメントの公開の手動ビルド手順をcircleciのジョブに移動します。これらのジョブは、パッケージが署名されて公開されたことを示す手動承認を待って保留されます。
  • コンパイラリポジトリのdockerfileとvagrantfileに関して、いくつかのクリーンアップが必要です。これらはほとんどの場合古く、以前のユースケースのいくつかが変更されています。
  • Linuxディストリビューション用の適切なナイトリーリポジトリがあると便利です。crystal: stableおよびcrystal: nightlyオプションを許可するために、主にTravisで使用されるものがあります。将来的には、CIから直接ナイトリーリポジトリの更新を自動化できると便利です。
  • OSXのリリースプロセスで使用されるLLVMバージョンをまだ更新していません。現在、カスタムのLLVM 3.9.1が埋め込まれているため、出荷されるバイナリは少し小さくなっています。しかし、歴史的な理由から、LLVM 3.8以前は、いくつかのエッジ機能を使用するためにカスタムビルドを強制されていました。公式のLLVMリリースに切り替えることができるかもしれません。オムニバスから逸脱し始めたため、現在では、Linux用のサードパーティがプリコンパイルしたLLVMを使用しています。HomebrewからCrystalをインストールした場合、OSXを使用している場合は、LLVMが4.0または5.0を使用していることに気づくかもしれません。そのコンパイラはHomebrewによってビルドされており、最新の安定リリースを使用しています。
  • Homebrewのformulaのパッチの作成を自動化できるため、新しいリリースのPRを送信するのが非常に魅力的であり、さらに重要なことに、忘れられないものになります。
  • より多くのプラットフォームにコンパイラを出荷することは可能ですが、自動化できる限りです。最終的に意味をなすことは、異なるディストリビューション用のパッケージを作成するために使用できるコンパイラの1つのバージョンを出荷し、可能な場合はネイティブパッケージを使用することです。これにより、各ディストリビューションのCrystalコンパイラパッケージを小さくすることができ、依存関係を宣言する必要がある方法に関してうまく機能します。2

次のステップ

次の目標は、コンパイラのパフォーマンスを向上させるための調査を行うことです。そのため、言語のセマンティクスを通過して、既知のいくつかの問題を解決します。これにより、言語を確固たるものにすることができ、1.0以降に破壊的な変更がないことを保証できます。1月から41時間、2月に受け取った寄付から64時間が残っており、3月にこれを投資します。引き続き私たちの活動をサポートしてください。それは大きな違いを生み出します!

  1. OSX CircleCI VMで利用可能なRubyのバージョンは限られています。特定のRubyバージョンをインストールしてビルドするには時間がかかりますhttps://discuss.circleci.com/t/cache-of-installed-ruby/19606  

  2. https://github.com/crystal-lang/crystal/issues/5650