宝石開発者ガイドライン
GitLabは、モノリシックなコードベースにおけるコードの再利用性とモジュール性を向上させるツールとしてGemsを使用しています。
私たちは、その機能が高度に分離されており、私たち自身で他のアプリケーションで使用したい場合、またはそれがより広いコミュニティに利益をもたらすと考えられる場合に、コードベースからライブラリを抽出します。
gemにコードを抽出することで、gemが私たちのアプリケーションコードに隠れた依存関係を含まないようにすることもできます。
Gemは、GitLabのビジネスロジックから切り離され、別々に開発することができる、分離された機能を実装する場合に常に使用する必要があります。
Railsのコードベースで新しいgemsを抽出するのに最適な場所はlib/フォルダです。
私たちのlib/フォルダには、汎用的なコード、GitLab 固有のコード、他のコードベースと密接にインテグレーションされたコードが混在しています。
コードベースの一部を Gem として抽出するかどうかを決めるには、次のような質問を自分に投げかけてみましょう:
- このコードは、独立した小さなプロジェクトとして実行できる汎用的なものですか?
- Monolithの外部で内部使用されることを期待していますか?
- 別のコンポーネントとしてリリースすることを検討すべき、より広いコミュニティにとって有用なものでしょうか?
上記の質問のいずれかに答えがYesの場合、新しいGemを作成することを強く検討すべきです。
同じリポジトリに新しいGemを作成することから始めて、後でそれを別のリポジトリにマイグレーションするかどうかを評価することができます。
ジェムを使用する利点
Gemsを使用することで、コードのメンテナンスにいくつかの利点があります:
-
コードの再利用性 - Gemsは、単一の目的を果たす分離されたライブラリです。Gemsを使用する場合、共通の関数をシンプルなパッケージに分離することができ、十分に文書化され、テストされ、異なるアプリケーションで再利用されます。
-
モジュール性 - Gemsは、自己完結型ライブラリ内に特定の機能をカプセル化することで、分離を作成するのに役立ちます。これは、コードをよりよく整理し、特定のモジュールのオーナーをよりよく定義し、特定のgemsのメンテナーやアップデートを容易にします。
-
小規模 - 分離された一連の機能を実装するため、設計上gemは小規模になります。小さなプロジェクトは、理解、拡張、メンテナーがより簡単です。
-
テスト - Gemは小さいので、Gemを使用すると、すべてのテストを実行するのが非常に速くなります。gemはパッケージ化されており、あまり頻繁に変更されないので、CIテスト時間を改善するためにテストを実行する頻度を減らすこともできます。
gemのネーミング
宝石は3つの異なるケースに分類されます:
-
unique_gem
:gem に GitLab 固有のものが含まれていない場合は、gem 名にgitlab
を含めないでください。 -
existing_gem-gitlab
:公開されているgemをフォークして修正・拡張する場合は、Rubygemsの規約に従って-gitlab
。 -
gitlab-unique_gem
:GitLabプロジェクトのコンテキストでのみ有用なgemには、gitlab-
接頭辞を付けましょう。
既存のgemの例:
-
y-rb
:yrsのRubyバインディング。Yrsの “wires “はYjsフレームワークのRust移植版。 -
activerecord-gitlab
:activerecord
公開 gem に GitLab 固有のパッチを追加。 -
gitlab-rspec
andgitlab-utils
: 特定のコンテキストで役立つGitLab固有のクラスセット、またはコードの再利用。
同じリポジトリの
既存のコードベースからGemsを抽出する場合、GitLab monorepoのgems/
。
そうすることで、gemsの利点(モジュール化されたコード、開発者のテスト実行の迅速化)を得ることができ、複雑さ(レポ間の変更、新しい権限、複数のプロジェクトなどの調整)を防ぐことができます。
同じリポジトリに保存されたgemsは、path:
構文でGemfile
。
新しいGemの作成と使用
- Gemの命名規則に従って、gemの良い名前を選んでください。
-
gems/<name-of-gem>
でbundle gem gems/<name-of-gem> --no-exe --no-coc --no-ext --no-mit
を使って新しいgemを作成します。 -
gems/<name-of-gem>
の.git
フォルダーをrm -rf gems/<name-of-gem>/.git
で削除します。 -
gems/<name-of-gem>/README.md
を編集して、Gemの簡単な説明を記述してください。 -
gems/<name-of-gem>/<name-of-gem>.gemspec
を編集し、次の例のようにジェムの詳細を記入します:# frozen_string_literal: true require_relative "lib/name/of/gem/version" Gem::Specification.new do |spec| spec.name = "<name-of-gem>" spec.version = Name::Of::Gem::Version::VERSION spec.authors = ["group::tenant-scale"] spec.email = ["engineering@gitlab.com"] spec.summary = "Gem summary" spec.description = "A more descriptive text about what the gem is doing." spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/<name-of-gem>" spec.license = "MIT" spec.required_ruby_version = ">= 3.0" spec.metadata["rubygems_mfa_required"] = "true" spec.files = Dir['lib/**/*.rb'] spec.require_paths = ["lib"] end
-
gems/<name-of-gem>/.rubocop.yml
を更新します:inherit_from: - ../config/rubocop.yml
-
新しく追加されたGem用にCIを設定します:
-
gems/<name-of-gem>/.gitlab-ci.yml
を追加します:include: - local: gems/gem.gitlab-ci.yml inputs: gem_name: "<name-of-gem>"
-
.gitlab/ci/gitlab-gems.gitlab-ci.yml
に追加:include: - local: .gitlab/ci/templates/gem.gitlab-ci.yml inputs: gem_name: "<name-of-gem>"
-
-
Gemfile
の Gem を参照してください:gem '<name-of-gem>', path: 'gems/<name-of-gem>'
Gemの抽出例
gitlab-utils
は、strong_memoize
やGitlab::Utils.to_boolean
のような、GitLab 開発者が使う一般的な組み込み関数を実装したクラスのセットを含む Gem です。
gitlab-database-schema-migrations
は、データベースのマイグレーションをリポジトリに保存する方法を改善するRailsフレームワークの拡張を含むGemです。これはRailsの上に構築されるもので、GitLabというアプリケーションに特化したものではなく、他のプロジェクトに一般的に使われたり、アップストリームされたりする可能性があります。
先ほどと似たgitlab-database-load-balancing
は、GitLab 固有のロードバランシングを Rails のデータベース処理に実装するための Gem です。これはかなり複雑で非常に特殊なコードなので、分離され、よくテストされたGemでその複雑さをメンテナーすることは、大きなモノリシックなコードベースからこの複雑さを取り除くのに役立つでしょう。
gitlab-flipper
は、機能フラグをコードベースでサポートするためのカスタム拡張機能をすべて実装した、もうひとつの可能性のあるGemです。一貫性チェックや追加された機能フラグのオーナーを追跡するためのさまざまなヘルパーが追加されました。これはGitLabのビジネスロジックの一部ではないので、Flipper の実装をより良く追跡するために使うことができます。
activerecord-gitlab
は GitLab 固有の Active Record パッチを追加する gem です。複雑さを分離するために、このようなパッチを個別に管理することはとても望ましいことです。
その他の使用例
gitlab-ci-config
は、.gitlab-ci.yml
を解析するために使われる私たちのすべての CI コードを含む潜在的な Gem です。このコードは、適切な抽象化がされていないため、現在GitLabアプリケーションと軽く連動しています。しかし、これを専用のGemに移行することで、アプリケーションのGitLabとのインテグレーションを処理するための様々なアダプタを構築できるようになります。例えば、includes:
を解決するアダプタを定義するようなインターフェイスです。gitlab-ci-config
Gemができれば、GitLab内部でもGitLab RailsやGitLab CLIの外部でも使えるようになります。
外部リポジトリの
一般的に、このようなことを行うには重大なデメリットがあるため、よく考えてから行うようにしたいものです。
外部リポジトリに保存されたGemsは、version
の構文でGemfile
を参照しなければなりません。それらは常にRubyGemsに公開されなければなりません。
使用例
GitLabでは多くの外部gemを使用しています:
デメリット
- Gemsは(GitLabによってメンテナーされているものであっても)必ずしもメインのRailsアプリケーションと同じコードレビュープロセスを経るわけではありません。これはアプリケーションセキュリティにとって特に重要です。
- 一貫したコードレビュー基準をサポートするDangerのようなツールを含め、CI/CDをゼロからセットアップする必要があります。
- コードを別のプロジェクトに抽出するということは、機能を変更するために最低でも2つのマージリクエストが必要だということです。
- 2回目のMRを必要とする
gitlab-rails
とのインテグレーションは、インテグレーションの問題が発見されるのが遅れる可能性があることを意味します。 -
gitlab-rails
、レビュアーやメンテナーの人数が少ないため、コードのレビューに時間がかかり、”バスファクター “の影響が大きくなる可能性があります。 - 新しいgemバージョンのリリース方法に関する一貫性のないワークフロー。現在のところ、どのように動作するかはライブラリのメンテナーの裁量に任されています。
-
gitlab-rails
に比べてコードの可視性や露出度が低いため、ナレッジサイロを昇格させます。 - GitLab には、レビュアーからメンテナーに昇格させるための明確なプロセスがあります。これは、抽出されたライブラリには当てはまらず、コードレビューのハードルを下げ、変更を出荷するリスクを高めます。
- 私たち自身のgemの使い方のニーズと、より広いコミュニティのニーズが一致しないかもしれません。一般的に、もし私たちが自分たちのgemの最新バージョンを使っていないのであれば、それは警告のサインかもしれません。
潜在的な利点
- より小さなリポジトリに対してCI/CDが実行されるため、フィードバックループがより速くなります。
- プロジェクトをより広いコミュニティに公開し、外部からの貢献から利益を得ることができます。
- リポジトリのオーナーは、変更をレビューする最適な対象者である可能性が高いため、
gitlab-rails
で適切なレビュアー探しの必要性を減らすことができます。
Ruby gemの作成と公開
新しいGemのプロジェクトは、常にgitlab-org/ruby/gems
名前空間 に作成する必要があります:
- gemの適切な名前を決めます。GitLab所有のGemであれば、Gem名の前に
gitlab-
をつけます。例えば、gitlab-sidekiq-fetcher
。 - 必要に応じて、ローカルで gem を作成したりフォークしたりします。
-
空の
0.0.1
バージョンの gem を rubygems.org に公開し、gem 名が予約されていることを確認します。 -
gitlab_rubygems
、gitlab-qa
のユーザーを新しいgemのオーナーとして追加します:gem owner <gem-name> --add gitlab_rubygems gem owner <gem-name> --add gitlab-qa
- オプション。以下のユーザーの一部またはすべてを共同オーナーとして追加します:
- オプション。その他関連する開発者を共同オーナーとして追加してください。
-
https://rubygems.org/gems/<gem-name>
にアクセスし、gem が正常に公開され、gitlab_rubygems
とgitlab-qa
もオーナーであることを確認します。 -
gitlab-org/ruby/gems
グループ にプロジェクトを作成します。このプロジェクトを作成します:- 新規プロジェクトの手順に従ってください。
- CI/CD設定の手順に従ってください。
-
共有されたCI/CD設定を使用して、新しいgemバージョンをリリースして公開するには、そのgemの
.gitlab-ci.yml
:include: - project: 'gitlab-org/quality/pipeline-common' file: '/ci/gem-release.yml'
このジョブは gem のビルドと公開 (
gitlab-org/ruby/gems
グループから継承したgilab-qa
Rubygems.org API トークンを使用して gem パッケージを公開します)、タグの作成、リリース、Generate changelog dataAPI エンドポイントを使用したリリースノートの作成を行います。いつ、どのように変更ログエントリファイルを生成するかについては、専用のChangelog entriesページを参照してください。GitLab プロジェクトとの一貫性を保つために、Gem プロジェクトでも
gitlab-styles
gem](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/.gitlab/changelog_config.yml)の[と同じ内容の changelog YAML 設定ファイルを.gitlab/changelog_config.yml
に定義することができます。 - リリースプロセスを簡単にするために、
gitlab-styles
gem](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/raw/master/.gitlab/merge_request_templates/Release.md) の[と同じ内容の.gitlab/merge_request_templates/Release.md
MR テンプレートを作成することもできます (gitlab-styles
を実際の gem 名に置き換えてください)。 - プロジェクトを公開する手順に従ってください。
注意事項場合によっては、gem を独自の名前空間に移動させたいことがあります。その例としては、当然ながら複数のプロジェクトを持つことになる場合(たとえば、プラグインを別のライブラリとして持っているような場合)や、GitLabのチームメンバーだけでなくGitLab外のユーザーがこのプロジェクトのメンテナーになることを想定している場合などがあります。後者の場合 (GitLab 外のメンテナー) は、現在 GitLab で働いている人が GitLab での仕事を超えてその gem をメンテナンスしたい場合にも当てはまります。
そのvendor/gems/
vendor/
の目的は、GitLab monorepo に外部の依存関係を取り込むことです。外部のリポジトリがありますが、シンプルにするために monorepo に保存します:
- スクリプトや手動で外部リポジトリからプルする場合のみ、
vendor/gems/
を使用しなければなりません。 -
vendor/gems/
は内部 gems の保存に使用してはいけません。 -
vendor/gems/
は GitLab monorepo でビルドできるように修正してもかまいません。 -
gems/
は GitLab monorepo の一部であるすべての内部 gems を保存するために使用しなければなりません(MUST)。 -
RubyGemsは、GitLab monorepo の
gems/
にない、外部で保存されたすべての依存関係に使用しなければなりません(MUST)。
既存の gems をvendor/gems
-
外部リポジトリがなく、現在
vendor/gems/
に保存されている内部ジェムの場合:-
他のリポジトリで使用されているGemsの場合:
- 独自のリポジトリにマイグレーションします。
- RubyGemsでの公開を開始または継続します。
- これらのGemsは
Gemfile
のバージョンで参照され、RubyGemsから取得されます。
-
monorepoでのみ使用されるGemsの場合:
- RubyGemsへの新バージョンの公開を停止します。
- RubyGemsに依存しているアプリケーションがあるかもしれないので、すでに公開されているバージョンからの引き抜きは行いません。
- これらのgemsは
gems/
に移動します。 - これらの宝石は
Gemfile
のpath:
を介して参照されます。
-
-
vendor/gems/
、モノレポの内部で販売されています:- アップストリームできない、あるいはまだアップストリームされていない修正が必要な場合は、リポジトリでメンテナーします。
- サードパーティによってベンダリングされたgemが公開される可能性があります。
- それらのジェムは弊社からRubyGemsに公開されることはありません。
- RubyGemsに依存することはできないため、それらのGemは
Gemfile
のpath:
を介して参照されます。
ジェム名の予約
私たちは、新しいgemを含むコードを公開する前に、RubyGemsで名前を横取りされないようにするための予防措置として、gem名を予約しています。
gem名を予約するには、Create and publish a Ruby gemの手順に従ってください:
- バージョンは
0.0.0
。 -
raise "Reserved for GitLab"
という内容のファイルlib/NAME.rb
を1つ含めます。 -
build
とpublish
を実行し、https://rubygems.org/gems/ をチェックして成功したことを確認します。