From 4b0886537aa9f1ca98fbb437ba19eb73422b6a7f Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Mon, 20 Jul 2020 08:26:28 -0300 Subject: [PATCH 1/6] chore: obtain git hash too in the list of linted commits --- action.js | 41 +++++++---------- gitCommits.js | 43 ++++++++++++++++++ package-lock.json | 112 +++++++++++++++++++--------------------------- package.json | 26 +++++------ 4 files changed, 116 insertions(+), 106 deletions(-) create mode 100644 gitCommits.js diff --git a/action.js b/action.js index d8fa98b..89351bb 100644 --- a/action.js +++ b/action.js @@ -5,7 +5,7 @@ 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 pullRequestEvent = 'pull_request' @@ -76,16 +76,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 +92,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 +126,15 @@ 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)) { + if (hasOnlyWarnings(lintedCommits)) { handleOnlyWarnings(formattedResults) } else if (formattedResults) { setFailed(formattedResults) diff --git a/gitCommits.js b/gitCommits.js new file mode 100644 index 0000000..694da16 --- /dev/null +++ b/gitCommits.js @@ -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 diff --git a/package-lock.json b/package-lock.json index d9b2036..7fd8a6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b19cec8..dbaba1c 100644 --- a/package.json +++ b/package.json @@ -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.1.1", + "@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": { From 550792f0ca7bb2cb7e9b15afee32ffead2b237e5 Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Mon, 20 Jul 2020 08:45:05 -0300 Subject: [PATCH 2/6] feat: add `results` output Resolves #39 --- README.md | 44 ++++++++++++++++++++++++++++++++++ action.js | 3 +++ action.test.js | 60 +++++++++++++++++++++++++++++++++++++++++++++- action.yml | 3 +++ generateOutputs.js | 24 +++++++++++++++++++ package-lock.json | 6 ++--- package.json | 2 +- 7 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 generateOutputs.js diff --git a/README.md b/README.md index 35d902e..2aace07 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/action.js b/action.js index 89351bb..d103d49 100644 --- a/action.js +++ b/action.js @@ -6,6 +6,7 @@ const lint = require('@commitlint/lint') const { format } = require('@commitlint/format') const load = require('@commitlint/load') const gitCommits = require('./gitCommits') +const generateOutputs = require('./generateOutputs') const pullRequestEvent = 'pull_request' @@ -134,6 +135,8 @@ const showLintResults = async ([from, to]) => { ) const formattedResults = formatErrors(lintedCommits) + generateOutputs(lintedCommits) + if (hasOnlyWarnings(lintedCommits)) { handleOnlyWarnings(formattedResults) } else if (formattedResults) { diff --git a/action.test.js b/action.test.js index e9fceae..fae0e1a 100644 --- a/action.test.js +++ b/action.test.js @@ -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') @@ -317,6 +320,8 @@ describe('Commit Linter action', () => { }) describe('when all errors are just warnings', () => { + let expectedResultsOutput + beforeEach(async () => { cwd = await git.bootstrap('fixtures/conventional') await gitEmptyCommit( @@ -328,6 +333,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 +353,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 +371,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 +409,30 @@ describe('Commit Linter action', () => { ) }) + it('should show the results in an output', async () => { + await runAction() + + 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'], + }, + ] + + td.verify(core.setOutput(resultsOutputId, expectedResultsOutput)) + }) + describe('and failOnWarnings is set to true', () => { beforeEach(() => { td.when(core.getInput('failOnWarnings')).thenReturn('true') diff --git a/action.yml b/action.yml index e717ac6..1db01b5 100644 --- a/action.yml +++ b/action.yml @@ -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' diff --git a/generateOutputs.js b/generateOutputs.js new file mode 100644 index 0000000..9fc8f0a --- /dev/null +++ b/generateOutputs.js @@ -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 diff --git a/package-lock.json b/package-lock.json index 7fd8a6a..51a897d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index dbaba1c..2be406c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "license": "ISC", "homepage": "https://github.com/wagoid/commitlint-github-action", "dependencies": { - "@actions/core": "^1.1.1", + "@actions/core": "^1.2.4", "@actions/github": "^1.1.0", "@commitlint/config-angular": "^8.3.4", "@commitlint/config-conventional": "^8.3.4", From 25a8edceb7df552bca5fe5d355177825b0ee463b Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Tue, 21 Jul 2020 05:48:25 -0300 Subject: [PATCH 3/6] refactor: move action files to src folder --- .dockerignore | 4 ++-- run.js | 2 +- action.js => src/action.js | 0 action.test.js => src/action.test.js | 0 generateOutputs.js => src/generateOutputs.js | 0 gitCommits.js => src/gitCommits.js | 0 testUtils.js => src/testUtils.js | 0 7 files changed, 3 insertions(+), 3 deletions(-) rename action.js => src/action.js (100%) rename action.test.js => src/action.test.js (100%) rename generateOutputs.js => src/generateOutputs.js (100%) rename gitCommits.js => src/gitCommits.js (100%) rename testUtils.js => src/testUtils.js (100%) diff --git a/.dockerignore b/.dockerignore index 241304b..be870c0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,5 +6,5 @@ action.yml CHANGELOG.md coverage fixtures -action.test.js -testUtils.js +src/action.test.js +src/testUtils.js diff --git a/run.js b/run.js index 275a984..cad4224 100644 --- a/run.js +++ b/run.js @@ -1,3 +1,3 @@ -const action = require('./action') +const action = require('./src/action') action() diff --git a/action.js b/src/action.js similarity index 100% rename from action.js rename to src/action.js diff --git a/action.test.js b/src/action.test.js similarity index 100% rename from action.test.js rename to src/action.test.js diff --git a/generateOutputs.js b/src/generateOutputs.js similarity index 100% rename from generateOutputs.js rename to src/generateOutputs.js diff --git a/gitCommits.js b/src/gitCommits.js similarity index 100% rename from gitCommits.js rename to src/gitCommits.js diff --git a/testUtils.js b/src/testUtils.js similarity index 100% rename from testUtils.js rename to src/testUtils.js From 8d360d2e46637d8aa54d7df2d56bc19d71808921 Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Tue, 21 Jul 2020 05:52:53 -0300 Subject: [PATCH 4/6] ci: add example that shows json results --- .github/workflows/commitlint.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index a96dbcc..2f4814c 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -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: From 231d02e17f49d967361f4739879d9a6dfc3e2040 Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Tue, 21 Jul 2020 06:05:43 -0300 Subject: [PATCH 5/6] test: add test to expect an output on success scenario too --- src/action.test.js | 62 +++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/action.test.js b/src/action.test.js index fae0e1a..567b665 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -73,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') @@ -319,6 +304,49 @@ describe('Commit Linter action', () => { 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 @@ -410,8 +438,6 @@ describe('Commit Linter action', () => { }) it('should show the results in an output', async () => { - await runAction() - const expectedResultsOutput = [ { hash: secondHash, @@ -430,6 +456,8 @@ describe('Commit Linter action', () => { }, ] + await runAction() + td.verify(core.setOutput(resultsOutputId, expectedResultsOutput)) }) From 0c7da1b0a2a3d0b58eba294cdcffbb26511243da Mon Sep 17 00:00:00 2001 From: Wagner Santos Date: Sat, 1 Aug 2020 11:27:40 -0300 Subject: [PATCH 6/6] test: add output tests for PR scenario --- src/action.test.js | 144 ++++++++++++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 48 deletions(-) diff --git a/src/action.test.js b/src/action.test.js index 567b665..a83e036 100644 --- a/src/action.test.js +++ b/src/action.test.js @@ -247,61 +247,109 @@ describe('Commit Linter action', () => { td.verify(core.setFailed(contains('wrong commit from another branch'))) }) - it('should lint all commits from a pull request', 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 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) + describe('when there are multiple commits failing in the pull request', () => { + let expectedResultsOutput + const firstMessage = 'wrong message 1' + const secondMessage = 'wrong message 2' - await runAction() + 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) + td.when( + listCommits({ + owner: 'wagoid', + repo: 'commitlint-github-action', + pull_number: '1', + }), + ).thenResolve({ + data: [first, to].map(sha => ({ sha })), + }) + td.replace(process, 'cwd', () => cwd) - td.verify(core.setFailed(contains('message from before push')), { - times: 0, + 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, + }) + }) + + 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)) }) - 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 () => { - 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) + 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') + 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() + it('should show an error message', async () => { + 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'))) + td.verify( + core.setFailed( + 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", () => {