newmo 技術ブログ

技術で地域をカラフルに

renovatebotとpnpm catalogで実現する依存関係の自動アップデートとハンドブックによる安全な更新

はじめに

モノレポ環境での依存関係管理は、プロジェクトの規模が大きくなるほど複雑になります。 newmoでpnpm catalogを導入してOne Version Ruleを実装し、パッケージのバージョンを一元管理する仕組みを構築しました。詳細はmonorepo内でのパッケージのバージョンを1つだけに統一するOne Version Ruleをpnpm catalogで実装する - newmo 技術ブログを参照してください。 しかし、一元管理の実現だけでは依存関係の更新作業そのものは依然として手動であり、セキュリティアップデートの適用遅延やレビュー負荷の増大という課題が残っていました。

本記事では、renovatebotとpnpm catalogを組み合わせた依存関係の自動更新システムについて、実装の詳細から運用方法まで解説します。 特にAuto Merge機能において、パッケージの自動更新とハンドブックを活用した安全な更新を両立する方法に焦点を当てていきます。

自動化の必要性とコスト削減

依存関係の自動更新を導入する背景には、開発効率の向上だけでなく、具体的なコスト削減の必要性がありました。

newmoでは、renovateのパッケージのアップデートPRを、ハンドブックを用いてレビューしてマージする運用をしていました。 一方で、一部のCIでは、Chromaticを使用したVisual Regression Testを実施しています。

このような中で、renovateのライブラリのアップデートPRが手動レビュー待ちで滞留する場合が あります。 これによって、renovatebotが自動的に実行するリベース処理により、CI実行回数が想定以上に増加していました。

具体的な問題の流れは次の通りです。

  1. renovatebotがライブラリのアップデートPRを作成すると、レビュー待ちの状態になる
  2. その間にmainブランチへ他のPRがマージされると、renovatebotはPRのコンフリクトを解消するために自動的にリベースを実行する
  3. リベースのたびにCIが実行されるため、結果として実際のPR数よりも多くのCIが実行される

このようのパッケージのアップデートのPRが滞留すると、Chromaticのような実行回数制限があるサービスでは、無駄なリソース消費につながっていました。

この問題の解決策として、Auto Merge機能の導入を決定しました。 安全にAuto Mergeできるパッケージを選別します。CI通過後は即座にマージすることで、PRの滞留時間を短縮し、リベース回数とCI実行回数を削減します。

Renovateのrebaseを減らす方法として、パッケージのアップデートをDraft PRにすることも検討しました。 しかし、ハンドブックを活用した手動レビューの段階で、多くが自動的にマージして問題ないこともわかっていました。 Draft PRにしてもコストは減らせますが、目的はパッケージのアップデートなので、Auto Mergeを実装する方法を選択しました。

pnpm catalogによるパッケージ管理

renovatebotによる自動化の前提として、pnpm catalogによるバージョン管理があります。 この仕組みについては既存の技術ブログ記事「pnpm catalog で構築する One Version Rule」で詳しく解説していますが、簡単に振り返ります。

pnpm catalogは、pnpm 9.5で追加された機能です。pnpm-workspace.yamlに依存関係のバージョンを集約して管理できます。 各パッケージのpackage.jsonではcatalog:プロトコルを使用してcatalogのバージョンを参照します。これにより、モノレポ全体で同じパッケージが同じバージョンで使用されることを保証できます。

newmoでは、このpnpm catalogに対してアノテーションを追加することで、メタ情報を管理しています。 具体的には、@pkg-groupでパッケージのグループ化、@pkg-automergeでAuto Merge対象の指定、@pkg-ignoreで更新対象外の指定をします。これらのアノテーションは、renovateの設定を自動生成する際の基礎となります。

# `pnpm-workspace.yaml`
catalog:
  # @pkg-group: react
  # @pkg-automerge
  react: 19.0.0
  
  # @pkg-group: react
  # @pkg-automerge
  react-dom: 19.0.0
  
  # @pkg-group: react
  # @pkg-automerge
  "@types/react": 19.0.8

