見出し画像

[GitHub][JavaScript][Node.js] Renovateでライブラリ更新を自動化する PR作成・package.jsonとlockファイル更新・テスト実行まで

はじめに

こんにちは、SHIFTの開発部門に所属している Katayama です。

ライブラリの更新作業を手作業でやっていたが、GitHub の記事を読み、私も自動化しなければと思い立ったので、今回は GitHub においてRenovateを利用したライブラリの更新をやってみました。

執筆者プロフィール:Katayama Yuta
認証認可(SHIFTアカウント)や課金決済のプラットフォーム開発に従事。リードエンジニア。 経歴としては、SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、DevOpsの一環でCICD導入支援をする案件にも従事。その後現在のプラットフォーム開発に参画。

以下、手順のサマリになる。

  • Renovate(GitHub Apps)をインストールする

  • config ファイル書き換えて設定を変更する

  • package.json のバージョンを固定にする

  • PR 作成でテストを実行する

  • PR のマージ条件を設定する

※Renovateによるライブラリの更新の自動化の目的・期待する事としては以下が挙げられるだろう。

  • 常に最新のversionに追従する事で、

    • 脆弱性・Bugなどの修正を素早く取り込む

    • 自力で修正patchを実装するなどの手間をなくす

    • 自力で機能を追加するのではなく、OSSであればPRで機能追加を提案できる

※GitHubであればDependabotが選択肢に出てくると思われるが、DependabotはGitHubのみであり他のプラットフォームでは利用できないデメリットがある。また、RenovateではDependabotに比べて柔軟に設定でき、機能面でもDependabotにはデメリットがある。

Renovate(GitHub Apps)をインストールする

公式の Docにリンクがあるので、そこからインストールする。

"install"をクリックすると、認可の画面になるので必要に応じてリポジトリの選択などを行いインストールを継続する。

インストールをした後、config ファイルがない場合にはそれを作成するための PR が自動で作成される。

PR によって作成される config ファイルの中身はconfig:baseを継承するという単純なものだが、これにより以下の画像の説明の通りの設定がなされる。

ここまでで Renovate のインストール作業は完了になる。続いて、config ファイルを変更していく。

config ファイル書き換えて設定を変更する

今回は以下のような設定をしてみた。

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    "group:allNonMajor",
    ":separateMultipleMajorReleases",
    "schedule:earlyMondays",
    ":prConcurrentLimitNone",
    ":prHourlyLimitNone",
    ":timezone(Asia/Tokyo)"
  ],
  "labels": ["renovate"],
  "enabledManagers": ["npm"],
  "prConcurrentLimit": 0,
  "prHourlyLimit": 0,
  "major": { "labels": ["renovate", "update_major"] }
}

上記の設定について補足する。

  • extends
    いくつか Renovate にデフォルトで用意されている設定があるので、それを継承している。ここに設定できる項目は Presets と呼ばれ、Default PresetsGroup Presetsなどがある。

  • config:base
    これはあらゆる言語で共通に設定するといいものがまとまったプリセット

  • group:allNonMajor
    マイナーアップデートとパッチアップデートをまとめて行うという設定のためのプリセット。これにより以下のような PR が作成される。

  • schedule:earlyMondays
    毎週月曜日の早朝に実行されるスケジュールを設定するためのプリセット

  • :separateMultipleMajorReleases
    メジャーアップデートは 1 つずつの PR に分けるという設定のためのプリセット。これにより以下のような PR が作成される。

  • :prConcurrentLimitNone
    PR をオープンできる限度を解除する設定のためのプリセット

  • :prHourlyLimitNone
    1 時間ごとの PR 作成の上限を解除する設定のためのプリセット

  • labels
    PR に付けるラベルを設定している。これにより以下のようにラベルが付く。

Renovate の設定としては以上になる。続いて、Renovate でライブラリの更新を行うために package.json のバージョンを固定にする修正を行う。

package.json のバージョンを固定にする

これは公式に書かれている通り、Renovate を利用しようとする場合には package.json のバージョンを固定にする事が推奨されている。理由についてはWhy pin dependencies?に書かれている通り。

元々の package.json では"チルダ(^)"でバージョンを設定したので、それを固定バージョンに修正した。また、"yarn add"の際にバージョンを固定して package.json に追加されるようにするために、"npm config set save-exact=true --location project"コマンドで".npmrc"を作成する。

save-exact=true

また、yarn の場合、既知の問題があり、".yarnrc"に"save-prefix false"の設定が必要になる。

save-prefix false

※一応、上記のように固定した version にしない方法もある。その場合、:doNotPinPackage()を extends に追加する必要がある(または自分で matchPackageNames とrangeStrategyを設定するでもいい)。ちなみに、rangeStrategy の設定で以下のように更新のされ方が変わってくる。

  • replace:新しいバージョンが範囲外になる場合は新しいものに置き換え、それ以外の場合は何も更新しない

  • bump:新バージョンが既存の範囲を満たしていても範囲を広げる
    bump の場合、バージョンを更新する PR は以下のようになるのでチルダ(^)ありでも Renovate を使う事ができるようになる

※上記のbumpに設定した際のrenovate.jsonの例は以下。

