Skip to content

Commit

Permalink
fix: Passing of flags to executed programs (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
doosuu authored Aug 23, 2023
1 parent 2dab079 commit ed7f69c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 13 deletions.
47 changes: 36 additions & 11 deletions src/commands/exec/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,54 @@ Executing script...
static args = {
component: Args.string({ description: 'The component which provides the program', required: true }),
ref: Args.string({ description: 'Reference to the ID of the program to execute', required: true }),
'args...': Args.string({ description: 'Args for the executed program', required: false }),
'args...': Args.string({
description:
'Args for the executed program. All arguments and flags provided after the ref are forwarded to the invoked program.',
required: false,
}),
};

static flags = {
verbose: Flags.boolean({ char: 'v', aliases: ['verbose'], description: 'Enable verbose logging', required: false, default: false }),
args: Flags.string({ description: 'Args for the executed program', multiple: true, required: false }),
verbose: Flags.boolean({
char: 'v',
aliases: ['verbose'],
description:
'Enable verbose logging. The flag may be provided before or in between the 2 positional arguments of exec. Providing the flag after the 2nd positional argument will forward the flag to the invoked program.',
required: false,
default: false,
}),
};

private _extractProgramArgsAndFlags(): string[] {
// we expect 2 positional args: component and program-ref
// everything after that is an argument for the invoked
// program.
// the verbose flag may be anywhere in between but **NOT**
// after the program-ref.
let positionalArgsCount = 0;
let programArgsStartIndex;
for (programArgsStartIndex = 0; programArgsStartIndex < this.argv.length; ++programArgsStartIndex) {
if (!this.argv[programArgsStartIndex].startsWith('-')) {
++positionalArgsCount;
}

if (positionalArgsCount >= 2) {
break;
}
}

return this.argv.splice(Math.min(programArgsStartIndex + 1, this.argv.length));
}

async run(): Promise<void> {
const programArgsAndFlags = this._extractProgramArgsAndFlags();
const { args, flags } = await this.parse(Exec);

const projectConfig = ProjectConfig.read();

// OCLIF does not support varargs (lists) out of the box.
// Their suggestion is to set "strict" argument parsing to false but no further documentation is available.
// Hence we access the Command.argv attribute which holds all arguments passed to the CLI
// and access everything STARTING AFTER the args."ref" argument as "custom args" for the invoked program.
const customArgsOffset = Object.keys(args).indexOf('ref') + 1;
const execArgs: string[] = this.argv.length > customArgsOffset ? this.argv.slice(customArgsOffset) : [];

const execSpec: ExecSpec = {
ref: args.ref,
args: execArgs,
args: programArgsAndFlags,
};

const appManifestData = readAppManifest();
Expand Down
55 changes: 53 additions & 2 deletions test/system-test/exec.stest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ describe('CLI command', () => {
encoding: DEFAULT_BUFFER_ENCODING,
});

const lines = result.stdout.split('\r\n');
const lines = result.stdout.split('\n').map((line) => line.trim());
expect(lines.length).to.be.equal(6);
expect(lines[0]).to.be.equal('./print-args.py');
expect(lines[1]).to.be.equal('default');
Expand All @@ -115,12 +115,63 @@ describe('CLI command', () => {
encoding: DEFAULT_BUFFER_ENCODING,
});

const lines = result.stdout.split('\r\n');
const lines = result.stdout.split('\n').map((line) => line.trim());
expect(lines.length).to.be.equal(2);
expect(lines[0]).to.be.equal('./print-args.py');
expect(lines[1]).to.be.equal('');
});

it('should pass flags after the program-ref to the executed program', async () => {
const result = spawnSync(VELOCITAS_PROCESS, ['exec', 'test-component', 'print-args-no-default', '--flag', 'myValue'], {
encoding: DEFAULT_BUFFER_ENCODING,
});

const lines = result.stdout.split('\n').map((line) => line.trim());
expect(lines.length).to.be.equal(4);
expect(lines[0]).to.be.equal('./print-args.py');
expect(lines[1]).to.be.equal('--flag');
expect(lines[2]).to.be.equal('myValue');
expect(lines[3]).to.be.equal('');
});

it('should not pass the verbose flag in 2nd position to the executed program', async () => {
const result = spawnSync(
VELOCITAS_PROCESS,
['exec', 'test-component', '-v', 'print-args-no-default', '-random', 'flag', 'regular=flag2'],
{
encoding: DEFAULT_BUFFER_ENCODING,
},
);

const lines = result.stdout.split('\n').map((line) => line.trim());
expect(lines.length).to.be.equal(6);
expect(lines[0]).to.be.equal('Starting test-component/print-args-no-default');
expect(lines[1]).to.be.equal('./print-args.py');
expect(lines[2]).to.be.equal('-random');
expect(lines[3]).to.be.equal('flag');
expect(lines[4]).to.be.equal('regular=flag2');
expect(lines[5]).to.be.equal('');
});

it('should not pass the verbose flag in 1st position to the executed program', async () => {
const result = spawnSync(
VELOCITAS_PROCESS,
['exec', '-v', 'test-component', 'print-args-no-default', '-other', 'thing', 'regular=flag2'],
{
encoding: DEFAULT_BUFFER_ENCODING,
},
);

const lines = result.stdout.split('\n').map((line) => line.trim());
expect(lines.length).to.be.equal(6);
expect(lines[0]).to.be.equal('Starting test-component/print-args-no-default');
expect(lines[1]).to.be.equal('./print-args.py');
expect(lines[2]).to.be.equal('-other');
expect(lines[3]).to.be.equal('thing');
expect(lines[4]).to.be.equal('regular=flag2');
expect(lines[5]).to.be.equal('');
});

it('should return the error code of the first executed program which returns an error', async () => {
const result = spawnSync(VELOCITAS_PROCESS, ['exec', 'test-component', 'exit'], {
encoding: DEFAULT_BUFFER_ENCODING,
Expand Down

0 comments on commit ed7f69c

Please sign in to comment.