この記事は、ニフティグループ Advent Calendar 2023 3日目の記事です。
はじめに
こんにちは。会員システムグループでエンジニアをしている山田です。
私の担当しているプロダクトではシステム刷新を進めており、20年来のレガシーなJavaシステムからNode.js(Next.js)を利用したフロントエンドシステムへのフルリプレースを行いました。その後の運用体制を整えていく中で、GitHub Dependabotを使ったアップデート運用を導入したので、この紹介になります。
これまでの課題
今までミドルウェアや依存パッケージなどのアップデート対応には明確な指針がなく、だいたい以下のどちらかの運用になっていました。
- なんらかの問題(脆弱性など)が起こるまで放置
- 担当者が気づいた時に更新
前者は変更の少ない言語環境であれば機能する可能性がありますが、Node.jsのような依存パッケージ数も変更頻度も多い環境では大きなリスクを生みます。いざ変更が必要になった時には変更差分が把握しきれない量になっていたり、依存関係の都合で複数のアップデートを同時に行う必要が生じることも頻繁にあります。しかもこれが突発的に発生することで、必要な開発の妨げになる可能性を孕んでいました。
一方で後者は属人化しており、担当者の知識や意識に左右されます。担当者の異動などで継続不能になる可能性を孕んでいます。
以上から、継続的にアップデートを続ける体制を作っていく必要がありました。
Dependabotとは
DependabotはGitHubの機能として利用できるサービスであり、プロジェクト内の依存関係に含まれる脆弱性やアップデートの有無を検知して自動でプルリクエストを作成したり、アラートを出すことができるサービスです。
類似のサービスとしてRenovateがありますが、RenovateはGitHub AppsのインストールやGitHub Organization管理者による設定が一部必要と、導入にややハードルがあります。
一方でDependabotはGitHub組み込みの機能なので、リポジトリごとの設定で有効化することで使い始めることが可能です。
設定
Dependabotの設定
Dependabotの設定は .github/dependabot.ymlに記述します。
アップデート可能なパッケージがある場合、Dependabotはこの設定を元に自動的でプルリクエストを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
version: 2 updates: - package-ecosystem: "npm" directory: "frontend" schedule: interval: "weekly" groups: nextjs: patterns: - next - react - react-dom - "@types/react" - "@types/react-dom" - eslint-config-next dependencies: dependency-type: "production" devDependencies: dependency-type: "development" ignore: - dependency-name: "*" update-types: ["version-update:semver-patch"] |
詳細な文法はドキュメントを参照していただくとして、大まかに以下の設定をしています。
package-ecosystem
npmなどパッケージ種別を指定します。指定値としてはnpmでもyarnでも「npm」の指定になります。
実際にDependabotがどのパッケージマネージャに対応しているかは、ドキュメントで確認が必要です。例えば「pip」はpipenvとpoetryに対応していますが、ryeには対応していません。
言語ごとのパッケージだけでなく、Dockerfileのベースイメージなどにも対応しています。
directory
チェック対象ディレクトリ指定です。モノレポ構成などの場合に、個別ディレクトリを指定することで分けて管理することができるようになります。
schedule
チェック間隔です。daily、weeklyなどが指定できます。
groups
グルーピングの設定です。2023年になってから追加された機能になります。
デフォルトでは、パッケージ1つ1つのアップデートに対してプルリクエストが作成されてしまうため、多すぎて管理しきれなくなります。groupsを設定することにより、指定したパターンに一致するパッケージアップデートを1つのプルリクエストにまとめることができます。
私のプロジェクトでは
- Next.js関連
- TypeScript関連
- その他dependencies
- その他devDependencies
くらいのグループに分割して管理するようにしています。
ignore
除外条件です。特定パッケージやバージョンを除外することができます。
パッチバージョンまですべて追う必要はないと考えているため、パッチバージョンの除外指定を入れています。
以上の設定の他にも、プルリクエストコメントやラベルのカスタマイズなどが可能です。
設定によっては自動マージも可能ですが、リスクを鑑みて適用していません。
他のGitHub Actionsの修正
Dependabotに限った話ではないのですが、botアカウントは権限が絞られているため、GitHub Actionsのワークフローをbot権限で実行してしまうと失敗する場合があります。
私のプロジェクトではプルリクエスト起票者をAssigneeに追加するワークフローがあったため、botアカウントを除外するような対応を行いました。
1 2 3 4 5 6 7 |
jobs: add-info: name: Auto Assign runs-on: ubuntu-latest if: ${{ !contains(github.actor, '[bot]') }} # [bot]を含む場合は除外 steps: ... |
運用
アップデート対応
Dependabotのチェック頻度をweeklyに設定し、毎週のミーティングで自動作成されたプルリクエストを全て確認し、アップデート可否の判断をするようにしました。
動作そのものはGitHub Actionsで組まれたCIで担保していますが、隠れたリスクの発見や業界情勢の把握のため、全てのリリースノートを確認するようにしています。
除外対応
CIを用意しているとはいえ、Next.jsのメジャーバージョンアップなどの影響の大きい変更はリスクがあるため、一定期間を置いてから適用するようにしています。
この間、そのままだと同一グループの他のパッケージもアップデートができなくなってしまいます。このような場合はプルリクエストコメントに
1 |
@dependabot ignore <パッケージ> |
とコマンドを打つと、アップデート対象から一時的に除外できます。アップデートできるようになったら
1 |
@dependabot unignore <パッケージ名> |
で解除します。
運用してみて
良かったところ
目論見通り、属人化していたアップデートを誰でも行うことができるようになりました。
またリリースノートの確認を行うようになったことでコミュニティの動きをチームメンバー全員が把握できるようになり、知識向上にもつながるようになりました。
微妙なところ
一方で微妙だな、と思うポイントもいくつかあります。
コンフリクト時の挙動
複数グループにアップデートがある場合、1つのプルリクエストをマージするとロックファイルがコンフリクトを起こすため、他のプルリクエストは修正が必要になります。
このような場合、Dependabotは
- rebaseしてforce-push
- 既存プルリクエストをcloseし、新規に作成
のどちらかの対応を行うようなのですが、どちらになるかが現状予測できません。無駄なプルリクエストを減らすため、できれば前者に寄せたいところです。
またいずれの場合も数分待たされるので、グループ数が多いとすべて解消するのに時間がかかります。CIの実行時間もかかるので、グループ数はなるべく少なくした方が良いでしょう。
複数グループに同じパッケージが出現する
グループについて、ドキュメントには以下の記載があります。
Dependabot creates groups in the order they appear in your
dependabot.yml
file. If a dependency update could belong to more than one group, it is only assigned to the first group it matches with.
つまり上から順に評価され、複数グループに同じパッケージがマッチすることはないと読めます。
これはスケジュール通りの実行時には確かに機能するのですが、コンフリクト解消時にはうまく機能してくれないようで、rebaseされたタイミングで複数グループに同じパッケージが存在する状態になってしまうことがありました。
グループの設定で exclude-patternsを設定すれば確実に除外ができるのですが、全てのグループに除外設定を書く必要が出てくるため非常に煩雑です。
複数種にまたがるアップデートを同時に行えない
Node.jsのメジャーバージョンを上げたい場合、私のプロジェクトでは
- 本番用のDockerfileのベースイメージ
- 開発用のDockerfileのベースイメージ
- GitHub Actions上でのsetup-node
を揃えてバージョンアップする必要があります。しかしDependabotのグループ設定は複数ファイルにまたがる更新に対応しておらず、複数のプルリクエストに分かれてしまいます。
メジャーバージョンアップ自体は頻繁に発生するものではないので、個別で手動対応するようにしています。
まとめ
GitHub Dependabotを利用したアップデートプルリクエストの自動作成と、その運用についてご紹介しました。
Node.jsに限らず最近の言語ではOSSパッケージへの依存度が高く、そのアップデート対応は煩雑になりがちです。GitHubを利用されている方は、ぜひ活用して見ることをお勧めします。