このアノテーション方式を採用した理由は、pnpm-workspace.yamlをSingle Source of Truthとして扱うためです。 renovateの設定ファイルを直接編集するのではなく、pnpm-workspace.yamlから自動生成することで、設定の一貫性を保ち、他のツールへの移行も容易にします。

その結果、pnpm-workspace.yamlには次の情報が含まれます。 すべてのパッケージ、パッケージを導入したDesign DocのURL、パッケージのグループ分け、パッケージをAuto Merge対象として指定するための情報です。 この設計については、次の発表資料で詳しく説明しています。

renovatebotの選定

当初はGitHubのdependabotを使用していました。 しかし、pnpm catalogの動作がrenovatebotの方が安定していたことと、設定の柔軟さから、renovatebotへの移行を決定しました。

renovatebotがpnpm catalogに対応したのは2025年1月28日のv39.138.0リリースです。 dependabotも2025年2月4日に対応を発表し、現在はどちらもpnpm catalogに対応しています。 renovatebotを選択した理由は、対応の速さもあります。 しかし、主な理由は設定のカスタマイズ性が高く、グループ化やスケジューリングなど、モノレポ運用に必要な機能が充実しているためです。

renovateの設定の自動生成

renovateの設定を手動で管理する代わりに、pnpm-workspace.yamlとそのアノテーションから自動生成するスクリプトを作成しました。 このスクリプトは、pnpm-workspace.yamlを解析してrenovate.json5を生成します。 これにより、パッケージの追加や削除があった場合でも、設定の更新を自動化できます。

グループ分けの基準

レビューしやすくするために、renovateのgroup機能を使ってグループ単位でパッケージのアップデートをしています。グループ化は、レビューや動作検証がしやすい単位を意識して設計しています。

パッケージのグループ化は、次のような基準で行っています。

  1. アップデートが同じタイミングのものをグループ化(バージョンが同じタイミングで上がるもの)
  2. 意味的なまとまりでグループ化(ESLintなどの関連パッケージ)
  3. 確認手順が全く同じになるものをグループ化

また、特定のアプリケーションのみで使用しているパッケージは、{app-name}-sdkのようにアプリケーション名のprefixをつけてまとめます。 これにより、レビューや動作検証がしやすい単位でグループ化され、Pull Requestもこの単位で作成されます。

renovateの設定の生成ロジック

生成ロジックは次の通りです。@pkg-groupが同じパッケージは同じグループとしてまとめられ、単一のPRで更新されます。 さらに、@pkg-automergeが付与されたパッケージは、別のグループに分割され、Auto Mergeが有効化されます。

例えば、pnpm-workspace.yamlに次のような設定がある場合を考えます。

# `pnpm-workspace.yaml`
catalog:
  # @pkg-group: react
  # @pkg-automerge
  react: 19.0.0

  # @pkg-group: react
  # @pkg-automerge
  react-dom: 19.0.0

  # @pkg-group: react
  react-some-pkg: 1.0.0

このpnpm-workspace.yamlから、次のような2つのグループが生成されます。

[
  {
    "matchPackageNames": ["react", "react-dom"],
    "groupName": "web/react automerge group",
    "groupSlug": "react-automerge",
    "reviewers": ["team:frontend"],
    "addLabels": ["dependencies/automerge"],
    "automerge": true,
    "platformAutomerge": true
  },
  {
    "matchPackageNames": ["react-some-pkg"],
    "groupName": "web/react group",
    "groupSlug": "react",
    "reviewers": ["team:frontend"],
    "automerge": false
  }
]

このように、pnpm-workspace.yamlからスクリプトを使ってrenovateの設定を生成しています。パッケージの追加や変更があった場合は、スクリプトを実行してrenovate.json5を更新します。

automerge機能の実装

automerge機能は、opt-in方式で実装しました。デフォルトでは全てのパッケージが手動マージ対象であり、@pkg-automergeタグを明示的に付与したパッケージのみがAuto Mergeされます。この方針により、安全性を保ちながら段階的に自動化の範囲を拡大できます。

