mirror of
https://github.com/wagoid/commitlint-github-action.git
synced 2025-11-07 08:06:54 +00:00
Merge pull request #745 from wagoid/feat/use-only-github-api-to-list-commits
feat!: use github event payload and API to list commits
This commit is contained in:
commit
1afdc05f78
10 changed files with 222 additions and 346 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
4
.github/workflows/commitlint.yml
vendored
4
.github/workflows/commitlint.yml
vendored
|
|
@ -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'
|
||||
|
|
|
|||
14
README.md
14
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.
|
||||
|
|
@ -38,14 +34,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.
|
||||
|
|
@ -147,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'
|
||||
|
|
|
|||
16
action.yml
16
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: >
|
||||
|
|
|
|||
1
package-lock.json
generated
1
package-lock.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -58,31 +41,13 @@ const getRangeForEvent = async () => {
|
|||
owner,
|
||||
repo,
|
||||
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) {
|
||||
|
|
@ -124,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)
|
||||
|
|
@ -163,7 +128,7 @@ const exitWithMessage = (message) => (error) => {
|
|||
}
|
||||
|
||||
const commitLinterAction = () =>
|
||||
getRangeForEvent()
|
||||
getEventCommits()
|
||||
.catch(
|
||||
exitWithMessage("error trying to get list of pull request's commits"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,84 +281,46 @@ 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',
|
||||
repo: 'commitlint-github-action',
|
||||
pull_number: '1',
|
||||
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: [],
|
||||
|
|
@ -367,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 () => {
|
||||
|
|
@ -388,15 +360,14 @@ 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',
|
||||
repo: 'commitlint-github-action',
|
||||
pull_number: '1',
|
||||
per_page: 100,
|
||||
}),
|
||||
).thenReject(new Error('HttpError: Bad credentials'))
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
|
@ -420,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')
|
||||
})
|
||||
|
|
@ -448,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: [],
|
||||
|
|
@ -466,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,
|
||||
|
|
@ -489,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: [],
|
||||
|
|
@ -533,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')
|
||||
})
|
||||
|
|
@ -564,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,
|
||||
|
|
@ -603,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 <john.doe@example.com>',
|
||||
)
|
||||
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 <john.doe@example.com>',
|
||||
},
|
||||
],
|
||||
})
|
||||
updatePushEnvVars(cwd)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
td.replace(console, 'log')
|
||||
})
|
||||
|
|
@ -625,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')
|
||||
})
|
||||
|
|
@ -644,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()
|
||||
|
|
@ -662,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)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue