From 2be323bc447ae46dae3c61171525e091f31d42c3 Mon Sep 17 00:00:00 2001 From: Wagner Santos <7467450+wagoid@users.noreply.github.com> Date: Thu, 20 Jul 2023 05:54:29 -0300 Subject: [PATCH 1/3] feat: list up to 100 commits at once resolves #717 --- src/action.js | 1 + src/action.test.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/action.js b/src/action.js index 5c72a4ea..66be513d 100644 --- a/src/action.js +++ b/src/action.js @@ -58,6 +58,7 @@ const getRangeForEvent = async () => { owner, repo, pull_number: number, + per_page: 100, }) const commitShas = commits.map((commit) => commit.sha) const [from] = commitShas diff --git a/src/action.test.js b/src/action.test.js index 89f5dc34..cd5850e6 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -332,6 +332,7 @@ describe('Commit Linter action', () => { owner: 'wagoid', repo: 'commitlint-github-action', pull_number: '1', + per_page: 100, }), ).thenResolve({ data: [first, to].map((sha) => ({ sha })), @@ -397,6 +398,7 @@ describe('Commit Linter action', () => { owner: 'wagoid', repo: 'commitlint-github-action', pull_number: '1', + per_page: 100, }), ).thenReject(new Error('HttpError: Bad credentials')) td.replace(process, 'cwd', () => cwd) From a31f4b57934da285bea117cbd95f5e32ec6f5536 Mon Sep 17 00:00:00 2001 From: Wagner Santos <7467450+wagoid@users.noreply.github.com> Date: Sat, 22 Jul 2023 07:25:21 -0300 Subject: [PATCH 2/3] feat!: use github event payload and API to list commits resolves #456 That way we guarantee we're linting the same commits that appear on github. BREAKING CHANGE: "firstParent" option has been removed --- README.md | 8 - action.yml | 16 +- package-lock.json | 1 - package.json | 1 - src/action.js | 68 ++------ src/action.test.js | 386 ++++++++++++++++++++++----------------------- src/gitCommits.js | 43 ----- src/testUtils.js | 30 ++-- 8 files changed, 219 insertions(+), 334 deletions(-) delete mode 100644 src/gitCommits.js diff --git a/README.md b/README.md index 613dc2f5..53ed711f 100644 --- a/README.md +++ b/README.md @@ -38,14 +38,6 @@ If the config file doesn't exist, [config-conventional](https://github.com/conve Details on the configuration file can be found on [the commitlint website](https://commitlint.js.org/#/reference-configuration). -### `firstParent` - -When set to true, we follow only the first parent commit when seeing a merge commit. - -This helps to ignore errors in commits that were already present in your default branch (e.g. `master`) before adding conventional commit checks. More info in [git-log docs](https://git-scm.com/docs/git-log#Documentation/git-log.txt---first-parent). - -Default: `true` - ### `failOnWarnings` Whether you want to fail on warnings or not. diff --git a/action.yml b/action.yml index 2ff6451a..51d927e3 100644 --- a/action.yml +++ b/action.yml @@ -3,24 +3,18 @@ description: Lints Pull Request commit messages with commitlint author: Wagner Santos inputs: configFile: - description: Commitlint config file. If the file doesn't exist, config-conventional settings will be + description: + Commitlint config file. If the file doesn't exist, config-conventional settings will be loaded as a fallback. default: ./commitlint.config.js required: false - firstParent: - description: > - When set to true, we follow only the first parent commit when seeing a merge commit. - More info in git-log docs - https://git-scm.com/docs/git-log#Documentation/git-log.txt---first-parent - default: "true" - required: false failOnWarnings: description: Whether you want to fail on warnings or not - default: "false" + default: 'false' required: false failOnErrors: description: Whether you want to fail on errors or not - default: "true" + default: 'true' required: true helpURL: description: Link to a page explaining your commit message convention @@ -28,7 +22,7 @@ inputs: required: false commitDepth: description: When set to a valid Integer value - X, considers only the latest X number of commits. - default: "" + default: '' required: false token: description: > diff --git a/package-lock.json b/package-lock.json index 4bc8f12a..0d946880 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "conventional-changelog-conventionalcommits": "^4.6.3", "conventional-changelog-lint-config-canonical": "^1.0.0", "dargs": "^8.1.0", - "execa": "^5.1.1", "lerna": "^5.1.4" }, "devDependencies": { diff --git a/package.json b/package.json index b9c7897c..b90e831b 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "conventional-changelog-conventionalcommits": "^4.6.3", "conventional-changelog-lint-config-canonical": "^1.0.0", "dargs": "^8.1.0", - "execa": "^5.1.1", "lerna": "^5.1.4" }, "devDependencies": { diff --git a/src/action.js b/src/action.js index 66be513d..bc21e990 100644 --- a/src/action.js +++ b/src/action.js @@ -5,14 +5,13 @@ import { context as eventContext, getOctokit } from '@actions/github' import lint from '@commitlint/lint' import { format } from '@commitlint/format' import load from '@commitlint/load' -import gitCommits from './gitCommits' import generateOutputs from './generateOutputs' const pullRequestEvent = 'pull_request' const pullRequestTargetEvent = 'pull_request_target' const pullRequestEvents = [pullRequestEvent, pullRequestTargetEvent] -const { GITHUB_EVENT_NAME, GITHUB_SHA } = process.env +const { GITHUB_EVENT_NAME } = process.env const configPath = resolve(process.env.GITHUB_WORKSPACE, getInput('configFile')) @@ -23,34 +22,18 @@ const getCommitDepth = () => { return Number.isNaN(commitDepth) ? null : Math.max(commitDepth, 0) } -const pushEventHasOnlyOneCommit = (from) => { - const gitEmptySha = '0000000000000000000000000000000000000000' +const getPushEventCommits = () => { + const mappedCommits = eventContext.payload.commits.map((commit) => ({ + message: commit.message, + hash: commit.id, + })) - return from === gitEmptySha + return mappedCommits } -const getRangeForPushEvent = () => { - let from = eventContext.payload.before - const to = GITHUB_SHA - - if (eventContext.payload.forced) { - // When a commit is forced, "before" field from the push event data may point to a commit that doesn't exist - console.warn( - 'Commit was forced, checking only the latest commit from push instead of a range of commit messages', - ) - from = null - } - - if (pushEventHasOnlyOneCommit(from)) { - from = null - } - - return [from, to] -} - -const getRangeForEvent = async () => { +const getEventCommits = async () => { if (!pullRequestEvents.includes(GITHUB_EVENT_NAME)) - return getRangeForPushEvent() + return getPushEventCommits() const octokit = getOctokit(getInput('token')) const { owner, repo, number } = eventContext.issue @@ -60,30 +43,11 @@ const getRangeForEvent = async () => { pull_number: number, per_page: 100, }) - const commitShas = commits.map((commit) => commit.sha) - const [from] = commitShas - const to = commitShas[commitShas.length - 1] - // Git revision range doesn't include the "from" field in "git log", so for "from" we use the parent commit of PR's first commit - const fromParent = `${from}^1` - - return [fromParent, to] -} - -function getHistoryCommits(from, to) { - const options = { - from, - to, - } - - if (getInput('firstParent') === 'true') { - options.firstParent = true - } - - if (!from) { - options.maxCount = 1 - } - return gitCommits(options) + return commits.map((commit) => ({ + message: commit.commit.message, + hash: commit.sha, + })) } function getOptsFromConfig(config) { @@ -125,8 +89,8 @@ const handleOnlyWarnings = (formattedResults) => { } } -const showLintResults = async ([from, to]) => { - let commits = await getHistoryCommits(from, to) +const showLintResults = async (eventCommits) => { + let commits = eventCommits const commitDepth = getCommitDepth() if (commitDepth) { commits = commits?.slice(0, commitDepth) @@ -164,7 +128,7 @@ const exitWithMessage = (message) => (error) => { } const commitLinterAction = () => - getRangeForEvent() + getEventCommits() .catch( exitWithMessage("error trying to get list of pull request's commits"), ) diff --git a/src/action.test.js b/src/action.test.js index cd5850e6..8b96c405 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -1,15 +1,13 @@ /* eslint-env jest */ import { git } from '@commitlint/test' import { describe } from '@jest/globals' -import execa from 'execa' import td from 'testdouble' import { - gitEmptyCommit, - getCommitHashes, updatePushEnvVars, createPushEventPayload, createPullRequestEventPayload, updatePullRequestEnvVars, + buildResponseCommit, } from './testUtils' const resultsOutputId = 'results' @@ -51,7 +49,6 @@ describe('Commit Linter action', () => { td.replace(core, 'setFailed') td.replace(core, 'setOutput') td.when(core.getInput('configFile')).thenReturn('./commitlint.config.js') - td.when(core.getInput('firstParent')).thenReturn('true') td.when(core.getInput('failOnWarnings')).thenReturn('false') td.when(core.getInput('helpURL')).thenReturn( 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint', @@ -67,10 +64,15 @@ describe('Commit Linter action', () => { it('should use default config when config file does not exist', async () => { td.when(core.getInput('configFile')).thenReturn('./not-existing-config.js') cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'wrong message') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-message', + message: 'wrong message', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) await runAction() @@ -87,10 +89,15 @@ describe('Commit Linter action', () => { it('should fail for single push with incorrect message', async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'wrong message') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-message', + message: 'wrong message', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) await runAction() @@ -100,12 +107,19 @@ describe('Commit Linter action', () => { it('should fail for push range with wrong messages', async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'wrong message 1') - await gitEmptyCommit(cwd, 'wrong message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-message-1', + message: 'wrong message 1', + }, + { + id: 'wrong-message-2', + message: 'wrong message 2', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) await runAction() @@ -116,12 +130,19 @@ describe('Commit Linter action', () => { it('should pass for push range with wrong messages with failOnErrors set to false', async () => { td.when(core.getInput('failOnErrors')).thenReturn('false') cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'wrong message 1') - await gitEmptyCommit(cwd, 'wrong message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-message-1', + message: 'wrong message 1', + }, + { + id: 'wrong-message-2', + message: 'wrong message 2', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') @@ -136,12 +157,19 @@ describe('Commit Linter action', () => { it('should pass for push range with correct messages with failOnErrors set to false', async () => { td.when(core.getInput('failOnErrors')).thenReturn('false') cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'chore: correct message 1') - await gitEmptyCommit(cwd, 'chore: correct message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'correct-message-1', + message: 'chore: correct message 1', + }, + { + id: 'correct-message-2', + message: 'chore: correct message 2', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') @@ -153,12 +181,19 @@ describe('Commit Linter action', () => { it('should pass for push range with correct messages', async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'chore: correct message 1') - await gitEmptyCommit(cwd, 'chore: correct message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'correct-message-1', + message: 'chore: correct message 1', + }, + { + id: 'correct-message-2', + message: 'chore: correct message 2', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') @@ -168,52 +203,17 @@ describe('Commit Linter action', () => { td.verify(console.log('Lint free! 🎉')) }) - it('should lint only last commit for forced push', async () => { - cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'wrong message 1') - await gitEmptyCommit(cwd, 'wrong message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to, forced: true }) - updatePushEnvVars(cwd, to) - td.replace(process, 'cwd', () => cwd) - td.replace(console, 'warn') - - await runAction() - - td.verify( - console.warn( - 'Commit was forced, checking only the latest commit from push instead of a range of commit messages', - ), - ) - td.verify(core.setFailed(contains('wrong message 1')), { times: 0 }) - td.verify(core.setFailed(contains('wrong message 2'))) - }) - - it('should lint only last commit when "before" field is an empty sha', async () => { - const gitEmptySha = '0000000000000000000000000000000000000000' - cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'wrong message 1') - await gitEmptyCommit(cwd, 'wrong message 2') - const [, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before: gitEmptySha, to }) - updatePushEnvVars(cwd, to) - td.replace(process, 'cwd', () => cwd) - - await runAction() - - td.verify(core.setFailed(contains('wrong message 1')), { times: 0 }) - td.verify(core.setFailed(contains('wrong message 2'))) - }) - it('should fail for commit with scope that is not a lerna package', async () => { cwd = await git.bootstrap('fixtures/lerna-scopes') td.when(core.getInput('configFile')).thenReturn('./commitlint.config.yml') - await gitEmptyCommit(cwd, 'chore(wrong): not including package scope') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + message: 'chore(wrong): not including package scope', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) await runAction() @@ -226,10 +226,15 @@ describe('Commit Linter action', () => { it('should pass for scope that is a lerna package', async () => { cwd = await git.bootstrap('fixtures/lerna-scopes') td.when(core.getInput('configFile')).thenReturn('./commitlint.config.yml') - await gitEmptyCommit(cwd, 'chore(second-package): this works') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'correct-message', + message: 'chore(second-package): this works', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') @@ -241,10 +246,15 @@ describe('Commit Linter action', () => { it("should fail for commit that doesn't comply with jira rules", async () => { cwd = await git.bootstrap('fixtures/jira') td.when(core.getInput('configFile')).thenReturn('./commitlint.config.js') - await gitEmptyCommit(cwd, 'ib-21212121212121: without jira ticket') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-message', + message: 'ib-21212121212121: without jira ticket', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) await runAction() @@ -271,62 +281,23 @@ describe('Commit Linter action', () => { ) }) - it('should NOT consider commits from another branch', async () => { - cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'chore: commit before') - await gitEmptyCommit(cwd, 'chore: correct message') - await execa.command('git checkout -b another-branch', { cwd }) - await gitEmptyCommit(cwd, 'wrong commit from another branch') - await execa.command('git checkout -', { cwd }) - await execa.command('git merge --no-ff another-branch', { cwd }) - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) - td.replace(process, 'cwd', () => cwd) - td.replace(console, 'log') - - await runAction() - - td.verify(console.log('Lint free! 🎉')) - }) - - it('should consider commits from another branch when firstParent is false', async () => { - cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'chore: commit before') - await gitEmptyCommit(cwd, 'chore: correct message') - await execa.command('git checkout -b another-branch', { cwd }) - await gitEmptyCommit(cwd, 'wrong commit from another branch') - await execa.command('git checkout -', { cwd }) - await execa.command('git merge --no-ff another-branch', { cwd }) - const [before, , , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) - td.replace(process, 'cwd', () => cwd) - td.when(core.getInput('firstParent')).thenReturn('false') - - await runAction() - - td.verify(core.setFailed(contains('wrong commit from another branch'))) - }) - describe.each(['pull_request', 'pull_request_target'])( 'when there are multiple commits failing in the %s event', (eventName) => { let expectedResultsOutput - const firstMessage = 'wrong message 1' - const secondMessage = 'wrong message 2' + const firstCommit = buildResponseCommit('first-commit', 'wrong message 1') + const secondCommit = buildResponseCommit( + 'second-commit', + 'wrong message 2', + ) beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') td.when(core.getInput('configFile')).thenReturn( './commitlint.config.js', ) - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, firstMessage) - await gitEmptyCommit(cwd, secondMessage) await createPullRequestEventPayload(cwd) - const [, first, to] = await getCommitHashes(cwd) - updatePullRequestEnvVars(cwd, to, { eventName }) + updatePullRequestEnvVars(cwd, { eventName }) td.when( listCommits({ owner: 'wagoid', @@ -335,21 +306,21 @@ describe('Commit Linter action', () => { per_page: 100, }), ).thenResolve({ - data: [first, to].map((sha) => ({ sha })), + data: [firstCommit, secondCommit], }) td.replace(process, 'cwd', () => cwd) expectedResultsOutput = [ { - hash: to, - message: secondMessage, + hash: firstCommit.sha, + message: firstCommit.commit.message, valid: false, errors: ['subject may not be empty', 'type may not be empty'], warnings: [], }, { - hash: first, - message: firstMessage, + hash: secondCommit.sha, + message: secondCommit.commit.message, valid: false, errors: ['subject may not be empty', 'type may not be empty'], warnings: [], @@ -368,13 +339,13 @@ describe('Commit Linter action', () => { it('should show errors for the first wrong message', async () => { await runAction() - td.verify(core.setFailed(contains(firstMessage))) + td.verify(core.setFailed(contains(firstCommit.commit.message))) }) it('should show errors for the second wrong message', async () => { await runAction() - td.verify(core.setFailed(contains(secondMessage))) + td.verify(core.setFailed(contains(secondCommit.commit.message))) }) it('should generate a JSON output of the errors', async () => { @@ -389,10 +360,8 @@ describe('Commit Linter action', () => { beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') td.when(core.getInput('configFile')).thenReturn('./commitlint.config.js') - await gitEmptyCommit(cwd, 'commit message') await createPullRequestEventPayload(cwd) - const [to] = await getCommitHashes(cwd) - updatePullRequestEnvVars(cwd, to) + updatePullRequestEnvVars(cwd) td.when( listCommits({ owner: 'wagoid', @@ -422,15 +391,15 @@ describe('Commit Linter action', () => { }) describe("when there's a single commit with correct message", () => { - let commitHash + const commit = { + id: 'correct-message', + message: 'chore: correct message', + } beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'chore: correct message') - const [to] = await getCommitHashes(cwd) - commitHash = to - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { commits: [commit] }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') }) @@ -450,8 +419,8 @@ describe('Commit Linter action', () => { it('should generate a JSON output of the messages', async () => { const expectedResultsOutput = [ { - hash: commitHash, - message: 'chore: correct message', + hash: commit.id, + message: commit.message, valid: true, errors: [], warnings: [], @@ -468,22 +437,26 @@ describe('Commit Linter action', () => { let expectedResultsOutput beforeEach(async () => { + const correctCommit = { + id: 'correct-commit', + message: 'chore: correct message with no warnings', + } + const commitWithWarning = { + id: 'commit-with-warning', + message: + 'chore: correct message\nsome context without leading blank line', + } cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'chore: previous commit') - await gitEmptyCommit(cwd, 'chore: correct message with no warnings') - await gitEmptyCommit( - cwd, - 'chore: correct message\nsome context without leading blank line', - ) - const [before, from, to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [commitWithWarning, correctCommit], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') expectedResultsOutput = [ { - hash: to, + hash: commitWithWarning.id, message: 'chore: correct message\n\nsome context without leading blank line', valid: true, @@ -491,7 +464,7 @@ describe('Commit Linter action', () => { warnings: ['body must have leading blank line'], }, { - hash: from, + hash: correctCommit.id, message: 'chore: correct message with no warnings', valid: true, errors: [], @@ -535,22 +508,22 @@ describe('Commit Linter action', () => { }) describe('when a subset of errors are just warnings', () => { - let firstHash - let secondHash + const commitWithWarning = { + id: 'first-commit', + message: + 'chore: correct message\nsome context without leading blank line', + } + const wrongCommit = { + id: 'second-commit', + message: 'wrong-message', + } beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit( - cwd, - 'chore: correct message\nsome context without leading blank line', - ) - await gitEmptyCommit(cwd, 'wrong message') - const [before, firstCommit, to] = await getCommitHashes(cwd) - firstHash = firstCommit - secondHash = to - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [wrongCommit, commitWithWarning], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') }) @@ -566,14 +539,14 @@ describe('Commit Linter action', () => { it('should show the results in an output', async () => { const expectedResultsOutput = [ { - hash: secondHash, - message: 'wrong message', + hash: wrongCommit.id, + message: wrongCommit.message, valid: false, errors: ['subject may not be empty', 'type may not be empty'], warnings: [], }, { - hash: firstHash, + hash: commitWithWarning.id, message: 'chore: correct message\n\nsome context without leading blank line', valid: true, @@ -605,13 +578,16 @@ describe('Commit Linter action', () => { describe('when commit contains required signed-off-by message', () => { beforeEach(async () => { cwd = await git.bootstrap('fixtures/signed-off-by') - await gitEmptyCommit( - cwd, - 'chore: correct message\n\nsome context without leading blank line.\n\nSigned-off-by: John Doe ', - ) - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'correct-commit', + message: + 'chore: correct message\n\nsome context without leading blank line.\n\nSigned-off-by: John Doe ', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') }) @@ -627,10 +603,15 @@ describe('Commit Linter action', () => { describe('when a different helpUrl is provided in the config', () => { beforeEach(async () => { cwd = await git.bootstrap('fixtures/custom-help-url') - await gitEmptyCommit(cwd, 'wrong message') - const [to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { + id: 'wrong-commit', + message: 'wrong message', + }, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') }) @@ -646,17 +627,24 @@ describe('Commit Linter action', () => { }) describe('when commitDepth is provided in the config', () => { + const incorrectCommit = { + id: 'incorrect-message', + message: 'incorrect message within commit depth', + } + beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') - await gitEmptyCommit(cwd, 'message from before push') - await gitEmptyCommit(cwd, 'incorrect message within commit depth') - await gitEmptyCommit(cwd, 'chore: correct message 2') - const [before, , to] = await getCommitHashes(cwd) - await createPushEventPayload(cwd, { before, to }) - updatePushEnvVars(cwd, to) + await createPushEventPayload(cwd, { + commits: [ + { id: 'correct-commit', message: 'chore: correct message 2' }, + incorrectCommit, + ], + }) + updatePushEnvVars(cwd) td.replace(process, 'cwd', () => cwd) td.replace(console, 'log') }) + it('should pass when only considering messages defined by commitDepth', async () => { td.when(core.getInput('commitDepth')).thenReturn('1') await runAction() @@ -664,21 +652,19 @@ describe('Commit Linter action', () => { td.verify(core.setFailed(), { times: 0, ignoreExtraArgs: true }) td.verify(console.log('Lint free! 🎉')) }) + it('should fail when older commits have lint errors', async () => { td.when(core.getInput('commitDepth')).thenReturn('2') await runAction() - td.verify( - core.setFailed(contains('incorrect message within commit depth')), - ) + td.verify(core.setFailed(contains(incorrectCommit.message))) }) + it('should consider all commits when an invalid commit depth is passed in config', async () => { td.when(core.getInput('commitDepth')).thenReturn('xzy') await runAction() - td.verify( - core.setFailed(contains('incorrect message within commit depth')), - ) + td.verify(core.setFailed(contains(incorrectCommit.message))) }) }) }) diff --git a/src/gitCommits.js b/src/gitCommits.js deleted file mode 100644 index 5e0ab0e7..00000000 --- a/src/gitCommits.js +++ /dev/null @@ -1,43 +0,0 @@ -import dargs from 'dargs' -import execa from 'execa' - -const commitDelimiter = '--------->commit---------' - -const hashDelimiter = '--------->hash---------' - -const format = `%H${hashDelimiter}%B%n${commitDelimiter}` - -const buildGitArgs = (gitOpts) => { - const { from, to, ...otherOpts } = gitOpts - const formatArg = `--format=${format}` - const fromToArg = [from, to].filter(Boolean).join('..') - - const gitArgs = ['log', formatArg, fromToArg] - - return gitArgs.concat( - dargs(gitOpts, { - includes: Object.keys(otherOpts), - }), - ) -} - -const gitCommits = async (gitOpts) => { - const args = buildGitArgs(gitOpts) - - const { stdout } = await execa('git', args, { - cwd: process.cwd(), - }) - - const commits = stdout.split(`${commitDelimiter}\n`).map((messageItem) => { - const [hash, message] = messageItem.split(hashDelimiter) - - return { - hash, - message: message.replace(commitDelimiter, ''), - } - }) - - return commits -} - -export default gitCommits diff --git a/src/testUtils.js b/src/testUtils.js index 02cde3a4..33abb10d 100644 --- a/src/testUtils.js +++ b/src/testUtils.js @@ -1,7 +1,6 @@ import path from 'path' import fs from 'fs' import { promisify } from 'util' -import execa from 'execa' const writeFile = promisify(fs.writeFile) @@ -11,32 +10,21 @@ export const updateEnvVars = (envVars) => { }) } -export const gitEmptyCommit = (cwd, message) => - execa('git', ['commit', '--allow-empty', '-m', message], { cwd }) - -export const getCommitHashes = async (cwd) => { - const { stdout } = await execa.command('git log --pretty=%H', { cwd }) - const hashes = stdout.split('\n').reverse() - - return hashes -} - -export const updatePushEnvVars = (cwd, to) => { +export const updatePushEnvVars = (cwd) => { updateEnvVars({ GITHUB_WORKSPACE: cwd, GITHUB_EVENT_NAME: 'push', - GITHUB_SHA: to, }) } export const createPushEventPayload = async ( cwd, - { before = null, to, forced = false }, + { forced = false, headCommit = null, commits = [] }, ) => { const payload = { - after: to, - before, forced, + head_commit: headCommit, + commits, } const eventPath = path.join(cwd, 'pushEventPayload.json') @@ -64,12 +52,18 @@ export const createPullRequestEventPayload = async (cwd) => { await writeFile(eventPath, JSON.stringify(payload), 'utf8') } -export const updatePullRequestEnvVars = (cwd, to, options = {}) => { +export const updatePullRequestEnvVars = (cwd, options = {}) => { const { eventName = 'pull_request' } = options updateEnvVars({ GITHUB_WORKSPACE: cwd, GITHUB_EVENT_NAME: eventName, - GITHUB_SHA: to, }) } + +export const buildResponseCommit = (sha, message) => ({ + sha, + commit: { + message, + }, +}) From a8f11416e02a569738f76e25ea3896dc47cbddfd Mon Sep 17 00:00:00 2001 From: Wagner Santos <7467450+wagoid@users.noreply.github.com> Date: Sat, 22 Jul 2023 09:02:38 -0300 Subject: [PATCH 3/3] docs: remove fetch-depth from instructions since it's not needed anymore --- .github/workflows/ci.yml | 2 -- .github/workflows/commitlint.yml | 4 ---- README.md | 6 ------ 3 files changed, 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d827ffa..16d9d314 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,6 @@ jobs: DOCKER_REGISTRY_URL: registry.hub.docker.com steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: actions/setup-node@v3 with: node-version: '16.5.0' diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index cb3b0fd9..9555ac43 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -6,8 +6,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - run: sed -i -E "s/(docker:.+)/Dockerfile/" ./action.yml - run: echo -n '' > .dockerignore - uses: actions/setup-node@v3 @@ -33,8 +31,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: actions/setup-node@v3 with: node-version: '16.5.0' diff --git a/README.md b/README.md index 53ed711f..23b118aa 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: wagoid/commitlint-github-action@v5 ``` Alternatively, you can run on other event types such as `on: [push]`. In that case the action will lint the push event's commit(s) instead of linting commits from a pull request. You can also combine `push` and `pull_request` together in the same workflow. -**Note**: It's necessary that you specify the `fetch-depth` argument to `actions/checkout@v2` step. By default they fetch only latest commit of the branch, but we need more commits since we validate a range of commit messages. - ## Inputs You can supply these inputs to the `wagoid/commitlint-github-action@v5` step. @@ -139,8 +135,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - uses: actions/setup-node@v2 with: node-version: '14'