UNPKG

@enplug/scripts

Version:
359 lines (318 loc) 9.33 kB
#! /usr/bin/env node 'use strict'; const commandLineArgs = require('command-line-args'); const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const AWS = require('aws-sdk'); const uploadDir = require('./functions/uploadDir'); const rootPath = process.cwd(); const packagePath = rootPath + '/package.json'; const pkg = require(packagePath); const inquirer = require('inquirer'); require('util.promisify/shim')(); const {promisify} = require('util'); const _ = require('lodash'); let bucket; let appName; let target; // Parse inline arguments const optionDefinitions = [ { name: 'bucket', alias: 'b', type: String }, { name: 'appName', alias: 'a', type: String }, { name: 'target', alias: 't', type: String }, ]; const options = commandLineArgs(optionDefinitions); const appsDestinationPrefix = 'i18n/apps' /** * Entry point for the script. */ promptForData().then(function(answer) { if (answer.s3Bucket) { bucket = answer.s3Bucket; } if (answer.target) { target = answer.target; } if (target === 'gateway') { return releaseProject('gateway', 'i18n/gateway'); } else if (target === 'dashboard project') { return releaseProject('dashboard', 'i18n/dashboard'); } else if (target === 'everything') { return releaseEverything(); } else if (target === 'player-web') { return releaseProject('player-web', 'i18n/player'); } else if (target === 'app') { return releaseApp(); } }) /** * Asks user to which bucket the App should be released. */ function promptForData() { const questions = []; if (!options['bucket']) { questions.push({ message: 'Which bucket do you want to use?', name: 's3Bucket', type: 'list', choices: pkg.config.aws.buckets }); } else { bucket = options.bucket; } if (!options['target']) { questions.push({ message: 'What project\'s localization files you want to release?', name: 'target', type: 'list', choices: [ 'app', 'gateway', 'dashboard project', 'everything', 'player-web' ] }); } else { target = options.target; } return inquirer.prompt(questions); } /** Release app files */ function releaseApp() { return askForAppName().then((response) => { if (response.confirm) { return syncAppTranslations(bucket, appName); } }).then(result => { console.log('Release succesful, invalidating cache...'); return invalidateAppCache(appName); }); } function askForAppName() { let appCollection; return collectAppNames().then((apps) => { appCollection = apps; if (!options['appName']) { return promptForApp(appCollection).then(answer => { appName = answer.appName; return confirmDestination(appName); }); } else { appName = options.appName; return confirmDestination(appName); } }); } /** Releases every translation */ function releaseEverything() { return confirmReleaseEverything().then((answer) => { if (answer.confirm) { return syncEverything(); } else { return Promise.reject('canceled'); } }).then(() => { console.log('Release finished, invalidating cache...'); return invalidatePaths(['/i18n/*']); }).then(() => { console.log('Everything released!'); }, error => { console.log('Release not finished: ', error); }); } function confirmReleaseEverything() { return inquirer.prompt({ message: `All localization files will be overwritten with what is currently in this directory. Are you sure?`, name: 'confirm', type: 'confirm' }); } function syncEverything() { let s3Client = createS3Client(); return Promise.all([ uploadDir(s3Client, 'apps', bucket, 'i18n/apps'), uploadDir(s3Client, 'dashboard', bucket, 'i18n/dashboard'), uploadDir(s3Client, 'gateway', bucket, 'i18n/gateway') ]).catch(err => { console.log('Error releasing everything: ', err.stack); }); } /** Releases every apps translation */ function releaseAllApps() { return confirmReleaseAllApps().then((answer) => { if (answer.confirm) { return syncAllApps(); } else { return Promise.reject('canceled'); } }).then(() => { console.log('Release finished, invalidating cache...'); return invalidatePaths(['/i18n/apps/*']); }).then(() => { console.log('Every app localization file has been released!'); }, error => { console.log('Release not finished: ', error); }); } function confirmReleaseAllApps() { return inquirer.prompt({ message: `Every app localization file will be overwritten with what is currently in this directory. Are you sure?`, name: 'confirm', type: 'confirm' }); } function syncAllApps() { let s3Client = createS3Client(); return Promise.all([ uploadDir(s3Client, 'apps', bucket, 'i18n/apps'), uploadDir(s3Client, 'dashboard', bucket, 'i18n/dashboard'), uploadDir(s3Client, 'gateway', bucket, 'i18n/gateway') ]).catch(err => { console.log('Error releasing everything: ', err.stack); }); } /* Release gateway */ function releaseProject(name, releaseDir) { return confirmReleaseProject(name, releaseDir).then((answer) => { if (answer.confirm) { return syncProject(name, releaseDir); } else { return Promise.reject('canceled'); } }).then(() => { console.log('Release finished, invalidating cache...'); return invalidatePaths([`/${releaseDir}/*`]); }).then(() => { console.log(`${name} localization files released!`); }, error => { console.log('Release not finished: ', error); }) } function confirmReleaseProject(name, releaseDir) { return inquirer.prompt({ message: `Confirm release of project ${name} localization files to ${bucket}/${releaseDir}`, name: 'confirm', type: 'confirm' }); } function syncProject(localDir, remoteDir) { let s3Client = createS3Client(); return uploadDir(s3Client, localDir, bucket, remoteDir); } /** Common functions */ /** * Looks into `dashboard` and `player` directories and gathers list of apps' files in each of them. */ function collectAppNames() { const readdirAsync = promisify(fs.readdir); return readdirAsync('apps/'); } /** * Asks user which app's localization strings should be released. */ function promptForApp(appList) { if (!options['appName']) { return inquirer.prompt({ message: `Which app's files do you want to release?`, name: 'appName', type: 'list', choices: appList }); } else Promise.resolve(options['appName']); } function confirmDestination(appName) { let uploadPath = path.posix.join(bucket, appsDestinationPrefix, appName); return inquirer.prompt({ message: `Confirm release of localization files for '${appName}' to ${uploadPath}`, name: 'confirm', type: 'confirm' }); } /** * Creates an S3 client. */ function createS3Client() { let creds; const s3Options = Object.assign({}, pkg.config.aws.s3); try { creds = JSON.parse(fs.readFileSync(path.resolve(rootPath, './aws.private.json'), 'utf8')); } catch (e) { console.error(`Error finding/parsing ${chalk.default.cyan('aws.private.json')}`); throw e; } if (creds.accessKeyId == null || creds.secretAccessKey == null) { throw new TypeError(`Error could not find accessKeyId or secretAccessKey in ${chalk.default.cyan('aws.private.json')} file.`); } // Setting up the credentials globally AWS.config.update({ accessKeyId: creds.accessKeyId, secretAccessKey: creds.secretAccessKey, maxRetries: 3, }); return new AWS.S3(s3Options); } /** * * @param {*} bucket * @param {*} appName */ function syncAppTranslations(bucket, appName) { if (appName.startsWith('/')) { appName = appName.substr(1); } let s3Client = createS3Client(); return uploadDir(s3Client, `apps/${appName}`, bucket, `i18n/apps/${appName}`).catch(function (error) { console.error('There was an error during the release process!'); console.error(error.stack); return null; }); } /** * Invalidates cache for `appName`. * @param {string} appName * @returns {Promise} */ function invalidateAppCache(appName) { const paths = [ `/i18n/apps/${appName}/dashboard/*`, `/i18n/apps/${appName}/player/*` ]; return invalidatePaths(paths); } /** * Invalidates cache on paths that are passed. * @param {Array<string>} paths - paths to invalidate. * @returns {Promise} */ function invalidatePaths(paths) { return new Promise((resolve, reject) => { const distributionIds = { 'dev-apps.enplug.in': 'E3IQ50D070L7PA', 'apps.enplug.in': 'EG84KEU04G44V', 'apps.enplug.com': 'E1YGTSTR7FTNEX' } AWS.config.loadFromPath('./aws.private.json'); const cloudfront = new AWS.CloudFront(); var params = { DistributionId: distributionIds[bucket], InvalidationBatch: { CallerReference: String(Date.now()), Paths: { Quantity: paths.length, Items: paths } } }; cloudfront.createInvalidation(params, function(err, data) { if (err) { console.error(`Cache invalidation error: ${err}`); reject(err); } else { console.log('Cache invalidation finished.'); resolve(data); } }); }); }