shuntaka.dev
shuntaka.dev
npmモジュールをモノレポで管理し、煩雑なリリース業務を効率化する

01/10 13:32 2021

01/03 14:22 2026

はじめに

ある程度サービス開発を続けているとリポジトリを跨いだ処理の共通化、ライブラリを作成する必要がある。Node.js/TypeScriptでは、npmを利用することでバージョン毎にビルド済資材を管理でき、非常に便利。

一方で13人のチームでライブラリが34つあった場合にそれぞれリポジトリを分けて管理すると、リリースタスクが非常に煩雑。例えば..

  • ほぼ同じようなCI/CDパイプラインを各リポジトリで実装する必要あり
  • 開発ツール(eslint,husky,pritter,etc..)のアップデートを各リポジトリ毎に行う必要あり

本記事では1つリポジトリで、なるべく人の手を使わずに複数のnpmモジュールのリリース管理をする方法について書く。以下注意点。

  • lernaでのmonorepoにおけるリリースフロー(Fixed/Independent)の記事が良くまとまっており、本記事と合わせて読むことを推奨
  • 完成系のサンプルリポジトリに用意しおり、詳細な設定が気になる場合は参照して欲しい

実現したいこと

  • リリース時のセマンティックバージョン推論
  • 更新があるモジュールの同時リリース
  • eslintやprettierといった開発ツールの共通化

利用するツールや概念

lerna

リポジトリ。複数のパッケージでJavaScriptプロジェクトを管理するためのツール。Babel、React、Angular、 Ember、Meteor、Jestといったツールで利用されている。

Yarn Workspaces

下記の記事を参考にした。ここは時間のあるとき再考したい。

  • LernaとYarn WorkspacesでMonorepo管理
  • Yarn workspaces から Lerna に移行した

lernaのみで管理することも可能だが下記の点でYarn Workspacesと併用することとした

  • yarnによるコマンドの巻き上げで影響がでるケースが、現状の要件上なさそうだった
  • 併用しない場合、yarn addによるパッケージ追加が出来なくなる。lernaコマンド経由でのパッケージインストールは少し癖があると感じた

パッケージ共有でインストール場合はコマンドは下記

lerna add eslint

任意のパッケージのみで利用したい場合は、scopeを設定する必要がある

lerna add lodash --scope lib-a

併用すれば、各パッケージルートでyarn add出来るため、慣れているやり方でパッケージ追加/削除が可能となる。

Conventional Commits

Conventional Commits

人間と機械が読みやすく、意味のあるコミットメッセージにするための仕様

本仕様に従うことで、lernaでセマンティックバージョニングベースでのリリースバージョン推論が可能となる。

Format

フォーマットは下記。optionalは任意項目。

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

各項目で名付け規則が存在する。コミットする際には、こちらのルールを参考にしたい。

バージョン推論

Conventional Commitsを通して、下記ようにバージョン推論が行われる

  • MAJORバージョンがインクリメントされるのは、bodyまたはfooterにBREAKING CHANGE:がある場合
  • MINOR PATCHバージョンがインクリメントされるのは、下記の表参考
type名 説明 補足
build ビルド設定
ci CI設定
chore 雑事(カテゴライズする必要ないようなもの)
docs ドキュメント
feat 新機能 MINORバージョンに該当
fix バグフィックス PATCHバージョンに該当
perf パフォーマンス
refactor リファクタリング
revert コミット取り消し(git revert)
style コードスタイル修正
test テスト

例

# ドキュメントを更新した場合
docs(readme): 起動方法を変更
# 新機能を追加した場合(scopeは無理してつける必要なし!)
feat: 起動方法を変更
# renovateが自動アップデートした場合
chore(deps): update dependency @types/jest to v26.0.20

ツールの設定

完成系

詳細設定はリポジトリを参考にして欲しい

lerna

lerna.json

{
  "npmClient": "yarn", // npmクライアントにyarnを指定
  "useWorkspaces": true, // Yarn Workspacesを有効化
  "packages": [
    "packages/*"
  ],
  "version": "independent" // ①
  "command": {
    "version": {
      "message": "chore(release): publish", // ②
      "conventionalCommits": true // conventionalCommitsを使ったリリーズバージョン推論の有効化
    }
  },
}

