From b1a0bd115c2e1ba970f045931446d39e309e3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 5 Jul 2019 10:46:25 +0200 Subject: [PATCH] Build: Adds pre-commit check that fails if node versions are not synced (#17820) * Build: Adds pre-commit check that fails if node versions are not synced * Build: Throws error instead * Minor refactoring and testing --- .circleci/config.yml | 2 +- package.json | 3 + .../src/cli/tasks/nodeVersionChecker.ts | 78 +++++++++++++++++++ .../src/cli/tasks/precommit.ts | 34 +++++--- 4 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b71800a2e5..2138ce63dcd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -598,7 +598,7 @@ jobs: store-build-artifacts: docker: - - image: circleci/node:8 + - image: circleci/node:10 steps: - attach_workspace: at: . diff --git a/package.json b/package.json index fb0b5330ba9..0f196c33afd 100644 --- a/package.json +++ b/package.json @@ -255,5 +255,8 @@ }, "_moduleAliases": { "puppeteer": "node_modules/puppeteer-core" + }, + "engines": { + "node": ">=10 <11" } } diff --git a/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts b/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts new file mode 100644 index 00000000000..6141c5b89e4 --- /dev/null +++ b/packages/grafana-toolkit/src/cli/tasks/nodeVersionChecker.ts @@ -0,0 +1,78 @@ +import { Task, TaskRunner } from './task'; +import chalk from 'chalk'; +import { coerce, satisfies } from 'semver'; +import { readFileSync } from 'fs'; + +interface FailedVersionCheck { + file: string; + line: string; +} + +interface NodeVersionCheckerOptions {} + +const pattern = /(circleci\/|FROM )node\:([0-9]+(\.[0-9]+){0,2})/gm; +const packageJsonFile = 'package.json'; + +const failures: FailedVersionCheck[] = []; + +export const nodeVersionFiles = [packageJsonFile, 'Dockerfile', '.circleci/config.yml']; + +const nodeVersionCheckerRunner: TaskRunner = async () => { + // Read version from package json and treat that as the expected version in all other locations + const packageJson = require(`${process.cwd()}/${packageJsonFile}`); + const expectedVersion = packageJson.engines.node; + + console.log(chalk.yellow(`Specified node version in package.json is: ${expectedVersion}`)); + + for (const file of nodeVersionFiles) { + const fileContent = readFileSync(`${process.cwd()}/${file}`); + const matches = fileContent.toString('utf8').match(pattern); + + if (!matches) { + continue; + } + + for (const match of matches) { + const actualVersion = coerce(match); + if (!actualVersion) { + failures.push({ + file, + line: match, + }); + continue; + } + + const satisfied = satisfies(actualVersion, expectedVersion); + if (!satisfied) { + failures.push({ + file, + line: match, + }); + } + } + } + + if (failures.length > 0) { + console.log(chalk.red('--------------------------------------------------------------------')); + console.log(chalk.red(`These entries don't satisfy the engine version in ${packageJsonFile}`)); + console.log(chalk.red('--------------------------------------------------------------------')); + + for (let index = 0; index < failures.length; index++) { + const failure = failures[index]; + console.log(chalk.green(`\tIn ${failure.file} the line ${failure.line} does not satisfy ${expectedVersion}.`)); + } + + throw new Error('Node versions not in sync'); + } + + console.log(chalk.yellow('--------------------------------------------------------------------')); + console.log(chalk.yellow('All node versions seem ok.')); + console.log(chalk.yellow("Don't forget to sync https://github.com/grafana/grafana-build-container")); + console.log(chalk.yellow(`also if you changed the engine version in ${packageJsonFile}`)); + console.log(chalk.yellow('--------------------------------------------------------------------')); +}; + +export const nodeVersionCheckerTask = new Task( + 'Node Version Checker', + nodeVersionCheckerRunner +); diff --git a/packages/grafana-toolkit/src/cli/tasks/precommit.ts b/packages/grafana-toolkit/src/cli/tasks/precommit.ts index 6431b486451..d97efc56541 100644 --- a/packages/grafana-toolkit/src/cli/tasks/precommit.ts +++ b/packages/grafana-toolkit/src/cli/tasks/precommit.ts @@ -5,6 +5,8 @@ import get from 'lodash/get'; // @ts-ignore import flatten from 'lodash/flatten'; import execa = require('execa'); +import { nodeVersionCheckerTask, nodeVersionFiles } from './nodeVersionChecker'; +import { execTask } from '../utils/execTask'; const simpleGit = require('simple-git/promise')(process.cwd()); interface PrecommitOptions {} @@ -27,27 +29,37 @@ const tasks = { }, }; +interface GitStatus { + files: GitFile[]; +} + +interface GitFile { + path: string; +} + const precommitRunner: TaskRunner = async () => { - const status = await simpleGit.status(); + const status: GitStatus = await simpleGit.status(); const sassFiles = status.files.filter( - (file: any) => - (file.path as string).match(/^[a-zA-Z0-9\_\-\/]+(\.scss)$/g) || file.path.indexOf('.sass-lint.yml') > -1 + file => file.path.match(/^[a-zA-Z0-9\_\-\/]+(\.scss)$/g) || file.path.indexOf('.sass-lint.yml') > -1 ); - const tsFiles = status.files.filter((file: any) => (file.path as string).match(/^[a-zA-Z0-9\_\-\/]+(\.(ts|tsx))$/g)); - const testFiles = status.files.filter((file: any) => - (file.path as string).match(/^[a-zA-Z0-9\_\-\/]+(\.test.(ts|tsx))$/g) - ); - const goTestFiles = status.files.filter((file: any) => - (file.path as string).match(/^[a-zA-Z0-9\_\-\/]+(\_test.go)$/g) - ); - const grafanaUiFiles = tsFiles.filter((file: any) => (file.path as string).indexOf('grafana-ui') > -1); + const tsFiles = status.files.filter(file => file.path.match(/^[a-zA-Z0-9\_\-\/]+(\.(ts|tsx))$/g)); + const testFiles = status.files.filter(file => file.path.match(/^[a-zA-Z0-9\_\-\/]+(\.test.(ts|tsx))$/g)); + const goTestFiles = status.files.filter(file => file.path.match(/^[a-zA-Z0-9\_\-\/]+(\_test.go)$/g)); + const grafanaUiFiles = tsFiles.filter(file => file.path.indexOf('grafana-ui') > -1); + const affectedNodeVersionFiles = status.files + .filter(file => nodeVersionFiles.indexOf(file.path) !== -1) + .map(f => f.path); const grafanaUIFilesChangedOnly = tsFiles.length > 0 && tsFiles.length - grafanaUiFiles.length === 0; const coreFilesChangedOnly = tsFiles.length > 0 && grafanaUiFiles.length === 0; const taskPaths = []; + if (affectedNodeVersionFiles.length > 0) { + await execTask(nodeVersionCheckerTask)({}); + } + if (sassFiles.length > 0) { taskPaths.push('lint.sass'); }