Skip to content

Commit

Permalink
Merge pull request #1869 from microsoft/octogonz/tsc-improvements
Browse files Browse the repository at this point in the history
[ts-command-line] Add defineCommandLineRemainder() feature
  • Loading branch information
octogonz authored May 15, 2020
2 parents 510dfd9 + 8571c7a commit 818e085
Show file tree
Hide file tree
Showing 35 changed files with 1,411 additions and 386 deletions.
15 changes: 15 additions & 0 deletions build-tests/ts-command-line-test/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Widget CLI",
"program": "${workspaceFolder}/lib/start.js",
"args": [ "run", "1", "2", "3" ]
}
]
}
139 changes: 139 additions & 0 deletions build-tests/ts-command-line-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# ts-command-line-test

This project folder is a minimal code sample illustrating how to make a command-line tool
using the [@rushstack/ts-command-line](https://www.npmjs.com/package/@rushstack/ts-command-line) library.
Building this project is one of the CI tests for the library.

## Trying the demo

Compile the project:
```sh
# clone the repo
$ git clone https://github.com/microsoft/rushstack
$ cd rushstack

# build the code
$ rush install
$ rush rebuild

# run the demo using Bash
$ cd build_tests/ts-command-line-test
$ ./widget.sh --help

# OR, run the demo using Windows shell
$ cd build_tests\ts-command-line-test
$ widget --help
```

You should see something like this:

```
usage: widget [-h] [-v] <command> ...
The "widget" tool is a code sample for using the @rushstack/ts-command-line
library.
Positional arguments:
<command>
push Pushes a widget to the service
run This action (hypothetically) passes its command line
arguments to the shell to be executed.
Optional arguments:
-h, --help Show this help message and exit.
-v, --verbose Show extra logging detail
For detailed help about a specific command, use: widget <command> -h
```

This top-level command line is defined in [WidgetCommandLine.ts](./src/WidgetCommandLine.ts).

## Command line "actions"

Actions are an optional feature of **ts-command-line**. They work like Git subcommands.
Our `widget` demo supports two actions, `push` and `run`. For example, if you type this:

```sh
$ ./widget.sh push --help
```

...then you should see specialized help for the "push" action:

```
usage: widget push [-h] [-f] [--protocol {ftp,webdav,scp}]
Here we provide a longer description of how our action works.
Optional arguments:
-h, --help Show this help message and exit.
-f, --force Push and overwrite any existing state
--protocol {ftp,webdav,scp}
Specify the protocol to use. This parameter may
alternatively specified via the WIDGET_PROTOCOL
environment variable. The default value is "scp".
```

The "push" action is defined in [PushAction.ts](./src/PushAction.ts).


The demo prints its command line arguments when you invoke the action:

```sh
$ ./widget.sh push --protocol webdav --force

Business logic configured the logger: verbose=false
Received parameters: force=true, protocol="webdav"
Business logic did the work.
```

## Some advanced features

The `run` command illustrates a couple other interesting features. It shows how to
use `defineCommandLineRemainder()` to capture the remainder of the command line arguments.

```
usage: widget run [-h] [--title TITLE] ...
This demonstrates how to use the defineCommandLineRemainder() API.
Positional arguments:
"..." The remaining arguments are passed along to the command
shell.
Optional arguments:
-h, --help Show this help message and exit.
--title TITLE An optional title to show in the console window. This
parameter may alternatively specified via the WIDGET_TITLE
environment variable.
```

The "run" action is defined in [RunAction.ts](./src/RunAction.ts).

Example invocation:

```sh
$ ./widget.sh run --title "Hello" 1 2 3

Business logic configured the logger: verbose=false
Console Title: Hello
Arguments to be executed: ["1","2","3"]
```

Also, notice that `environmentVariable: 'WIDGET_TITLE'` allows the title to be specified using a
Bash environment variable:

```sh
$ export WIDGET_TITLE="Default title"
$ ./widget.sh run 1 2 3

Business logic configured the logger: verbose=false
Console Title: Default title
Arguments to be executed: ["1","2","3"]
```

For more about environment variables, see the [IBaseCommandLineDefinition.environmentVariable](https://rushstack.io/pages/api/ts-command-line.ibasecommandlinedefinition.environmentvariable/) documentation.

## More information

See [@rushstack/ts-command-line](https://www.npmjs.com/package/@rushstack/ts-command-line) for details.

20 changes: 20 additions & 0 deletions build-tests/ts-command-line-test/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const fsx = require('fs-extra');
const child_process = require('child_process');
const path = require('path');
const process = require('process');

function executeCommand(command) {
console.log('---> ' + command);
child_process.execSync(command, { stdio: 'inherit' });
}

// Clean the old build outputs
console.log(`==> Starting build.js for ${path.basename(process.cwd())}`);
fsx.emptyDirSync('dist');
fsx.emptyDirSync('lib');
fsx.emptyDirSync('temp');

// Run the TypeScript compiler
executeCommand('node node_modules/typescript/lib/tsc');

console.log(`==> Finished build.js for ${path.basename(process.cwd())}`);
16 changes: 16 additions & 0 deletions build-tests/ts-command-line-test/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "ts-command-line-test",
"description": "Building this project is a regression test for ts-command-line",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "node build.js",
"start": "node ./lib/start.js"
},
"devDependencies": {
"@rushstack/ts-command-line": "4.3.14",
"@types/node": "10.17.13",
"fs-extra": "~7.0.1",
"typescript": "~3.7.2"
}
}
13 changes: 13 additions & 0 deletions build-tests/ts-command-line-test/src/BusinessLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

export class BusinessLogic {
public static async doTheWork(force: boolean, protocol: string): Promise<void> {
console.log(`Received parameters: force=${force}, protocol="${protocol}"`);
console.log(`Business logic did the work.`);
}

public static configureLogger(verbose: boolean): void {
console.log(`Business logic configured the logger: verbose=${verbose}`);
}
}
38 changes: 38 additions & 0 deletions build-tests/ts-command-line-test/src/PushAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { CommandLineFlagParameter, CommandLineAction, CommandLineChoiceParameter } from '@rushstack/ts-command-line';
import { BusinessLogic } from './BusinessLogic';

export class PushAction extends CommandLineAction {
private _force: CommandLineFlagParameter;
private _protocol: CommandLineChoiceParameter;

public constructor() {
super({
actionName: 'push',
summary: 'Pushes a widget to the service',
documentation: 'Here we provide a longer description of how our action works.'
});
}

protected onExecute(): Promise<void> { // abstract
return BusinessLogic.doTheWork(this._force.value, this._protocol.value || "(none)");
}

protected onDefineParameters(): void { // abstract
this._force = this.defineFlagParameter({
parameterLongName: '--force',
parameterShortName: '-f',
description: 'Push and overwrite any existing state'
});

this._protocol = this.defineChoiceParameter({
parameterLongName: '--protocol',
description: 'Specify the protocol to use',
alternatives: ['ftp', 'webdav', 'scp'],
environmentVariable: 'WIDGET_PROTOCOL',
defaultValue: 'scp'
});
}
}
37 changes: 37 additions & 0 deletions build-tests/ts-command-line-test/src/RunAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { CommandLineAction, CommandLineStringParameter } from '@rushstack/ts-command-line';


export class RunAction extends CommandLineAction {
private _title: CommandLineStringParameter;

public constructor() {
super({
actionName: 'run',
summary: 'This action (hypothetically) passes its command line arguments to the shell to be executed.',
documentation: 'This demonstrates how to use the defineCommandLineRemainder() API.'
});
}

protected onExecute(): Promise<void> { // abstract
console.log(`Console Title: ${this._title.value || '(none)'}`);
console.log('Arguments to be executed: ' + JSON.stringify(this.remainder!.values));

return Promise.resolve();
}

protected onDefineParameters(): void { // abstract
this._title = this.defineStringParameter({
parameterLongName: '--title',
argumentName: 'TITLE',
environmentVariable: 'WIDGET_TITLE',
description: 'An optional title to show in the console window'
});

this.defineCommandLineRemainder({
description: 'The remaining arguments are passed along to the command shell.'
});
}
}
34 changes: 34 additions & 0 deletions build-tests/ts-command-line-test/src/WidgetCommandLine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { CommandLineParser, CommandLineFlagParameter } from '@rushstack/ts-command-line';
import { PushAction } from './PushAction';
import { RunAction } from './RunAction';
import { BusinessLogic } from './BusinessLogic';

export class WidgetCommandLine extends CommandLineParser {
private _verbose: CommandLineFlagParameter;

public constructor() {
super({
toolFilename: 'widget',
toolDescription: 'The "widget" tool is a code sample for using the @rushstack/ts-command-line library.'
});

this.addAction(new PushAction());
this.addAction(new RunAction());
}

protected onDefineParameters(): void { // abstract
this._verbose = this.defineFlagParameter({
parameterLongName: '--verbose',
parameterShortName: '-v',
description: 'Show extra logging detail'
});
}

protected onExecute(): Promise<void> { // override
BusinessLogic.configureLogger(this._verbose.value);
return super.onExecute();
}
}
7 changes: 7 additions & 0 deletions build-tests/ts-command-line-test/src/start.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { WidgetCommandLine } from './WidgetCommandLine';

const commandLine: WidgetCommandLine = new WidgetCommandLine();
commandLine.execute();
26 changes: 26 additions & 0 deletions build-tests/ts-command-line-test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es6",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"types": [
"node"
],
"lib": [
"es5",
"scripthost",
"es2015.collection",
"es2015.promise",
"es2015.iterable",
"dom"
],
"outDir": "lib"
},
"include": [
"src/**/*.ts"
]
}
2 changes: 2 additions & 0 deletions build-tests/ts-command-line-test/widget.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
node .\lib\start.js %*
2 changes: 2 additions & 0 deletions build-tests/ts-command-line-test/widget.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exec node ./lib/start.js "$@"
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/ts-command-line",
"comment": "Add a new feature defineCommandLineRemainder() which allows additional unvalidated CLI arguments, e.g. to pass along to another tool",
"type": "minor"
}
],
"packageName": "@rushstack/ts-command-line",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/ts-command-line",
"comment": "Fix a bug with environmentVariable mapping for CommandLineFlagParameter",
"type": "patch"
}
],
"packageName": "@rushstack/ts-command-line",
"email": "[email protected]"
}
Loading

0 comments on commit 818e085

Please sign in to comment.