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
This commit is contained in:
Hugo Häggmark 2019-07-05 10:46:25 +02:00 committed by Torkel Ödegaard
parent 315177fad7
commit b1a0bd115c
4 changed files with 105 additions and 12 deletions

View File

@ -598,7 +598,7 @@ jobs:
store-build-artifacts:
docker:
- image: circleci/node:8
- image: circleci/node:10
steps:
- attach_workspace:
at: .

View File

@ -255,5 +255,8 @@
},
"_moduleAliases": {
"puppeteer": "node_modules/puppeteer-core"
},
"engines": {
"node": ">=10 <11"
}
}

View File

@ -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<NodeVersionCheckerOptions> = 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<NodeVersionCheckerOptions>(
'Node Version Checker',
nodeVersionCheckerRunner
);

View File

@ -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<PrecommitOptions> = 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');
}