git とプロジェクトの依存関係
Nicola Paolucci
開発者アドボケート
次の質問を考えてみてください。
プロジェクトの依存関係を git
でどう処理していますか。
私たちのプロジェクトは互いに依存関係にある複数のリポジトリから構成されています。今はそうしたリポジトリを svn:externals
で管理しています。これを git
で処理する最適の方法は何でしょうか。
git
を使って、非常に大きなリポジトリを小さなコンポーネントに分割するにはどうすればよいでしょうか。
これらはよく寄せられる質問です。
このテーマは git
を採用しているソフトウェア チームの多くが抱える悩みの種のようです。この記事ではこの問題に焦点を当ててみます。
プロジェクトの依存関係とビルド インフラストラクチャは互いに絡み合った 2 つの領域であることは明らかであり、アトラシアン社内でさえも「ビルドの未来」について議論が巻き起こりました。
複数のリポジトリが分かれている場合は、リポジトリが 1 つである場合と違って、難しい点がいくつか生じます。しかし、少なくとも次の 2 つの主な理由から、リポジトリが複数になるのは、ソフトウェア プロジェクトの発展過程では比較的自然なことであり、時にはそうならざるを得ない場合もあります。その 2 つの理由とは、ビルド時間の増加とプロジェクト間で共有する依存関係です。
対応の概要:ガイドラインと次善の解決策
では、質問に戻りましょう。git
を使って、プロジェクトの依存関係をどのように追跡し、管理するのでしょうか。
そんなことはしないにこしたことはないですよね!
冗談はさておき、まずはじめにをおおまかに回答し、後から深く掘り下げましょう。Git を使う場合でも、別のやり方の場合でも、プロジェクトの依存関係に関連するすべての問題を何の苦もなく解決する特効薬
はないことを認識してください。
プロジェクトが特定のサイズを超えると、論理コンポーネントに分割するのが妥当です。しかし、1 つのリポジトリのコードが 1 億行を超えるのを待つ必要はありません。したがって、以下は独自のアプローチを考案するためのガイドラインにすぎません。
関連資料
Git チュートリアル - Gitをインストールする
ソリューションを見る
Bitbucket Cloud での Git の使用方法についてのチュートリアルです。
第一の選択:Git に代わる適当なビルド / 依存関係ツールを使用する
依存関係の管理ツールは、規模の大きいプロジェクトで増大していく手間とビルド時間に対処する方法として、私が現在推奨しているものです。
モジュールを個々のリポジトリに分けて保存し、そのジョブ用に構築されたツールを使用して、モジュール間の相互依存性を管理します。(ほぼ)すべてのテクノロジーに応じたツールが 1 つずつ用意されています。次に、いくつか例をあげます。
- Maven (または Gradle): Java を使用している場合
- Npm: ノード アプリ
- Bower、Component.io など: Javascript を使用している場合 (更新しました!)
- Pip、requirements.txt: Python を使用している場合
- RubyGems、Bundler: Ruby を使用している場合
- NuGet: .NET を使用している場合
- Ivy (または何らかのカスタム CMake アクション): C++ を使用している場合 (更新しました!)
- CocoaPods: Cocoa iOS アプリ用
- Composer または Phing: PHP 用 (追加しました!)
- Go では、ビルド/依存関係インフラストラクチャの一部が言語に組み込まれています (より完全なソリューションを開発中です。「godep」を参照)。Git サーバー (Bitbucket) では、Maven と Bower の両方が使用されています。ビルド時に、選択したツールによって適切なバージョンの依存関係がプルされて、メイン プロジェクトがビルドされます。これらのツールの中には制限があり、最適ではない仮定を行うものもありますが、どれも実証済みで実用に供します。
プロジェクトを分割するのにかかる手間
簡単に言うと、プロジェクトの開始時には、すべてが 1 つのビルドにまとめられています。しかし、プロジェクトが大きくなると、これではビルドが遅くなりすぎることがあります。この時点で「キャッシング」が必要になり、ここが依存関係の出番です。ちなみにこれは、サブモジュール (下記参照) が動的言語に非常に適していることなどを意味します。基本的に、ほとんどの人はある時点でビルド時間を気にする必要があると思います。そのため、依存関係管理ツールを使用する必要があります。
コンポーネントを別々のリポジトリに分割するには、相当の手間がかかりす。順不同であげると、次のようになります。
- コンポーネントに変更を加えると、リリースが必要になる
- 時間がかかり、多くの愚かな理由で失敗する可能性がある
- 小さな変更には適さない
- コンポーネントごとに新しいビルドを手動で設定しなければならない
- リポジトリの検索がしにくくなる
- 単一のリポジトリですべてのソースが利用可能でない場合のリファクタリング
- セットアップによっては(当社の場合など)、API の更新には、まず製品、次にプラグイン、さらに再び製品のマイルストーン リリースが必要になり、多分途中でいくつか見逃しが生じます。しかし、ここで気づきます。これでは問題の完全な解決には遠い、と。
第 2 の選択肢: git サブモジュールを使う
依存関係ツールを使用できない、または使用したくない場合、git
にはサブモジュール
を処理する機能があります。サブモジュールは、特に動的言語に便利です。ただし、ビルド時間を短縮できるとは限りません。私はこれまでにいくつかのガイドラインとヒントを書いており、代替案も検討しました。インターネット上でもサブモジュールに反対する意見が大半です。
svn: externals と git の 1:1 の対応
しかし、svn: externals
と git
を 1 対 1 で対応させたい場合は、サブモジュール
を使用してサブモジュール
がリリース ブランチのみを追跡し、ランダムなコミットは追跡しないようにします。
第 3 の選択:他のビルドおよびクロススタック依存関係ツールを使う
完全に統一されていて、単一のツールを使用して構築され、構成されているプロジェクトばかり扱えるわけではありません。たとえば、一部のモバイル プロジェクトでは Java と C++ の依存関係を両立させたり、独自のツールを使用してアセットを生成したりする必要があります。こうした複雑な状況では、最上部に余分のレイヤーを追加して、git
を拡張できます。この分野での良い例は、Android リポジトリです。
詳しく調べてみる価値のある他のビルドツール:
結論と参考文献
Charles O'Farrell が提案する、ビルド・インフラストラクチャ(および Maven)トピックに関するさらなる読み物:
私は上記の最後の記事からのこの素晴らしい引用で、この投稿を締めくくりたいと思います。これは Maven に関するものですが、他のビルド ツールや依存関係ツールにも同様に当てはまります。
「キャッシュは処理速度の向上以外に何の働きもしません。キャッシュをすべて削除しても、周囲のシステムは以前同様に動作し、速度が遅くなるだけです。キャッシュには副作用もありません。これまでにキャッシュを使ってどんなことを実行してきたとしても、キャッシュに対してあるクエリを実行した結果と、今後同じクエリを行った結果はまったく同じ値になります。
Maven のエクスペリエンスは、私が説明したものとは大きく異なります。Maven リポジトリはキャッシュと同じように使用されますが、キャッシュのプロパティがありません。Maven リポジトリから何かを要求する場合、過去に何を行ったかが非常に重要です。直近に格納した内容が返されます。格納していない内容を要求すると失敗する場合があります」
この記事を共有する
次のトピック
おすすめコンテンツ
次のリソースをブックマークして、DevOps チームのタイプに関する詳細や、アトラシアンの DevOps についての継続的な更新をご覧ください。