{
	"$schema": "https://docs.renovatebot.com/renovate-schema.json",
	"extends": [
		"config:base",
		"group:allNonMajor",
		":separateMultipleMajorReleases",
		"schedule:daily",
		":prConcurrentLimitNone",
		":prHourlyLimitNone",
		":timezone(Asia/Tokyo)"
	],
	"labels": ["renovate"],
	"enabledManagers": ["npm"],
	"major": { "labels": ["renovate", "update_major"] },
	"platformAutomerge": true,
	"packageRules": [
		{
			"description": "Automerge non-major updates",
			"matchUpdateTypes": ["minor", "patch"],
			"automerge": true
		},
		{
			"matchPackageNames": [
				"axios",
				"axios-retry",
				"camelcase-keys",
				"core-js",
				"lodash",
				"moment",
				"snakecase-keys",
				"@vue/cli",
				"@vue/cli-plugin-babel",
				"@vue/cli-plugin-eslint",
				"@vue/cli-service",
				"@vue/eslint-config-airbnb",
				"babel-eslint",
				"eslint",
				"eslint-config-prettier",
				"eslint-plugin-import",
				"eslint-plugin-vue",
				"eslint-plugin-vuejs-accessibility",
				"license-webpack-plugin",
				"luxon",
				"prettier",
				"sass",
				"sass-loader",
				"vue-cli-plugin-vuetify",
				"vue-template-compiler",
				"vuetify-loader",
				"webpack",
				"webpack-bundle-analyzer",
				"webpack-version-file"
			],
			"rangeStrategy": "bump" // <- ここ
		}
	],
	"ignoreDeps": ["vue", "vue-router", "vuetify", "vuex"]
}

PR 作成でテストを実行する

続いて、PR の作成後に自動でテストを流すように workflow を作成する。これは GitHub Actions で実行したい処理を yaml に記述するだけなので、詳細は省くが、今回は以下のような test.yaml を作成した(gRPC の E2E のようなテストを実装したのでサーバーの起動をしている)。

name: test

on:
  pull_request:
    branches:
      - '**'

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: install
        run: yarn install --frozen-lockfile

      - name: build
        run: yarn build

      - name: start gRPC server
        run: node dist/server.js &

      - name: test
        run: yarn test

      - name: Stop gRPC server
        run: killall node

最後に、PR のマージ条件を設定し、テストが OK でない場合にはマージできないようにする設定を行う。

PR のマージ条件を設定する

上記の設定で、ライブラリの更新の PR 作成時に CI でテスト実行を行うように設定する事ができた。ただ、今の状態だと CI のテストが成功しようが失敗しようが、以下のようにマージができる状態になっている("Merge pull request"ボタンがグリーンとノーマルでノーマルの方はクリックできないように見えるが実際にはクリックでき、"confirm merge"ボタンが表示される)。

これでは CI でテストをしている意味がないので、 テスト失敗時にはマージもできないようにする設定を追加したいと思う。方法は簡単で、Settings > Branchs のメニュー > Branch protection rules から設定を追加するだけ。

設定としては以下のようにした。

少し設定内容について補足をする。

  • Branch name pattern
    ルールを設定したいブランチ名を記載

  • Require status checks to pass before merging
    ここが今回の肝で、マージする前にステータスチェックが合格しているか?を確認するようにする設定。チェックを必須にしたものは"Status checks that are required."の項目にリストアップされる。リストに追加するチェック項目は、GitHub Actions の job 名になる(今回は以下の job を CI として実行していたので、それを設定している)。

  • Do not allow bypassing the above settings
    これは管理者であろうとこのルールを skip できないようにするために設定している

上記のように設定を行うと、CI のテストが失敗した場合、以下のようにマージができないようになる(ちょっとわかりにくいが、以下の画像の"Merge pull request"ボタンは disabled になっておりクリックできない)。また、Required というラベルがチェック内容に追加されている事も確認できる。

以上で、ライブラリの更新の PR 作成から CI でのテストまでを自動化できた。あとは、人間が確認してマージボタンをポチっとするだけでライブラリの更新作業が完了する。

※ちなみに、"Do not allow bypassing the above settings"を Off にした場合、PR のマージボタンは以下のように表示され、ブランチ保護のチェックを迂回しているけどいいか?の確認チェックボックスが表示される。

まとめとして

今回は Renovate を利用して、ライブラリの更新を自動化するという事をやってみた。ライブラリの更新という後回しにしがちなタスクを半自動化する事で、ライブラリの更新を常にやるメリットを享受できるようになるため、最初面倒でも仕組みを構築するのは重要だと思った。

※今回は最後は人間の判断でマージするという事で、マージまでは自動化しなかったが、「おまけ」で取り上げているような方法で自動化する事も可能なので、全自動も夢ではない。ただ、自動でマージしていいのか?は議論が分かれると思うので、そこは組織・チームで議論すべきだろうと思った。

おまけ

チェックが OK になった PR を自動でマージする

さすがにメジャーバージョンアップ時は後方互換がなくなる事もあり、人間が確認すべき事もあると思うので、マイナー・パッチバージョンアップの際に自動でマージする設定を追加してみる。詳細は公式に書かれているが、以下のような設定を追加すればいい。

{
  ...,
  "platformAutomerge": true,
  "packageRules": [
    {
      "description": "Automerge non-major updates",
      "matchUpdateTypes": ["minor", "patch"],
      "automerge": true
    }
  ]
}

これで PR の作成、CI でのテスト、テストが OK であれば自動でマージ、という事が実現できる。

GitHub Merge Queueについての記述があるが、Github の公式ページに以下のように書かれている通り、個人の Github では利用できない。そのため上記では Merge Queue の設定は行っていない。

pull request マージ キューは、Organization が所有するパブリック リポジトリ、または GitHub Enterprise Cloud を使っている Organization が所有するプライベート リポジトリで使うことができます。

《この公式ブロガーの記事一覧》

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/