5
0
Fork 0
mirror of https://github.com/wagoid/commitlint-github-action.git synced 2025-11-07 16:06:56 +00:00

Merge pull request #41 from wagoid/feat/add-outputs-to-the-action

feat: add `results` output to the action
This commit is contained in:
Wagner Santos 2020-08-02 07:00:37 -03:00 committed by GitHub
commit 6c53f9a94c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 398 additions and 176 deletions

View file

@ -6,5 +6,5 @@ action.yml
CHANGELOG.md
coverage
fixtures
action.test.js
testUtils.js
src/action.test.js
src/testUtils.js

View file

@ -13,6 +13,10 @@ jobs:
- run: sed -i -E "s/([']docker:.+)/Dockerfile/" ./action.yml
- run: echo -n '' > .dockerignore
- uses: ./
id: run_commitlint
- name: Show results from JSON output
if: ${{ always() }}
run: echo ${{ toJSON(steps.run_commitlint.outputs.results) }}
commitlint-with-yml-file:
runs-on: ubuntu-latest
env:

View file

@ -54,6 +54,50 @@ Link to a page explaining your commit message convention.
default: `https://github.com/conventional-changelog/commitlint/#what-is-commitlint`
## Outputs
### `results`
The error and warning messages for each one of the analyzed commits. This is useful if you want to use the commitlint results in a JSON format in other jobs. See [the documentation](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#fromjson) on how to read JSON information from outputs.
Below you can see an example text output together with its corresponding JSON output:
```
You have commit messages with errors
⧗ input: wrong message
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]
✖ found 2 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
⧗ input: chore: my message
⚠ body must have leading blank line [body-leading-blank]
⚠ found 0 problems, 1 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
```
```JSON
[
{
"hash": "cb0f846f13b490c2fd17bd5ed0b6f65ba9b86c75",
"message": "wrong message",
"valid": false,
"errors": ["subject may not be empty", "type may not be empty"],
"warnings": [],
},
{
"hash": "cb14483cbde23b61322ffb8d3fcdc87f514a3141",
"message": "chore: my message\n\nsome context without leading blank line",
"valid": true,
"errors": [],
"warnings": ["body must have leading blank line"],
},
]
```
## About `extends` in your config file
This is a [`Docker` action](https://github.com/actions/toolkit/blob/e2adf403d6d14a9ca7474976ccaca20f72ff8209/docs/action-types.md#why-would-i-choose-a-docker-action), and was made like this so that you can run it with minimum setup, regardless of your repo's environment. It comes packed with the most famous shared configurations that you can use in your commitlint config's `extends` field:

View file

@ -18,6 +18,9 @@ inputs:
description: 'Link to a page explaining your commit message convention'
default: 'https://github.com/conventional-changelog/commitlint/#what-is-commitlint'
required: false
outputs:
results:
description: The error and warning messages for each one of the analyzed commits
runs:
using: 'docker'
image: 'docker://wagoid/commitlint-github-action:1.7.0'

118
package-lock.json generated
View file

@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"@actions/core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.1.1.tgz",
"integrity": "sha512-O5G6EmlzTVsng7VSpNtszIoQq6kOgMGNTFB/hmwKNNA4V71JyxImCIrL27vVHCt2Cb3ImkaCr6o27C2MV9Ylwg=="
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz",
"integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg=="
},
"@actions/github": {
"version": "1.1.0",
@ -1349,6 +1349,14 @@
"through2": "^2.0.0"
},
"dependencies": {
"dargs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
"integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"through2": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@ -3784,6 +3792,15 @@
"through2": "^2.0.0"
},
"dependencies": {
"dargs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
"integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"through2": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@ -4019,6 +4036,14 @@
"through2": "^2.0.0"
},
"dependencies": {
"dargs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
"integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@ -4151,10 +4176,9 @@
}
},
"cross-spawn": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
"integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
"dev": true,
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@ -4164,14 +4188,12 @@
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@ -4179,14 +4201,12 @@
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
@ -4222,12 +4242,9 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk="
},
"dargs": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
"integrity": "sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc=",
"requires": {
"number-is-nan": "^1.0.0"
}
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz",
"integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="
},
"dashdash": {
"version": "1.14.1",
@ -4600,10 +4617,9 @@
"dev": true
},
"execa": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz",
"integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==",
"dev": true,
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz",
"integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==",
"requires": {
"cross-spawn": "^7.0.0",
"get-stream": "^5.0.0",
@ -4612,7 +4628,6 @@
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.0",
"onetime": "^5.1.0",
"p-finally": "^2.0.0",
"signal-exit": "^3.0.2",
"strip-final-newline": "^2.0.0"
},
@ -4621,7 +4636,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
"integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
@ -4629,20 +4643,17 @@
"is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
"dev": true
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
},
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
},
"npm-run-path": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.0.tgz",
"integrity": "sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==",
"dev": true,
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
"requires": {
"path-key": "^3.0.0"
}
@ -4651,22 +4662,14 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
"integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
"dev": true,
"requires": {
"mimic-fn": "^2.1.0"
}
},
"p-finally": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
"integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
"dev": true
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
}
}
},
@ -5775,28 +5778,6 @@
"assert-plus": "^1.0.0"
}
},
"git-raw-commits": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.2.tgz",
"integrity": "sha512-HVvl6J3dx7CS9fWTtyZXA2ejhdq9p/GSU9EEVlJPb2pSgMuD7IWK3dERcUPsJj9SZrJJ6IIB+3Rsjx9FUDdE1Q==",
"requires": {
"dargs": "^4.0.1",
"lodash.template": "^4.0.2",
"meow": "^4.0.0",
"split2": "^2.0.0",
"through2": "^3.0.0"
},
"dependencies": {
"through2": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
"integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
"requires": {
"readable-stream": "2 || 3"
}
}
}
},
"git-remote-origin-url": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz",
@ -6066,8 +6047,7 @@
"human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
"dev": true
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
},
"humanize-ms": {
"version": "1.2.1",
@ -8178,8 +8158,7 @@
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"merge2": {
"version": "1.3.0",
@ -10356,8 +10335,7 @@
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"dev": true
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
},
"strip-indent": {
"version": "2.0.0",

View file

@ -15,8 +15,8 @@
"license": "ISC",
"homepage": "https://github.com/wagoid/commitlint-github-action",
"dependencies": {
"@actions/core": "1.1.1",
"@actions/github": "1.1.0",
"@actions/core": "^1.2.4",
"@actions/github": "^1.1.0",
"@commitlint/config-angular": "^8.3.4",
"@commitlint/config-conventional": "^8.3.4",
"@commitlint/config-lerna-scopes": "^8.3.4",
@ -24,21 +24,21 @@
"@commitlint/format": "^8.3.4",
"@commitlint/lint": "^8.3.5",
"@commitlint/load": "^8.3.5",
"commitlint-config-jira": "1.2.0",
"commitlint-plugin-jira-rules": "1.2.0",
"conventional-changelog-lint-config-canonical": "1.0.0",
"git-raw-commits": "2.0.2",
"lerna": "3.18.1"
"commitlint-config-jira": "^1.2.0",
"commitlint-plugin-jira-rules": "^1.2.0",
"conventional-changelog-lint-config-canonical": "^1.0.0",
"dargs": "^7.0.0",
"execa": "^4.0.3",
"lerna": "^3.18.1"
},
"devDependencies": {
"@commitlint/test": "^8.2.0",
"conventional-changelog-cli": "2.0.23",
"execa": "3.4.0",
"husky": "3.0.7",
"conventional-changelog-cli": "^2.0.23",
"husky": "^3.0.7",
"jest": "^24.9.0",
"prettier": "1.18.2",
"pretty-quick": "1.11.1",
"testdouble": "3.12.4"
"prettier": "^1.18.2",
"pretty-quick": "^1.11.1",
"testdouble": "^3.12.4"
},
"husky": {
"hooks": {

2
run.js
View file

@ -1,3 +1,3 @@
const action = require('./action')
const action = require('./src/action')
action()

View file

@ -5,7 +5,8 @@ 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 gitCommits = require('./gitCommits')
const generateOutputs = require('./generateOutputs')
const pullRequestEvent = 'pull_request'
@ -76,16 +77,7 @@ function getHistoryCommits(from, to) {
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)
})
})
return gitCommits(options)
}
function getOptsFromConfig(config) {
@ -101,26 +93,21 @@ function getOptsFromConfig(config) {
}
}
const formatErrors = results =>
const formatErrors = lintedCommits =>
format(
{ results },
{ results: lintedCommits.map(commit => commit.lintResult) },
{
color: true,
helpUrl: core.getInput('helpURL'),
},
)
const hasOnlyWarnings = results => {
const resultsWithOnlyWarnings = results.filter(
result => result.valid && result.warnings.length,
const hasOnlyWarnings = lintedCommits =>
lintedCommits.length &&
lintedCommits.every(
({ lintResult }) => lintResult.valid && lintResult.warnings.length,
)
return (
resultsWithOnlyWarnings.length &&
resultsWithOnlyWarnings.length === results.length
)
}
const setFailed = formattedResults => {
core.setFailed(`You have commit messages with errors\n\n${formattedResults}`)
}
@ -140,12 +127,17 @@ const showLintResults = async ([from, to]) => {
? await load({}, { file: configPath })
: {}
const opts = getOptsFromConfig(config)
const results = await Promise.all(
commits.map(commit => lint(commit, config.rules, opts)),
const lintedCommits = await Promise.all(
commits.map(async commit => ({
lintResult: await lint(commit.message, config.rules, opts),
hash: commit.hash,
})),
)
const formattedResults = formatErrors(results)
const formattedResults = formatErrors(lintedCommits)
if (hasOnlyWarnings(results)) {
generateOutputs(lintedCommits)
if (hasOnlyWarnings(lintedCommits)) {
handleOnlyWarnings(formattedResults)
} else if (formattedResults) {
setFailed(formattedResults)

View file

@ -11,6 +11,8 @@ const {
updatePullRequestEnvVars,
} = require('./testUtils')
const resultsOutputId = 'results'
const {
matchers: { contains },
} = td
@ -43,6 +45,7 @@ describe('Commit Linter action', () => {
core = require('@actions/core')
td.replace(core, 'getInput')
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')
@ -70,21 +73,6 @@ describe('Commit Linter action', () => {
td.verify(core.setFailed(contains('You have commit messages with errors')))
})
it('should pass for single push with correct message', async () => {
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 () => {
cwd = await git.bootstrap('fixtures/conventional')
await gitEmptyCommit(cwd, 'message from before push')
@ -259,15 +247,19 @@ describe('Commit Linter action', () => {
td.verify(core.setFailed(contains('wrong commit from another branch')))
})
it('should lint all commits from a pull request', async () => {
describe('when there are multiple commits failing in the pull request', () => {
let expectedResultsOutput
const firstMessage = 'wrong message 1'
const secondMessage = '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, 'wrong message 1')
await gitEmptyCommit(cwd, 'wrong message 2')
await gitEmptyCommit(cwd, 'wrong message 3')
await gitEmptyCommit(cwd, firstMessage)
await gitEmptyCommit(cwd, secondMessage)
await createPullRequestEventPayload(cwd)
const [, first, second, to] = await getCommitHashes(cwd)
const [, first, to] = await getCommitHashes(cwd)
updatePullRequestEnvVars(cwd, to)
td.when(
listCommits({
@ -276,21 +268,57 @@ describe('Commit Linter action', () => {
pull_number: '1',
}),
).thenResolve({
data: [first, second, to].map(sha => ({ sha })),
data: [first, to].map(sha => ({ sha })),
})
td.replace(process, 'cwd', () => cwd)
expectedResultsOutput = [
{
hash: to,
message: secondMessage,
valid: false,
errors: ['subject may not be empty', 'type may not be empty'],
warnings: [],
},
{
hash: first,
message: firstMessage,
valid: false,
errors: ['subject may not be empty', 'type may not be empty'],
warnings: [],
},
]
})
it('should NOT show errors for a message from before the push', async () => {
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 () => {
it('should show errors for the first wrong message', async () => {
await runAction()
td.verify(core.setFailed(contains(firstMessage)))
})
it('should show errors for the second wrong message', async () => {
await runAction()
td.verify(core.setFailed(contains(secondMessage)))
})
it('should generate a JSON output of the errors', async () => {
await runAction()
td.verify(core.setOutput(resultsOutputId, expectedResultsOutput))
})
})
describe('when it fails to fetch commits', () => {
beforeEach(async () => {
cwd = await git.bootstrap('fixtures/conventional')
td.when(core.getInput('configFile')).thenReturn('./commitlint.config.js')
await gitEmptyCommit(cwd, 'commit message')
@ -305,7 +333,9 @@ describe('Commit Linter action', () => {
}),
).thenReject(new Error('HttpError: Bad credentials'))
td.replace(process, 'cwd', () => cwd)
})
it('should show an error message', async () => {
await runAction()
td.verify(
@ -313,10 +343,61 @@ describe('Commit Linter action', () => {
contains("error trying to get list of pull request's commits"),
),
)
})
it('should show the original error message', async () => {
await runAction()
td.verify(core.setFailed(contains('HttpError: Bad credentials')))
})
})
describe("when there's a single commit with correct message", () => {
let commitHash
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)
td.replace(process, 'cwd', () => cwd)
td.replace(console, 'log')
})
it('should pass', async () => {
await runAction()
td.verify(core.setFailed(), { times: 0, ignoreExtraArgs: true })
})
it('should show success message', async () => {
await runAction()
td.verify(console.log('Lint free! 🎉'))
})
it('should generate a JSON output of the messages', async () => {
const expectedResultsOutput = [
{
hash: commitHash,
message: 'chore: correct message',
valid: true,
errors: [],
warnings: [],
},
]
await runAction()
td.verify(core.setOutput(resultsOutputId, expectedResultsOutput))
})
})
describe('when all errors are just warnings', () => {
let expectedResultsOutput
beforeEach(async () => {
cwd = await git.bootstrap('fixtures/conventional')
await gitEmptyCommit(
@ -328,6 +409,17 @@ describe('Commit Linter action', () => {
updatePushEnvVars(cwd, to)
td.replace(process, 'cwd', () => cwd)
td.replace(console, 'log')
expectedResultsOutput = [
{
hash: to,
message:
'chore: correct message\n\nsome context without leading blank line',
valid: true,
errors: [],
warnings: ['body must have leading blank line'],
},
]
})
it('should pass and show that warnings exist', async () => {
@ -337,6 +429,12 @@ describe('Commit Linter action', () => {
td.verify(console.log(contains('You have commit messages with warnings')))
})
it('should show the results in an output', async () => {
await runAction()
td.verify(core.setOutput(resultsOutputId, expectedResultsOutput))
})
describe('and failOnWarnings is set to true', () => {
beforeEach(() => {
td.when(core.getInput('failOnWarnings')).thenReturn('true')
@ -349,18 +447,30 @@ describe('Commit Linter action', () => {
core.setFailed(contains('You have commit messages with errors')),
)
})
it('should show the results in an output', async () => {
await runAction()
td.verify(core.setOutput(resultsOutputId, expectedResultsOutput))
})
})
})
describe('when a subset of errors are just warnings', () => {
let firstHash
let secondHash
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, to] = await getCommitHashes(cwd)
const [before, firstCommit, to] = await getCommitHashes(cwd)
firstHash = firstCommit
secondHash = to
await createPushEventPayload(cwd, { before, to })
updatePushEnvVars(cwd, to)
td.replace(process, 'cwd', () => cwd)
@ -375,6 +485,30 @@ describe('Commit Linter action', () => {
)
})
it('should show the results in an output', async () => {
const expectedResultsOutput = [
{
hash: secondHash,
message: 'wrong message',
valid: false,
errors: ['subject may not be empty', 'type may not be empty'],
warnings: [],
},
{
hash: firstHash,
message:
'chore: correct message\n\nsome context without leading blank line',
valid: true,
errors: [],
warnings: ['body must have leading blank line'],
},
]
await runAction()
td.verify(core.setOutput(resultsOutputId, expectedResultsOutput))
})
describe('and failOnWarnings is set to true', () => {
beforeEach(() => {
td.when(core.getInput('failOnWarnings')).thenReturn('true')

24
src/generateOutputs.js Normal file
View file

@ -0,0 +1,24 @@
const core = require('@actions/core')
const resultsOutputId = 'results'
const mapMessageValidation = item => item.message
const mapResultOutput = ({
hash,
lintResult: { valid, errors, warnings, input },
}) => ({
hash,
message: input,
valid,
errors: errors.map(mapMessageValidation),
warnings: warnings.map(mapMessageValidation),
})
const generateOutputs = lintedCommits => {
const resultsOutput = lintedCommits.map(mapResultOutput)
core.setOutput(resultsOutputId, resultsOutput)
}
module.exports = generateOutputs

43
src/gitCommits.js Normal file
View file

@ -0,0 +1,43 @@
const dargs = require('dargs')
const execa = require('execa')
const commitDelimiter = '--------->commit---------'
const hashDelimiter = '--------->hash---------'
const format = `%H${hashDelimiter}%B%n${commitDelimiter}`
const buildGitArgs = gitOpts => {
const { from, to, ...otherOpts } = gitOpts
var formatArg = `--format=${format}`
var fromToArg = [from, to].filter(Boolean).join('..')
var gitArgs = ['log', formatArg, fromToArg]
return gitArgs.concat(
dargs(gitOpts, {
includes: Object.keys(otherOpts),
}),
)
}
const gitCommits = async gitOpts => {
var args = buildGitArgs(gitOpts)
var { 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,
}
})
return commits
}
module.exports = gitCommits