UNPKG

@pradyumn-el/pollycli

Version:

pollycli lets users access the functionalities of Polly over a command line interface

328 lines (246 loc) 8.95 kB
const https = require('https'); const fs = require('fs'); const { execSync, spawnSync } = require('child_process'); const { spawn } = require('child_process'); const os = require('os'); const { stdout, stderr } = require('process') const path = require('path'); const { EventEmitter } = require('events'); class FileDownloader extends EventEmitter {} const fileDownloader = new FileDownloader(); const supported_platforms = ['linux', 'win32', 'darwin'] const urls = { 'linux': 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip', 'win32': 'https://awscli.amazonaws.com/AWSCLIV2.msi', 'darwin': 'https://awscli.amazonaws.com/AWSCLIV2.pkg' } const fileNames = { 'linux': 'awscli-linux.zip', 'win32': 'AWSCLIV2.msi', 'darwin': 'AWSCLIV2.pkg' } const installation_paths = { 'linux': os.homedir() + path.sep + ".local" + path.sep + "bin" + path.sep + "polly-cli", 'darwin': os.homedir() + path.sep + ".local" + path.sep + "bin" + path.sep + "polly-cli", 'win32': os.homedir() + path.sep + "awscli" } async function installOnMac(url, fileName, update) { if(awsIsPresent()) { if(update) { await updateAwsCli(); return; } return; } await downloadAndInstall(url, fileName, update); } function createXml(targetDir) { /* * choices.js contains the contents of xml file in a string * The string is modified to set the path of target directory(where aws-cli will be installed) * Finally, all the content is written to a file called choices.xml, which is used by the macOS * installer */ const { xmlContent } = require('./choices') var _xmlContent_ = xmlContent _xmlContent_ = _xmlContent_.replace('AWSCLIPATH', targetDir) // create choices xml file in the current directory fs.writeFileSync('./choices.xml', _xmlContent_); } function runMacInstaller(fileName, update) { /* * Installing only for current users. That is, installing in current user's home * directory - /Users/username/.local/bin/polly-cli/aws-cli/ * To install in a custom path the following things need to be in place * 1. Custom dir should exist * 2. A XML file that specifies the location of target directory * For more details, check doc https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html */ const targetDir = installation_paths['darwin']; // Create target dir if not present if(!fs.existsSync(targetDir)) { console.log('Target dir does not exist. Creating it now'); const createdPath = fs.mkdirSync(targetDir, {recursive: true}); console.log(createdPath); } // Create the xml file createXml(targetDir); const child = spawnSync('installer', ['-pkg', fileName, '-target', 'CurrentUserHomeDirectory', '-applyChoiceChangesXML', './choices.xml']); if(child.status === 0) { console.log('Success: Installed optional packages!'); } else { console.log('Warning: Could not install optional packages...'); } } function extractFile(fileName) { /* * Only applicable for Linux * Extracts all the contents to a folder called 'aws' * ./aws/install is the installer file that needs to be run * to install aws cli */ const AdmZip = require('adm-zip'); const zip = new AdmZip(fileName); zip.extractAllTo('.', true) } function downloadSynchronously(url) { return new Promise((resolve, reject) => { https.get(url, function(response) { let respBytes = []; response.on('data', (chunk) => { respBytes.push(chunk); }); response.on('end', () => { let body = Buffer.concat(respBytes); resolve(body); }); response.on('error', (error) => { reject(error); }); }); }); } async function downloadAndInstall(url, fileName, update=false) { try { let httpsPromise = downloadSynchronously(url); let respBody = await httpsPromise; fs.writeFileSync(fileName, respBody); if(os.platform() === 'linux') { extractFile(fileName); runInstaller('./aws/install', update); } if(os.platform() === 'darwin') { runMacInstaller(fileName); } } catch(error) { console.log('Warning: Could not install optional packages'); } } function runInstaller(filePath, update) { try { if (filePath) { // WORKAROUND: Executable permissions of aws/install and aws/dist/aws files are lost // after unzipping. Seems like an issue with the library. The fix is to manually set // executable permissions fs.chmodSync(filePath, '755'); fs.chmodSync('./aws/dist/aws', '755'); /* Note: Installing in user's home directory(~/.local/bin/polly-cli) as it won't require admin privileges */ const installDir = installation_paths['linux'] const args = update ? ['-i', installDir, '-b', installDir, '--update'] : ['-i', installDir, '-b', installDir] const child = spawnSync(filePath, args); if(child.status === 0) { console.log('Success: Installed optional packages!'); } else { console.log('Warning: Could not install optional packages...'); } } } catch(err) { console.log(`Warning: Could not install optional dependencies`); } } function installOnWindows(url) { // Installs awscli in user's home directory const installDir = installation_paths['win32']; /* /qn = quite mode with no ui /l* log_file = log any possible output/error in the log_file(optional parameter) */ const child = spawnSync('msiexec.exe', ['/a', url, '/qn', `TARGETDIR=${installDir}`]); if(child.status === 0) { console.log('Success: Installed optional packages!'); } else { console.log('Warning: Could not install optional packages...'); } } function amIroot() { if (process.env.SUDO_UID || process.getuid() === 0) { return true } else { return false } } function getAwsPath() { if(os.platform() === 'linux') { return installation_paths['linux'] + path.sep + 'aws'; } if(os.platform() === 'darwin') { return installation_paths['darwin'] + path.sep + 'aws-cli' + path.sep + "aws"; } if(os.platform() === 'win32') { return installation_paths['win32'] + path.sep + 'Amazon' + path.sep + 'AWSCLIV2' + path.sep + 'aws'; } } function awsIsPresent() { /* On linux system, AWS CLI is installed in ~/.local/bin/polly-cli directory If aws-cli is already installed, running 'aws --version' command should exit with 0 exit-code */ const awsCliPath = getAwsPath() const child = spawnSync(awsCliPath, ['--version']) if(child.status === 0) { // console.log(`Output - ${child.output}`) return true } else { /* Note - User might have uninstalled aws incorrectly. That is, just the symlinked files have been deleted(i.e /.local/bin/polly-cli/aws) but the actual dir where aws is placed is still present(~/.local/bin/polly-cli/v2). In such a case aws --version command will fail, giving us the impression that aws is not present. In such a scenario, if we continue with the installation, our installation will also fail. Here a new check should be added to check if the dir, /.local/bin/polly-cli/v2/ is present or not. If it's not present only then we can say with certain surity that aws-cli is not installed */ // console.log(`Error - ${child.error}`) return false } } async function updateAwsCli() { const input = get_input_files() if(os.platform() === 'linux' || os.platform() === 'darwin') { const update = true await downloadAndInstall(input.fileUrl, input.fileName, update) } } async function installOnLinux(url, fileName, update) { if(awsIsPresent()) { if(update) { updateAwsCli(); return; } return } await downloadAndInstall(url, fileName, update); } function get_input_files() { const platform = os.platform() return { 'fileUrl': urls[platform], 'fileName': fileNames[platform] } } async function installExtraDependencies(update) { try { if(!os.platform() in supported_platforms) { console.warn(`Warning : Cannot proceed with Installation. Platform ${os.platform()} is not supported`); return; } // Get url and file name as per the OS const input = get_input_files(); if(os.platform() === 'linux') { await installOnLinux(input.fileUrl, input.fileName, update) } if(os.platform() === 'win32') { installOnWindows(input.fileUrl) } if(os.platform() === 'darwin') { await installOnMac(input.fileUrl, input.fileName, update); } } catch(error) { console.log('Warning: Could not install optional dependencies'); } } module.exports = { installExtraDependencies, awsIsPresent }