RenovateをGitLabで定期実行する
Renovate でパッケージのアップデートをする
Renovate は、依存関係の更新チェックを自動化するツールです。
本記事では、Renovate を owned の GitLab で定期実行し、npm パッケージのアップデートを自動化する方法を紹介します。
renovate.jsonを作成する
renovate.json
をプロジェクトルートに作成します。
{
"extends": ["config:base"], // 公式の推奨設定を継承
"timezone": "Asia/Tokyo", // タイムゾーンの設定
"enabledManagers": ["npm"], // パッケージマネージャの名称
"lockFileMaintenance": {
"enabled": true // package-lock.json / yarn.lock を更新
},
"rangeStrategy": "pin", // バージョンを固定
"packageRules": [
{
"matchPackagePatterns": ["*"], // 全パッケージが対象
},
{
"matchDepTypes": ["dependencies"], // dependencies のみの MR を作成
"groupName": "dependencies"
},
{
"matchDepTypes": ["devDependencies"], // devDependencies のみの MR を作成
"groupName": "devDependencies"
}
],
"baseBranches": ["main"], // 更新対象のブランチ
"assignees": ["user_name"] // 更新チェックの担当者名(GitLab のユーザー名)
}
config:base
の設定内容は公式ドキュメントに記載があります。
また、package.json
がプロジェクトルートにない場合は、packageFileDir
でディレクトリを指定します。
なお、renovate.json はプロジェクトの要求に応じて柔軟に設定ができます。詳しいオプションはConfiguration Options を参照してください。
GitLab CI で定期実行の設定をする
.gitlab-ci.yml
に Renovate 用の設定を記述します。
stages:
- dependency
renovate:
stage: dependency
image:
name: renovate/renovate:24.95.0
entrypoint: [""]
script:
- renovate --platform gitlab --token $API_TOKEN --endpoint $CI_SERVER_URL/api/v4 $CI_PROJECT_PATH
rules:
- if: $RENOVATE && $CI_PIPELINE_SOURCE == "schedule"
.gitlab-ci.yml
のポイントは以下です。
- Renovate 自体のバージョンアップデートが頻繁にあり、その都度 renovate.json の書き方が少し変わるため、docker image のバージョンを固定します。
$API_TOKEN
は GitLab ユーザーのプロジェクト用のトークンを利用します。read/write と api 実行の権限が必要です。- Job を作成する際、cron で週に一度の定期実行の設定します。本業では月曜日の午前3時に設定していました。
- Job作成時、作成画面で
$RENOVATE
という変数を設定し、他の定期 Job を実行した時に Renovate の Job が起動しないようにします。
$CI_
から始まる変数は、 GitLab CIの環境変数です 。CI でログ多様な値が利用できて便利です。
定期実行ジョブは、GitLab のメニューのCI/CD > Schedules > New Schedule
から設定しましょう。
ローカルで Renovate の動作確認をする
Docker image を使用し、Renovate コマンドに--dry-run
オプションを与えて、ローカルで動作確認をしましょう。
対象となるリモートブランチ(今回はorigin/main
)に renovate.json が存在していることが前提です。
$ docker run --rm renovate/renovate:24.95.0 renovate --platform gitlab --token your_api_token --endpoint https://your-gitlab-hostname.com/api/v4 your/project-name --dry-run
実行結果は以下の通りです。
$ docker run --rm renovate/renovate:24.95.0 renovate --platform gitlab --token your_api_token --endpoint https://your-gitlab-hostname.com/api/v4 your/project-name --dry-run
INFO: Repository started (repository=your/project-name)
"renovateVersion": "24.95.0"
INFO: Dependency extraction complete (repository=your/project-name)
"baseBranch": "main",
"stats": {
"managers": {"npm": {"fileCount": 1, "depCount": 82}},
"total": {"fileCount": 1, "depCount": 82}
}
...
INFO: DRY-RUN: Would commit files to branch renovate/pin-dependencies (repository=your/project-name, branch=renovate/pin-dependencies)
INFO: DRY-RUN: Would commit files to branch renovate/dependencies (repository=your/project-name, branch=renovate/dependencies)
INFO: DRY-RUN: Would commit files to branch renovate/devdependencies (repository=your/project-name, branch=renovate/devdependencies)
INFO: DRY-RUN: Would commit files to branch renovate/major-dependencies (repository=your/project-name, branch=renovate/major-dependencies)
INFO: DRY-RUN: Would commit files to branch renovate/major-devdependencies (repository=your/project-name, branch=renovate/major-devdependencies)
INFO: Repository finished (repository=your/project-name)
"durationMs": 117962
--dry-run
なしで実行した場合、以下の MR が作成されることが実行ログからわかります。
- dependencies のバージョンを固定する
renovate/pin-dependencies
- minor, patch バージョンのアップデート
renovate/dependencies
、renovate/devdependencies
- メジャーバージョンのアップデート
renovate/major-dependencies
、renovate/major-devdependencies
Renovate 導入のメリット
Renovate 導入のメリットは多岐に渡ります。
- 週に一度の更新なので、差分が多くないため更新内容を把握しやすい
- 依存関係を最新にできるため、セキュリティリスクを低減できる
- trivy のような脆弱性検査に引っ掛かる回数が減る
- パッケージ更新のための人的コストが削減できる
型ファイルやリント、テスト関係のパッケージは独立した MR として作成するようにしても良いでしょう。
また、外部ライブラリを利用している箇所のテストがないプロジェクトでは毎回の動作確認の負担が重くなるため、テストを書くモチベーションにもなります。
Renovateの運用について
私が担当しているプロジェクトでの Renovate の運用方法を少し紹介します。前提として、プロジェクトの特徴は以下の通りです。
- Next.js + TypeScript の中規模のプロジェクト
- Jest による Unit Test を記述
- テストカバレッジは90%程度
- エンジニアは私を含めて3名
運用では、devDependencies の マイナー・パッチバージョンのアップデートの場合、まず staging 環境にマージし CI を実行して、CI が落ちない場合は master マージするというように省力化を図っていました。
CI で TypeScript による静的な型検査、eslint と Jest の実行、Next.js のビルドをしているため、CI が落ちない限りアプリケーションの動作には影響がないと判断しているからです。
メジャーバージョンのアップデートの場合は、CI 上で実行しているコマンドをローカル環境で実行しています。
その他、大規模なプロジェクトの場合は、どうしてもアップデートできないパッケージがあるはずです。
そのようなパッケージは、一旦 renovate.json で更新対象から外す設定をしましょう。そして、残りのバージョンを更新できるパッケージだけを更新することで、いつか来るリファクタリングに備えておくのが良いでしょう。
また、「WEB+DB PRESS Vol.119(Amazon)」 の「フロントエンド脱レガシー」特集によると、cybozu 社では社内共通の renovate.json を作成しているとのことです。
その設定を extend してプロジェクトごとに renovate.json を何度も設定する手間を省いているそうです(cybozu/renovate-config)。
まとめ
手動での動作確認はコストがかかりすぎるため、依存関係の頻繁な更新のためには自動テストが必要不可欠です。外部ライブラリを使う場合はアダプターを作成するとテストが書きやすくなり、テストが落ちる範囲も限定できます。
renovate によるパッケージアップデートで CI/CD の実行回数が増えます。ユーザーをバグから守るテストと CI/CD は両の車輪であり、エクストリーム・プログラミングのプラクティスにも CI/CD とテストの両方が含まれています。
パッケージの更新は少々大変ですが、テストも書きつつ、ソフトウェアのメンテナンスの一環として継続して実施していきたいものです。
Happy Coding 🎉