Skip to content

Commit e7b430e

Browse files
authored
feat: Add dependabotInterval configuration (#518)
This pull request introduces enhancements to the Dependabot configuration system, focusing on adding support for customizable update schedules at both global and branch-specific levels. It also includes updates to the test suite to ensure comprehensive coverage of these new features.
1 parent 9c6a2ff commit e7b430e

File tree

4 files changed

+181
-4
lines changed

4 files changed

+181
-4
lines changed

lib/content/dependabot-yml.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ updates:
55
- package-ecosystem: npm
66
directory: /
77
schedule:
8-
interval: daily
8+
interval: {{ interval }}
99
target-branch: "{{ branch }}"
1010
allow:
1111
- dependency-type: direct

lib/content/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ module.exports = {
187187
esm: false,
188188
updateNpm: true,
189189
dependabot: 'increase-if-necessary',
190+
dependabotInterval: 'daily',
190191
unwantedPackages: [
191192
'eslint',
192193
'eslint-plugin-node',

lib/util/dependabot.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const { minimatch } = require('minimatch')
44
const parseDependabotConfig = v => (typeof v === 'string' ? { strategy: v } : (v ?? {}))
55

66
module.exports = (config, defaultConfig, branches) => {
7-
const { dependabot } = config
8-
const { dependabot: defaultDependabot } = defaultConfig
7+
const { dependabot, dependabotInterval } = config
8+
const { dependabot: defaultDependabot, dependabotInterval: defaultInterval } = defaultConfig
99

1010
if (!dependabot) {
1111
return false
@@ -15,8 +15,13 @@ module.exports = (config, defaultConfig, branches) => {
1515
.filter(b => dependabot[b] !== false)
1616
.map(branch => {
1717
const isReleaseBranch = minimatch(branch, config.releaseBranch)
18+
19+
// Determine the interval to use: branch-specific > package-specific > default
20+
const interval = parseDependabotConfig(dependabot[branch]).interval || dependabotInterval || defaultInterval
21+
1822
return {
1923
branch,
24+
interval,
2025
allowNames: isReleaseBranch ? [NAME] : [],
2126
labels: isReleaseBranch ? ['Backport', branch] : [],
2227
...parseDependabotConfig(defaultDependabot),

test/apply/dependabot.js

Lines changed: 172 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ const setup = require('../setup.js')
55
const setupDependabot = async (t, { branches = ['main'], ...config } = {}) => {
66
const s = await setup(t, {
77
package: {
8-
templateOSS: config,
8+
templateOSS: {
9+
...config,
10+
// Include branches in the templateOSS config so they get processed
11+
branches: branches.length > 1 ? branches : undefined,
12+
},
913
},
1014
mocks: {
1115
'@npmcli/git': {
@@ -101,3 +105,170 @@ t.test('no dependabot', async t => {
101105
t.equal(s.dependabot, false)
102106
t.equal(s.postDependabot, false)
103107
})
108+
109+
t.test('custom interval', async t => {
110+
const s = await setupDependabot(t, {
111+
dependabotInterval: 'weekly',
112+
})
113+
114+
t.match(s.dependabot[0], {
115+
schedule: { interval: 'weekly' },
116+
})
117+
})
118+
119+
t.test('branch-specific interval', async t => {
120+
const s = await setupDependabot(t, {
121+
dependabot: {
122+
main: { interval: 'monthly' },
123+
},
124+
})
125+
126+
t.match(s.dependabot[0], {
127+
schedule: { interval: 'monthly' },
128+
})
129+
})
130+
131+
t.test('mixed interval configuration', async t => {
132+
const s = await setupDependabot(t, {
133+
branches: ['main', 'develop'],
134+
dependabotInterval: 'weekly',
135+
dependabot: {
136+
main: { interval: 'monthly' },
137+
},
138+
})
139+
140+
t.equal(s.dependabot.length, 2)
141+
142+
// main branch should use branch-specific interval
143+
const mainBranch = s.dependabot.find(d => d['target-branch'] === 'main')
144+
t.match(mainBranch, {
145+
schedule: { interval: 'monthly' },
146+
})
147+
148+
// develop branch should use global interval
149+
const developBranch = s.dependabot.find(d => d['target-branch'] === 'develop')
150+
t.match(developBranch, {
151+
schedule: { interval: 'weekly' },
152+
})
153+
})
154+
155+
t.test('branch-specific interval with strategy', async t => {
156+
const s = await setupDependabot(t, {
157+
dependabot: {
158+
main: { interval: 'weekly', strategy: 'auto' },
159+
},
160+
})
161+
162+
t.match(s.dependabot[0], {
163+
schedule: { interval: 'weekly' },
164+
'versioning-strategy': 'auto',
165+
})
166+
})
167+
168+
t.test('global interval with branch-specific strategy only', async t => {
169+
const s = await setupDependabot(t, {
170+
dependabotInterval: 'monthly',
171+
dependabot: {
172+
main: { strategy: 'lockfile-only' },
173+
},
174+
})
175+
176+
t.match(s.dependabot[0], {
177+
schedule: { interval: 'monthly' },
178+
'versioning-strategy': 'lockfile-only',
179+
})
180+
})
181+
182+
t.test('fallback to daily when no interval specified', async t => {
183+
const s = await setupDependabot(t, {
184+
dependabot: 'increase-if-necessary',
185+
})
186+
187+
t.match(s.dependabot[0], {
188+
schedule: { interval: 'daily' },
189+
'versioning-strategy': 'increase-if-necessary',
190+
})
191+
})
192+
193+
t.test('mixed branches with some having interval and some not', async t => {
194+
const s = await setupDependabot(t, {
195+
branches: ['main', 'develop', 'staging'],
196+
dependabotInterval: 'weekly',
197+
dependabot: {
198+
main: { interval: 'monthly' },
199+
develop: { strategy: 'auto' },
200+
// staging gets global interval
201+
},
202+
})
203+
204+
t.equal(s.dependabot.length, 3)
205+
206+
const mainBranch = s.dependabot.find(d => d['target-branch'] === 'main')
207+
t.match(mainBranch, {
208+
schedule: { interval: 'monthly' },
209+
})
210+
211+
const developBranch = s.dependabot.find(d => d['target-branch'] === 'develop')
212+
t.match(developBranch, {
213+
schedule: { interval: 'weekly' },
214+
'versioning-strategy': 'auto',
215+
})
216+
217+
const stagingBranch = s.dependabot.find(d => d['target-branch'] === 'staging')
218+
t.match(stagingBranch, {
219+
schedule: { interval: 'weekly' },
220+
})
221+
})
222+
223+
t.test('empty branch config falls back to global interval', async t => {
224+
const s = await setupDependabot(t, {
225+
dependabotInterval: 'monthly',
226+
dependabot: {
227+
main: {}, // empty object should fall back to global interval
228+
},
229+
})
230+
231+
t.match(s.dependabot[0], {
232+
schedule: { interval: 'monthly' },
233+
})
234+
})
235+
236+
t.test('no package interval and no branch interval falls back to default', async t => {
237+
const s = await setupDependabot(t, {
238+
// no dependabotInterval at package level
239+
dependabot: {
240+
main: {}, // empty object, no interval
241+
},
242+
})
243+
244+
t.match(s.dependabot[0], {
245+
schedule: { interval: 'daily' }, // should fall back to default
246+
})
247+
})
248+
249+
t.test('branch config as string without interval falls back properly', async t => {
250+
const s = await setupDependabot(t, {
251+
// no dependabotInterval at package level
252+
dependabot: {
253+
main: 'auto', // string config, no interval property
254+
},
255+
})
256+
257+
t.match(s.dependabot[0], {
258+
schedule: { interval: 'daily' }, // should fall back to default
259+
'versioning-strategy': 'auto',
260+
})
261+
})
262+
263+
t.test('falsy package interval and no branch interval falls back to default', async t => {
264+
const s = await setupDependabot(t, {
265+
dependabotInterval: null, // explicitly falsy
266+
dependabot: {
267+
main: {}, // empty object, no interval
268+
},
269+
})
270+
271+
t.match(s.dependabot[0], {
272+
schedule: { interval: 'daily' }, // should fall back to default
273+
})
274+
})

0 commit comments

Comments
 (0)