Add pre-commit check for cli.markdown updates and coffeelint execution

These checks compare the timestamps of cli.markdown with those of staged files,
effectively enforcing that 'npm run build' or 'npm test' are executed.

Change-type: patch
This commit is contained in:
Paulo Castro 2020-02-17 16:56:52 +00:00
parent bdc7c0fa39
commit 03053e125f
3 changed files with 108 additions and 5 deletions

93
automation/check-doc.js Normal file
View File

@ -0,0 +1,93 @@
/**
* @license
* Copyright 2020 Balena Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { stripIndent } = require('common-tags');
const _ = require('lodash');
const { fs } = require('mz');
const path = require('path');
const simplegit = require('simple-git/promise');
const ROOT = path.normalize(path.join(__dirname, '..'));
/**
* Compare the timestamp of cli.markdown with the timestamp of staged files,
* issuing an error if cli.markdown is older. Besides the purpose of ensuring
* that cli.markdown is updated, it effectively also ensures that coffeelint
* is executed (via `npm run build` or `npm test`) on the developers laptop,
* so that there is at least a chance that the developer will spot any linter
* warnings (that could reveal bugs) sooner than later. (The CI does not
* currently fail in case of coffeelint warnings.)
* If cli.markdown does not require updating and the developer cannot run
* `npm run build` on their laptop, the error message suggests a workaround
* using `touch`.
*/
async function checkBuildTimestamps() {
const git = simplegit(ROOT);
const docFile = path.join(ROOT, 'doc', 'cli.markdown');
const [docStat, gitStatus] = await Promise.all([
fs.stat(docFile),
git.status(),
]);
const stagedFiles = _.uniq([
...gitStatus.created,
...gitStatus.staged,
...gitStatus.renamed.map(o => o.to),
])
// select only staged files that start with lib/ or typings/
.filter(f => f.match(/^(lib|typings)[/\\]/))
.map(f => path.join(ROOT, f));
const fStats = await Promise.all(stagedFiles.map(f => fs.stat(f)));
fStats.forEach((fStat, index) => {
if (fStat.mtimeMs > docStat.mtimeMs) {
const fPath = stagedFiles[index];
throw new Error(stripIndent`
--------------------------------------------------------------------------------
ERROR: at least one staged file: "${fPath}"
has a more recent modification timestamp than the documentation file:
"${docFile}"
This probably means that \`npm run build\` or \`npm test\` have not been executed,
and this error can be fixed by doing so. Running \`npm run build\` or \`npm test\`
before commiting is currently a requirement (documented in the CONTRIBUTING.md
file) for three reasons:
1. To update the CLI markdown documentation (in case any command-line options
were updated, added or removed).
2. To reveal coffeelint warnings that the CI currently ignores (in case any
Coffeescript files were modified).
3. To catch Typescript type check errors sooner and reduce overall waiting time,
given that balena-cli CI builds/tests are currently rather lengthy.
If you need/wish to bypass this check without running \`npm run build\`, run:
npx touch -am "${docFile}"
and then try again.
--------------------------------------------------------------------------------
`);
}
});
}
async function run() {
try {
await checkBuildTimestamps();
} catch (err) {
console.error(err.message);
process.exitCode = 1;
}
}
run();

15
npm-shrinkwrap.json generated
View File

@ -7233,9 +7233,9 @@
"integrity": "sha1-GZT/rs3+nEQe0r2sdFK3u0yeQaQ="
},
"husky": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.2.1.tgz",
"integrity": "sha512-Qa0lRreeIf4Tl92sSs42ER6qc3hzoyQPPorzOrFWfPEVbdi6LuvJEqWKPk905fOWIR76iBpp7ECZNIwk+a8xuQ==",
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-4.2.3.tgz",
"integrity": "sha512-VxTsSTRwYveKXN4SaH1/FefRJYCtx+wx04sSVcOpD7N2zjoHxa+cEJ07Qg5NmV3HAK+IRKOyNVpi2YBIVccIfQ==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
@ -16536,6 +16536,15 @@
"simple-concat": "^1.0.0"
}
},
"simple-git": {
"version": "1.131.0",
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.131.0.tgz",
"integrity": "sha512-z/art7YYtmPnnLItT/j+nKwJt6ap6nHZ4D8sYo9PdCKK/ug56SN6m/evfxJk7uDV3e9JuCa8qIyDU2P3cxmiNQ==",
"dev": true,
"requires": {
"debug": "^4.0.1"
}
},
"single-line-log": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz",

View File

@ -77,7 +77,7 @@
},
"husky": {
"hooks": {
"pre-commit": "node automation/check-npm-version.js"
"pre-commit": "node automation/check-npm-version.js && node automation/check-doc.js"
}
},
"oclif": {
@ -139,7 +139,7 @@
"gulp-coffee": "^2.2.0",
"gulp-inline-source": "^2.1.0",
"gulp-shell": "^0.5.2",
"husky": "^4.2.1",
"husky": "^4.2.3",
"intercept-stdout": "^0.1.2",
"mocha": "^6.2.2",
"nock": "^11.7.2",
@ -149,6 +149,7 @@
"publish-release": "^1.6.1",
"resin-lint": "^3.2.4",
"rewire": "^3.0.2",
"simple-git": "^1.131.0",
"sinon": "^7.5.0",
"ts-node": "^8.6.2",
"typescript": "^3.7.5"