automerge機能の実現には、Renovate Approve GitHub Appを使用します。このアプリは、renovatebotが作成したPRを自動的に承認します。GitHubのBranch Protection設定でapprovalを必須にしている場合でも、Renovate Approve Appの承認により、Auto Mergeが可能になります。

automergeの設定

renovate.json5では、automergeplatformAutomergeの両方を有効にします。 automerge: trueはrenovatebot自身によるマージを有効にし、platformAutomerge: trueはGitHubのAuto-merge機能を利用します。 両方を有効にすることで、Branch Protectionの設定に応じた方法でマージされます。

ただし、全てのパッケージをAuto Mergeするわけではありません。Auto Mergeの対象は、次の条件を満たすパッケージに限定します。

  1. CIでの自動チェックが可能である
  2. バージョンアップデートによる影響範囲が明確である
  3. パッチバージョンまたはマイナーバージョンの更新である

さらに、Auto Mergeには次の設定を設けています。

制限項目 設定値 説明
バージョン minor/patch only メジャーバージョンは自動除外
minimumReleaseAge 7日間 リリース後7日経過が必要
実行時間 平日9-18時 営業時間内のみ実行
CI必須 すべてグリーン テスト失敗時はマージしない

minimumReleaseAgeは、新しいバージョンに潜在的な問題があった場合でも、コミュニティで発見される期間を設けることができます。

メジャーバージョンの更新は、設定に関わらずrenovatebotの対象外としています。 メジャーバージョンアップデートは破壊的変更を含む可能性が高く、CIの失敗や影響範囲の不明確さから、即座の対応が困難です。 そのため、メジャーバージョンアップデートはLinearでIssueを作成し、手動またはDevinで対応します。

スクリプトで生成されるrenovate.json5には、全てのパッケージのメジャーバージョンアップデートを無視する設定が含まれています。

{
  "packageRules": [
    {
      "matchPackageNames": ["*"],
      "matchUpdateTypes": ["major"],
      "enabled": false
    }
  ]
}

この設定により、メジャーバージョンアップデートのPRは作成されません。メジャーバージョンの更新が必要な場合は、目視でのチェックまたはDevinによる月次通知で検知し、手動で対応します。

CODEOWNERSとの共存

Renovate Approve Appを使う方法は、CODEOWNERSファイルと相性が悪いという課題があります。しかし、pnpm catalogの仕組みを活用することで、この問題をうまく解決できます。

CODEOWNERSファイルでは、次のように設定しています。

# フロントエンドの依存関係の管理
**/package.json                  @newmohq/frontend

# renovatebotでautomergeするためコードオーナーを外す
**/pnpm-lock.yaml
**/`pnpm-workspace.yaml`

ポイントは、pnpm-workspace.yamlpnpm-lock.yamlのアップデートだけはCODEOWNERSを外し、renovate appでもapproveできるようにしていることです。 一方で、package.jsonはfrontendチームをレビュアーに入れています。

pnpm catalogでは、パッケージのバージョンアップデート時にpackage.jsonを変更する必要がありません。 バージョンはpnpm-workspace.yamlで一元管理されているため、アップデートはpnpm-workspace.yamlpnpm-lock.yamlのみの変更で完結します。

この仕組みにより、次のような運用が可能になります。

  • 新規パッケージの追加: package.jsonを変更するため、frontendチームのレビューが必須
  • パッケージのアップデート: package.jsonを変更しないため、automergeが可能

ハンドブックによる運用

全てのパッケージがautomergeできるわけではありません。 そのため、newmoでは全てのパッケージについて、レビュー時の確認手順をハンドブックとしてドキュメント化しています。 このハンドブックは、Notionのデータベースで管理され、パッケージグループごとに確認手順が記載されています。

Notionのハンドブック

ハンドブックの重要な原則は、初めてそのパッケージや該当アプリケーションを触る人でもわかるように書くことです。 既存の知識を前提とした記述は避け、具体的な手順を明確に示します。

良い例として、次のような記述があります。

1. https://localhost:8090/test にアクセス
2. "送信する"ボタンが表示されることを確認
3. ボタンをクリックして、送信が成功することを確認

