Skip to content

Commit

Permalink
Add --all, refactor display formatting to happen once
Browse files Browse the repository at this point in the history
  • Loading branch information
jpb committed May 22, 2019
1 parent 4022d0d commit a3e61b8
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 43 deletions.
70 changes: 46 additions & 24 deletions src/cfn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as nameGenerator from 'project-name-generator';
import * as querystring from 'querystring';
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as inquirer from 'inquirer';
import calcElapsedSeconds from '../calcElapsedSeconds';
import {GenericCLIArguments} from '../cli/utils';
import confirmationPrompt from '../confirmationPrompt';
Expand Down Expand Up @@ -158,36 +159,57 @@ export const estimateCost = wrapCommandCtor(EstimateStackCost);

export async function updateExistingMain(argv: GenericCLIArguments): Promise<number> {
const providedOptions = tracking.nonDefaultOptions(argv);
const stacks = tracking.trackedStacks(providedOptions);
let stacks = tracking.trackedStacks();

if(_.isEmpty(stacks)) {
logger.info(`No tracked stacks in ${process.cwd()} matching ${tracking.unparseArgv(providedOptions).join(' ')}`);
logger.info(`No tracked stacks in ${process.cwd()}`);
return SUCCESS;
}

if(argv.all) {
// Include all stacks
// stacks = stacks;
} else if (_.some(providedOptions)) {
stacks = tracking.filterOnOptions(stacks, providedOptions);
if(_.isEmpty(stacks)) {
const cliArgs = tracking.unparseArgv(providedOptions).join(' ');
logger.info(`No tracked stacks in ${process.cwd()} matching ${cliArgs}`);
return SUCCESS;
}
} else {
for(const stack of stacks) {
if (!fs.existsSync(stack.argsfile)) {
logger.error(`Tracked argsfile '${stack.argsfile}' does not exist`);
return FAILURE;
}
// If no options are provided (eg. --region ... or --environment ...), prompt the user for input
const {selected} = await inquirer.prompt<{selected: typeof stacks}>({
name: 'selected',
type: 'checkbox',
default: [],
choices: _.map(stacks, (stack) => ({ name: stack.displayCommand, value: stack })),
message: 'Select stacks to update'
});

if(_.isEmpty(selected)) {
logger.info(`No stacks selected`);
return SUCCESS;
} else {
stacks = selected;
}
}

const args = [process.argv[1], 'update-stack', stack.argsfile, ...tracking.unparseArgv(stack.args)];
const relevantEnvVars = tracking.relevantEnvVars(stack.argsfile);

const envVars = _.reduce(
{...stack.environment, ...relevantEnvVars},
(acc: string[], value, name) => acc.concat(`${name}=${value}`),
[]
);
logger.info(`${envVars.join(' ')} ${args.join(' ')}`);

// Allow environment variables to be overridden by merging in relevantEnvVars last
const env = { ...process.env, ...stack.environment, ...relevantEnvVars };
const child = child_process.spawnSync(process.argv[0], args, { stdio: 'inherit', env });
if(child.status !== 0) {
return FAILURE;
}
for(const stack of stacks) {
if (!fs.existsSync(stack.argsfile)) {
logger.error(`Tracked argsfile '${stack.argsfile}' does not exist`);
return FAILURE;
}

logger.info(`Running: ${stack.displayCommand}`);

// Allow environment variables to be overridden by merging in relevantEnvVars last
const env = { ...process.env, ...stack.env };
const child = child_process.spawnSync(process.argv[0], stack.argv, { stdio: 'inherit', env });
if(child.status !== 0) {
return FAILURE;
}
return SUCCESS;
}
return SUCCESS;
}

export async function createOrUpdateStackMain(argv: GenericCLIArguments): Promise<number> {
Expand Down
4 changes: 4 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ export function buildArgs(commands = new LazyCommands(), wrapMainHandler = wrapC
description('update existing tracked stacks'),
(args) => args
.demandCommand(0, 0)
.option('all', {
type: 'boolean', default: false,
description: description('Update all tracked stacks without prompting')
})
.usage('Usage: iidy update-existing'),
wrapMainHandler(commands.updateExistingMain))

Expand Down
65 changes: 46 additions & 19 deletions src/tracking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,26 @@ import * as yaml from '../yaml';
const stackfilePath = '.iidy/stacks.yaml';

type Stackfile = {
stacks: StackMetadata[]
stacks: StackfileMetadata[]
}

type StackMetadata = {
// "Private" metadata, written to .iidy/stacks.yaml
type StackfileMetadata = {
name: string;
argsfile: string;
args: Partial<Arguments>;
environment: { [name: string]: string };
};

// "Public" metadata for use by cfn module
type StackMetadata = {
args: Partial<Arguments>;
argsfile: string;
displayCommand: string;
argv: string[];
env: object;
};

function importedEnvVars(argsfile: string): string[] {
const vars: string[] = [];
const argsdata = yaml.loadString(fs.readFileSync(argsfile), argsfile);
Expand Down Expand Up @@ -70,7 +80,7 @@ export function unparseArgv(argv: Partial<Arguments>) {
return args;
}

function stackMetadata(stackName: string, argsfile: string, argv: Arguments): StackMetadata {
function stackfileMetadata(stackName: string, argsfile: string, argv: Arguments): StackfileMetadata {
return {
name: stackName,
argsfile: argsfile,
Expand All @@ -79,19 +89,6 @@ function stackMetadata(stackName: string, argsfile: string, argv: Arguments): St
}
}

export function loadStackfile(): Stackfile {
try {
const existing = jsyaml.safeLoad(fs.readFileSync(stackfilePath).toString());
if (_.isObject(existing) && _.isArray(existing.stacks)) {
return existing;
} else {
return { stacks: [] };
}
} catch {
return { stacks: [] };
}
}

export function nonDefaultOptions(argv: Arguments): Partial<Arguments> {
const result: Partial<Arguments> = {};
// @ts-ignore
Expand All @@ -106,8 +103,7 @@ export function nonDefaultOptions(argv: Arguments): Partial<Arguments> {
return result;
}

export function trackedStacks(providedOptions: Partial<Arguments>): StackMetadata[] {
const {stacks} = loadStackfile();
export function filterOnOptions(stacks: StackMetadata[], providedOptions: Partial<Arguments>): StackMetadata[] {
const matchingStacks: StackMetadata[] = [];

for(const stack of stacks) {
Expand All @@ -119,6 +115,37 @@ export function trackedStacks(providedOptions: Partial<Arguments>): StackMetadat
return matchingStacks;
}

export function trackedStacks(): StackMetadata[] {
const stackfile = loadStackfile();

return _.map(stackfile.stacks, (stack) => {
const displayArgs = ['iidy', 'update-stack', stack.argsfile, ...unparseArgv(stack.args)];
const env = {...stack.environment, ...relevantEnvVars(stack.argsfile)};
const envVars = _.reduce(env, (acc: string[], v, k) => acc.concat(`${k}=${v}`), []);

return {
displayCommand: [...envVars, ...displayArgs].join(' '),
args: stack.args,
argsfile: stack.argsfile,
argv: [process.argv[1], 'update-stack', stack.argsfile, ...unparseArgv(stack.args)],
env
};
});
}

function loadStackfile(): Stackfile {
try {
const existing = jsyaml.safeLoad(fs.readFileSync(stackfilePath).toString());
if (_.isObject(existing) && _.isArray(existing.stacks)) {
return existing;
} else {
return { stacks: [] };
}
} catch {
return { stacks: [] };
}
}

function writeStackfile(stackfile: Stackfile) {
try {
fs.mkdirSync(path.dirname(stackfilePath));
Expand All @@ -137,7 +164,7 @@ function writeStackfile(stackfile: Stackfile) {

export function track(stackName: string, argsfile: string, argv: Arguments) {
const stackfile = loadStackfile();
const stack = stackMetadata(stackName, argsfile, argv);
const stack = stackfileMetadata(stackName, argsfile, argv);

if(!_.some(stackfile.stacks, (existing) => _.isEqual(existing, stack))) {
stackfile.stacks.push(stack);
Expand Down

0 comments on commit a3e61b8

Please sign in to comment.