①について

lernaには、下記のモードが存在する

  • Fixed/Locked mode
  • Independent mode

前者はモノレポ配下のパッケージを1つのバージョンで固定する方法。後者はパッケージ毎に任意のバージョンが指定可能。今回は、Independent modeを指定。

②について

messageオプション。lerna versionをするときにlernaはコミットのバージョンをインクリメントして、CHANGELOG.mdを更新してコミットを作る。そのコミットに指定するメッセージを定義出来る。 Fixed/Locked modeであれば、メッセージに%sが含まれている場合は、「v」で始まる新しいグローバルバージョンのバージョン番号。%vが含まれている場合、先頭に「v」が付いていない新しいグローバルバージョンのバージョン番号に置き換えることが可能。

{
  "command": {
    "version": {
      "message": "chore(release): publish %s"
    }
  }
}

後述のcommitlintを設定している場合、デフォルトだとルール違反と判定されてlerna versionが失敗します。commitlintと併用する場合は設定が必要。

commitlint

Conventional Commitsに従っているか人の目で確認するのは難しいため、commitlintを利用。 .commitlintrc.json

{
  "extends": ["@commitlint/config-conventional"]
}

commitlintのルールは、@commitlint/config-conventionalの他にも@commitlint/config-angularがある。今のところ違いがよくわからないため、追加で調査したい。

husky

コミットをする際に、commitlintとeslintが走るように設定する。 .huskyrc.json

{
  "hooks": {
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
    "pre-commit": "yarn lint:fix"
  }
}

コマンド関連

パッケージの追加

共通利用するパッケージをインストールする。WオプションはYarn Workspaces内にパッケージの追加をすることを表しています。

yarn add -D -W @typescript-eslint/eslint-plugin

ビルド

各パッケージでyarn buildが実行されます。

lerna run build # yarn buildに設定するとよい

バージョンアップ

Independent modeの場合、更新のあるパッケージのバージョンインクリメントをします。最初にバーションを対話式で選んだら、tag打ち、CHANGELOG.mdの更新をしコミットを打ってpushするとこまで自動で行います。

lerna version

パッケージの公開

publishコマンドを利用する

lerna publish              # 前回のリリース以降に変更されたパッケージを公開する
lerna publish from-git     # 現在のコミットでタグ付けされたパッケージを明示的に公開する
lerna publish from-package # 最新バージョンがレジストリ(npm)に存在しないパッケージを明示的に公開する

from-gitのユースケースは正直よくわからない。タグベースでリリースの公開制御するのはデメリットが大きいと考えている。例えばleran versionは成功し、npmにログインしておらず、leran publishに失敗した場合にタグを削除しないとpublishすることが出来ないなど..。 from-packageを使えば漏れなくパッケージ公開可能なので、今回採用した。

npm-scriptsにまとめる

npm-scriptsを利用し、新規リリースをする場合はビルドとバージョンアップとパッケージ公開はyarn releaseにまとめている。

  "scripts": {
    "build": "lerna run build",
    "test": "lerna run build",
    "lerna:pub": "lerna publish from-package",
    "lerna:version": "lerna version",
    "release": "yarn build && yarn lerna:version && yarn lerna:pub",
    "lint:ts": "eslint --ext=.ts .",
    "lint:fix": "yarn lint:ts --fix"
  },

CHANGELOGの抜き出し

特定のCHANGELOGを抜き出す

yarn monorepo-utils-collect-changelog --directory . "@sugoi-site/lib-a@0.4.0"

おわりに

出来ていないこととして、GitHub ReleaseへCHANGELOGの書き出しがあり、ここは色々試してみる予定です。実際に個人ブログのCSSやマークダウンのパースライブラリを本記事の方法で管理します。その過程で使い辛い点は見直して、本記事に反映します!!

参考文献

lernaでのmonorepoにおけるリリースフロー(Fixed/Independent) fast-csv