Browse Source

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
Hugo Häggmark 6 years ago
parent
commit
b1a0bd115c

+ 1 - 1
.circleci/config.yml

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

+ 3 - 0
package.json

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

+ 78 - 0
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<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
+);

+ 23 - 11
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<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');
   }