悪い例は、次のような記述です。

既存のものとは動作が変わらないことを確認

この記述では、既存の動作を知らない人は確認できません。 初めての人でも対応できるように、比較対象を明確に示すか、期待される動作を具体的に記述します。

ハンドブックには、次の情報を含めます。

  1. 何に使っているライブラリか
  2. 何をすると動作が確認できるか
  3. リリースノートを確認するポイント
  4. 過去の事例へのリンク

実際の例として、GIS系のパッケージ(deck.gl、duckdb-wasm等)のハンドブックでは、次のような具体的な確認手順を記載しています。

### エリア編集ページでの確認手順

1. エリア管理ページへアクセス
2. 任意のエリアの編集ページを開く
3. ポリゴンを編集(例:頂点を移動、エリアを削除/追加)
4. 編集内容が次のように表示されることを確認
   - 削除された部分:赤色で表示
   - 追加された部分:緑色で表示
5. 「GeoJSONで確認」ボタンをクリック
6. 出力されたGeoJSONが次の構造を持つことを確認
   - `type: "FeatureCollection"`が含まれている
   - `features`配列に編集内容が反映されている
   - ジオメトリの座標が正しい形式である

この例のように、具体的な操作手順と期待される結果を明示することで、初めてそのパッケージに触れる人でも確認ができます。 さらに、スクリーンショットを交えることで、視覚的にわかりやすい手順にしています。視覚的な期待値(赤色/緑色)や技術的な検証項目(GeoJSONの構造)を明記することで、確認の客観性を高めています。

また、確認手順を明文化することで、それが自動化できるかをチェックできます。実際に、7割のパッケージの確認手順は自動テストでカバーされているため、automerge対象となっています。

レビュープロセスは次の流れになります。

  1. renovatebotが自動的にPRを作成し、reviewerにfrontendチームをアサイン
  2. レビュアーはリリースノートをチェック
  3. めぼしい変更があればコメント
  4. ハンドブックを元に動作確認
  5. LGTMしてマージ

automerge対象のパッケージは、CIが通過すれば自動的にマージされるため、レビュアーの介入は不要です。ただし、CIが失敗した場合は、reviewerに設定されている人が対応します。

Devinによる支援

レビューの効率化のため、Devinとの連携も活用しています。

PRレビューの支援

@devin !depsというマクロコマンドでDevinに依存のアップデートを手伝ってもらえるようにしています。 実際のプロンプトは次の通りです。

## macro: deps

現在の {repo_name} のOpen状態のPRを確認して、`dependencies``frontend`のラベルがついているPRについて以下のタスクを実行してください。

## タスク

ライブラリバージョン更新に伴う以下の作業を行ってください:

1. 各PRの変更内容を確認し、更新されるライブラリのバージョンを特定する
2. 更新されるライブラリのリリースノートを確認し、Breaking Changesがないかを確認する
3. Breaking Changesがある場合は、それがコードにどのような影響を与えるかを分析する
4. Breaking Changesに対応する修正が必要な場合は、元のPRのブランチから新しいブランチを切って、必要な修正を加えたPRを作成する
   - 修正PRを作成する場合は、元のPRをこの修正PRにリンクする
   - renovateがついているPRは、Renovatebotが自動的に作成するので、PRに対する直接の変更は避ける
5. PRのレビューコメントに、以下の情報を日本語で投稿する:
   - 更新されるライブラリとそのバージョンの変更内容のサマリ
   - Breaking Changesの有無とその影響
   - 修正が必要な場合は、修正PRへのリンク
   - 動作への影響の評価

{repo_name} のソースコードへのアクセス方法:
- GitHub CLI: 利用可能
- Web UI: 利用可能

## 出力

各PRについて、レビューコメントとして投稿してください。
複数のPRがある場合は、それぞれのPRに対して個別にレビューを実施してください。

ただし、Devinはドキュメントデータベースを読めないため、ハンドブックの確認は人間が担当します。Devinはあくまで補助であり、最終的な判断はレビュアーが行います。

メジャーバージョンアップデートの通知

