Skip to content

Commit

Permalink
fix: semver like titles (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
Eomm authored Dec 8, 2021
1 parent c8f3391 commit 8695e84
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 2 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 are skipped.
An example of a non-semantic version is a commit hash when using git submodules.

### `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)
Expand Down
90 changes: 89 additions & 1 deletion dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -6244,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:
Expand Down Expand Up @@ -9179,8 +9250,11 @@ function isMajorRelease(pullRequest) {
"use strict";

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]+)/

Expand All @@ -9190,13 +9264,27 @@ const checkTargetMatchToPR = (prTitle, target) => {
if (!match) {
return true
}
const diff = semverDiff(match[1], match[2])

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 !(
diff &&
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


Expand Down
19 changes: 18 additions & 1 deletion src/checkTargetMatchToPR.js
Original file line number Diff line number Diff line change
@@ -1,7 +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]+)/

Expand All @@ -11,11 +14,25 @@ const checkTargetMatchToPR = (prTitle, target) => {
if (!match) {
return true
}
const diff = semverDiff(match[1], match[2])

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 !(
diff &&
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
26 changes: 26 additions & 0 deletions test/action.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: 'Bump dotbot from `aa93350` to `ac5793c`',
user: { login: BOT_NAME },
head: { ref: 'dependabot/submodules/dotbot-ac5793c' },
}
},
inputs: {
PR_NUMBER,
TARGET: 'minor',
EXCLUDE_PKGS: [],
API_URL: 'custom one',
DEFAULT_API_URL,
}
})

await action()

t.ok(stubs.logStub.logWarning.calledOnceWith('Target specified does not match to PR, skipping.'))
t.ok(stubs.fetchStub.notCalled)
})
36 changes: 36 additions & 0 deletions test/checkTargetMatchToPR.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ 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'
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 => {
Expand Down Expand Up @@ -140,4 +145,35 @@ 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))
})
})

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))
t.notOk(checkTargetMatchToPR(submodulesAlpha, targetOptions.major))
})
})

0 comments on commit 8695e84

Please sign in to comment.