#!/usr/bin/env node /** * Run this script (from root directory): npx babel-node scripts/checksum * * This will check root `package.json` matches each exercise's `package.json`. * But the catch is there are some dependencies used for build but not served to end users * We skip those dependencies while performing checksum. * See `SKIP_PACKAGES_FOR_CHECKSUM` in helpers.js for list of skipped packages. */ const path = require('path'); const shell = require('shelljs'); const helpers = require('./helpers'); /** * Compares the file of the assignment with some known value * * @param filename file to check (local to the assignment directory) * @param assignment slug with type, eg. practice/two-fer * @param baseFile the file path that {filename} must be compared against * @param expectedSha known value of {baseFile} */ function checksumAssignment(assignment, filename, baseFile, expectedSha) { if (!assignment) { return; } const filePath = path.join('exercises', assignment, filename); let fileSha; if (filename == 'package.json') { const packageJson = helpers.prepareExercisePackageJson(filePath, false); fileSha = helpers.shaPackageJson(packageJson); } else { const fileContents = shell.cat(filePath).toString(); fileSha = helpers.sha(fileContents); } if (fileSha !== expectedSha) { const chalk = require('chalk'); const diff = require('diff'); // prettier-ignore shell.echo( `[Failure] ${filename} did not match for ${assignment} (${chalk.red(expectedSha)} != ${chalk.green(fileSha)})\n`, `! Expected ${chalk.red(baseFile)} to match ${chalk.green(filePath)}\n`, `! Did you forget to run ${chalk.bold(`npx babel-node scripts/sync`)}?\n` ); if (chalk.supportsColor) { const diffParts = diff.diffLines( shell.cat(filePath).toString(), shell.cat(baseFile).toString(), { newlineIsToken: false } ); const output = diffParts .map((part) => { const color = part.added ? chalk.green : part.removed ? chalk.red : chalk.gray; return color(part.value); }) .join(''); shell.echo(output); } shell.exit(1); } } /** * Check all the exercises, or given, {filename} against {rootFileName} * * @param filename filename in the exercise directory * @param rootFileName filename in the root directory */ function checksumAll(filename, rootFileName = filename) { const assignments = [shell.env['ASSIGNMENT']].filter(Boolean); let expectedSha; if (rootFileName == 'exercise-package.json') { expectedSha = shell.cat('exercise-package.json.sha').toString(); } else { const fileContents = shell.cat(rootFileName).toString(); expectedSha = helpers.sha(fileContents); } if (assignments.length > 0) { if ( !assignments.every((assignment) => helpers.assertAssignment(assignment)) ) { shell.exit(); } return assignments.every((assignment) => { shell.echo(`Checking integrity of ${assignment}/${filename}...`); return checksumAssignment( assignment, filename, rootFileName, expectedSha ); }); } shell.echo( `Checking integrity of ${filename} in all ${helpers.assignments.length} exercises` ); helpers.assignments.forEach((assignment) => checksumAssignment(assignment, filename, rootFileName, expectedSha) ); } helpers.registerExitHandler(); helpers.createExercisePackageJson(true); checksumAll('package.json', 'exercise-package.json'); ['.eslintrc', '.npmrc', 'babel.config.js', 'LICENSE'].forEach((fileToCheck) => { checksumAll(fileToCheck); }); shell.echo('All files passed the checksum test');