Skip to content

Commit

Permalink
✨ Automatically include package information with CLI commands (#850)
Browse files Browse the repository at this point in the history
  • Loading branch information
wwilsman authored Mar 28, 2022
1 parent a2d03ae commit 18fce17
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 11 deletions.
3 changes: 3 additions & 0 deletions packages/cli-command/src/command.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ async function runCommandWithContext(parsed) {
// include flags, args, argv, logger, exit helper, and env info
let context = { flags, args, argv, log, exit };
let env = context.env = process.env;
let pkg = command.packageInformation;
let def = command.definition;

// automatically include a preconfigured percy instance
Expand All @@ -112,6 +113,8 @@ async function runCommandWithContext(parsed) {

// set defaults and prune preconfiguraton options
let conf = del({ server: false, ...def.percy }, 'discoveryFlags');
if (pkg) conf.clientInfo ||= `${pkg.name}/${pkg.version}`;
conf.environmentInfo ||= `node/${process.version}`;

Object.defineProperty(context, 'percy', {
configurable: true,
Expand Down
13 changes: 10 additions & 3 deletions packages/cli-command/src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async function maybeParseCommand(input, parsed) {
// Parses input to identify a flag option. Consumes input and adds to parsed output as needed,
// returning true if any defined flag was found. If a found flag is missing a required argument, the
// next input option will be consumed as that argument. Boolean flags support negation if their
// default value is true, integer flags are automaticaly parsed, and flags that can be provided
// default value is true, integer flags are automatically parsed, and flags that can be provided
// multiple times will be concatenated together. Implicit help and version flags are also supported.
async function maybeParseFlag(input, parsed) {
if (!isFlag(input[0])) return;
Expand Down Expand Up @@ -323,8 +323,15 @@ async function normalizeCommand(command, properties) {
definition.commands = await definition.commands();
}

// return shallow copy with additional properties
return { ...command, ...properties, definition };
// create a shallow copy with additional properties
let normalized = { ...command, ...properties, definition };

// inherit parent package information by default
normalized.packageInformation ||= (
properties?.parent || command.parent
)?.packageInformation;

return normalized;
}

// Parses and validates command-line arguments according to a command definition.
Expand Down
22 changes: 14 additions & 8 deletions packages/cli-command/test/command.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,27 @@ describe('Command', () => {
it('initializes the percy instance with provided percy options', async () => {
let test = command('foo', {
flags: [{
name: 'client',
type: 'info',
percyrc: 'clientInfo'
name: 'dry',
percyrc: 'dryRun'
}],
percy: {
environmentInfo: 'env/456'
environmentInfo: 'env/4.5.6'
}
}, ({ percy }) => {
test.client = percy.client;
test.percy = percy;
});

await test(['--client', 'client/123']);
// automatic client info from package.json
test.packageInformation = {
name: 'percy-cli-sdk',
version: '1.2.3'
};

await test(['--dry']);

expect(test.client.clientInfo).toEqual(new Set(['client/123']));
expect(test.client.environmentInfo).toEqual(new Set(['env/456']));
expect(test.percy.dryRun).toBe(true);
expect(test.percy.client.clientInfo).toEqual(new Set(['percy-cli-sdk/1.2.3']));
expect(test.percy.client.environmentInfo).toEqual(new Set(['env/4.5.6']));
});

it('handles logging unhandled action errors', async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export async function importCommands() {
pkgs.set(pkg.name, () => Promise.all(
pkg['@percy/cli'].commands.map(async cmdPath => {
let module = await import(path.join(pkgPath, cmdPath));
module.default.packageInformation ||= pkg;
return module.default;
})
));
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/test/commands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ describe('CLI commands', () => {
expect(logger.stderr).toEqual([]);
});

it('automatically includes package information', async () => {
mockModuleCommands(path.resolve('.'), mockCmds);
let cmds = await importCommands();

expect(cmds[0].packageInformation.name).toEqual('@percy/cli-config');
});

it('handles errors and logs debug info', async () => {
fs.$vol.fromJSON({ './node_modules': null });
fs.readdirSync.and.throwError(new Error('EACCES'));
Expand Down

0 comments on commit 18fce17

Please sign in to comment.