mirror of
https://github.com/wagoid/commitlint-github-action.git
synced 2025-11-07 16:06:56 +00:00
Merge pull request #20 from wagoid/add-tests
test: add tests for the action
This commit is contained in:
commit
3e346d92de
19 changed files with 4568 additions and 192 deletions
|
|
@ -1,7 +1,10 @@
|
|||
node_modules
|
||||
.commitlintrc.yml
|
||||
commitlint.config.js
|
||||
.commitlintrc-with-lerna-scopes
|
||||
action.yml
|
||||
.github
|
||||
CHANGELOG.md
|
||||
coverage
|
||||
fixtures
|
||||
action.test.js
|
||||
testUtils.js
|
||||
|
|
|
|||
14
.github/workflows/ci.yml
vendored
Normal file
14
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
name: CI
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- run: npm install
|
||||
- run: npm test --ci
|
||||
13
.github/workflows/commitlint.yml
vendored
13
.github/workflows/commitlint.yml
vendored
|
|
@ -1,5 +1,5 @@
|
|||
name: Commitlint
|
||||
on: [pull_request]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
commitlint:
|
||||
|
|
@ -22,14 +22,3 @@ jobs:
|
|||
- uses: ./
|
||||
with:
|
||||
configFile: './.commitlintrc.yml'
|
||||
commitlint-with-lerna-scopes:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: sed -i -E "s/([']docker:.+)/Dockerfile/" ./action.yml
|
||||
- run: echo -n '' > .dockerignore
|
||||
- uses: ./
|
||||
with:
|
||||
configFile: './.commitlintrc-with-lerna-scopes.yml'
|
||||
|
|
|
|||
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
|
|
@ -1,13 +0,0 @@
|
|||
name: Test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
commitlint:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: sed -i -E "s/([']docker:.+)/Dockerfile/" ./action.yml
|
||||
- run: echo -n '' > .dockerignore
|
||||
- uses: ./
|
||||
143
action.js
Normal file
143
action.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
const { existsSync } = require('fs')
|
||||
const { resolve } = require('path')
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const lint = require('@commitlint/lint')
|
||||
const { format } = require('@commitlint/format')
|
||||
const load = require('@commitlint/load')
|
||||
const gitRawCommits = require('git-raw-commits')
|
||||
|
||||
const pullRequestEvent = 'pull_request'
|
||||
|
||||
const { GITHUB_TOKEN, GITHUB_EVENT_NAME, GITHUB_SHA } = process.env
|
||||
|
||||
const configPath = resolve(
|
||||
process.env.GITHUB_WORKSPACE,
|
||||
core.getInput('configFile'),
|
||||
)
|
||||
|
||||
const { context: eventContext } = github
|
||||
|
||||
const pushEventHasOnlyOneCommit = from => {
|
||||
const gitEmptySha = '0000000000000000000000000000000000000000'
|
||||
|
||||
return from === gitEmptySha
|
||||
}
|
||||
|
||||
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 () => {
|
||||
if (GITHUB_EVENT_NAME !== pullRequestEvent) return getRangeForPushEvent()
|
||||
|
||||
const octokit = new github.GitHub(GITHUB_TOKEN)
|
||||
const { owner, repo, number } = eventContext.issue
|
||||
const { data: commits } = await octokit.pulls.listCommits({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: number,
|
||||
})
|
||||
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 (core.getInput('firstParent') === 'true') {
|
||||
options.firstParent = true
|
||||
}
|
||||
|
||||
if (!from) {
|
||||
options.maxCount = 1
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = []
|
||||
|
||||
gitRawCommits(options)
|
||||
.on('data', chunk => data.push(chunk.toString('utf-8')))
|
||||
.on('error', reject)
|
||||
.on('end', () => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getOptsFromConfig(config) {
|
||||
return {
|
||||
parserOpts:
|
||||
config.parserPreset != null && config.parserPreset.parserOpts != null
|
||||
? config.parserPreset.parserOpts
|
||||
: {},
|
||||
plugins: config.plugins != null ? config.plugins : {},
|
||||
ignores: config.ignores != null ? config.ignores : [],
|
||||
defaultIgnores:
|
||||
config.defaultIgnores != null ? config.defaultIgnores : true,
|
||||
}
|
||||
}
|
||||
|
||||
const showLintResults = async ([from, to]) => {
|
||||
const commits = await getHistoryCommits(from, to)
|
||||
const config = existsSync(configPath)
|
||||
? await load({}, { file: configPath })
|
||||
: {}
|
||||
const opts = getOptsFromConfig(config)
|
||||
const results = await Promise.all(
|
||||
commits.map(commit => lint(commit, config.rules, opts)),
|
||||
)
|
||||
const formattedResults = format(
|
||||
{ results },
|
||||
{
|
||||
color: true,
|
||||
helpUrl:
|
||||
'https://github.com/conventional-changelog/commitlint/#what-is-commitlint',
|
||||
},
|
||||
)
|
||||
|
||||
if (formattedResults) {
|
||||
core.setFailed(
|
||||
`You have commit messages with errors\n\n${formattedResults}`,
|
||||
)
|
||||
} else {
|
||||
console.log('Lint free! 🎉')
|
||||
}
|
||||
}
|
||||
|
||||
const exitWithMessage = message => error => {
|
||||
core.setFailed(`${message}\n${error.message}\n${error.stack}`)
|
||||
}
|
||||
|
||||
const commitLinterAction = () =>
|
||||
getRangeForEvent()
|
||||
.catch(
|
||||
exitWithMessage("error trying to get list of pull request's commits"),
|
||||
)
|
||||
.then(showLintResults)
|
||||
.catch(exitWithMessage('error running commitlint'))
|
||||
|
||||
module.exports = commitLinterAction
|
||||
313
action.test.js
Normal file
313
action.test.js
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
const { git } = require('@commitlint/test')
|
||||
const execa = require('execa')
|
||||
const td = require('testdouble')
|
||||
const {
|
||||
updateEnvVars,
|
||||
gitEmptyCommit,
|
||||
getCommitHashes,
|
||||
updatePushEnvVars,
|
||||
createPushEventPayload,
|
||||
createPullRequestEventPayload,
|
||||
updatePullRequestEnvVars,
|
||||
} = require('./testUtils')
|
||||
|
||||
const {
|
||||
matchers: { contains },
|
||||
} = td
|
||||
|
||||
const initialEnv = { ...process.env }
|
||||
|
||||
const listCommits = td.func('listCommits')
|
||||
|
||||
const runAction = () => {
|
||||
const github = require('@actions/github')
|
||||
class MockOctokit {
|
||||
constructor() {
|
||||
this.pulls = {
|
||||
listCommits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateEnvVars({ GITHBU_TOKEN: 'test-github-token' })
|
||||
td.replace(github, 'GitHub', MockOctokit)
|
||||
|
||||
return require('./action')()
|
||||
}
|
||||
|
||||
describe('Commit Linter action', () => {
|
||||
let core
|
||||
|
||||
beforeEach(() => {
|
||||
core = require('@actions/core')
|
||||
td.replace(core, 'getInput')
|
||||
td.replace(core, 'setFailed')
|
||||
td.when(core.getInput('configFile')).thenReturn('./commitlint.config.js')
|
||||
td.when(core.getInput('firstParent')).thenReturn('true')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
td.reset()
|
||||
process.env = initialEnv
|
||||
jest.resetModules()
|
||||
})
|
||||
|
||||
it('should fail for single push with incorrect message', async () => {
|
||||
const cwd = await git.bootstrap('fixtures/conventional')
|
||||
await gitEmptyCommit(cwd, 'wrong message')
|
||||
const [to] = await getCommitHashes(cwd)
|
||||
await createPushEventPayload(cwd, { to })
|
||||
updatePushEnvVars(cwd, to)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(core.setFailed(contains('You have commit messages with errors')))
|
||||
})
|
||||
|
||||
it('should pass for single push with correct message', async () => {
|
||||
const cwd = await git.bootstrap('fixtures/conventional')
|
||||
await gitEmptyCommit(cwd, 'chore: correct message')
|
||||
const [to] = await getCommitHashes(cwd)
|
||||
await createPushEventPayload(cwd, { to })
|
||||
updatePushEnvVars(cwd, to)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
td.replace(console, 'log')
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(core.setFailed(), { times: 0, ignoreExtraArgs: true })
|
||||
td.verify(console.log('Lint free! 🎉'))
|
||||
})
|
||||
|
||||
it('should fail for push range with wrong messages', async () => {
|
||||
const 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)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(core.setFailed(contains('wrong message 1')))
|
||||
td.verify(core.setFailed(contains('wrong message 2')))
|
||||
})
|
||||
|
||||
it('should pass for push range with correct messages', async () => {
|
||||
const 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)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
td.replace(console, 'log')
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(core.setFailed(), { times: 0, ignoreExtraArgs: true })
|
||||
td.verify(console.log('Lint free! 🎉'))
|
||||
})
|
||||
|
||||
it('should lint only last commit for forced push', async () => {
|
||||
const 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'
|
||||
const cwd = await git.bootstrap('fixtures/conventional')
|
||||
await gitEmptyCommit(cwd, 'message from before push')
|
||||
await gitEmptyCommit(cwd, 'wrong message 1')
|
||||
await gitEmptyCommit(cwd, 'chore(WRONG): message 2')
|
||||
const [before, , 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('chore(WRONG): message 2')))
|
||||
})
|
||||
|
||||
it('should fail for commit with scope that is not a lerna package', async () => {
|
||||
const 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)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(
|
||||
core.setFailed(contains('chore(wrong): not including package scope')),
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass for scope that is a lerna package', async () => {
|
||||
const 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)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
td.replace(console, 'log')
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(console.log('Lint free! 🎉'))
|
||||
})
|
||||
|
||||
it("should fail for commit that doesn't comply with jira rules", async () => {
|
||||
const 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)
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(
|
||||
core.setFailed(contains('ib-21212121212121: without jira ticket')),
|
||||
)
|
||||
td.verify(
|
||||
core.setFailed(
|
||||
contains(
|
||||
'ib-21212121212121 taskId must not be loonger than 9 characters',
|
||||
),
|
||||
),
|
||||
)
|
||||
td.verify(
|
||||
core.setFailed(
|
||||
contains('ib-21212121212121 taskId must be uppercase case'),
|
||||
),
|
||||
)
|
||||
td.verify(
|
||||
core.setFailed(
|
||||
contains('ib-21212121212121 commitStatus must be uppercase case'),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
it('should NOT consider commits from another branch', async () => {
|
||||
const 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 () => {
|
||||
const 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')))
|
||||
})
|
||||
|
||||
it('should lint all commits from a pull request', async () => {
|
||||
const 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, 'wrong message 1')
|
||||
await gitEmptyCommit(cwd, 'wrong message 2')
|
||||
await gitEmptyCommit(cwd, 'wrong message 3')
|
||||
await createPullRequestEventPayload(cwd)
|
||||
const [, first, second, to] = await getCommitHashes(cwd)
|
||||
updatePullRequestEnvVars(cwd, to)
|
||||
td.when(
|
||||
listCommits({
|
||||
owner: 'wagoid',
|
||||
repo: 'commitlint-github-action',
|
||||
pull_number: '1',
|
||||
}),
|
||||
).thenResolve({
|
||||
data: [first, second, to].map(sha => ({ sha })),
|
||||
})
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(core.setFailed(contains('message from before push')), {
|
||||
times: 0,
|
||||
})
|
||||
td.verify(core.setFailed(contains('wrong message 1')))
|
||||
td.verify(core.setFailed(contains('wrong message 2')))
|
||||
td.verify(core.setFailed(contains('wrong message 3')))
|
||||
})
|
||||
|
||||
it('should show an error message when failing to fetch commits', async () => {
|
||||
const 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)
|
||||
td.when(
|
||||
listCommits({
|
||||
owner: 'wagoid',
|
||||
repo: 'commitlint-github-action',
|
||||
pull_number: '1',
|
||||
}),
|
||||
).thenReject(new Error('HttpError: Bad credentials'))
|
||||
td.replace(process, 'cwd', () => cwd)
|
||||
|
||||
await runAction()
|
||||
|
||||
td.verify(
|
||||
core.setFailed(
|
||||
contains("error trying to get list of pull request's commits"),
|
||||
),
|
||||
)
|
||||
td.verify(core.setFailed(contains('HttpError: Bad credentials')))
|
||||
})
|
||||
})
|
||||
3
fixtures/conventional/commitlint.config.js
Normal file
3
fixtures/conventional/commitlint.config.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
}
|
||||
4
fixtures/jira/commitlint.config.js
Normal file
4
fixtures/jira/commitlint.config.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
plugins: ['commitlint-plugin-jira-rules'],
|
||||
extends: ['jira'],
|
||||
}
|
||||
5
fixtures/lerna-scopes/lerna.json
Normal file
5
fixtures/lerna-scopes/lerna.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "fixture-lerna-scopes",
|
||||
"version": "independent",
|
||||
"packages": ["packages/*"]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "first-package",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "second-package",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {}
|
||||
}
|
||||
15
fixtures/payloads/push.json
Normal file
15
fixtures/payloads/push.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"after": "cb3c15aee3d0fb546fa1509eef4286e421529e1e",
|
||||
"base_ref": null,
|
||||
"before": "d33e92a269fea31eac29d2dcbe964902cf0aee61",
|
||||
"commits": [],
|
||||
"compare": "https://github.com/wagoid/commitlint-github-action/compare/d33e92a269fe...cb3c15aee3d0",
|
||||
"created": false,
|
||||
"deleted": false,
|
||||
"forced": false,
|
||||
"head_commit": {},
|
||||
"pusher": {},
|
||||
"ref": "refs/heads/master",
|
||||
"repository": {},
|
||||
"sender": {}
|
||||
}
|
||||
9
jest.config.js
Normal file
9
jest.config.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// For a detailed explanation regarding each configuration property, visit:
|
||||
// https://jestjs.io/docs/en/configuration.html
|
||||
|
||||
module.exports = {
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
// The test environment that will be used for testing
|
||||
testEnvironment: 'node',
|
||||
}
|
||||
3984
package-lock.json
generated
3984
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@
|
|||
"description": "commitlint github action",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"test": "NODE_PATH=./node_modules jest",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
|
||||
},
|
||||
"repository": {
|
||||
|
|
@ -31,10 +31,14 @@
|
|||
"lerna": "3.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/test": "8.2.0",
|
||||
"conventional-changelog-cli": "2.0.23",
|
||||
"execa": "3.4.0",
|
||||
"husky": "3.0.7",
|
||||
"jest": "^24.9.0",
|
||||
"prettier": "1.18.2",
|
||||
"pretty-quick": "1.11.1"
|
||||
"pretty-quick": "1.11.1",
|
||||
"testdouble": "3.12.4"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
|
|
|||
144
run.js
144
run.js
|
|
@ -1,143 +1,3 @@
|
|||
const { existsSync } = require('fs')
|
||||
const { resolve } = require('path')
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const lint = require('@commitlint/lint')
|
||||
const { format } = require('@commitlint/format')
|
||||
const load = require('@commitlint/load')
|
||||
const gitRawCommits = require('git-raw-commits')
|
||||
const action = require('./action')
|
||||
|
||||
const pullRequestEvent = 'pull_request'
|
||||
|
||||
const { GITHUB_TOKEN, GITHUB_EVENT_NAME, GITHUB_SHA } = process.env
|
||||
|
||||
const configPath = resolve(
|
||||
process.env.GITHUB_WORKSPACE,
|
||||
core.getInput('configFile'),
|
||||
)
|
||||
|
||||
const { context: eventContext } = github
|
||||
|
||||
const pushEventHasOnlyOneCommit = from => {
|
||||
const gitEmptySha = '0000000000000000000000000000000000000000'
|
||||
|
||||
return from === gitEmptySha
|
||||
}
|
||||
|
||||
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 () => {
|
||||
if (GITHUB_EVENT_NAME !== pullRequestEvent) return getRangeForPushEvent()
|
||||
|
||||
const octokit = new github.GitHub(GITHUB_TOKEN)
|
||||
const { owner, repo, number } = eventContext.issue
|
||||
const { data: commits } = await octokit.pulls.listCommits({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: number,
|
||||
})
|
||||
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 (core.getInput('firstParent') === 'true') {
|
||||
options.firstParent = true
|
||||
}
|
||||
|
||||
if (!from) {
|
||||
options.maxCount = 1
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const data = []
|
||||
|
||||
gitRawCommits(options)
|
||||
.on('data', chunk => data.push(chunk.toString('utf-8')))
|
||||
.on('error', reject)
|
||||
.on('end', () => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getOptsFromConfig(config) {
|
||||
return {
|
||||
parserOpts:
|
||||
config.parserPreset != null && config.parserPreset.parserOpts != null
|
||||
? config.parserPreset.parserOpts
|
||||
: {},
|
||||
plugins: config.plugins != null ? config.plugins : {},
|
||||
ignores: config.ignores != null ? config.ignores : [],
|
||||
defaultIgnores:
|
||||
config.defaultIgnores != null ? config.defaultIgnores : true,
|
||||
}
|
||||
}
|
||||
|
||||
const showLintResults = async ([from, to]) => {
|
||||
const commits = await getHistoryCommits(from, to)
|
||||
const config = existsSync(configPath)
|
||||
? await load({}, { file: configPath })
|
||||
: {}
|
||||
const opts = getOptsFromConfig(config)
|
||||
const results = await Promise.all(
|
||||
commits.map(commit => lint(commit, config.rules, opts)),
|
||||
)
|
||||
const formattedResults = format(
|
||||
{ results },
|
||||
{
|
||||
color: true,
|
||||
helpUrl:
|
||||
'https://github.com/conventional-changelog/commitlint/#what-is-commitlint',
|
||||
},
|
||||
)
|
||||
|
||||
if (formattedResults) {
|
||||
core.setFailed(
|
||||
`You have commit messages with errors\n\n${formattedResults}`,
|
||||
)
|
||||
} else {
|
||||
console.log('Lint free! 🎉')
|
||||
}
|
||||
}
|
||||
|
||||
const exitWithMessage = message => error => {
|
||||
core.setFailed(`${message}\n${error.message}\n${error.stack}`)
|
||||
}
|
||||
|
||||
const main = () =>
|
||||
getRangeForEvent()
|
||||
.catch(
|
||||
exitWithMessage("error trying to get list of pull request's commits"),
|
||||
)
|
||||
.then(showLintResults)
|
||||
.catch(exitWithMessage('error running commitlint'))
|
||||
|
||||
main()
|
||||
action()
|
||||
|
|
|
|||
1
scripts/release.sh
Normal file
1
scripts/release.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
image=$()
|
||||
74
testUtils.js
Normal file
74
testUtils.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { promisify } = require('util')
|
||||
const execa = require('execa')
|
||||
const td = require('testdouble')
|
||||
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
|
||||
const updateEnvVars = (exports.updateEnvVars = envVars => {
|
||||
Object.keys(envVars).forEach(key => {
|
||||
process.env[key] = envVars[key]
|
||||
})
|
||||
})
|
||||
|
||||
exports.gitEmptyCommit = (cwd, message) =>
|
||||
execa('git', ['commit', '--allow-empty', '-m', message], { cwd })
|
||||
|
||||
exports.getCommitHashes = async cwd => {
|
||||
const { stdout } = await execa.command('git log --pretty=%H', { cwd })
|
||||
const hashes = stdout.split('\n').reverse()
|
||||
|
||||
return hashes
|
||||
}
|
||||
|
||||
exports.updatePushEnvVars = (cwd, to) => {
|
||||
updateEnvVars({
|
||||
GITHUB_WORKSPACE: cwd,
|
||||
GITHUB_EVENT_NAME: 'push',
|
||||
GITHUB_SHA: to,
|
||||
})
|
||||
}
|
||||
|
||||
exports.createPushEventPayload = async (
|
||||
cwd,
|
||||
{ before = null, to, forced = false },
|
||||
) => {
|
||||
const payload = {
|
||||
after: to,
|
||||
before,
|
||||
forced,
|
||||
}
|
||||
const eventPath = path.join(cwd, 'pushEventPayload.json')
|
||||
|
||||
updateEnvVars({ GITHUB_EVENT_PATH: eventPath })
|
||||
await writeFile(eventPath, JSON.stringify(payload), 'utf8')
|
||||
}
|
||||
|
||||
exports.createPullRequestEventPayload = async cwd => {
|
||||
const payload = {
|
||||
number: '1',
|
||||
repository: {
|
||||
owner: {
|
||||
login: 'wagoid',
|
||||
},
|
||||
name: 'commitlint-github-action',
|
||||
},
|
||||
}
|
||||
|
||||
const eventPath = path.join(cwd, 'pullRequestEventPayload.json')
|
||||
|
||||
updateEnvVars({
|
||||
GITHUB_EVENT_PATH: eventPath,
|
||||
GITHUB_REPOSITORY: 'wagoid/commitlint-github-action',
|
||||
})
|
||||
await writeFile(eventPath, JSON.stringify(payload), 'utf8')
|
||||
}
|
||||
|
||||
exports.updatePullRequestEnvVars = (cwd, to) => {
|
||||
updateEnvVars({
|
||||
GITHUB_WORKSPACE: cwd,
|
||||
GITHUB_EVENT_NAME: 'pull_request',
|
||||
GITHUB_SHA: to,
|
||||
})
|
||||
}
|
||||
Loading…
Reference in a new issue