Skip to content

Commit

Permalink
[7.9] [kbn/dev-utils] add RunWithCommands utility (#72311) (#72343)
Browse files Browse the repository at this point in the history
Co-authored-by: spalger <spalger@users.noreply.github.com>
  • Loading branch information
Spencer and spalger authored Jul 17, 2020
1 parent 3561cc8 commit 42585b2
Show file tree
Hide file tree
Showing 10 changed files with 743 additions and 138 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-dev-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export {
KBN_P12_PATH,
KBN_P12_PASSWORD,
} from './certs';
export { run, createFailError, createFlagError, combineErrors, isFailError, Flags } from './run';
export { REPO_ROOT } from './repo_root';
export { KbnClient } from './kbn_client';
export * from './run';
export * from './axios';
export * from './stdio';
export * from './ci_stats_reporter';
Expand Down
94 changes: 94 additions & 0 deletions packages/kbn-dev-utils/src/run/cleanup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { inspect } from 'util';

import exitHook from 'exit-hook';

import { ToolingLog } from '../tooling_log';
import { isFailError } from './fail';

export type CleanupTask = () => void;

export class Cleanup {
static setup(log: ToolingLog, helpText: string) {
const onUnhandledRejection = (error: any) => {
log.error('UNHANDLED PROMISE REJECTION');
log.error(
error instanceof Error
? error
: new Error(`non-Error type rejection value: ${inspect(error)}`)
);
process.exit(1);
};

process.on('unhandledRejection', onUnhandledRejection);

const cleanup = new Cleanup(log, helpText, [
() => process.removeListener('unhandledRejection', onUnhandledRejection),
]);

cleanup.add(exitHook(() => cleanup.execute()));

return cleanup;
}

constructor(
private readonly log: ToolingLog,
public helpText: string,
private readonly tasks: CleanupTask[]
) {}

add(task: CleanupTask) {
this.tasks.push(task);
}

execute(topLevelError?: any) {
const tasks = this.tasks.slice(0);
this.tasks.length = 0;

for (const task of tasks) {
try {
task();
} catch (error) {
this.onError(error);
}
}

if (topLevelError) {
this.onError(topLevelError);
}
}

private onError(error: any) {
if (isFailError(error)) {
this.log.error(error.message);

if (error.showHelp) {
this.log.write(this.helpText);
}

process.exitCode = error.exitCode;
} else {
this.log.error('UNHANDLED ERROR');
this.log.error(error);
process.exitCode = 1;
}
}
}
18 changes: 7 additions & 11 deletions packages/kbn-dev-utils/src/run/flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ import { getFlags } from './flags';
it('gets flags correctly', () => {
expect(
getFlags(['-a', '--abc=bcd', '--foo=bar', '--no-bar', '--foo=baz', '--box', 'yes', '-zxy'], {
flags: {
boolean: ['x'],
string: ['abc'],
alias: {
x: 'extra',
},
allowUnexpected: true,
boolean: ['x'],
string: ['abc'],
alias: {
x: 'extra',
},
allowUnexpected: true,
})
).toMatchInlineSnapshot(`
Object {
Expand Down Expand Up @@ -60,10 +58,8 @@ it('gets flags correctly', () => {
it('guesses types for unexpected flags', () => {
expect(
getFlags(['-abc', '--abc=bcd', '--no-foo', '--bar'], {
flags: {
allowUnexpected: true,
guessTypesForUnexpectedFlags: true,
},
allowUnexpected: true,
guessTypesForUnexpectedFlags: true,
})
).toMatchInlineSnapshot(`
Object {
Expand Down
79 changes: 38 additions & 41 deletions packages/kbn-dev-utils/src/run/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@
* under the License.
*/

import { relative } from 'path';

import dedent from 'dedent';
import getopts from 'getopts';

import { Options } from './run';
import { RunOptions } from './run';

export interface Flags {
verbose: boolean;
Expand All @@ -36,23 +33,52 @@ export interface Flags {
[key: string]: undefined | boolean | string | string[];
}

export function getFlags(argv: string[], options: Options): Flags {
export interface FlagOptions {
allowUnexpected?: boolean;
guessTypesForUnexpectedFlags?: boolean;
help?: string;
alias?: { [key: string]: string | string[] };
boolean?: string[];
string?: string[];
default?: { [key: string]: any };
}

export function mergeFlagOptions(global: FlagOptions = {}, local: FlagOptions = {}): FlagOptions {
return {
alias: {
...global.alias,
...local.alias,
},
boolean: [...(global.boolean || []), ...(local.boolean || [])],
string: [...(global.string || []), ...(local.string || [])],
default: {
...global.alias,
...local.alias,
},

help: local.help,

allowUnexpected: !!(global.allowUnexpected || local.allowUnexpected),
guessTypesForUnexpectedFlags: !!(global.allowUnexpected || local.allowUnexpected),
};
}

export function getFlags(argv: string[], flagOptions: RunOptions['flags'] = {}): Flags {
const unexpectedNames = new Set<string>();
const flagOpts = options.flags || {};

const { verbose, quiet, silent, debug, help, _, ...others } = getopts(argv, {
string: flagOpts.string,
boolean: [...(flagOpts.boolean || []), 'verbose', 'quiet', 'silent', 'debug', 'help'],
string: flagOptions.string,
boolean: [...(flagOptions.boolean || []), 'verbose', 'quiet', 'silent', 'debug', 'help'],
alias: {
...(flagOpts.alias || {}),
...flagOptions.alias,
v: 'verbose',
},
default: flagOpts.default,
default: flagOptions.default,
unknown: (name: string) => {
unexpectedNames.add(name);
return flagOpts.guessTypesForUnexpectedFlags;
return !!flagOptions.guessTypesForUnexpectedFlags;
},
} as any);
});

const unexpected: string[] = [];
for (const unexpectedName of unexpectedNames) {
Expand Down Expand Up @@ -119,32 +145,3 @@ export function getFlags(argv: string[], options: Options): Flags {
...others,
};
}

export function getHelp(options: Options) {
const usage = options.usage || `node ${relative(process.cwd(), process.argv[1])}`;

const optionHelp = (
dedent(options?.flags?.help || '') +
'\n' +
dedent`
--verbose, -v Log verbosely
--debug Log debug messages (less than verbose)
--quiet Only log errors
--silent Don't log anything
--help Show this message
`
)
.split('\n')
.filter(Boolean)
.join('\n ');

return `
${usage}
${dedent(options.description || 'Runs a dev task')
.split('\n')
.join('\n ')}
Options:
${optionHelp + '\n\n'}`;
}
Loading

0 comments on commit 42585b2

Please sign in to comment.