import { spawn } from 'child_process'; import * as path from 'path'; import * as fs from 'fs'; const PROJECT_BASE = path.resolve('./'); const ORACLE_BRANCH = 'develop'; const GET_PACKAGE_CMD = `git show ${ORACLE_BRANCH}:package.json`; const GET_DIFF_CMD = `git diff origin/${ORACLE_BRANCH}`; const newFileRegEx = /^\+\+\+ b\//; const frozenFolderRegEx = /\/\*$/; const start = async () => { try { const packageStr = await runShCommand(GET_PACKAGE_CMD); const diff = await runShCommand(GET_DIFF_CMD); const { frozen } = JSON.parse(packageStr); if (frozen === undefined) { console.log(`Freezer: No config found in package.json on branch ${ORACLE_BRANCH}. Exiting.`); return; } const newFiles = getNewFiles(diff); const frozenFiles = getFrozenFiles(frozen); const frozenFolders = getFrozenFolders(frozen); ensureNewFilesAreNotFrozen(newFiles, frozenFiles, frozenFolders); } catch (err) { console.log(err.message); exit(); } }; const ensureNewFilesAreNotFrozen = ( newFiles: string[], frozenFiles: string[], frozenFolders: string[] ): void => { const errors = newFiles .map(file => { if (frozenFiles.indexOf(file) !== -1) { return `"${file}" is frozen`; } if (isFileInFrozenFolders(file, frozenFolders)) { return `"${file}" is in a frozen folder`; } }) .filter(err => err); if (errors.length) { throw new Error(`Frozen files have been modified:\n${errors.join('\n')}`); } else { console.log('Freezer: no frozen files modified.'); } }; const isFileInFrozenFolders = (file: string, folders: string[]): boolean => folders.reduce((isFrozen, folder) => { if (isFrozen) { return isFrozen; } const folderSplit = folder.replace(frozenFolderRegEx, '').split('/'); const fileSplit = file.split('/').slice(0, folderSplit.length); return JSON.stringify(folderSplit) === JSON.stringify(fileSplit); }, false); const getFrozenFiles = (frozen: string[]): string[] => frozen.filter(f => !frozenFolderRegEx.test(f)); const getFrozenFolders = (frozen: string[]): string[] => frozen.filter(f => frozenFolderRegEx.test(f)); const getNewFiles = (diff: string): string[] => diff .split('\n') .filter(line => newFileRegEx.test(line)) .map(line => line.replace(newFileRegEx, '')); const runShCommand = (cmd: string): Promise => new Promise((resolve, reject) => { const sh = spawn('sh', ['-c', cmd]); const stdout: string[] = []; const stderr: string[] = []; sh.stdout.on('data', data => { stdout.push(data.toString()); }); sh.stderr.on('data', data => { stderr.push(data.toString()); }); sh.on('close', code => { if (code !== 0) { console.error(stderr.join('')); reject(`Child process closed with code ${code}`); } resolve(stdout.join('')); }); }); const isTravisPushJob = () => { const prb = process.env.TRAVIS_PULL_REQUEST_BRANCH; return typeof prb === 'string' && prb.length === 0; }; const exit = () => setTimeout(() => process.exit(1), 100); // check to make sure that all of the freezer config in // the "frozen" property of package.json is valid const validateConfig = () => { try { const packagePath = path.resolve(PROJECT_BASE, 'package.json'); const { frozen } = JSON.parse(fs.readFileSync(packagePath, 'utf8')); if (frozen === undefined) { console.log(`Freezer: No config found in package.json on branch ${ORACLE_BRANCH}. Exiting.`); return; } if (!Array.isArray(frozen)) { throw new Error(`Property "frozen" is not an array`); } const errors = frozen .map(filePath => { const isFolder = frozenFolderRegEx.test(filePath); const fullPath = isFolder ? path.resolve(PROJECT_BASE, filePath.replace(frozenFolderRegEx, '')) : path.resolve(PROJECT_BASE, filePath); if (!fs.existsSync(fullPath)) { return `"${filePath}" does not exist`; } const stats = fs.lstatSync(fullPath); if (isFolder) { if (!stats.isDirectory()) { return `"${filePath}" is not a folder`; } } else { if (!stats.isFile()) { return `"${filePath}" is not a file`; } } }) .filter(err => err); if (errors.length) { throw new Error(errors.join('\n')); } else { console.log('Freezer: Config is valid.'); } } catch (err) { console.log(`Freezer: Invalid config on package.json:\n${err.message}`); exit(); } }; if (isTravisPushJob()) { console.log('Freezer: Travis push job detected. Exiting.'); } else if (process.argv[2] === '--validate') { validateConfig(); } else { start(); }