diff --git a/action/lib/parse.js b/action/lib/parse.js index 8bc6dff8..ac7d72e5 100644 --- a/action/lib/parse.js +++ b/action/lib/parse.js @@ -45,13 +45,13 @@ export default function ({ title, labels = [], config = [], dependencies = {} }) const from = title.match(new RegExp('from v?' + regex.semver.source))?.groups const to = title.match(new RegExp('to v?' + regex.semver.source))?.groups - if (!from || !to) { + if (!to) { core.warning('failed to parse title: no recognizable versions') return process.exit(0) // soft exit } // exit early - if (!semver.valid(from.version) || !semver.valid(to.version)) { + if (!semver.valid(to.version)) { core.warning('failed to parse title: invalid semver') return process.exit(0) // soft exit } @@ -78,13 +78,17 @@ export default function ({ title, labels = [], config = [], dependencies = {} }) } // log - core.info(`from: ${from.version}`) + core.info(`from: ${from ? from.version : 'unknown'}`) core.info(`to: ${to.version}`) core.info(`dependency type: ${isProd ? 'production' : 'development'}`) core.info(`security critical: ${isSecurity}`) // analyze with semver - const versionChange = semver.diff(from.version, to.version) + let versionChange + + if (from && from.version) { + versionChange = semver.diff(from.version, to.version) + } // check all configuration variants to see if one matches for (const { match: { dependency_name, dependency_type, update_type } } of config) { @@ -119,6 +123,17 @@ export default function ({ title, labels = [], config = [], dependencies = {} }) // skip when config is for security update and PR is not security if (type === 'security' && !isSecurity) continue + if (target === 'all') { + core.info(`${dependency_name || dependency_type}:${update_type} detected, will auto-merge`) + return true + } + + // when there is no "from" version, there is no change detected + if (!versionChange) { + core.warning('no version range detected in PR title') + continue + } + // evaluate weight of detected change if ((weight[target] || 0) >= (weight[versionChange] || 0)) { // tell dependabot to merge diff --git a/action/test/parse/dep-name.js b/action/test/parse/dep-name.js index 02dfa825..704b9d6a 100644 --- a/action/test/parse/dep-name.js +++ b/action/test/parse/dep-name.js @@ -85,7 +85,8 @@ tap.test('parse -> out of range', async assert => { tap.test('parse -> edge cases', async assert => { const titles = [ { message: 'Update rake requirement from 10.4.0 to 13.0.0', name: 'rake' }, - { message: 'Bump actions/cache from v2.0.0 to v2.1.2', name: 'actions/cache' } + { message: 'Bump actions/cache from v2.0.0 to v2.1.2', name: 'actions/cache' }, + { message: 'Update actions/setup-python requirement to v2.1.4', name: 'actions/setup-python' } ] assert.plan(titles.length * 3) diff --git a/action/test/parse/match-target.js b/action/test/parse/match-target.js index 04db8c6d..834ad9c0 100644 --- a/action/test/parse/match-target.js +++ b/action/test/parse/match-target.js @@ -8,8 +8,8 @@ import fs from 'fs' // module import parse from '../../lib/parse.js' -function config (target) { - return [{ match: { dependency_type: 'all', update_type: `semver:${target}` } }] +function config (update_type) { + return [{ match: { dependency_type: 'all', update_type } }] } tap.test('title -> in range', async assert => { @@ -20,7 +20,7 @@ tap.test('title -> in range', async assert => { const options = { title: 'chore(deps): bump api-problem from 6.1.2 to 6.1.4 in /path', - config: config('major') + config: config('semver:major') } assert.ok(parse(options)) @@ -36,6 +36,60 @@ tap.test('title -> in range', async assert => { fs.existsSync.restore() }) +tap.test('title -> in range, no from', async assert => { + assert.plan(8) + + sinon.stub(core, 'info') + // sinon.stub(core, 'warning') + sinon.stub(fs, 'existsSync').returns(false) + + const options = { + title: 'Update actions/setup-python requirement to v2.1.4 in /path', + config: config('all') + } + + assert.ok(parse(options)) + assert.ok(core.info.called) + // assert.ok(core.warning.called) + assert.equal(core.info.getCall(0)?.firstArg, 'title: "Update actions/setup-python requirement to v2.1.4 in /path"') + assert.equal(core.info.getCall(1)?.firstArg, 'depName: actions/setup-python') + assert.equal(core.info.getCall(2)?.firstArg, 'from: unknown') + assert.equal(core.info.getCall(3)?.firstArg, 'to: 2.1.4') + assert.equal(core.info.getCall(6)?.firstArg, 'config: all:all') + assert.equal(core.info.getCall(7)?.firstArg, 'all:all detected, will auto-merge') + // assert.equal(core.warning.getCall(0)?.firstArg, 'no version range detected in PR title') + + core.info.restore() + fs.existsSync.restore() +}) + +tap.test('title -> in range, no from', async assert => { + assert.plan(8) + + sinon.stub(core, 'info') + // sinon.stub(core, 'warning') + sinon.stub(fs, 'existsSync').returns(false) + + const options = { + title: 'Update actions/setup-python requirement to v2.1.4 in /path', + config: config('semver:all') + } + + assert.ok(parse(options)) + assert.ok(core.info.called) + // assert.ok(core.warning.called) + assert.equal(core.info.getCall(0)?.firstArg, 'title: "Update actions/setup-python requirement to v2.1.4 in /path"') + assert.equal(core.info.getCall(1)?.firstArg, 'depName: actions/setup-python') + assert.equal(core.info.getCall(2)?.firstArg, 'from: unknown') + assert.equal(core.info.getCall(3)?.firstArg, 'to: 2.1.4') + assert.equal(core.info.getCall(6)?.firstArg, 'config: all:semver:all') + assert.equal(core.info.getCall(7)?.firstArg, 'all:semver:all detected, will auto-merge') + // assert.equal(core.warning.getCall(0)?.firstArg, 'no version range detected in PR title') + + core.info.restore() + fs.existsSync.restore() +}) + tap.test('parse -> out of range', async assert => { assert.plan(5) @@ -44,7 +98,7 @@ tap.test('parse -> out of range', async assert => { const options = { title: 'chore(deps): bump api-problem from 6.1.2 to 7.0.0 in /path', - config: config('patch') + config: config('semver:patch') } assert.notOk(parse(options), false) @@ -56,3 +110,31 @@ tap.test('parse -> out of range', async assert => { core.info.restore() fs.existsSync.restore() }) + +tap.test('title -> out of range, no from', async assert => { + assert.plan(10) + + sinon.stub(core, 'info') + sinon.stub(core, 'warning') + sinon.stub(fs, 'existsSync').returns(false) + + const options = { + title: 'Update actions/setup-python requirement to v2.1.4 in /path', + config: config('semver:major') + } + + assert.notOk(parse(options)) + assert.ok(core.info.called) + assert.ok(core.warning.called) + assert.equal(core.info.getCall(0)?.firstArg, 'title: "Update actions/setup-python requirement to v2.1.4 in /path"') + assert.equal(core.info.getCall(1)?.firstArg, 'depName: actions/setup-python') + assert.equal(core.info.getCall(2)?.firstArg, 'from: unknown') + assert.equal(core.info.getCall(3)?.firstArg, 'to: 2.1.4') + assert.equal(core.info.getCall(6)?.firstArg, 'config: all:semver:major') + assert.equal(core.info.getCall(7)?.firstArg, 'manual merging required') + assert.equal(core.warning.getCall(0)?.firstArg, 'no version range detected in PR title') + + core.info.restore() + core.warning.restore() + fs.existsSync.restore() +})