Skip to content

Commit

Permalink
Merge pull request #2 from jedster1111/GH-1
Browse files Browse the repository at this point in the history
GH-1 Add ability to check commit messages
  • Loading branch information
Vijay Ramesh authored Sep 2, 2019
2 parents 5b50f29 + 7dea184 commit b15614d
Show file tree
Hide file tree
Showing 5 changed files with 1,248 additions and 1,080 deletions.
1 change: 1 addition & 0 deletions fixtures/all.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
projects: ['PROJ']
check_title: true
check_branch: true
check_commits: true
ignore_case: true
5 changes: 5 additions & 0 deletions fixtures/commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
projects: ['PROJ']
check_title: false
check_branch: false
check_commits: true
ignore_case: false
65 changes: 55 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ const defaults = {
projects: ['PROJ'],
check_title: true,
check_branch: false,
check_commits: false,
ignore_case: false
}

Toolkit.run(
async tools => {
const { repository, pull_request } = tools.context.payload

const repoInfo = {
owner: tools.context.payload.repository.owner.login,
repo: tools.context.payload.repository.name,
ref: tools.context.payload.pull_request.head.ref
owner: repository.owner.login,
repo: repository.name,
ref: pull_request.head.ref
}

const config = {
Expand All @@ -24,18 +27,18 @@ Toolkit.run(
}

const title = config.ignore_case ?
tools.context.payload.pull_request.title.toLowerCase() :
tools.context.payload.pull_request.title
pull_request.title.toLowerCase() :
pull_request.title

const head_branch = config.ignore_case ?
tools.context.payload.pull_request.head.ref.toLowerCase() :
tools.context.payload.pull_request.head.ref
pull_request.head.ref.toLowerCase() :
pull_request.head.ref

const projects = config.projects.map(project => config.ignore_case ? project.toLowerCase() : project)
const title_passed = (() => {
if (config.check_title) {
// check the title matches [PROJECT-1234] somewhere
if (!projects.some(project => title.match(new RegExp('\\[' + project + '-\\d*\\]')))) {
if (!projects.some(project => title.match(createWrappedProjectRegex(project)))) {
tools.log('PR title ' + title + ' does not contain approved project')
return false
}
Expand All @@ -46,15 +49,36 @@ Toolkit.run(
const branch_passed = (() => {
// check the branch matches PROJECT-1234 or PROJECT_1234 somewhere
if (config.check_branch) {
if (!projects.some(project => head_branch.match(new RegExp(project + '[-_]\\d*')))) {
if (!projects.some(project => head_branch.match(createProjectRegex(project)))) {
tools.log('PR branch ' + head_branch + ' does not contain an approved project')
return false
}
}
return true
})()

const statuses = [title_passed, branch_passed]
const commits_passed = await (async () => {
// check the branch matches PROJECT-1234 or PROJECT_1234 somewhere
if (config.check_commits) {
const listCommitsParams = {
owner: repository.owner.login,
repo: repository.name,
pull_number: pull_request.number
}
const commitsInPR = (await tools.github.pulls.listCommits(listCommitsParams)).data
const failedCommits = findFailedCommits(projects, commitsInPR, config.ignore_case);

if(failedCommits.length) {
failedCommits.forEach(
failedCommit => tools.log('Commit message \'' + failedCommit + '\' does not contain an approved project')
)
return false
}
}
return true
})()

const statuses = [title_passed, branch_passed, commits_passed]

if (statuses.some(status => status === false )){
tools.exit.failure("PR Linting Failed")
Expand All @@ -64,3 +88,24 @@ Toolkit.run(
},
{ event: ['pull_request.opened', 'pull_request.edited', 'pull_request.synchronize'], secrets: ['GITHUB_TOKEN'] }
)

function findFailedCommits(projects, commitsInPR, ignoreCase) {
const failedCommits = [];
projects.forEach(project => {
commitsInPR.forEach(commit => {
const commitMessage = ignoreCase ? commit.commit.message.toLowerCase() : commit.commit.message
if (!commitMessage.match(createProjectRegex(project))) {
failedCommits.push(commitMessage);
}
});
});
return failedCommits;
}

function createProjectRegex(project) {
return new RegExp(project + '[-_]\\d*')
}

function createWrappedProjectRegex(project) {
return new RegExp('\\[' + project + '-\\d*\\]')
}
104 changes: 101 additions & 3 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ describe('pr-lint-action', () => {
const good_title_and_bad_branch = { title: '[PROJ-1234] a good PR title', ref_name: 'fix_things' }
const bad_title_and_good_branch = { title: 'no ticket in me', ref_name: 'bug/PROJ_1234/a_good_branch' }
const lower_case_good_title_and_branch = { title: '[proj-1234] a lower case good title', ref_name: 'bug/proj_1234/a_good_lowercase_branch' }
const good_commits = [
{ commit: { message: "PROJ-1234 Commit 1" } },
{ commit: { message: "PROJ-1234 Commit 2" } },
{ commit: { message: "PROJ-1234 Commit 3" } }
];
const lower_case_good_commits = [
{ commit: { message: "PROJ-1234 Commit 1" } },
{ commit: { message: "PROJ-1234 Commit 2" } },
{ commit: { message: "PROJ-1234 Commit 3" } }
];
const bad_commits = [
{ commit: { message: "PRJ-123 Commit 1" } },
{ commit: { message: "PROJ-1234 Commit 2" } },
{ commit: { message: "Commit 3" } }
];

beforeEach(() => {
// Create a new Toolkit instance
Expand Down Expand Up @@ -112,13 +127,70 @@ describe('pr-lint-action', () => {
expect.assertions(1)
})

it('passes if check_commits is true and all commits match', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('commits.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(good_title_and_branch)
await action(tools)
expect(tools.exit.success).toHaveBeenCalled()
expect.assertions(1)
})

it('fails if check_commits is true and some commits do not match', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('commits.yml'))

mockGetPRCommitListRequest(bad_commits)

tools.context.payload = pullRequestOpenedFixture(good_title_and_branch)
await action(tools)
expect(tools.exit.failure).toHaveBeenCalledWith("PR Linting Failed")
expect.assertions(1)
})

it('passes if check_commits is false and all commits match', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('title.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(good_title_and_branch)
await action(tools)
expect(tools.exit.success).toHaveBeenCalled()
expect.assertions(1)
})

it('passes if check_commits is false and some commits do not match', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('title.yml'))

mockGetPRCommitListRequest(bad_commits)

tools.context.payload = pullRequestOpenedFixture(good_title_and_branch)
await action(tools)
expect(tools.exit.success).toHaveBeenCalled()
expect.assertions(1)
})

it('fails if check_branch and check_title is true and title does not match', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('all.yml'))


mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(bad_title_and_good_branch)
await action(tools)
expect(tools.exit.failure).toHaveBeenCalledWith("PR Linting Failed")
Expand All @@ -132,6 +204,8 @@ describe('pr-lint-action', () => {
.query(true)
.reply(200, configFixture('all.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(bad_title_and_good_branch)
await action(tools)
expect(tools.exit.failure).toHaveBeenCalledWith("PR Linting Failed")
Expand All @@ -145,6 +219,7 @@ describe('pr-lint-action', () => {
.query(true)
.reply(200, configFixture('all.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(good_title_and_branch)
await action(tools)
Expand All @@ -158,27 +233,50 @@ describe('pr-lint-action', () => {
.query(true)
.reply(200, configFixture('all.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(lower_case_good_title_and_branch)
await action(tools)
expect(tools.exit.success).toHaveBeenCalled()
expect.assertions(1)
})

it('passes if ignore_case and lower case commits', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('all.yml'))

mockGetPRCommitListRequest(lower_case_good_commits);

it('fails if not ignore_case and lower case title/branch', async () => {
tools.context.payload = pullRequestOpenedFixture(lower_case_good_title_and_branch)
await action(tools)
expect(tools.exit.success).toHaveBeenCalled()
expect.assertions(1)
})

it('fails if not ignore_case and lower case title/branch', async () => {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/contents/.github/pr-lint.yml')
.query(true)
.reply(200, configFixture('no-ignore-case.yml'))

mockGetPRCommitListRequest(good_commits);

tools.context.payload = pullRequestOpenedFixture(lower_case_good_title_and_branch)
await action(tools)
expect(tools.exit.failure).toHaveBeenCalledWith("PR Linting Failed")
expect.assertions(1)
})
})
})


function mockGetPRCommitListRequest(commits) {
nock('https://api.github.com')
.get('/repos/vijaykramesh/pr-lint-action-test/pulls/1/commits')
.query(true)
.reply(200, commits);
}

function encodeContent(content) {
return Buffer.from(content).toString('base64')
Expand Down
Loading

0 comments on commit b15614d

Please sign in to comment.