From bdc7c0fa39dda964430524eefedebb37c360920e Mon Sep 17 00:00:00 2001 From: Paulo Castro Date: Mon, 17 Feb 2020 14:31:08 +0000 Subject: [PATCH 1/2] Fix 'test:fast' npm script definition Change-type: patch --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9b65698a..1beec46f 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "release": "ts-node --transpile-only -P automation/tsconfig.json automation/run.ts release", "pretest": "npm run build", "test": "mocha --timeout 6000 -r ts-node/register/transpile-only \"tests/**/*.spec.ts\"", - "test:fast": "npm run build:fast && npm run test", + "test:fast": "npm run build:fast && mocha --timeout 6000 -r ts-node/register/transpile-only \"tests/**/*.spec.ts\"", "catch-uncommitted": "ts-node --transpile-only -P automation/tsconfig.json automation/run.ts catch-uncommitted", "ci": "npm run test && npm run catch-uncommitted", "watch": "gulp watch", From 03053e125f0a75e25b50a8bd0a5ecfe82efc3c0b Mon Sep 17 00:00:00 2001 From: Paulo Castro Date: Mon, 17 Feb 2020 16:56:52 +0000 Subject: [PATCH 2/2] 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 --- automation/check-doc.js | 93 +++++++++++++++++++++++++++++++++++++++++ npm-shrinkwrap.json | 15 +++++-- package.json | 5 ++- 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 automation/check-doc.js diff --git a/automation/check-doc.js b/automation/check-doc.js new file mode 100644 index 00000000..276b55b0 --- /dev/null +++ b/automation/check-doc.js @@ -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(); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 9a363bc2..765612f9 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -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", diff --git a/package.json b/package.json index 1beec46f..8233db2f 100644 --- a/package.json +++ b/package.json @@ -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"