概要
前回の投稿では、Dependabot PRを効率的に処理するためのリスク分類基準について学びました。
この投稿では、前回のブログで定義した分類基準に基づいて、Dependabot設定とGitHub Actionsを使用してPR処理を自動化する方法について学びます。
Dependabot設定
Dependabotを使用して依存関係の更新PRを自動的に生成するには、.github/dependabot.ymlファイルを作成し、以下のように設定します。
version: 2
enable-beta-ecosystems: true
updates:
# GitHub Actions
- package-ecosystem: github-actions
directory: '/'
open-pull-requests-limit: 5
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(common)'
groups:
github-actions:
patterns:
- '*'
# Root npm dependencies (common dependency management)
- package-ecosystem: npm
directory: '/'
open-pull-requests-limit: 10
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(common)'
groups:
# TypeScript types (low risk)
typescript-types:
patterns:
- '@types/*'
update-types:
- minor
- patch
# ESLint/Prettier related (low risk)
linting:
patterns:
- 'eslint*'
- '@eslint/*'
- 'prettier*'
- 'stylelint*'
update-types:
- minor
- patch
# Testing tools (low risk)
testing:
patterns:
- '@testing-library/*'
- 'vitest*'
- 'jest*'
update-types:
- minor
- patch
# Patch updates batch processing
patch-updates:
update-types:
- patch
exclude-patterns:
- 'react*'
- 'typescript'
- 'vite*'
# Apps settings
- package-ecosystem: npm
directory: '/apps/app1'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(app1)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/apps/app2'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(app2)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/apps/app3'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(app3)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/apps/app4'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(app4)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/apps/app5'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(app5)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/apps/template'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(template)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
minor-and-patch:
update-types:
- minor
- patch
# Config packages
- package-ecosystem: npm
directory: '/packages/config/eslint'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(common)'
groups:
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/packages/config/stylelint'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(common)'
groups:
minor-and-patch:
update-types:
- minor
- patch
# Shared packages
- package-ecosystem: npm
directory: '/packages/lib/components'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(components)'
ignore:
- dependency-name: '@packages/*'
- dependency-name: 'eslint-config-custom'
groups:
storybook:
patterns:
- '@storybook/*'
- 'storybook'
update-types:
- minor
- patch
minor-and-patch:
update-types:
- minor
- patch
- package-ecosystem: npm
directory: '/packages/lib/CommonAPI'
open-pull-requests-limit: 3
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Tokyo
commit-message:
prefix: 'deps(common)'
groups:
minor-and-patch:
update-types:
- minor
- patch
設定の主要ポイント
このDependabot設定の主な特徴は以下の通りです。
スケジュール設定
- 毎週月曜日午前9時(東京時間)にPR生成
- 週単位でアップデートを確認
PR数の制限
- ルートディレクトリ:最大10個
- GitHub Actions:最大5個
- 個別アプリ/パッケージ:最大3個
グループ化戦略
typescript-types:@types/*パッケージをグループ化(Minor/Patchのみ)linting:ESLint、Prettier、Stylelint関連パッケージをグループ化testing:テストツールをグループ化patch-updates:Patchアップデートを一括処理(高リスクパッケージを除く)storybook:Storybook関連パッケージをグループ化
コミットメッセージプレフィックス
deps(common):共通依存関係deps(app1)、deps(app2)など:アプリ別依存関係deps(components):コンポーネントパッケージ依存関係
除外設定
- 内部パッケージ(
@packages/*)はDependabotの対象から除外 - 共有ESLint設定(
eslint-config-custom)も除外
GitHub Actionsによる自動化
GitHub Actionsを使用して、Dependabot PRに自動的にラベルを追加し、チェックリストコメントを作成できます。
.github/workflows/dependabot-labeler.ymlファイルを作成し、以下のように記述します。
name: Dependabot PR Labeler
on:
pull_request:
types:
- opened
- synchronize
jobs:
label-and-comment:
name: Add risk labels and checklist
runs-on: ubuntu-latest
timeout-minutes: 5
if: github.event.pull_request.user.login == 'dependabot[bot]'
permissions:
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Analyze and label PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const path = require('path');
const { owner, repo } = context.repo;
const prNumber = context.payload.pull_request.number;
const prTitle = context.payload.pull_request.title;
const prBody = context.payload.pull_request.body || '';
// High risk packages - runtime + affects all apps + hard to detect
const HIGH_RISK = [
'react',
'react-dom',
'@reduxjs/toolkit',
'react-redux',
'react-router-dom'
];
// Medium risk packages - categorized by risk behavior
// Category A: Major → medium, Minor/Patch → low (CI detectable)
const MEDIUM_RISK_CI_DETECTABLE = [
// Build tools
'vite',
'typescript',
'@vitejs/plugin-react',
'turbo',
// Test frameworks
'jest',
'vitest',
'@playwright/test',
// Linting & Code quality
'eslint',
'@typescript-eslint/',
// Storybook
'storybook',
'@storybook/'
];
// Category B: Major/Minor → medium, Patch → low (Runtime impact)
const MEDIUM_RISK_RUNTIME = [
// Runtime - all apps
'axios',
'i18next',
'react-i18next',
// App-specific runtime
'konva',
'react-konva',
'recharts'
];
// Low risk - Pure dev tools (always low risk, all update types)
const LOW_RISK_DEV_ONLY = [
'@types/',
'prettier',
'stylelint',
'eslint-config-',
'eslint-plugin-',
'@testing-library/',
'ts-jest',
'@vitest/ui',
'sass',
'husky',
'lint-staged',
'remark-',
'cspell',
'chromatic'
];
// Low risk - Runtime utilities (Major → medium, Minor/Patch → low)
const LOW_RISK_RUNTIME = [
'lodash',
'js-cookie',
'date-fns',
'clsx',
'react-ga4',
'qrcode.react',
'react-hotkeys-hook',
'papaparse'
];
// Extract package name from PR title
// Format: "deps(scope): bump <package> from x.x.x to y.y.y"
const packageMatch = prTitle.match(/bump\s+(@?[\w\-\/]+)\s+from/i);
const packageName = packageMatch ? packageMatch[1] : 'unknown';
// Determine update type from PR body or title
let updateType = 'patch';
const versionMatch = prBody.match(/from\s+(\d+)\.(\d+)\.(\d+)\s+to\s+(\d+)\.(\d+)\.(\d+)/);
if (versionMatch) {
const [, fromMajor, fromMinor, , toMajor, toMinor] = versionMatch;
if (fromMajor !== toMajor) {
updateType = 'major';
} else if (fromMinor !== toMinor) {
updateType = 'minor';
}
}
// Determine base risk level (from package category)
const isHighRiskPackage = HIGH_RISK.some(pkg => packageName === pkg || packageName.startsWith(pkg));
const isMediumRiskCIDetectable = MEDIUM_RISK_CI_DETECTABLE.some(pkg => packageName === pkg || packageName.startsWith(pkg));
const isMediumRiskRuntime = MEDIUM_RISK_RUNTIME.some(pkg => packageName === pkg || packageName.startsWith(pkg));
const isLowRiskDevOnly = LOW_RISK_DEV_ONLY.some(pkg => packageName === pkg || packageName.startsWith(pkg));
const isLowRiskRuntime = LOW_RISK_RUNTIME.some(pkg => packageName === pkg || packageName.startsWith(pkg));
// Determine final risk level based on package category and update type
let riskLevel = 'low';
if (isHighRiskPackage) {
// High risk packages: only major updates remain high risk
riskLevel = updateType === 'major' ? 'high' : 'medium';
} else if (isMediumRiskCIDetectable) {
// CI detectable (build tools, test, lint, storybook): Major → medium, Minor/Patch → low
riskLevel = updateType === 'major' ? 'medium' : 'low';
} else if (isMediumRiskRuntime) {
// Runtime impact (axios, i18n, app-specific): Major/Minor → medium, Patch → low
riskLevel = updateType === 'patch' ? 'low' : 'medium';
} else if (isLowRiskDevOnly) {
// Pure dev tools: always low risk
riskLevel = 'low';
} else if (isLowRiskRuntime) {
// Low risk runtime utilities: Major → medium, Minor/Patch → low
riskLevel = updateType === 'major' ? 'medium' : 'low';
} else {
// Unknown packages: major updates become medium risk
riskLevel = updateType === 'major' ? 'medium' : 'low';
}
// Define labels
const riskLabels = {
high: { name: 'risk: high', color: 'B60205', description: 'High risk dependency update' },
medium: { name: 'risk: medium', color: 'FBCA04', description: 'Medium risk dependency update' },
low: { name: 'risk: low', color: '0E8A16', description: 'Low risk dependency update' }
};
const updateLabels = {
major: { name: 'update: major', color: 'B60205', description: 'Major version update' },
minor: { name: 'update: minor', color: 'FBCA04', description: 'Minor version update' },
patch: { name: 'update: patch', color: '0E8A16', description: 'Patch version update' }
};
// Create labels if they don't exist
const labelsToCreate = [riskLabels[riskLevel], updateLabels[updateType]];
for (const label of labelsToCreate) {
try {
await github.rest.issues.getLabel({
owner,
repo,
name: label.name
});
} catch (error) {
if (error.status === 404) {
await github.rest.issues.createLabel({
owner,
repo,
name: label.name,
color: label.color,
description: label.description
});
}
}
}
// Add labels to PR
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: [riskLabels[riskLevel].name, updateLabels[updateType].name]
});
// Read checklist template from docs/dependencies
const templatePath = path.join(process.env.GITHUB_WORKSPACE, 'docs', 'dependencies', `${riskLevel}-risk.md`);
let commentBody = '';
try {
commentBody = fs.readFileSync(templatePath, 'utf8');
// Replace placeholders
commentBody = commentBody.replace(/\{\{packageName\}\}/g, packageName);
} catch (error) {
console.log(`Template file not found: ${templatePath}`);
commentBody = `## ${riskLevel.charAt(0).toUpperCase() + riskLevel.slice(1)} Risk Dependency Update\n\nThis PR updates \`${packageName}\`.`;
}
// Check if bot already commented
const comments = await github.rest.issues.listComments({
owner,
repo,
issue_number: prNumber
});
const botComment = comments.data.find(
comment => comment.user.login === 'github-actions[bot]' &&
comment.body.includes('Risk Dependency Update')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner,
repo,
comment_id: botComment.id,
body: commentBody
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: commentBody
});
}
console.log(`PR #${prNumber}: ${packageName} - Risk: ${riskLevel}, Update: ${updateType}`);
このGitHub Actionsワークフローは以下の機能を実行します。
- Dependabot PR検出:
dependabot[bot]が作成したPRのみを処理 - パッケージ分類:PRタイトルからパッケージ名を抽出し、リスクカテゴリを判定
- バージョン分析:PR本文からバージョン変更タイプ(Major/Minor/Patch)を検出
- ラベル追加:リスクレベルとアップデートタイプに応じたラベルを自動追加
- チェックリストコメント:
docs/dependencies/フォルダのテンプレートを使用してチェックリストコメントを作成
自動ラベリング
Dependabot PRが作成されると、自動的にリスクラベルとアップデートタイプラベルが追加されます。
リスクラベル
| ラベル | 色 | 条件 |
|---|---|---|
risk: high | 赤 | 高リスクライブラリのMajorアップデート |
risk: medium | 黄 | 高リスクライブラリのMinor/Patch、中リスクライブラリのMajor(CI検出可能)、中リスクライブラリのMajor/Minor(ランタイム)、低リスクランタイムのMajor |
risk: low | 緑 | 中リスクライブラリのMinor/Patch(CI検出可能)、中リスクライブラリのPatch(ランタイム)、低リスク開発ツール(全バージョン)、低リスクランタイムのMinor/Patch |
アップデートラベル
| ラベル | 色 | 条件 |
|---|---|---|
update: major | 赤 | Majorバージョンアップデート |
update: minor | 黄 | Minorバージョンアップデート |
update: patch | 緑 | Patchバージョンアップデート |
チェックリストコメントテンプレート
docs/dependencies/フォルダを作成し、GitHub Actionsワークフローで使用するチェックリストテンプレートファイルを以下のように作成します。
docs/dependencies/high-risk.md
## 高リスク依存関係の更新
このPRは**高リスク**の依存関係(`{{packageName}}`)を更新します。慎重にレビューしてください。
### 必須チェックリスト
- [ ] CI通過確認
- [ ] CHANGELOG、Breaking changes確認
- [ ] マイグレーションガイドを確認(該当する場合)
- [ ] 全てのアプリテストとチームメンバーレビュー
> **警告**: このパッケージのメジャーアップデートは、大規模なテストとコード変更が必要になる場合があります。
docs/dependencies/medium-risk.md
## 中リスク依存関係の更新
このPRは**中リスク**の依存関係(`{{packageName}}`)を更新します。
### 必須チェックリスト
- [ ] CI通過確認
- [ ] CHANGELOG確認(Breaking changesがないこと確認)
- [ ] 影響を受けるアプリテストとチームメンバーレビュー(`yarn why {{packageName}}`)
### 影響を受けるアプリ
`yarn why {{packageName}}`コマンドを実行して影響を受けるアプリを確認してください。
> **注意**: マージ前にCHANGELOGを確認してください。
docs/dependencies/low-risk.md
## 低リスク依存関係の更新
このPRは**低リスク**の依存関係(`{{packageName}}`)を更新します。
### 必須チェックリスト
- [ ] CI通過確認
- [ ] 自動アサインされたメンバーがレビュー
> この更新は安全と見なされます。CI通過後にマージしてください。
これらのテンプレートファイルはPRが作成されるとGitHub Actionsによって自動的にコメントに追加されます。{{packageName}}は実際のパッケージ名に自動的に置換されます。
完了
これでDependabot設定とGitHub Actionsを使用してPR処理を自動化する方法について学びました。
この自動化を適用すると、以下のメリットが得られます。
- ライブラリアップデートのリスクを自動分類
- PRに自動的にラベルとチェックリストを追加
- 一貫したレビュープロセスを維持
- チームメンバーのレビュー負担を軽減
プロジェクトの特性に合わせてこれらの設定をカスタマイズしてご利用ください。
前回の投稿でリスク分類基準を確認できますので、再度参考にしてください。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。