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

chore: obtain git hash too in the list of linted commits

This commit is contained in:
Wagner Santos 2020-07-20 08:26:28 -03:00
parent 5c56dec16a
commit 4b0886537a
4 changed files with 116 additions and 106 deletions

View file

@ -5,7 +5,7 @@ const github = require('@actions/github')
const lint = require('@commitlint/lint') const lint = require('@commitlint/lint')
const { format } = require('@commitlint/format') const { format } = require('@commitlint/format')
const load = require('@commitlint/load') const load = require('@commitlint/load')
const gitRawCommits = require('git-raw-commits') const gitCommits = require('./gitCommits')
const pullRequestEvent = 'pull_request' const pullRequestEvent = 'pull_request'
@ -76,16 +76,7 @@ function getHistoryCommits(from, to) {
options.maxCount = 1 options.maxCount = 1
} }
return new Promise((resolve, reject) => { return gitCommits(options)
const data = []
gitRawCommits(options)
.on('data', chunk => data.push(chunk.toString('utf-8')))
.on('error', reject)
.on('end', () => {
resolve(data)
})
})
} }
function getOptsFromConfig(config) { function getOptsFromConfig(config) {
@ -101,26 +92,21 @@ function getOptsFromConfig(config) {
} }
} }
const formatErrors = results => const formatErrors = lintedCommits =>
format( format(
{ results }, { results: lintedCommits.map(commit => commit.lintResult) },
{ {
color: true, color: true,
helpUrl: core.getInput('helpURL'), helpUrl: core.getInput('helpURL'),
}, },
) )
const hasOnlyWarnings = results => { const hasOnlyWarnings = lintedCommits =>
const resultsWithOnlyWarnings = results.filter( lintedCommits.length &&
result => result.valid && result.warnings.length, lintedCommits.every(
({ lintResult }) => lintResult.valid && lintResult.warnings.length,
) )
return (
resultsWithOnlyWarnings.length &&
resultsWithOnlyWarnings.length === results.length
)
}
const setFailed = formattedResults => { const setFailed = formattedResults => {
core.setFailed(`You have commit messages with errors\n\n${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 }) ? await load({}, { file: configPath })
: {} : {}
const opts = getOptsFromConfig(config) const opts = getOptsFromConfig(config)
const results = await Promise.all( const lintedCommits = await Promise.all(
commits.map(commit => lint(commit, config.rules, opts)), 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) handleOnlyWarnings(formattedResults)
} else if (formattedResults) { } else if (formattedResults) {
setFailed(formattedResults) setFailed(formattedResults)

43
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

112
package-lock.json generated
View file

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

View file

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