RenovateをGitLabで定期実行する

@Panda_Program

Renovate でパッケージのアップデートをする

Renovate は、依存関係の更新チェックを自動化するツールです。

本記事では、Renovate を owned の GitLab で定期実行し、npm パッケージのアップデートを自動化する方法を紹介します。

renovate.jsonを作成する

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 用の設定を記述します。

.gitlab-ci.yml
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から設定しましょう。

GitLabの画面

ローカルで 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/dependenciesrenovate/devdependencies
  • メジャーバージョンのアップデート
    • renovate/major-dependenciesrenovate/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 🎉

パンダのイラスト
パンダ

記事が面白いと思ったらツイートやはてブをお願いします!皆さんの感想が執筆のモチベーションになります。最後まで読んでくれてありがとう。

  • Share on Hatena
  • Share on Twitter
  • Share on Line
  • Copy to clipboard