renovateではメジャーバージョンアップデートを自動化していないため、手動でアップデートします。しかし、アップデートを忘れてしまうリスクがあります。

この問題に対して、Devinに毎月1回、メジャーバージョンアップデートをまとめて報告してもらう仕組みを導入しています。実際のプロンプトは次の通りです。

## Task: Monthly Major Version Update Report

{repo_name} のパッケージについて、メジャーバージョンアップデートをSlackで報告してください。

## 手順

1. リポジトリをクローン
2. `pnpm outdated -r` を実行し、メジャーバージョンアップデートのあるパッケージを検出
3. 検出された各パッケージについて、次の情報をSlackチャンネル {slack_channel} に投稿:
   - パッケージ名
   - 現在のバージョン
   - 最新のバージョン
   - リリースノートのURL
   - そのパッケージが使われているプロジェクト(workspaceの名前)
   - リリースノートからの簡単なサマリ(日本語、主な変更点を3〜5個程度)

4. 全てのパッケージの報告が終わったら、最後に @dev-frontend をメンションして完了を通知

## 注意事項

- パッケージごとに別々のメッセージとして投稿する(後でLinearでIssueを作成しやすくするため)
- リリースノートが見つからない場合は、GitHubのリリースページやCHANGELOGを確認する
- サマリは技術的な内容を含め、開発者が判断できる情報を提供する

{repo_name} のソースコードへのアクセス方法:
- GitHub CLI: 利用可能
- Slack API: 利用可能

実行の自動化には、Slack Workflowを使用しています。Slack Workflowで毎月1回Devinにメンションすることで、cronjobのようにDevinの実行を自動化できます。

実装の効果

automergeの適用状況

newmoの現在の状況は次の通りです。

  • パッケージ総数: 96個
  • automerge可能なパッケージ: 71個(約74%)
  • 手動マージが必要なパッケージ: 22個
  • 無視されたパッケージ: 3個
  • グループ数: 21グループ

約7割のパッケージがautomerge対象となっており、これらのパッケージについてはレビュー作業が不要になりました。 残りの約3割は、まだ自動テスト化できていない確認項目があるため人間によるレビューをしていますが、今後も段階的に自動化を進めていく予定です。

得られた効果

  1. パッケージ更新作業の削減:約7割のパッケージでレビュー作業が不要
  2. CI実行回数の削減:PR滞留時間の短縮により、リベース回数が減少
  3. セキュリティアップデートの適用速度向上:セキュリティパッチを即座に適用
  4. レビュー品質の向上:ハンドブックによる基準の明確化

Chromaticのビルド回数が削減され、コストの増加を抑制できています。 マージコンフリクトの発生頻度も減少して、Auto Mergeによりレビュー待ちで滞留する時間が短縮されました。

今後の展望

現在約7割のパッケージがautomerge対象ですが、残りの約3割についても、自動テストの拡充により段階的にautomerge対象を増やしていく予定です。

また、pnpm-workspace.yamlをSingle Source of Truthとしているため、将来的に他のツールへの移行が必要になった場合も、比較的容易に対応できます。実際に、dependabotからrenovatebotへ移行した際には、スクリプトを書き換えるだけで移行できています。

まとめ

renovatebotとpnpm catalogの組み合わせにより、依存関係の自動更新システムを実現しました。 重要なのは、全パッケージの確認手順を明文化したハンドブックです。この確認手順のうち自動テストでカバーできるもの(約7割)はautomerge対象とし、それ以外は人間がハンドブックを見て確認します。 ハンドブックによる基準の明確化があって初めて、どこまで自動化できるかの判断が可能になります。 automergeだけでは安全性が保てず、ハンドブックだけでは効率が悪くなります。この2つを組み合わせることで、効率性と安全性を両立した依存関係管理が可能になりました。

pnpm catalogによる一元管理が基盤となり、アノテーションによるメタ情報管理、renovateの設定自動生成、automerge機能、ハンドブックによる運用という一連の仕組みが連携しています。 この仕組みにより、monorepoでの依存関係管理の複雑さを軽減し、開発者がコア機能の開発に集中できる環境を整備できました。