Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/content/dependabot-yml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ updates:
- package-ecosystem: npm
directory: /
schedule:
interval: daily
interval: {{ interval }}
target-branch: "{{ branch }}"
allow:
- dependency-type: direct
Expand Down
1 change: 1 addition & 0 deletions lib/content/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ module.exports = {
esm: false,
updateNpm: true,
dependabot: 'increase-if-necessary',
dependabotInterval: 'daily',
unwantedPackages: [
'eslint',
'eslint-plugin-node',
Expand Down
9 changes: 7 additions & 2 deletions lib/util/dependabot.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const { minimatch } = require('minimatch')
const parseDependabotConfig = v => (typeof v === 'string' ? { strategy: v } : (v ?? {}))

module.exports = (config, defaultConfig, branches) => {
const { dependabot } = config
const { dependabot: defaultDependabot } = defaultConfig
const { dependabot, dependabotInterval } = config
const { dependabot: defaultDependabot, dependabotInterval: defaultInterval } = defaultConfig

if (!dependabot) {
return false
Expand All @@ -15,8 +15,13 @@ module.exports = (config, defaultConfig, branches) => {
.filter(b => dependabot[b] !== false)
.map(branch => {
const isReleaseBranch = minimatch(branch, config.releaseBranch)

// Determine the interval to use: branch-specific > package-specific > default
const interval = parseDependabotConfig(dependabot[branch]).interval || dependabotInterval || defaultInterval

return {
branch,
interval,
allowNames: isReleaseBranch ? [NAME] : [],
labels: isReleaseBranch ? ['Backport', branch] : [],
...parseDependabotConfig(defaultDependabot),
Expand Down
173 changes: 172 additions & 1 deletion test/apply/dependabot.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ const setup = require('../setup.js')
const setupDependabot = async (t, { branches = ['main'], ...config } = {}) => {
const s = await setup(t, {
package: {
templateOSS: config,
templateOSS: {
...config,
// Include branches in the templateOSS config so they get processed
branches: branches.length > 1 ? branches : undefined,
},
},
mocks: {
'@npmcli/git': {
Expand Down Expand Up @@ -101,3 +105,170 @@ t.test('no dependabot', async t => {
t.equal(s.dependabot, false)
t.equal(s.postDependabot, false)
})

t.test('custom interval', async t => {
const s = await setupDependabot(t, {
dependabotInterval: 'weekly',
})

t.match(s.dependabot[0], {
schedule: { interval: 'weekly' },
})
})

t.test('branch-specific interval', async t => {
const s = await setupDependabot(t, {
dependabot: {
main: { interval: 'monthly' },
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'monthly' },
})
})

t.test('mixed interval configuration', async t => {
const s = await setupDependabot(t, {
branches: ['main', 'develop'],
dependabotInterval: 'weekly',
dependabot: {
main: { interval: 'monthly' },
},
})

t.equal(s.dependabot.length, 2)

// main branch should use branch-specific interval
const mainBranch = s.dependabot.find(d => d['target-branch'] === 'main')
t.match(mainBranch, {
schedule: { interval: 'monthly' },
})

// develop branch should use global interval
const developBranch = s.dependabot.find(d => d['target-branch'] === 'develop')
t.match(developBranch, {
schedule: { interval: 'weekly' },
})
})

t.test('branch-specific interval with strategy', async t => {
const s = await setupDependabot(t, {
dependabot: {
main: { interval: 'weekly', strategy: 'auto' },
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'weekly' },
'versioning-strategy': 'auto',
})
})

t.test('global interval with branch-specific strategy only', async t => {
const s = await setupDependabot(t, {
dependabotInterval: 'monthly',
dependabot: {
main: { strategy: 'lockfile-only' },
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'monthly' },
'versioning-strategy': 'lockfile-only',
})
})

t.test('fallback to daily when no interval specified', async t => {
const s = await setupDependabot(t, {
dependabot: 'increase-if-necessary',
})

t.match(s.dependabot[0], {
schedule: { interval: 'daily' },
'versioning-strategy': 'increase-if-necessary',
})
})

t.test('mixed branches with some having interval and some not', async t => {
const s = await setupDependabot(t, {
branches: ['main', 'develop', 'staging'],
dependabotInterval: 'weekly',
dependabot: {
main: { interval: 'monthly' },
develop: { strategy: 'auto' },
// staging gets global interval
},
})

t.equal(s.dependabot.length, 3)

const mainBranch = s.dependabot.find(d => d['target-branch'] === 'main')
t.match(mainBranch, {
schedule: { interval: 'monthly' },
})

const developBranch = s.dependabot.find(d => d['target-branch'] === 'develop')
t.match(developBranch, {
schedule: { interval: 'weekly' },
'versioning-strategy': 'auto',
})

const stagingBranch = s.dependabot.find(d => d['target-branch'] === 'staging')
t.match(stagingBranch, {
schedule: { interval: 'weekly' },
})
})

t.test('empty branch config falls back to global interval', async t => {
const s = await setupDependabot(t, {
dependabotInterval: 'monthly',
dependabot: {
main: {}, // empty object should fall back to global interval
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'monthly' },
})
})

t.test('no package interval and no branch interval falls back to default', async t => {
const s = await setupDependabot(t, {
// no dependabotInterval at package level
dependabot: {
main: {}, // empty object, no interval
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'daily' }, // should fall back to default
})
})

t.test('branch config as string without interval falls back properly', async t => {
const s = await setupDependabot(t, {
// no dependabotInterval at package level
dependabot: {
main: 'auto', // string config, no interval property
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'daily' }, // should fall back to default
'versioning-strategy': 'auto',
})
})

t.test('falsy package interval and no branch interval falls back to default', async t => {
const s = await setupDependabot(t, {
dependabotInterval: null, // explicitly falsy
dependabot: {
main: {}, // empty object, no interval
},
})

t.match(s.dependabot[0], {
schedule: { interval: 'daily' }, // should fall back to default
})
})