From 78effd493f8faf13354877a395ed4f2e0979015b Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Tue, 7 Dec 2021 17:23:45 +0100 Subject: [PATCH 1/7] fix: support semver-like PRs --- src/checkTargetMatchToPR.js | 5 ++++- test/checkTargetMatchToPR.test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/checkTargetMatchToPR.js b/src/checkTargetMatchToPR.js index e8db5353..1993b257 100644 --- a/src/checkTargetMatchToPR.js +++ b/src/checkTargetMatchToPR.js @@ -1,5 +1,6 @@ 'use strict' const semverDiff = require('semver/functions/diff') +const semverCoerce = require('semver/functions/coerce') const { semanticVersionOrder } = require('./getTargetInput') @@ -11,7 +12,9 @@ const checkTargetMatchToPR = (prTitle, target) => { if (!match) { return true } - const diff = semverDiff(match[1], match[2]) + + const [, from, to] = match + const diff = semverDiff(semverCoerce(from), semverCoerce(to)) return !( diff && diff --git a/test/checkTargetMatchToPR.test.js b/test/checkTargetMatchToPR.test.js index 66cc0d81..99bbafd9 100644 --- a/test/checkTargetMatchToPR.test.js +++ b/test/checkTargetMatchToPR.test.js @@ -18,6 +18,9 @@ const preReleaseToPathUpgradePRTitle = 'chore(deps-dev): bump fastify from 3.18.0-alpha to 3.18.2' const sameVersion = 'chore(deps-dev): bump fastify from 3.18.0 to 3.18.0' const patchPRTitleInSubDirectory = 'chore(deps-dev): bump fastify from 3.18.0 to 3.18.1 in /packages/a' +const semverLikeMinor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 2.3' +const semverLikeMajor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 3' +const semverLikeBothWay = 'chore(deps): bump nearform/optic-release-automation-action from 2 to 3' tap.test('checkTargetMatchToPR', async t => { t.test('should return true when target is major', async t => { @@ -140,4 +143,27 @@ tap.test('checkTargetMatchToPR', async t => { t.notOk(checkTargetMatchToPR(preMajorPRTitle, targetOptions.minor)) }) }) + + t.test('semver-like PR titles', async t => { + t.test('semver to minor semver-like', async t => { + t.notOk(checkTargetMatchToPR(semverLikeMinor, targetOptions.prepatch)) + t.notOk(checkTargetMatchToPR(semverLikeMinor, targetOptions.patch)) + t.ok(checkTargetMatchToPR(semverLikeMinor, targetOptions.minor)) + t.ok(checkTargetMatchToPR(semverLikeMinor, targetOptions.major)) + }) + + t.test('semver to major semver-like', async t => { + t.notOk(checkTargetMatchToPR(semverLikeMajor, targetOptions.prepatch)) + t.notOk(checkTargetMatchToPR(semverLikeMajor, targetOptions.patch)) + t.notOk(checkTargetMatchToPR(semverLikeMajor, targetOptions.minor)) + t.ok(checkTargetMatchToPR(semverLikeMajor, targetOptions.major)) + }) + + t.test('semver-like to semver-like', async t => { + t.notOk(checkTargetMatchToPR(semverLikeBothWay, targetOptions.prepatch)) + t.notOk(checkTargetMatchToPR(semverLikeBothWay, targetOptions.patch)) + t.notOk(checkTargetMatchToPR(semverLikeBothWay, targetOptions.minor)) + t.ok(checkTargetMatchToPR(semverLikeBothWay, targetOptions.major)) + }) + }) }) From 019aa0bc2594e8f08d9beea3195d118f95b31aa5 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Tue, 7 Dec 2021 17:26:22 +0100 Subject: [PATCH 2/7] add build --- dist/index.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/dist/index.js b/dist/index.js index f65eabfb..3b1792b1 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6142,6 +6142,64 @@ class SemVer { module.exports = SemVer +/***/ }), + +/***/ 3466: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const SemVer = __nccwpck_require__(8088) +const parse = __nccwpck_require__(5925) +const {re, t} = __nccwpck_require__(9523) + +const coerce = (version, options) => { + if (version instanceof SemVer) { + return version + } + + if (typeof version === 'number') { + version = String(version) + } + + if (typeof version !== 'string') { + return null + } + + options = options || {} + + let match = null + if (!options.rtl) { + match = version.match(re[t.COERCE]) + } else { + // Find the right-most coercible string that does not share + // a terminus with a more left-ward coercible string. + // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' + // + // Walk through the string checking with a /g regexp + // Manually set the index so as to pick up overlapping matches. + // Stop when we get a match that ends at the string end, since no + // coercible string can be more right-ward without the same terminus. + let next + while ((next = re[t.COERCERTL].exec(version)) && + (!match || match.index + match[0].length !== version.length) + ) { + if (!match || + next.index + next[0].length !== match.index + match[0].length) { + match = next + } + re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + } + // leave it in a clean state + re[t.COERCERTL].lastIndex = -1 + } + + if (match === null) + return null + + return parse(`${match[2]}.${match[3] || '0'}.${match[4] || '0'}`, options) +} +module.exports = coerce + + /***/ }), /***/ 4309: @@ -9179,6 +9237,7 @@ function isMajorRelease(pullRequest) { "use strict"; const semverDiff = __nccwpck_require__(4297) +const semverCoerce = __nccwpck_require__(3466) const { semanticVersionOrder } = __nccwpck_require__(5013) @@ -9190,7 +9249,9 @@ const checkTargetMatchToPR = (prTitle, target) => { if (!match) { return true } - const diff = semverDiff(match[1], match[2]) + + const [, from, to] = match + const diff = semverDiff(semverCoerce(from), semverCoerce(to)) return !( diff && From f1a9143bf9664590f3c06c22e347001015819531 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Wed, 8 Dec 2021 09:16:47 +0100 Subject: [PATCH 3/7] add tests --- test/action.test.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/action.test.js b/test/action.test.js index 728095e2..f0d5ed84 100644 --- a/test/action.test.js +++ b/test/action.test.js @@ -263,3 +263,29 @@ tap.test('should call external api for github-action-merge-dependabot major rele t.ok(stubs.logStub.logWarning.calledOnce) t.ok(stubs.fetchStub.calledOnce) }) + +tap.test('should check submodules semver when target is set', async t => { + const PR_NUMBER = Math.random() + const { action, stubs } = buildStubbedAction({ + payload: { + pull_request: { + number: PR_NUMBER, + title: 'chore(deps): bump fastify/submodule from 2.5.0 to 2.6', + user: { login: BOT_NAME }, + head: { ref: 'dependabot/github_actions/fastify/submodule-2.6' }, + } + }, + inputs: { + PR_NUMBER, + TARGET: 'minor', + EXCLUDE_PKGS: [], + API_URL: 'custom one', + DEFAULT_API_URL, + } + }) + + await action() + + t.ok(stubs.logStub.logInfo.calledOnceWith('pr-text')) + t.ok(stubs.fetchStub.calledOnce) +}) From d0e1a0eaa0bcb1198b16e266cd6c286a309cefcb Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Wed, 8 Dec 2021 11:09:31 +0100 Subject: [PATCH 4/7] testing submodules --- README.md | 3 +++ src/checkTargetMatchToPR.js | 11 +++++++++++ test/action.test.js | 4 ++-- test/checkTargetMatchToPR.test.js | 8 ++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac9f508a..c4da6b9e 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,9 @@ Possible options are: For more details on how semantic version difference is calculated please see [semver](https://www.npmjs.com/package/semver) package. +If you set a value other than `any`, PRs that are not semantic version compliant will not be merged. +An example of a non-semantic version is a commit hash. + ### `pr-number` _Optional_ A pull request number, only required if triggered from a workflow_dispatch event. Typically this would be triggered by a script running in a seperate CI provider. See [Trigger action from workflow_dispatch event](#trigger-action-from-workflow_dispatch-event) diff --git a/src/checkTargetMatchToPR.js b/src/checkTargetMatchToPR.js index 1993b257..e0ff067b 100644 --- a/src/checkTargetMatchToPR.js +++ b/src/checkTargetMatchToPR.js @@ -14,6 +14,12 @@ const checkTargetMatchToPR = (prTitle, target) => { } const [, from, to] = match + + const isNotSemantic = isAlpha(from) + + || isAlpha(to) + + const diff = semverDiff(semverCoerce(from), semverCoerce(to)) return !( @@ -21,4 +27,9 @@ const checkTargetMatchToPR = (prTitle, target) => { semanticVersionOrder.indexOf(diff) > semanticVersionOrder.indexOf(target) ) } + +function isAlpha(version) { + return /[A-Z]+/i.test(version) +} + module.exports = checkTargetMatchToPR diff --git a/test/action.test.js b/test/action.test.js index f0d5ed84..748aa1d4 100644 --- a/test/action.test.js +++ b/test/action.test.js @@ -270,9 +270,9 @@ tap.test('should check submodules semver when target is set', async t => { payload: { pull_request: { number: PR_NUMBER, - title: 'chore(deps): bump fastify/submodule from 2.5.0 to 2.6', + title: 'Bump dotbot from aa93350 to ac5793c', user: { login: BOT_NAME }, - head: { ref: 'dependabot/github_actions/fastify/submodule-2.6' }, + head: { ref: 'dependabot/submodules/dotbot-ac5793c' }, } }, inputs: { diff --git a/test/checkTargetMatchToPR.test.js b/test/checkTargetMatchToPR.test.js index 99bbafd9..60d2b11f 100644 --- a/test/checkTargetMatchToPR.test.js +++ b/test/checkTargetMatchToPR.test.js @@ -21,6 +21,7 @@ const patchPRTitleInSubDirectory = 'chore(deps-dev): bump fastify from 3.18.0 to const semverLikeMinor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 2.3' const semverLikeMajor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 3' const semverLikeBothWay = 'chore(deps): bump nearform/optic-release-automation-action from 2 to 3' +const submodules = 'Bump dotbot from aa93350 to ac5793c' tap.test('checkTargetMatchToPR', async t => { t.test('should return true when target is major', async t => { @@ -166,4 +167,11 @@ tap.test('checkTargetMatchToPR', async t => { t.ok(checkTargetMatchToPR(semverLikeBothWay, targetOptions.major)) }) }) + + t.test('submodules', async t => { + t.notOk(checkTargetMatchToPR(submodules, targetOptions.prepatch)) + t.notOk(checkTargetMatchToPR(submodules, targetOptions.patch)) + t.notOk(checkTargetMatchToPR(submodules, targetOptions.minor)) + t.notOk(checkTargetMatchToPR(submodules, targetOptions.major)) + }) }) From 3dfe730088ab9457c87fd796f94302b08ccaa84f Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Wed, 8 Dec 2021 11:51:51 +0100 Subject: [PATCH 5/7] fix: support submodules --- README.md | 2 +- src/checkTargetMatchToPR.js | 11 +++++------ test/action.test.js | 6 +++--- test/checkTargetMatchToPR.test.js | 4 +++- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c4da6b9e..f61775d9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Possible options are: For more details on how semantic version difference is calculated please see [semver](https://www.npmjs.com/package/semver) package. -If you set a value other than `any`, PRs that are not semantic version compliant will not be merged. +If you set a value other than `any`, PRs that are not semantic version compliant are skipped. An example of a non-semantic version is a commit hash. ### `pr-number` diff --git a/src/checkTargetMatchToPR.js b/src/checkTargetMatchToPR.js index e0ff067b..c624cc00 100644 --- a/src/checkTargetMatchToPR.js +++ b/src/checkTargetMatchToPR.js @@ -15,10 +15,9 @@ const checkTargetMatchToPR = (prTitle, target) => { const [, from, to] = match - const isNotSemantic = isAlpha(from) - - || isAlpha(to) - + if(hasBadChars(from) || hasBadChars(to)) { + return false + } const diff = semverDiff(semverCoerce(from), semverCoerce(to)) @@ -28,8 +27,8 @@ const checkTargetMatchToPR = (prTitle, target) => { ) } -function isAlpha(version) { - return /[A-Z]+/i.test(version) +function hasBadChars(version) { + return /`/.test(version) } module.exports = checkTargetMatchToPR diff --git a/test/action.test.js b/test/action.test.js index 748aa1d4..7f756ad5 100644 --- a/test/action.test.js +++ b/test/action.test.js @@ -270,7 +270,7 @@ tap.test('should check submodules semver when target is set', async t => { payload: { pull_request: { number: PR_NUMBER, - title: 'Bump dotbot from aa93350 to ac5793c', + title: 'Bump dotbot from `aa93350` to `ac5793c`', user: { login: BOT_NAME }, head: { ref: 'dependabot/submodules/dotbot-ac5793c' }, } @@ -286,6 +286,6 @@ tap.test('should check submodules semver when target is set', async t => { await action() - t.ok(stubs.logStub.logInfo.calledOnceWith('pr-text')) - t.ok(stubs.fetchStub.calledOnce) + t.ok(stubs.logStub.logWarning.calledOnceWith('Target specified does not match to PR, skipping.')) + t.ok(stubs.fetchStub.notCalled) }) diff --git a/test/checkTargetMatchToPR.test.js b/test/checkTargetMatchToPR.test.js index 60d2b11f..f5d51a2a 100644 --- a/test/checkTargetMatchToPR.test.js +++ b/test/checkTargetMatchToPR.test.js @@ -21,7 +21,8 @@ const patchPRTitleInSubDirectory = 'chore(deps-dev): bump fastify from 3.18.0 to const semverLikeMinor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 2.3' const semverLikeMajor = 'chore(deps): bump nearform/optic-release-automation-action from 2.2.0 to 3' const semverLikeBothWay = 'chore(deps): bump nearform/optic-release-automation-action from 2 to 3' -const submodules = 'Bump dotbot from aa93350 to ac5793c' +const submodules = 'Bump dotbot from `aa93350` to `ac5793c`' +const submodulesAlpha = 'Bump dotbot from `aa93350` to `acaaaac`' tap.test('checkTargetMatchToPR', async t => { t.test('should return true when target is major', async t => { @@ -173,5 +174,6 @@ tap.test('checkTargetMatchToPR', async t => { t.notOk(checkTargetMatchToPR(submodules, targetOptions.patch)) t.notOk(checkTargetMatchToPR(submodules, targetOptions.minor)) t.notOk(checkTargetMatchToPR(submodules, targetOptions.major)) + t.notOk(checkTargetMatchToPR(submodulesAlpha, targetOptions.major)) }) }) From 2c50a1e15c99c85438708b40e81ae3cb5cc87f33 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Wed, 8 Dec 2021 13:34:08 +0100 Subject: [PATCH 6/7] feedback --- README.md | 2 +- src/checkTargetMatchToPR.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f61775d9..e30937af 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Possible options are: For more details on how semantic version difference is calculated please see [semver](https://www.npmjs.com/package/semver) package. If you set a value other than `any`, PRs that are not semantic version compliant are skipped. -An example of a non-semantic version is a commit hash. +An example of a non-semantic version is a commit hash when using git submodules. ### `pr-number` diff --git a/src/checkTargetMatchToPR.js b/src/checkTargetMatchToPR.js index c624cc00..97c32cad 100644 --- a/src/checkTargetMatchToPR.js +++ b/src/checkTargetMatchToPR.js @@ -1,8 +1,10 @@ 'use strict' const semverDiff = require('semver/functions/diff') const semverCoerce = require('semver/functions/coerce') +const semverValid = require('semver/functions/valid') const { semanticVersionOrder } = require('./getTargetInput') +const { logWarning } = require('./log') const expression = /from ([^\s]+) to ([^\s]+)/ @@ -15,7 +17,8 @@ const checkTargetMatchToPR = (prTitle, target) => { const [, from, to] = match - if(hasBadChars(from) || hasBadChars(to)) { + if ((!semverValid(from) && hasBadChars(from)) || (!semverValid(to) && hasBadChars(to))) { + logWarning(`PR title contains invalid semver versions from: ${from} to: ${to}`) return false } From 85abdcfdbdc7eb2679fcf612653184ede9e33b90 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Wed, 8 Dec 2021 13:37:55 +0100 Subject: [PATCH 7/7] add build --- dist/index.js | 27 +++++++++++++++++++++++++++ src/checkTargetMatchToPR.js | 1 + 2 files changed, 28 insertions(+) diff --git a/dist/index.js b/dist/index.js index 3b1792b1..7bfcf575 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6302,6 +6302,19 @@ const parse = (version, options) => { module.exports = parse +/***/ }), + +/***/ 9601: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const parse = __nccwpck_require__(5925) +const valid = (version, options) => { + const v = parse(version, options) + return v ? v.version : null +} +module.exports = valid + + /***/ }), /***/ 2293: @@ -9238,8 +9251,10 @@ function isMajorRelease(pullRequest) { const semverDiff = __nccwpck_require__(4297) const semverCoerce = __nccwpck_require__(3466) +const semverValid = __nccwpck_require__(9601) const { semanticVersionOrder } = __nccwpck_require__(5013) +const { logWarning } = __nccwpck_require__(653) const expression = /from ([^\s]+) to ([^\s]+)/ @@ -9251,6 +9266,12 @@ const checkTargetMatchToPR = (prTitle, target) => { } const [, from, to] = match + + if ((!semverValid(from) && hasBadChars(from)) || (!semverValid(to) && hasBadChars(to))) { + logWarning(`PR title contains invalid semver versions from: ${from} to: ${to}`) + return false + } + const diff = semverDiff(semverCoerce(from), semverCoerce(to)) return !( @@ -9258,6 +9279,12 @@ const checkTargetMatchToPR = (prTitle, target) => { semanticVersionOrder.indexOf(diff) > semanticVersionOrder.indexOf(target) ) } + +function hasBadChars(version) { + // recognize submodules title likes 'Bump dotbot from `aa93350` to `acaaaac`' + return /`/.test(version) +} + module.exports = checkTargetMatchToPR diff --git a/src/checkTargetMatchToPR.js b/src/checkTargetMatchToPR.js index 97c32cad..0130b8d5 100644 --- a/src/checkTargetMatchToPR.js +++ b/src/checkTargetMatchToPR.js @@ -31,6 +31,7 @@ const checkTargetMatchToPR = (prTitle, target) => { } function hasBadChars(version) { + // recognize submodules title likes 'Bump dotbot from `aa93350` to `acaaaac`' return /`/.test(version) }