diff --git a/README.md b/README.md index e68defc..644ea6e 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Automate releases with Conventional Commit Messages. | `fork` | Should the PR be created from a fork. Default `false`| | `command` | release-please command to run, either `github-release`, or `release-pr` (_defaults to running both_) | | `default-branch` | branch to open pull release PR against (detected by default) | +| `pull-request-title-pattern` | title pattern used to make release PR, defaults to using `chore${scope}: release${component} ${version}`. | | output | description | |:---:|---| diff --git a/action.yml b/action.yml index aed2ce5..c3e54ed 100644 --- a/action.yml +++ b/action.yml @@ -7,7 +7,7 @@ inputs: required: false default: ${{ github.token }} fork: - description: 'should the PR be proposed from a fork (does not work with secrets.GITHUB_TOKEN)' + description: 'should the PR be proposed from a fork, Default to false' required: false default: false clean: @@ -52,6 +52,11 @@ inputs: description: 'branch to open pull release PR against (detected by default)' required: false default: '' + pull-request-title-pattern: + description: 'add title pattern to make release PR, defaults to using "chore${scope}: release${component} ${version}".' + required: false + default: 'chore${scope}: release${component} ${version}' + runs: using: 'node12' main: 'dist/index.js' diff --git a/index.js b/index.js index 87bed07..02565a0 100644 --- a/index.js +++ b/index.js @@ -23,11 +23,12 @@ async function main () { const token = core.getInput('token', { required: true }) const fork = getBooleanInput('fork') const changelogPath = core.getInput('changelog-path') || undefined - const changelogTypes = core.getInput('changelog-types') + const changelogTypes = core.getInput('changelog-types') || undefined const changelogSections = changelogTypes && JSON.parse(changelogTypes) const command = core.getInput('command') || undefined const versionFile = core.getInput('version-file') || undefined const defaultBranch = core.getInput('default-branch') || undefined + const pullRequestTitlePattern = core.getInput('pull-request-title-pattern') || undefined // First we check for any merged release PRs (PRs merged with the label // "autorelease: pending"): @@ -41,7 +42,8 @@ async function main () { token, changelogPath, releaseType, - defaultBranch + defaultBranch, + pullRequestTitlePattern }) if (releaseCreated) { @@ -68,7 +70,8 @@ async function main () { bumpMinorPreMajor, changelogSections, versionFile, - defaultBranch + defaultBranch, + pullRequestTitlePattern }) if (pr) { @@ -82,6 +85,7 @@ const releasePlease = { getBooleanInput } +/* c8 ignore next 4 */ if (require.main === module) { main().catch(err => { core.setFailed(`release-please failed: ${err.message}`) diff --git a/package-lock.json b/package-lock.json index a537b24..0e4420e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -182,11 +182,11 @@ "integrity": "sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==" }, "@octokit/plugin-rest-endpoint-methods": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.12.0.tgz", - "integrity": "sha512-RgnQ1aoetdOJjZYC37LV5FNlL7GY/v1CdC5dur1Zp/UiADJlbRFbAz/xLx26ovXw67dK7EUtwCghS+6QyiI9RA==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.12.2.tgz", + "integrity": "sha512-5+MmGusB7wPw7OholtcGaMyjfrsFSpFqtJW8VsrbfU/TuaiQepY4wgVkS7P3TAObX257jrTbbGo/sJLcoGf16g==", "requires": { - "@octokit/types": "^6.10.0", + "@octokit/types": "^6.10.1", "deprecation": "^2.3.1" } }, @@ -216,20 +216,20 @@ } }, "@octokit/rest": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.2.0.tgz", - "integrity": "sha512-xsp6bIqL2sb/NmgLXTxw96caegobRw+YHnzdIi70ruquHtPPDW2cBAONhDYMUuAOeXx0JH2auOeplpk4SQJy1w==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.2.1.tgz", + "integrity": "sha512-DdQ1vps41JSyB2axyL1mBwJiXAPibgugIQPOmt0mL/yhwheQ6iuq2aKiJWgGWa9ldMfe3v9gIFYlrFgxQ5ThGQ==", "requires": { "@octokit/core": "^3.2.3", "@octokit/plugin-paginate-rest": "^2.6.2", "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "4.12.0" + "@octokit/plugin-rest-endpoint-methods": "4.12.2" } }, "@octokit/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.10.0.tgz", - "integrity": "sha512-aMDo10kglofejJ96edCBIgQLVuzMDyjxmhdgEcoUUD64PlHYSrNsAGqN0wZtoiX4/PCQ3JLA50IpkP1bcKD/cA==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.10.1.tgz", + "integrity": "sha512-hgNC5jxKG8/RlqxU/6GThkGrvFpz25+cPzjQjyiXTNBvhyltn2Z4GhFY25+kbtXwZ4Co4zM0goW5jak1KLp1ug==", "requires": { "@octokit/openapi-types": "^5.1.0" } @@ -2897,9 +2897,9 @@ "dev": true }, "release-please": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/release-please/-/release-please-11.0.1.tgz", - "integrity": "sha512-14mZ6SD3a+QKkfSbtMpi6AM14ZfxWcsPI6+mrKot41lG98Sg/ri29c7pQNO7uwLwT7z2JiYPuHvGvOW29duS8w==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/release-please/-/release-please-11.1.0.tgz", + "integrity": "sha512-0hGcGcxA23kweKtyb1mkhfjFY6JmDTi5qCESLZcS6HGo4ryGnlUcq8AaFGDogReO5yCOcyUi6lInf9Y3/XMFzw==", "requires": { "@conventional-commits/parser": "^0.4.1", "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index fc79ba7..64eb03c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "homepage": "https://github.com/bcoe/release-please-action#readme", "dependencies": { "@actions/core": "^1.2.6", - "release-please": "^11.0.1" + "release-please": "^11.1.0" }, "devDependencies": { "@vercel/ncc": "^0.27.0", diff --git a/test/release-please.js b/test/release-please.js index 19c050a..09aedc5 100644 --- a/test/release-please.js +++ b/test/release-please.js @@ -1,4 +1,4 @@ -const { describe, it, afterEach } = require('mocha') +const { describe, it, beforeEach, afterEach } = require('mocha') const action = require('../') const assert = require('assert') const core = require('@actions/core') @@ -16,13 +16,32 @@ const defaultInput = { 'changelog-types': '', command: '', 'version-file': '', - 'default-branch': '' + 'default-branch': '', + // eslint-disable-next-line no-template-curly-in-string + 'pull-request-title-pattern': 'chore${scope}: release${component} ${version}' } +let input +let output + const sandbox = sinon.createSandbox() process.env.GITHUB_REPOSITORY = 'google/cloud' describe('release-please-action', () => { + beforeEach(() => { + input = {} + output = {} + core.setOutput = (name, value) => { + output[name] = value + } + core.getInput = name => { + if (input[name] === undefined || input[name] == null) { + return defaultInput[name] + } else { + return input[name] + } + } + }) afterEach(() => { sandbox.restore() }) @@ -32,12 +51,9 @@ describe('release-please-action', () => { trueValue.forEach(value => { it(`get the boolean true with the input of '${value}'`, () => { - const input = { + input = { fork: value } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const actual = action.getBooleanInput('fork') assert.strictEqual(actual, true) }) @@ -45,58 +61,100 @@ describe('release-please-action', () => { falseValue.forEach(value => { it(`get the boolean with the input of '${value}'`, () => { - const input = { + input = { fork: value } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const actual = action.getBooleanInput('fork') assert.strictEqual(actual, false) }) }) it('get an error when inputting the wrong boolean value', () => { - const input = { + input = { fork: 'wrong' } - core.getInput = (name) => { - return input[name] || defaultInput[name] + assert.throws( + () => { + action.getBooleanInput('fork') + }, + { name: 'TypeError', message: "Wrong boolean value of the input 'fork'" } + ) + }) + + it('sets pull pullRequestTitlePattern to undefined, if empty string provided', async () => { + input = { + 'release-type': 'node', + // eslint-disable-next-line no-template-curly-in-string + 'pull-request-title-pattern': '' } - assert.throws(() => { - action.getBooleanInput('fork') - }, { name: 'TypeError', message: 'Wrong boolean value of the input \'fork\'' }) + + const runCommandStub = sandbox.stub(factory, 'runCommand') + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) + + await action.main() + + sinon.assert.calledOnce(githubReleasePRStub) + sinon.assert.calledWith( + githubReleasePRStub, + 'release-pr', + // eslint-disable-next-line no-template-curly-in-string + sinon.match.hasOwn('pullRequestTitlePattern', undefined) + ) }) - it('both opens PR to the default branch and tags GitHub releases by default', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value + it('opens PR with custom changelogSections', async () => { + input = { + 'release-type': 'node', + 'changelog-types': + '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]' } - const input = { + + const runCommandStub = sandbox.stub(factory, 'runCommand') + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) + + await action.main() + + sinon.assert.calledOnce(githubReleasePRStub) + sinon.assert.calledWith( + githubReleasePRStub, + 'release-pr', + sinon.match.hasOwn( + 'changelogSections', + JSON.parse( + '[{"type":"feat","section":"Features","hidden":false},{"type":"fix","section":"Bug Fixes","hidden":false},{"type":"chore","section":"Miscellaneous","hidden":false}]' + ) + ) + ) + }) + + it('both opens PR to the default branch and tags GitHub releases by default', async () => { + input = { 'release-type': 'node' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - const githubReleaseStub = runCommandStub.withArgs('github-release') - .returns({ - upload_url: 'http://example.com', - tag_name: 'v1.0.0' - }) + const githubReleaseStub = runCommandStub.withArgs('github-release').returns({ + upload_url: 'http://example.com', + tag_name: 'v1.0.0' + }) - const githubReleasePRStub = runCommandStub.withArgs('release-pr') - .returns(25) + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) await action.main() sinon.assert.calledOnce(githubReleaseStub) sinon.assert.calledOnce(githubReleasePRStub) - sinon.assert.calledWith(githubReleaseStub, 'github-release', sinon.match.hasOwn('defaultBranch', undefined)) - sinon.assert.calledWith(githubReleasePRStub, 'release-pr', sinon.match.hasOwn('defaultBranch', undefined)) + sinon.assert.calledWith( + githubReleaseStub, + 'github-release', + sinon.match.hasOwn('defaultBranch', undefined) + ) + sinon.assert.calledWith( + githubReleasePRStub, + 'release-pr', + sinon.match.hasOwn('defaultBranch', undefined) + ) assert.deepStrictEqual(output, { release_created: true, upload_url: 'http://example.com', @@ -106,35 +164,34 @@ describe('release-please-action', () => { }) it('both opens PR to a different default branch and tags GitHub releases by default', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', 'default-branch': 'dev' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - const githubReleaseStub = runCommandStub.withArgs('github-release') - .returns({ - upload_url: 'http://example.com', - tag_name: 'v1.0.0' - }) + const githubReleaseStub = runCommandStub.withArgs('github-release').returns({ + upload_url: 'http://example.com', + tag_name: 'v1.0.0' + }) - const githubReleasePRStub = runCommandStub.withArgs('release-pr') - .returns(25) + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) await action.main() sinon.assert.calledOnce(githubReleaseStub) - sinon.assert.calledWith(githubReleaseStub, 'github-release', sinon.match.hasOwn('defaultBranch', 'dev')) + sinon.assert.calledWith( + githubReleaseStub, + 'github-release', + sinon.match.hasOwn('defaultBranch', 'dev') + ) sinon.assert.calledOnce(githubReleasePRStub) - sinon.assert.calledWith(githubReleasePRStub, 'release-pr', sinon.match.hasOwn('defaultBranch', 'dev')) + sinon.assert.calledWith( + githubReleasePRStub, + 'release-pr', + sinon.match.hasOwn('defaultBranch', 'dev') + ) assert.deepStrictEqual(output, { release_created: true, @@ -145,28 +202,19 @@ describe('release-please-action', () => { }) it('only opens PR, if command set to release-pr', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', command: 'release-pr' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - const githubReleaseStub = runCommandStub.withArgs('github-release') - .returns({ - upload_url: 'http://example.com', - tag_name: 'v1.0.0' - }) + const githubReleaseStub = runCommandStub.withArgs('github-release').returns({ + upload_url: 'http://example.com', + tag_name: 'v1.0.0' + }) - const githubReleasePRStub = runCommandStub.withArgs('release-pr') - .returns(25) + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) await action.main() sinon.assert.notCalled(githubReleaseStub) @@ -174,35 +222,26 @@ describe('release-please-action', () => { }) it('only creates GitHub release, if command set to github-release', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', command: 'github-release' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - const githubReleaseStub = runCommandStub.withArgs('github-release') - .returns({ - upload_url: 'http://example.com', - tag_name: 'v1.0.0' - }) + const githubReleaseStub = runCommandStub.withArgs('github-release').returns({ + upload_url: 'http://example.com', + tag_name: 'v1.0.0' + }) - const githubReleasePRStub = runCommandStub.withArgs('release-pr') - .returns(25) + const githubReleasePRStub = runCommandStub.withArgs('release-pr').returns(25) await action.main() sinon.assert.calledOnce(githubReleaseStub) sinon.assert.notCalled(githubReleasePRStub) }) - it('sets approprite outputs when GitHub release created', async () => { + it('sets appropriate outputs when GitHub release created', async () => { const expected = { release_created: true, upload_url: 'http://example.com', @@ -215,69 +254,44 @@ describe('release-please-action', () => { sha: 'abc123', pr: 33 } - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', command: 'github-release' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - runCommandStub.withArgs('github-release') - .returns(expected) + runCommandStub.withArgs('github-release').returns(expected) - runCommandStub.withArgs('release-pr') - .returns(25) + runCommandStub.withArgs('release-pr').returns(25) await action.main() assert.deepStrictEqual(output, expected) }) it('sets appropriate outputs when release PR opened', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', command: 'release-pr' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - runCommandStub.withArgs('release-pr') - .returns(95) + runCommandStub.withArgs('release-pr').returns(95) await action.main() assert.strictEqual(output.pr, 95) }) it('does not set PR output, when no release PR is returned', async () => { - const output = {} - core.setOutput = (name, value) => { - output[name] = value - } - const input = { + input = { 'release-type': 'node', command: 'release-pr' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } const runCommandStub = sandbox.stub(factory, 'runCommand') - runCommandStub.withArgs('release-pr') - .returns(undefined) + runCommandStub.withArgs('release-pr').returns(undefined) await action.main() assert.strictEqual(Object.hasOwnProperty.call(output, 'pr'), false) @@ -285,32 +299,26 @@ describe('release-please-action', () => { it('creates and runs a ReleasePR instance, using factory', async () => { let maybeReleasePR - sandbox.replace(factory, 'call', (runnable) => { + sandbox.replace(factory, 'call', runnable => { maybeReleasePR = runnable }) - const input = { + input = { 'release-type': 'node', command: 'release-pr' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } await action.main() assert.ok(maybeReleasePR instanceof Node) }) it('creates and runs a GitHubRelease, using factory', async () => { let maybeGitHubRelease - sandbox.replace(factory, 'call', (runnable) => { + sandbox.replace(factory, 'call', runnable => { maybeGitHubRelease = runnable }) - const input = { + input = { 'release-type': 'node', command: 'github-release' } - core.getInput = (name) => { - return input[name] || defaultInput[name] - } await action.main() assert.ok(maybeGitHubRelease instanceof GitHubRelease) })