@pradyumn-el/pollycli
Version:
pollycli lets users access the functionalities of Polly over a command line interface
328 lines (246 loc) • 8.95 kB
JavaScript
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 }