#!/usr/bin/env node var program = require('commander'); var spawn = require('child_process').spawn; var chokidar = require('chokidar'); var fs = require('fs'); var kill = require('tree-kill'); program .option('-s, --suite ', 'The suite to run Authelia for. This suite represents a configuration for Authelia and a set of tests for that configuration.') .parse(process.argv) if (!program.suite) { throw new Error('Please provide a suite.'); } const ENVIRONMENT_FILENAME = '.suite'; const AUTHELIA_INTERRUPT_FILENAME = '.authelia-interrupt'; const CONFIG_FILEPATH = `test/suites/${program.suite}/config.yml`; var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules', AUTHELIA_INTERRUPT_FILENAME, CONFIG_FILEPATH], { persistent: true, ignoreInitial: true, }); // Properly cleanup server and client if ctrl-c is hit process.on('SIGINT', function() { killServer(); killClient(); fs.unlinkSync(ENVIRONMENT_FILENAME); process.exit(); }); let serverProcess; function reloadServer() { killServer(() => { startServer(); }); } function startServer() { if (fs.existsSync(AUTHELIA_INTERRUPT_FILENAME)) { console.log('Authelia is interrupted. Consider removing ' + AUTHELIA_INTERRUPT_FILENAME + ' if it\'s not expected.'); return; } exec('./node_modules/.bin/tslint', ['-c', 'server/tslint.json', '-p', 'server/tsconfig.json']) .then(function() { serverProcess = spawn('./scripts/run-dev-server.sh', [CONFIG_FILEPATH]); serverProcess.stdout.pipe(process.stdout); serverProcess.stderr.pipe(process.stderr); }); } let clientProcess; function startClient() { clientProcess = spawn('npm', ['run', 'start'], { cwd: './client', env: { ...process.env, 'BROWSER': 'none' } }); clientProcess.stdout.pipe(process.stdout); clientProcess.stderr.pipe(process.stderr); } function killServer(onExit) { if (serverProcess) { serverProcess.on('exit', () => { serverProcess = undefined; if (onExit) onExit(); }); try { kill(serverProcess.pid, 'SIGKILL'); } catch (e) { console.error(e); if (onExit) onExit(); } } else { if (onExit) onExit(); } } function killClient(onExit) { if (clientProcess) { clientProcess.on('exit', () => { clientProcess = undefined; if (onExit) onExit(); }); try { kill(clientProcess.pid, 'SIGKILL'); } catch (e) { console.error(e); if (onExit) onExit(); } } else { if (onExit) onExit(); } } function generateConfigurationSchema() { return ixec('./node_modules/.bin/typescript-json-schema', ['-o', 'server/src/lib/configuration/Configuration.schema.json', '--strictNullChecks', '--required', 'server/tsconfig.json', 'Configuration']); } function reload(path) { console.log(`File ${path} has been changed, reloading...`); if (path.startsWith('server/src/lib/configuration/schema')) { console.log('Schema needs to be regenerated.'); generateConfigurationSchema(); } else if (path === AUTHELIA_INTERRUPT_FILENAME) { if (fs.existsSync(path)) { console.log('Authelia is being interrupted.'); killServer(); } else { console.log('Authelia is restarting.'); startServer(); } return; } reloadServer(); } function exec(command, args) { return new Promise((resolve, reject) => { const cmd = spawn(command, args); cmd.stdout.pipe(process.stdout); cmd.stderr.pipe(process.stderr); cmd.on('close', (code) => { if (code == 0) { resolve(); return; } reject(new Error('Status code ' + code)); }); }); } async function main() { console.log(`Create suite file ${ENVIRONMENT_FILENAME}.`); fs.writeFileSync(ENVIRONMENT_FILENAME, program.suite); console.log(`Render nginx configuration...`); await exec('./example/compose/nginx/portal/render.js'); console.log(`Prepare environment with docker-compose...`); await exec('./scripts/utils/prepare-environment.sh'); console.log('Start watching...'); tsWatcher.on('add', reload); tsWatcher.on('unlink', reload); tsWatcher.on('change', reload); startServer(); startClient(); } main()