mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-09 11:02:48 +00:00
269 lines
8.5 KiB
TypeScript
269 lines
8.5 KiB
TypeScript
/*
|
|
* Copyright (C) 2018, 2019 StApps
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
import chalk from 'chalk';
|
|
import {execSync} from 'child_process';
|
|
import * as commander from 'commander';
|
|
import {copyFileSync, existsSync, readFileSync, writeFileSync} from 'fs';
|
|
import {join, resolve, sep} from 'path';
|
|
import {cwd} from 'process';
|
|
import {isDeepStrictEqual} from 'util';
|
|
import {parse, stringify} from 'yaml';
|
|
import {EXPECTED_CI_CONFIG, EXPECTED_LICENSES, NEEDED_FILES, NYC_CONFIGURATION, SCRIPTS} from './configuration';
|
|
|
|
/* tslint:disable:no-console */
|
|
|
|
/**
|
|
* Wrapper for console.info that outputs every argument in cyan
|
|
* @param args
|
|
*/
|
|
function consoleInfo(...args: string[]): void {
|
|
args.forEach((arg) => {
|
|
console.info('\n' + chalk.cyan(arg));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Wrapper for console.warn that outputs every argument in red
|
|
* @param args
|
|
*/
|
|
function consoleWarn(...args: string[]): void {
|
|
args.forEach((arg) => {
|
|
console.warn('\n' + chalk.red.bold(arg));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Wrapper for console.log that outputs every argument in green
|
|
* @param args
|
|
*/
|
|
function consoleLog(...args: string[]): void {
|
|
args.forEach((arg) => {
|
|
console.log('\n' + chalk.green.bold(arg));
|
|
});
|
|
}
|
|
|
|
const currentWorkingDirectory = cwd();
|
|
|
|
let suggestOverwrite = false;
|
|
|
|
// configure commander
|
|
commander
|
|
.version(JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json')).toString()).version)
|
|
.option('-p, --path <path>', `Path of project to add files to (${currentWorkingDirectory})`, currentWorkingDirectory)
|
|
.option('-r, --replace', 'Whether to replace existing files or not', false)
|
|
.parse(process.argv);
|
|
|
|
// make path absolute
|
|
const path = resolve(commander.path);
|
|
|
|
// check for existing package.json in provided path
|
|
if (!existsSync(resolve(path, 'package.json'))) {
|
|
throw new Error(`No package.json in "${path}".`);
|
|
}
|
|
|
|
// path to examined package.json
|
|
const packageJsonPath = resolve(path, 'package.json');
|
|
|
|
// wheter or not the contents of the package.json were changed
|
|
let packageJsonChanged = false;
|
|
|
|
// read package.json in provided path
|
|
const packageJson = JSON.parse(readFileSync(packageJsonPath).toString());
|
|
|
|
// check if provided path is this package
|
|
if (packageJson.name === '@openstapps/configuration') {
|
|
consoleInfo('I\'m not going to check myself!');
|
|
process.exit(0);
|
|
}
|
|
|
|
// check if license is one of the expected ones
|
|
if (EXPECTED_LICENSES.indexOf(packageJson.license) === -1) {
|
|
consoleWarn(`License should be one of "${EXPECTED_LICENSES.join(', ')}"!`);
|
|
}
|
|
|
|
// check if configuration files are extended
|
|
['tsconfig.json', 'tslint.json'].forEach((file) => {
|
|
const fileToCheck = resolve(path, file);
|
|
const expectedPath = `./node_modules/@openstapps/configuration/${file}`;
|
|
|
|
if (existsSync(fileToCheck)) {
|
|
const configFile = JSON.parse(readFileSync(fileToCheck).toString());
|
|
|
|
const configFileExtended =
|
|
(typeof configFile.extends === 'string' && configFile.extends === expectedPath)
|
|
|| (file === 'tslint.json' && Array.isArray(configFile.extends) && configFile.extends.indexOf(expectedPath) >= 0);
|
|
|
|
if (!configFileExtended) {
|
|
consoleWarn(`
|
|
File "${fileToCheck}" should extend "${expectedPath}"!
|
|
|
|
Example:
|
|
${readFileSync(resolve(__dirname, '..', 'templates', 'template-' + file))}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
// copy needed files
|
|
NEEDED_FILES.forEach((file) => {
|
|
let destinationFile = file;
|
|
|
|
// remove templates directory for destination files
|
|
if (destinationFile.indexOf('templates') === 0) {
|
|
destinationFile = destinationFile.split(sep).slice(1).join(sep);
|
|
file = join('templates', `template-${destinationFile}`);
|
|
}
|
|
|
|
const source = resolve(__dirname, '..', file);
|
|
const destination = resolve(path, destinationFile);
|
|
|
|
// check if file exists or replace flag is set
|
|
if (!existsSync(destination) || commander.replace) {
|
|
copyFileSync(source, destination);
|
|
consoleInfo(`Copied file "${source}" to "${destination}".`);
|
|
} else if (destinationFile === '.npmignore') {
|
|
const npmIgnore = readFileSync(destination).toString();
|
|
|
|
const ignoredPatterns = npmIgnore.split('\n');
|
|
|
|
let ignoresEverything = false;
|
|
let unignoresDocs = false;
|
|
|
|
for (const ignoredPattern of ignoredPatterns) {
|
|
if (ignoredPattern === '/*') {
|
|
ignoresEverything = true;
|
|
}
|
|
|
|
if (ignoredPattern === '!docs') {
|
|
unignoresDocs = true;
|
|
}
|
|
}
|
|
|
|
if (!ignoresEverything) {
|
|
consoleWarn(`'.npmignore' should have '/*' as first pattern to ignore everything.`);
|
|
|
|
suggestOverwrite = true;
|
|
}
|
|
|
|
if (unignoresDocs) {
|
|
consoleWarn(`'.npmignore' contains '!docs' and thus the package will contain the documentation.
|
|
Please double check that this is desired behavior since the docs can become huge:
|
|
https://gitlab.com/openstapps/configuration/issues/11`);
|
|
|
|
suggestOverwrite = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
// check if nyc is a dependency
|
|
if (typeof packageJson.devDependencies === 'object' && Object.keys(packageJson.devDependencies).indexOf('nyc') >= 0) {
|
|
if (typeof packageJson.nyc === 'undefined' || commander.replace) {
|
|
// add NYC configuration
|
|
packageJson.nyc = NYC_CONFIGURATION;
|
|
|
|
packageJsonChanged = true;
|
|
|
|
consoleLog(`Added NYC configuration in "${packageJsonPath}".`);
|
|
} else if (!isDeepStrictEqual(packageJson.nyc, NYC_CONFIGURATION)) {
|
|
consoleInfo(`NYC configuration in '${packageJsonPath}' differs from the proposed one. Please check manually...`);
|
|
|
|
suggestOverwrite = true;
|
|
}
|
|
}
|
|
|
|
// check if scripts is a map
|
|
if (typeof packageJson.scripts !== 'object') {
|
|
packageJson.scripts = {};
|
|
|
|
packageJsonChanged = true;
|
|
}
|
|
|
|
Object.keys(SCRIPTS).forEach((scriptName) => {
|
|
const scriptToCheck = packageJson.scripts[scriptName];
|
|
|
|
// check if script exists
|
|
if (typeof scriptToCheck === 'undefined' || commander.replace) {
|
|
packageJson.scripts[scriptName] = SCRIPTS[scriptName];
|
|
|
|
packageJsonChanged = true;
|
|
|
|
consoleInfo(`Added '${scriptName}' script to '${packageJsonPath}'.`);
|
|
} else if (typeof scriptToCheck === 'string' && scriptToCheck !== SCRIPTS[scriptName]) {
|
|
consoleWarn(`NPM script '${scriptName}' should be "${SCRIPTS[scriptName].replace('\n', '\\n')}".`);
|
|
}
|
|
});
|
|
|
|
const execBuffer = execSync('git log --format=\'%aN\' | sort -u');
|
|
for (let author of execBuffer.toString().split('\n')) {
|
|
author = author.trim();
|
|
|
|
if (author === '') {
|
|
continue;
|
|
}
|
|
|
|
let authorIsAttributed = false;
|
|
|
|
authorIsAttributed = authorIsAttributed
|
|
|| (typeof packageJson.author === 'string' && packageJson.author.indexOf(author) >= 0)
|
|
|| (Array.isArray(packageJson.contributors) && packageJson.contributors.find((contributor: string) => {
|
|
return contributor.indexOf(author) >= 0;
|
|
}));
|
|
|
|
if (!authorIsAttributed) {
|
|
consoleWarn(`'${author}' should be attributed as author or contributor.`);
|
|
}
|
|
}
|
|
|
|
// check CI config if it exists
|
|
const pathToCiConfig = resolve(path, '.gitlab-ci.yml');
|
|
if (existsSync(pathToCiConfig)) {
|
|
// read CI config
|
|
const buffer = readFileSync(pathToCiConfig);
|
|
try {
|
|
const ciConfig = parse(buffer.toString());
|
|
|
|
// check entries
|
|
for (const entry in EXPECTED_CI_CONFIG) {
|
|
if (!EXPECTED_CI_CONFIG.hasOwnProperty(entry)) {
|
|
continue;
|
|
}
|
|
|
|
if (!isDeepStrictEqual(EXPECTED_CI_CONFIG[entry], ciConfig[entry])) {
|
|
consoleWarn(`Entry '${entry}' in ${pathToCiConfig} is incorrect. Expected value is:`);
|
|
consoleInfo(stringify((() => {
|
|
const completeEntry: any = {};
|
|
completeEntry[entry] = EXPECTED_CI_CONFIG[entry];
|
|
return completeEntry;
|
|
})()));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
consoleWarn(`Could not parse ${pathToCiConfig} because of '${error.message}'.
|
|
Please ensure consistency of CI config manually.`);
|
|
consoleInfo(stringify(EXPECTED_CI_CONFIG));
|
|
}
|
|
}
|
|
|
|
if (packageJsonChanged) {
|
|
writeFileSync(resolve(path, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
consoleLog(`Changes were written to "${packageJsonPath}".`);
|
|
}
|
|
|
|
if (suggestOverwrite) {
|
|
consoleInfo(`You should consider to overwrite your configuration files and check for intended derivations:
|
|
npm run check-configuration -- -r`);
|
|
}
|
|
|
|
consoleLog(`Done checking the configuration in '${path}'.`);
|