From b13722bf8d3617c7e8a740dec45b0d68a8f30e2c Mon Sep 17 00:00:00 2001 From: Richard North Date: Mon, 3 Dec 2018 09:06:59 +0000 Subject: [PATCH] Add automatic naming of new draft releases Closes #91 --- index.js | 11 ++--- lib/releases.js | 26 ++++++++++- lib/versions.js | 29 ++++++++++++ package.json | 2 +- test/fixtures/config-with-next-versioning.yml | 3 ++ test/index.test.js | 26 +++++++++++ test/versions.test.js | 45 +++++++++++++++++++ 7 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 lib/versions.js create mode 100644 test/fixtures/config-with-next-versioning.yml create mode 100644 test/versions.test.js diff --git a/index.js b/index.js index 0288342..bec1aba 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const getConfig = require('probot-config') const { isTriggerableBranch } = require('./lib/triggerable-branch') -const { findReleases, generateReleaseBody } = require('./lib/releases') +const { findReleases, generateReleaseInfo } = require('./lib/releases') const { findCommits, findPullRequests } = require('./lib/commits') const log = require('./lib/log') @@ -30,20 +30,21 @@ module.exports = app => { const { draftRelease, lastRelease } = await findReleases({ app, context }) const commits = await findCommits({ app, context, branch, lastRelease }) const mergedPullRequests = await findPullRequests({ app, context, commits }) - const body = generateReleaseBody({ commits, config, lastRelease, mergedPullRequests }) + const releaseInfo = generateReleaseInfo({ commits, config, lastRelease, mergedPullRequests }) if (!draftRelease) { log({ app, context, message: 'Creating new draft release' }) await context.github.repos.createRelease(context.repo({ - tag_name: '', - body: body, + name: releaseInfo.name, + tag_name: releaseInfo.tag, + body: releaseInfo.body, draft: true })) } else { log({ app, context, message: 'Updating existing draft release' }) await context.github.repos.editRelease(context.repo({ release_id: draftRelease.id, - body: body + body: releaseInfo.body })) } }) diff --git a/lib/releases.js b/lib/releases.js index 4af02ae..d7c004d 100644 --- a/lib/releases.js +++ b/lib/releases.js @@ -1,5 +1,6 @@ const compareVersions = require('compare-versions') +const { getVersionInfo } = require('./versions') const log = require('./log') const UNCATEGORIZED = 'UNCATEGORIZED' @@ -92,7 +93,13 @@ const categorizePullRequests = (pullRequests, categories) => { }) } -module.exports.generateReleaseBody = ({ commits, config, lastRelease, mergedPullRequests }) => { +const templateNextVersion = (template, nextVersions) => { + return template.replace('$NEXT_MAJOR_VERSION', nextVersions.incrementedMajor) + .replace('$NEXT_MINOR_VERSION', nextVersions.incrementedMinor) + .replace('$NEXT_PATCH_VERSION', nextVersions.incrementedPatch); +} + +module.exports.generateReleaseInfo = ({ commits, config, lastRelease, mergedPullRequests }) => { let body = config.template const [categoriesConfig, orderedCategories] = getCategoriesConfig({ config }) @@ -130,5 +137,20 @@ module.exports.generateReleaseBody = ({ commits, config, lastRelease, mergedPull body = body.replace('$CONTRIBUTORS', contributorsSentence({ commits, pullRequests: mergedPullRequests })) - return body + let name = config['default-release-name'] || ''; + let tag = config['default-release-tag'] || ''; + if (lastRelease) { + const versionInfo = getVersionInfo(lastRelease); + if (versionInfo) { + body = templateNextVersion(body, versionInfo); + name = templateNextVersion(name, versionInfo); + tag = templateNextVersion(tag, versionInfo); + } + } + + return { + name, + tag, + body + } } diff --git a/lib/versions.js b/lib/versions.js new file mode 100644 index 0000000..15fe601 --- /dev/null +++ b/lib/versions.js @@ -0,0 +1,29 @@ +const semverRegex = /(\d+)\.(\d+)\.(\d+)/; + +module.exports.getVersionInfo = (lastRelease) => { + const lastReleaseTag = lastRelease.tag_name; + const lastReleaseName = lastRelease.name; + + let lastReleaseMatch; + lastReleaseMatch = lastReleaseTag.match(semverRegex); + if (! lastReleaseMatch) { + lastReleaseMatch = lastReleaseName.match(semverRegex); + } + + if (! lastReleaseMatch) { + return undefined; + } + + const major = (lastReleaseMatch[1] - 0); + const minor = (lastReleaseMatch[2] - 0); + const patch = (lastReleaseMatch[3] - 0); + + return { + major, + minor, + patch, + incrementedMajor: `${major + 1}.0.0`, + incrementedMinor: `${major}.${minor + 1}.0`, + incrementedPatch: `${major}.${minor}.${patch + 1}`, + } +} \ No newline at end of file diff --git a/package.json b/package.json index ec5a608..ca0857b 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ ] }, "jest": { - "collectCoverage": true, + "collectCoverage": false, "collectCoverageFrom": [ "index.js", "lib/**" diff --git a/test/fixtures/config-with-next-versioning.yml b/test/fixtures/config-with-next-versioning.yml new file mode 100644 index 0000000..95093b0 --- /dev/null +++ b/test/fixtures/config-with-next-versioning.yml @@ -0,0 +1,3 @@ +template: Placeholder with example. Automatically calculated values are next major=$NEXT_MAJOR_VERSION, minor=$NEXT_MINOR_VERSION, patch=$NEXT_PATCH_VERSION +default-release-name: "v$NEXT_PATCH_VERSION (Code name: Placeholder)" +default-release-tag: v$NEXT_PATCH_VERSION \ No newline at end of file diff --git a/test/index.test.js b/test/index.test.js index cd5b2f6..0c106ef 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -142,6 +142,32 @@ Previous tag: '' ) }) + it('makes next versions available as template placeholders', async () => { + github.repos.getContent = fn().mockReturnValueOnce(mockConfig('config-with-next-versioning.yml')) + github.repos.getReleases = fn().mockReturnValueOnce(Promise.resolve({ data: + [ require('./fixtures/release') ] + })) + github.repos.compareCommits = fn().mockReturnValueOnce(Promise.resolve({ data: { + commits: require('./fixtures/commits') + } })) + github.pullRequests.get = fn() + .mockReturnValueOnce(Promise.resolve(require('./fixtures/pull-request-1'))) + .mockReturnValueOnce(Promise.resolve(require('./fixtures/pull-request-2'))) + + github.repos.createRelease = fn() + + await app.receive({ name: 'push', payload: require('./fixtures/push') }) + + expect(github.repos.createRelease).toBeCalledWith( + expect.objectContaining({ + body: `Placeholder with example. Automatically calculated values are next major=3.0.0, minor=2.1.0, patch=2.0.1`, + draft: true, + name: 'v2.0.1 (Code name: Placeholder)', + tag_name: 'v2.0.1' + }) + ) + }) + describe('with custom changes-template config', () => { it('creates a new draft using the template', async () => { github.repos.getContent = fn().mockReturnValueOnce(mockConfig('config-with-changes-templates.yml')) diff --git a/test/versions.test.js b/test/versions.test.js new file mode 100644 index 0000000..473cae3 --- /dev/null +++ b/test/versions.test.js @@ -0,0 +1,45 @@ +const { getVersionInfo } = require('../lib/versions') + +describe('versions', () => { + it('extracts a version-like string from the last tag', () => { + const versionInfo = getVersionInfo({ + tag_name: 'v10.0.3', + name: 'Some release' + }); + + expect(versionInfo.incrementedMajor).toEqual('11.0.0'); + expect(versionInfo.incrementedMinor).toEqual('10.1.0'); + expect(versionInfo.incrementedPatch).toEqual('10.0.4'); + }) + + it('extracts a version-like string from the last release name', () => { + const versionInfo = getVersionInfo({ + tag_name: 'notaproperversion', + name: '10.0.3' + }); + + expect(versionInfo.incrementedMajor).toEqual('11.0.0'); + expect(versionInfo.incrementedMinor).toEqual('10.1.0'); + expect(versionInfo.incrementedPatch).toEqual('10.0.4'); + }) + + it('extracts a version-like string from the last tag instead of the release name, if conflicting', () => { + const versionInfo = getVersionInfo({ + tag_name: '10.0.3', + name: '8.1.0' + }); + + expect(versionInfo.incrementedMajor).toEqual('11.0.0'); + expect(versionInfo.incrementedMinor).toEqual('10.1.0'); + expect(versionInfo.incrementedPatch).toEqual('10.0.4'); + }) + + it('gives up if a semver version-like string cannot be found in either tag or release name', () => { + const versionInfo = getVersionInfo({ + tag_name: '10.0', + name: 'not a proper version' + }); + + expect(versionInfo).toEqual(undefined); + }) +}) \ No newline at end of file