From 296c11c7d020f01a8085f9ab6d8489534a816a3c Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Tue, 8 Oct 2019 17:45:46 -0400 Subject: [PATCH] feat(build): commit message integration check (#110) * commit message check, aids in changelog updates --- .travis.yml | 1 + tests/commit.test.js | 81 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/commit.test.js diff --git a/.travis.yml b/.travis.yml index 4c6c758ed..5d4e9f84d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ env: - REPO="git@github.com:RedHatInsights/curiosity-frontend-build" - REPO_DIR="curiosity-frontend-build" - BRANCH=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} + - IS_PR=${TRAVIS_PULL_REQUEST} - BUILD_STAGE="${TRAVIS_BUILD_STAGE_NAME}" - NODE_OPTIONS="--max-old-space-size=4096 --max_old_space_size=4096" - APP_BUILD_DIR="build" diff --git a/tests/commit.test.js b/tests/commit.test.js new file mode 100644 index 000000000..1627cc65b --- /dev/null +++ b/tests/commit.test.js @@ -0,0 +1,81 @@ +const { execSync } = require('child_process'); + +/** + * See CONTRIBUTING.md for commit messaging guidelines + * + * TypeScope is required and should be in the form of "([scope]):" or ":" + * + * IssueNumber is conditional and should be in the form of "issues/[issue number]" + * Exceptions are available for chore and fix type scopes, and "build" type scope "scopes". + * i.e. "chore([scope])", "fix([scope])", "(build):" + * An exception for having a PR reference at the end of the description is also allowed, + * i.e. " (#10)" + * + * Minimal description is required, can be in the form of "" + */ +describe('Commit Message', () => { + it('should have consistently formatted commit messages on pull requests', () => { + // see .travis.yml globals + if (!process.env.IS_PR || process.env.IS_PR === 'false') { + expect(1).toBe(1); + return; + } + + let stdout = ''; + + try { + stdout = execSync(`git cherry -v master`); + } catch (e) { + console.log(`Skipping commit check... ${e.message}`); + } + + const messages = stdout + .toString() + .trim() + .replace(/\+\s/g, '') + .split(/\n/g) + .map(message => { + const [hashTypeScope, ...issueNumberDescription] = + (/:/.test(message) && message.split(/:/)) || message.split(/\s/); + + const [hash, typeScope = ''] = hashTypeScope.split(/\s/); + const [issueNumber, ...description] = issueNumberDescription + .join(' ') + .trim() + .split(/\s/g); + + return { + hash, + typeScope: (typeScope && `${typeScope}:`) || '', + issueNumber, + description: description.join(' ') + }; + }); + + const messagesList = messages.map(message => { + const { typeScope = null, issueNumber = null, description = null } = message; + + const issueNumberException = + /(^chore\([\d\D]+\))|(^fix\([\d\D]+\))|(^[\d\D]+\(build\))/.test(typeScope) || + /\(#[\d\D]+\)$/.test(description); + + const typeScopeValid = + (/(^[\d\D]+\([\d\D]+\):$)|(^[\d\D]+:$)/.test(typeScope) && 'valid') || 'INVALID: type scope'; + + const issueNumberValid = + (/(^issues\/[\d,]+$)/.test(issueNumber) && 'valid') || + (issueNumberException && 'valid') || + 'INVALID: issue number'; + + const descriptionValid = + (/(^[\d\D]+$)/.test(description || (issueNumberException && issueNumber)) && 'valid') || + (issueNumberException && !description && issueNumber && 'valid') || + 'INVALID: description'; + + // ([scope]): issues/ + return `${typeScope}<${typeScopeValid}> ${issueNumber}<${issueNumberValid}> ${description}<${descriptionValid}>`; + }); + + expect(messagesList.filter(value => !/[\d\D]*[\d\D]*/.test(value))).toEqual([]); + }); +});