この記事の対象読者
- モノリポを複数運用することにしたが、一部のコードをライブラリとしてモノリポ間で共有したい(Googleみたいに一つのリポジトリに入れるのは嫌という人)
- 依存しているNPMパッケージにバグがあり、PRを出しつつ直したバージョンを使いたい
- プライベートリポジトリで作っているコードの一部を、ライブラリとしてパブリックなリポジトリで公開したい
と、グダグダ書きましたが要は ↓ のような状況を上手に扱うにはどのようにすればいいかを説明していきます。
Git Submodule
Git Submoduleは、あるリポジトリの中で別のリポジトリを参照し、管理するためのツールです。
今回なら、モノリポにライブラリのリポジトリを追加し、管理するために使います。
この記事で細かくGit submoduleについて説明するつもりはないので、詳しく知りたい方は公式ドキュメントを参照してください。
Yarn Workspace(あるいはnpm workspaces/pnpm workspaces)
Yarn Workspaceは、Yarnを使ってモノリポを運用するためのツールです。
例えば、
- app/package.json
- lib1/package.json
- package.json
というような構造があったときに、appの中のコードでrequire('lib1')
などということができるようになります。
こちらも詳しく説明するつもりはないので、公式ドキュメントへのリンクを掲載しておきます。
実際にやってみる
今回は、下記のリポジトリを用いて説明していくことにします。
- submodule (submoduleはライブラリ用のリポジトリでRollupのテンプレート1を使って用意しました。)
- monorepo1
まずmonorepo1の中でsubmoduleを利用するには、monorepo1のリポジトリでgit submodule add
を実行します。
git submodule add https://github.com/coder-ka/submodule
この操作により、
- submodule フォルダ
- .gitmodules ファイル
が追加されます。
.gitmodules
の中をのぞくと
[submodule "submodule"]
path = submodule
url = https://github.com/coder-ka/submodule
となっており、"submodule"というリポジトリがsubmoduleとしてsubmodule/
ディレクトリで管理されているということがわかります。(名前がややこしくてすいません)
ここで、submoduleの中のpackage.jsonの"name"
キーを@coder-ka/submodule
に変更しておいてください。(これによりrequire('@coder-ka/submodule')
となる)
つぎは、Yarn Workspaceです。
まずリポジトリルートで初期化を行います。
yarn init -y
そして、生成したpackage.jsonの内容を変更します。
{
"private": true,
"workspaces": [
"app",
"submodule"
]
}
このpackage.jsonはWorkspace管理のためのものなので"private" :true
が必須になっています。(NPMリポジトリで公開しないため)
また、"workspaces"
にはモノリポ内のリポジトリとして扱うフォルダのパスを配列で指定します。(packages/*
みたいな指定の仕方もできますが、今回は割愛)
submodule
は先ほど追加したので存在しますが、app
フォルダがないので作ります。
mkdir app
# ついでに初期化
cd app
yarn init -y
また、リポジトリ内であればどこでもいいので yarn
コマンドを実行しておきます。
yarn
これにより、各ワークスペースが依存するパッケージがインストールされ、リポジトリのルートフォルダにnode_modules/
が配置されます。(さらにnode_modules/@coder-ka/submodule/
は submodule/
へのSymlinkなので、ライブラリへの変更が即座に反映される)
次に app/index.js
を作成し、コードを書きます。
const howLongTillLunch = require('@coder-ka/submodule')
console.log('it will be lunchtime in ' + howLongTillLunch());
そして、今回に限っては使っているテンプレートの都合でRollupによるビルドを行う必要がありますが、これは本質的な内容ではないのであまり気にしないでください。
# submodule/フォルダで yarn build しているのと同じ
yarn workspace @coder-ka/submodule build
これで app/index.js
が正常に動くようになるので、
node app/index.js
とすれば、it will be lunchtime in 18 hours
とコンソールに出てきます。
これで無事に別リポジトリで管理しているライブラリを別のモノリポ内で動かすことができました。
懸念点と解決策
考えられる懸念点と解決策について書いていきます。
複数のモノリポからの依存
複数のモノリポからライブラリのリポジトリが参照される構成になると、ライブラリへの変更によるほかのモノリポに対する影響が気になってきます。
そんな時は、下記のような運用が良いと思われます。
例えば、
- submodule
- monorepo1
- monorepo2
がある場合は、submoduleのリポジトリにmonorepo1/monorepo2のブランチを作成し、それぞれのモジュールの変更はそのブランチに反映させるようにします。
Git Submoduleはデフォルトではmaster
を参照するので、.gitmodules
にbranch
を追加します。
[submodule "submodule"]
path = submodule
url = https://github.com/coder-ka/submodule
branch = monorepo1
これで git submodule update --remote
(サブモジュールの更新) をしたときにmonorepo1
の内容を参照してくれます。
master
には monorepo1 での運用で動作が安定していることが確認できた後に反映すれば、monorepo2 はそれを取り込むことができます。(もちろん、直接 monorepo1 から取り込むこともできます)
終わりに
Git Submodule + Yarn Workspace の組み合わせが有用であることが理解いただけたでしょうか?
今までは、公開や変更の手間などを気にしてライブラリを別リポジトリで作ることを躊躇していた方もいるかもしれませんが、この方法なら無理なくライブラリを運用かつ利用していくことができます。
ライブラリとして公開できる汎用性を持ったコードを作ることは、それにかかわるコミュニティをどんどん便利なものにしていってくれます。
是非この運用法を活用して、生産性向上に役立てていただければと思います。
-
Rollup公式のTypeScriptのUMDライブラリのテンプレートにava(テストフレームワーク)を私が追加したもの ↩