Table of Contents
Overview
In the previous post, we learned about risk classification criteria for efficiently handling Dependabot PRs.
In this post, we will learn how to automate PR handling using Dependabot configuration and GitHub Actions based on the classification criteria defined in the previous blog.
Dependabot Configuration
To automatically generate dependency update PRs using Dependabot, create a .github/dependabot.yml file and configure it as follows.
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
Key Configuration Points
The main features of this Dependabot configuration are as follows.
Schedule Settings
- Create PRs every Monday at 9:00 AM (Tokyo time)
- Check for updates on a weekly basis
PR Count Limits
- Root directory: Maximum 10
- GitHub Actions: Maximum 5
- Individual apps/packages: Maximum 3
Grouping Strategy
typescript-types: Group@types/*packages (Minor/Patch only)linting: Group ESLint, Prettier, Stylelint related packagestesting: Group testing toolspatch-updates: Batch process patch updates (excluding high-risk packages)storybook: Group Storybook related packages
Commit Message Prefixes
deps(common): Common dependenciesdeps(app1),deps(app2), etc.: App-specific dependenciesdeps(components): Component package dependencies
Exclusion Settings
- Internal packages (
@packages/*) are excluded from Dependabot targets - Shared ESLint configuration (
eslint-config-custom) is also excluded
Automation with GitHub Actions
Using GitHub Actions, you can automatically add labels and write checklist comments to Dependabot PRs.
Create a .github/workflows/dependabot-labeler.yml file and write as follows.
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}`);
This GitHub Actions workflow performs the following functions:
- Dependabot PR Detection: Only processes PRs created by
dependabot[bot] - Package Classification: Extracts package name from PR title and determines risk category
- Version Analysis: Detects version change type (Major/Minor/Patch) from PR body
- Label Addition: Automatically adds labels based on risk level and update type
- Checklist Comment: Writes checklist comments using templates from
docs/dependencies/folder
Automatic Labeling
When a Dependabot PR is created, risk labels and update type labels are automatically added.
Risk Labels
| Label | Color | Condition |
|---|---|---|
risk: high | Red | Major updates of high-risk libraries |
risk: medium | Yellow | Minor/Patch of high-risk libraries, Major of medium-risk libraries (CI detectable), Major/Minor of medium-risk libraries (runtime), Major of low-risk runtime |
risk: low | Green | Minor/Patch of medium-risk libraries (CI detectable), Patch of medium-risk libraries (runtime), all versions of low-risk dev tools, Minor/Patch of low-risk runtime |
Update Labels
| Label | Color | Condition |
|---|---|---|
update: major | Red | Major version update |
update: minor | Yellow | Minor version update |
update: patch | Green | Patch version update |
Checklist Comment Templates
Create the docs/dependencies/ folder and create checklist template files for use in the GitHub Actions workflow as follows.
docs/dependencies/high-risk.md
## High Risk Dependency Update
This PR updates a **high-risk** dependency (`{{packageName}}`). Please review carefully.
### Required Checklist
- [ ] Verify CI passes
- [ ] Check CHANGELOG and Breaking Changes
- [ ] Check migration guide (if applicable)
- [ ] Test all apps and team member review
> **Warning**: Major updates of this package may require extensive testing and code changes.
docs/dependencies/medium-risk.md
## Medium Risk Dependency Update
This PR updates a **medium-risk** dependency (`{{packageName}}`).
### Required Checklist
- [ ] Verify CI passes
- [ ] Check CHANGELOG (verify no breaking changes)
- [ ] Test affected apps and team member review (`yarn why {{packageName}}`)
### Affected Apps
Run `yarn why {{packageName}}` to identify affected apps.
> **Note**: Please check the CHANGELOG before merging.
docs/dependencies/low-risk.md
## Low Risk Dependency Update
This PR updates a **low-risk** dependency (`{{packageName}}`).
### Required Checklist
- [ ] Verify CI passes
- [ ] Review by auto-assigned member
> This update is considered safe. Please merge after CI passes.
These template files are automatically added to comments by GitHub Actions when PRs are created. {{packageName}} is automatically replaced with the actual package name.
Conclusion
This concludes our look at how to automate PR handling using Dependabot configuration and GitHub Actions.
By applying this automation, you can achieve the following benefits:
- Automatically classify risks of library updates
- Automatically add labels and checklists to PRs
- Maintain a consistent review process
- Reduce review burden on team members
Customize these settings according to your project’s characteristics.
Please refer to the previous post for risk classification criteria.
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku.Deku created the applications with Flutter.If you have interested, please try to download them for free.