Spike commander as replacement for yargs

This commit is contained in:
Michael Hopcroft 2020-03-20 15:26:19 -07:00
Родитель b7eca23918
Коммит 8ef4d59a28
4 изменённых файлов: 262 добавлений и 118 удалений

13
package-lock.json сгенерированный
Просмотреть файл

@ -1172,10 +1172,9 @@
}
},
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz",
"integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ=="
},
"component-emitter": {
"version": "1.3.0",
@ -4585,6 +4584,12 @@
"tsutils": "^2.29.0"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",

Просмотреть файл

@ -21,6 +21,7 @@
"@azure/storage-queue": "^12.0.3",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"commander": "^5.0.0",
"dockerode": "^3.1.0",
"dotenv": "^8.2.0",
"env-var": "^6.0.1",

Просмотреть файл

@ -1,8 +1,11 @@
import { Command } from 'commander';
import * as fs from 'fs';
import { Decoder } from 'io-ts';
import * as yaml from 'js-yaml';
import { DateTime } from 'luxon';
import * as yargs from 'yargs';
import { Decoder } from 'io-ts';
import * as path from 'path';
// import * as yargs from 'yargs';
import {
apiVersion,
@ -20,7 +23,6 @@ import {
import { formatTable, Alignment } from './formatting';
// TODO: set version
// TODO: Top-level try/catch error reporter
// TODO: errors across the wire
// TODO: list runs should show status column, suite, candidate
@ -28,81 +30,191 @@ import { formatTable, Alignment } from './formatting';
const endpoint = 'http://localhost:3000';
const lab = new LaboratoryClient(endpoint);
// tslint:disable-next-line:no-unused-expression
yargs
.command(
'create <type> <spec>',
'Create a benchmark, candidate, or suite from a specification where <type> is either "benchmark", "candidate", or "suite".',
() => {},
create
)
.command(
'list <type>',
'Display summary information about benchmarks, candidates, runs, and suites.',
() => {},
list
)
.command(
'results <benchmark> <suite>',
'Display the results of all runs against a named benchmark and suite.',
() => {},
results
)
.command(
'run <candidate> <suite>',
'Run a named <candidate> against a named <suite>.',
() => {},
run
)
.command(
'show <type> [name]',
'Display all benchmarks, candidates, suites, or runs. If optional [name] is specified, only show matching items.',
() => {},
show
)
.example(
'$0 create benchmark foo.yaml',
'Create or update the benchmark specified in foo.yaml.'
)
.example(
'$0 create candidate bar.yaml',
'Create or update the candidate specified in bar.yaml.'
)
.example(
'$0 create suite baz.yaml',
'Create or update the suite specified in baz.yaml.'
)
.example('$0 list benchmark', 'List benchmarks.')
.example('$0 list candidate', 'List candidates.')
.example('$0 list suite', 'List suites.')
.example('$0 list run', 'List runs.')
.example('$0 show benchmark', 'Display specifications for all benchmarks.')
.example('$0 show benchmark foo', 'Display specification for benchmark foo.')
.example('$0 show candidate', 'Display specifications for all candidates.')
.example('$0 show candidate bar', 'Display specification for candidate bar.')
.example('$0 show suite', 'Display specifications for all suites.')
.example('$0 show suite baz', 'Display specification for suite baz.')
.example('$0 run bar baz', 'Run candidate bar on suite baz.')
.example(
'$0 results foo baz',
'Display results for benchmark foo runs against suite baz.'
).argv;
function main2(argv: string[]) {
const program = new Command();
program.description('Data Contest Toolkit CLI');
program.version(apiVersion);
program
.command('create <type> <spec>')
.description(
'Create a benchmark, candidate, or suite from a specification where <type> is either "benchmark", "candidate", or "suite".'
)
.action(create);
program
.command('list <type>')
.description(
'Display summary information about benchmarks, candidates, runs, and suites.'
)
.action(list);
program
.command('results <benchmark> <suite>')
.description(
'Display the results of all runs against a named benchmark and suite.'
)
.action(results);
program
.command('run <candidate> <suite>')
.description('Run a named <candidate> against a named <suite>.')
.action(run);
program
.command('show <type> [name]')
.description(
'Display all benchmarks, candidates, suites, or runs. If optional [name] is specified, only show matching items.'
)
.action(show);
program
.command('examples')
.description('Show usage examples.')
.action(() => examples(argv));
// program.on('--help', showExamples);
program.parse(argv);
}
function examples(argv: string[]) {
const examples = [
['list benchmark', 'List benchmarks.'],
['list candidate', 'List candidates.'],
['list suite', 'List suites.'],
['list run', 'List runs.'],
['show benchmark', 'Display specifications for all benchmarks.'],
['show benchmark foo', 'Display specification for benchmark foo.'],
['show candidate', 'Display specifications for all candidates.'],
['show candidate bar', 'Display specification for candidate bar.'],
['show suite', 'Display specifications for all suites.'],
['show suite baz', 'Display specification for suite baz.'],
['run bar baz', 'Run candidate bar on suite baz.'],
[
'results foo baz',
'Display results for runs against benchmark and suite baz.',
],
];
const program = path.basename(argv[1]);
const alignments = [Alignment.LEFT, Alignment.LEFT];
const rows: string[][] = [];
for (const [cmd, text] of examples) {
rows.push([`node ${program} ${cmd}`, text]);
}
console.log(
'For more information and examples, see https://github.com/microsoft/data-contest-toolkit.'
);
console.log();
for (const row of formatTable(alignments, rows)) {
console.log(row);
}
}
// function main(args: string[]) {
// // tslint:disable-next-line:no-unused-expression
// yargs
// .command(
// 'create <type> <spec>',
// 'Create a benchmark, candidate, or suite from a specification where <type> is either "benchmark", "candidate", or "suite".',
// () => {},
// create
// )
// .command(
// 'list <type>',
// 'Display summary information about benchmarks, candidates, runs, and suites.',
// () => {},
// list
// )
// .command(
// 'results <benchmark> <suite>',
// 'Display the results of all runs against a named benchmark and suite.',
// () => {},
// results
// )
// .command(
// 'run <candidate> <suite>',
// 'Run a named <candidate> against a named <suite>.',
// () => {},
// run
// )
// .command(
// 'show <type> [name]',
// 'Display all benchmarks, candidates, suites, or runs. If optional [name] is specified, only show matching items.',
// () => {},
// show
// )
// // .command(
// // '$0',
// // 'The default command',
// // () => {},
// // () => {
// // console.log('Expected a command');
// // // yargs.showHelp();
// // }
// // )
// .demandCommand(1, 'xxx')
// // .demandCommand(1, 'xxx')
// .showHelpOnFail(true)
// .example(
// '$0 create benchmark foo.yaml',
// 'Create or update the benchmark specified in foo.yaml.'
// )
// .example(
// '$0 create candidate bar.yaml',
// 'Create or update the candidate specified in bar.yaml.'
// )
// .example(
// '$0 create suite baz.yaml',
// 'Create or update the suite specified in baz.yaml.'
// )
// .example('$0 list benchmark', 'List benchmarks.')
// .example('$0 list candidate', 'List candidates.')
// .example('$0 list suite', 'List suites.')
// .example('$0 list run', 'List runs.')
// .example('$0 show benchmark', 'Display specifications for all benchmarks.')
// .example('$0 show benchmark foo', 'Display specification for benchmark foo.')
// .example('$0 show candidate', 'Display specifications for all candidates.')
// .example('$0 show candidate bar', 'Display specification for candidate bar.')
// .example('$0 show suite', 'Display specifications for all suites.')
// .example('$0 show suite baz', 'Display specification for suite baz.')
// .example('$0 run bar baz', 'Run candidate bar on suite baz.')
// .example(
// '$0 results foo baz',
// 'Display results for benchmark foo runs against suite baz.'
// )
// .version(apiVersion)
// .parse(args.slice(2))
// // .parse(['list', 'benchmark'])
// .argv;
// console.log(args);
// }
///////////////////////////////////////////////////////////////////////////////
//
// Create
//
///////////////////////////////////////////////////////////////////////////////
async function create(params: { type: string; spec: string }) {
console.log(`execute create command ${params.type} ${params.spec}`);
await dispatch(
params.type,
['benchmark', 'candidate', 'suite'],
createHelper,
[params.spec]
);
async function create(type: string, spec: string) {
console.log(`execute create command ${type} ${spec}`);
await dispatch(type, ['benchmark', 'candidate', 'suite'], createHelper, [
spec,
]);
}
// async function create(params: { type: string; spec: string }) {
// console.log(`execute create command ${params.type} ${params.spec}`);
// await dispatch(
// params.type,
// ['benchmark', 'candidate', 'suite'],
// createHelper,
// [params.spec]
// );
// }
async function createHelper<T>(ops: ISpecOps<T>, specFile: string) {
const spec = ops.load(specFile);
await ops.upsert(spec);
@ -113,15 +225,24 @@ async function createHelper<T>(ops: ISpecOps<T>, specFile: string) {
// List
//
///////////////////////////////////////////////////////////////////////////////
async function list(params: { type: string }) {
async function list(type: string) {
await dispatch(
params.type,
type,
['benchmark', 'candidate', 'run', 'suite'],
listHelper,
[]
);
}
// async function list(params: { type: string }) {
// await dispatch(
// params.type,
// ['benchmark', 'candidate', 'run', 'suite'],
// listHelper,
// []
// );
// }
async function listHelper<T extends IEntityBase>(ops: ISpecOps<T>) {
const specs = await ops.all();
@ -148,9 +269,12 @@ async function listHelper<T extends IEntityBase>(ops: ISpecOps<T>) {
// Results
//
///////////////////////////////////////////////////////////////////////////////
async function results(params: { benchmark: string; suite: string }) {
console.log(`execute results command ${params.benchmark} ${params.suite}`);
const results = await lab.allRunResults(params.benchmark, params.suite);
// async function results(params: { benchmark: string; suite: string }) {
// console.log(`execute results command ${params.benchmark} ${params.suite}`);
// const results = await lab.allRunResults(params.benchmark, params.suite);
async function results(benchmark: string, suite: string) {
console.log(`execute results command ${benchmark} ${suite}`);
const results = await lab.allRunResults(benchmark, suite);
// TODO: format as table.
const columns = new Set<string>();
@ -201,27 +325,39 @@ async function results(params: { benchmark: string; suite: string }) {
// Run
//
///////////////////////////////////////////////////////////////////////////////
async function run(params: { candidate: string; suite: string }) {
console.log(`execute run command ${params.candidate} ${params.suite}`);
const run = await lab.createRunRequest(params);
async function run(candidate: string, suite: string) {
console.log(`execute run command ${candidate} ${suite}`);
const run = await lab.createRunRequest({ candidate, suite });
console.log(`Scheduling run ${run.name}`);
}
// async function run(params: { candidate: string; suite: string }) {
// console.log(`execute run command ${params.candidate} ${params.suite}`);
// const run = await lab.createRunRequest(params);
// console.log(`Scheduling run ${run.name}`);
// }
///////////////////////////////////////////////////////////////////////////////
//
// Show
//
///////////////////////////////////////////////////////////////////////////////
async function show(params: { type: string; name?: string }) {
console.log(`execute show command ${params.type} ${params.name}`);
await dispatch(
params.type,
['benchmark', 'candidate', 'run', 'suite'],
showHelper,
[params.name]
);
async function show(type: string, name?: string) {
console.log(`execute show command ${type} ${name}`);
await dispatch(type, ['benchmark', 'candidate', 'run', 'suite'], showHelper, [
name,
]);
}
// async function show(params: { type: string; name?: string }) {
// console.log(`execute show command ${params.type} ${params.name}`);
// await dispatch(
// params.type,
// ['benchmark', 'candidate', 'run', 'suite'],
// showHelper,
// [params.name]
// );
// }
async function showHelper<T>(ops: ISpecOps<T>, name?: string) {
if (name) {
const spec = await ops.one(name);
@ -336,32 +472,25 @@ async function go() {
// await show({type: 'suite'});
// await run({candidate: 'candidate1', suite: 'suite1'});
// await run({candidate: 'candidate1', suite: 'suite1'});
await list({ type: 'benchmark' });
await list({ type: 'run' });
await results({ benchmark: 'benchmark1', suite: 'suite1' });
await lab.reportRunResults('14b37f60-6a2a-11ea-bd94-8fa64eaf2878', {
passed: 5,
failed: 6,
});
await lab.reportRunResults('7984abd0-6a2a-11ea-bd94-8fa64eaf2878', {
passed: 5,
skipped: 7,
});
await results({ benchmark: 'benchmark1', suite: 'suite1' });
// await list({ type: 'benchmark' });
// await list({ type: 'run' });
// await results({ benchmark: 'benchmark1', suite: 'suite1' });
// await lab.reportRunResults('14b37f60-6a2a-11ea-bd94-8fa64eaf2878', {
// passed: 5,
// failed: 6,
// });
// await lab.reportRunResults('7984abd0-6a2a-11ea-bd94-8fa64eaf2878', {
// passed: 5,
// skipped: 7,
// });
// await results({ benchmark: 'benchmark1', suite: 'suite1' });
}
go();
// go();
// function go2() {
// const now = new Date();
// const l = DateTime.fromJSDate(now);
// console.log(now.toISOString());
// console.log(l.toFormat('D ttt'));
// console.log(l.toFormat('D TTT'));
// console.log(l.toFormat('y-LL-dd TTT')); // Best
// console.log(l.toFormat('y-LL-dd TTZZZZ'));
// }
// main2();
main2(process.argv);
// main(['node', 'dct.js', 'list', 'run']);
// go2();

Просмотреть файл

@ -3,12 +3,21 @@
* Top
* REVIEW: are suite names globally unique or namespaced to benchmarks?
* CLI
* Consider using luxon in reviver
* Set version
* x Spike commander as replacement for yargs
* examples in usage()
* Bash completion api
* usage configuration in yargs
* = Consider using luxon in reviver - probably can't since io-ts uses Date
* x Set version
* list runs should show status, suite, candidate
* ReferenceError: Cannot access 'benchmarkOps' before initialization
* node build\src\cli\dct.js list benchmark
* Convert to class
* error message
* node build\src\cli\dct.js create foo bar
* node build\src\cli\dct.js show foo
* x ReferenceError: Cannot access 'benchmarkOps' before initialization
* x node build\src\cli\dct.js list benchmark
* No error for bad command-line arguments
* node build\src\cli\dct.js r benchmark1 suite2
* x Convert to class - converted to function instead.
* Consider using SQL ids instead of GUIDs for run name
* Better error messages for errors transported on the wire
* Top-level try/catch reporter for better error messages