Skip to content

Commit 9a59d26

Browse files
committed
ci: add Dependabot daily with auto-merge and major-version analysis
1 parent fbf37b5 commit 9a59d26

3 files changed

Lines changed: 199 additions & 2 deletions

File tree

.github/dependabot.yml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,35 @@ updates:
2727
prefix: 'chore'
2828
include: 'scope'
2929
open-pull-requests-limit: 10
30+
cooldown:
31+
default-days: 1
3032

3133
- package-ecosystem: 'github-actions'
3234
directory: '/'
3335
schedule:
34-
interval: 'weekly'
36+
interval: 'daily'
3537
groups:
36-
github-actions:
38+
minor-and-patch:
39+
patterns:
40+
- '*'
41+
update-types:
42+
- 'minor'
43+
- 'patch'
44+
# Workaround for dependabot/dependabot-core#14202: without an explicit
45+
# major group, major updates matching the minor-and-patch pattern are
46+
# silently suppressed. Remove this group when #14202 is fixed to get
47+
# individual (ungrouped) PRs per major bump instead.
48+
major:
3749
patterns:
3850
- '*'
51+
update-types:
52+
- 'major'
53+
labels:
54+
- 'dependencies'
55+
- 'github-actions'
3956
commit-message:
4057
prefix: 'ci'
58+
include: 'scope'
59+
open-pull-requests-limit: 10
60+
cooldown:
61+
default-days: 1
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Dependabot Auto-Merge (Minor/Patch)
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, synchronize]
6+
7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
11+
jobs:
12+
auto-merge:
13+
runs-on: ubuntu-latest
14+
if: github.event.pull_request.user.login == 'dependabot[bot]'
15+
steps:
16+
- name: Fetch Dependabot metadata
17+
id: metadata
18+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
19+
with:
20+
github-token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Auto-approve and merge minor/patch github-actions updates
23+
if: >-
24+
steps.metadata.outputs.package-ecosystem == 'github_actions' &&
25+
(steps.metadata.outputs.update-type == 'version-update:semver-minor' ||
26+
steps.metadata.outputs.update-type == 'version-update:semver-patch')
27+
env:
28+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
29+
PR_URL: ${{ github.event.pull_request.html_url }}
30+
run: |
31+
gh pr review "$PR_URL" --approve
32+
gh pr merge "$PR_URL" --auto --merge
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
name: Dependabot Major Version Analysis
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
11+
jobs:
12+
analyze-major:
13+
runs-on: ubuntu-latest
14+
if: github.event.pull_request.user.login == 'dependabot[bot]'
15+
steps:
16+
- name: Fetch Dependabot metadata
17+
id: metadata
18+
uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0
19+
with:
20+
github-token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Analyze major version bump
23+
if: >-
24+
steps.metadata.outputs.package-ecosystem == 'github_actions' &&
25+
steps.metadata.outputs.update-type == 'version-update:semver-major'
26+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
27+
env:
28+
DEP_NAME: ${{ steps.metadata.outputs.dependency-names }}
29+
PREV_VERSION: ${{ steps.metadata.outputs.previous-version }}
30+
NEW_VERSION: ${{ steps.metadata.outputs.new-version }}
31+
with:
32+
github-token: ${{ secrets.GITHUB_TOKEN }}
33+
script: |
34+
const depName = process.env.DEP_NAME;
35+
const prevVersion = process.env.PREV_VERSION;
36+
const newVersion = process.env.NEW_VERSION;
37+
const parts = depName.split('/');
38+
const owner = parts[0];
39+
const repo = parts[1];
40+
const repoSlug = `${owner}/${repo}`;
41+
42+
let releases = [];
43+
try {
44+
const { data } = await github.rest.repos.listReleases({ owner, repo, per_page: 50 });
45+
releases = data;
46+
} catch (err) {
47+
core.warning(`Could not fetch releases for ${repoSlug}: ${err.message}`);
48+
}
49+
50+
const prevMajor = parseInt(prevVersion.replace(/^v/, ''), 10);
51+
const newMajor = parseInt(newVersion.replace(/^v/, ''), 10);
52+
53+
const relevantReleases = releases.filter(r => {
54+
const major = parseInt(r.tag_name.replace(/^v/, ''), 10);
55+
return major > prevMajor && major <= newMajor;
56+
});
57+
58+
let releaseNotesSummary = '';
59+
let breakingChanges = '';
60+
61+
if (relevantReleases.length === 0) {
62+
releaseNotesSummary = '_No releases found between these versions._';
63+
breakingChanges = `_Unable to determine breaking changes automatically. Please review the [full changelog](https://github.com/${repoSlug}/releases)._`;
64+
} else {
65+
for (const release of relevantReleases.slice(0, 10)) {
66+
const body = release.body || '_No release notes._';
67+
releaseNotesSummary += `### ${release.tag_name}${release.name && release.name !== release.tag_name ? ' — ' + release.name : ''}\n\n`;
68+
releaseNotesSummary += body.substring(0, 2000);
69+
if (body.length > 2000) releaseNotesSummary += '\n\n_...truncated_';
70+
releaseNotesSummary += '\n\n---\n\n';
71+
const lines = body.split('\n');
72+
for (const line of lines) {
73+
if (/breaking|BREAKING|removed|deprecated|incompatible|migration/i.test(line)) {
74+
breakingChanges += `- ${line.trim()}\n`;
75+
}
76+
}
77+
}
78+
}
79+
80+
if (!breakingChanges) {
81+
breakingChanges = '_No explicit breaking changes detected in release notes. Manual review recommended._';
82+
}
83+
84+
let commentBody = `## :warning: Major Version Update — Manual Review Required
85+
86+
| Field | Value |
87+
|-------|-------|
88+
| **Action** | [\`${depName}\`](https://github.com/${repoSlug}) |
89+
| **Previous** | \`v${prevVersion}\` |
90+
| **New** | \`v${newVersion}\` |
91+
| **Type** | Major (\`v${prevMajor}\` → \`v${newMajor}\`) |
92+
93+
### Breaking Changes
94+
95+
${breakingChanges}
96+
97+
### Release Notes (v${prevMajor + 1} → v${newMajor})
98+
99+
${releaseNotesSummary}
100+
101+
### Next Steps
102+
103+
1. Review breaking changes above
104+
2. Check if workflow inputs/outputs changed
105+
3. Verify compatibility with your CI/CD configuration
106+
107+
> Full changelog: https://github.com/${repoSlug}/releases
108+
109+
---
110+
_Generated automatically for Dependabot major version PRs._`.replace(/^ /gm, '');
111+
112+
if (commentBody.length > 64000) {
113+
commentBody = commentBody.substring(0, 63900) + '\n\n_...comment truncated due to size limit._';
114+
}
115+
116+
await github.rest.issues.createComment({
117+
owner: context.repo.owner,
118+
repo: context.repo.repo,
119+
issue_number: context.payload.pull_request.number,
120+
body: commentBody,
121+
});
122+
123+
try {
124+
const labelsToAdd = ['major-update', 'needs-review'];
125+
for (const label of labelsToAdd) {
126+
try {
127+
await github.rest.issues.getLabel({ owner: context.repo.owner, repo: context.repo.repo, name: label });
128+
} catch {
129+
const colors = { 'major-update': 'B60205', 'needs-review': 'FBCA04' };
130+
await github.rest.issues.createLabel({
131+
owner: context.repo.owner, repo: context.repo.repo,
132+
name: label, color: colors[label] || 'EDEDED',
133+
});
134+
}
135+
}
136+
await github.rest.issues.addLabels({
137+
owner: context.repo.owner,
138+
repo: context.repo.repo,
139+
issue_number: context.payload.pull_request.number,
140+
labels: labelsToAdd,
141+
});
142+
} catch (err) {
143+
core.warning(`Could not add labels: ${err.message}`);
144+
}

0 commit comments

Comments
 (0)