Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions packages/catalyst/src/cli/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { existsSync } from 'node:fs';
import { copyFile, cp, writeFile } from 'node:fs/promises';
import { join } from 'node:path';

import { withErrorHandler } from '../lib/error-handler';
import { getModuleCliPath } from '../lib/get-module-cli-path';
import { consola } from '../lib/logger';
import { getProjectConfig } from '../lib/project-config';
Expand Down Expand Up @@ -95,10 +96,9 @@ export const build = new Command('build')
'catalyst',
]),
)
.action(async (nextBuildOptions, options) => {
const coreDir = process.cwd();

try {
.action(
withErrorHandler('build', async (nextBuildOptions, options) => {
const coreDir = process.cwd();
const config = getProjectConfig();
const framework = options.framework ?? config.get('framework');

Expand Down Expand Up @@ -128,8 +128,5 @@ export const build = new Command('build')

await buildCatalystProject(projectUuid);
}
} catch (error) {
consola.error(error);
process.exit(1);
}
});
}),
);
10 changes: 2 additions & 8 deletions packages/catalyst/src/cli/commands/deploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,7 @@ describe('--prebuilt flag', () => {
]);

expect(consola.error).toHaveBeenCalledWith(
expect.objectContaining({
message:
'No build output found at .bigcommerce/dist/. Run `catalyst build` first or remove `--prebuilt` to build automatically.',
}),
'No build output found at .bigcommerce/dist/. Run `catalyst build` first or remove `--prebuilt` to build automatically.',
);
expect(exitMock).toHaveBeenCalledWith(1);

Expand Down Expand Up @@ -418,10 +415,7 @@ describe('--prebuilt flag', () => {
]);

expect(consola.error).toHaveBeenCalledWith(
expect.objectContaining({
message:
'No build output found at .bigcommerce/dist/. Run `catalyst build` first or remove `--prebuilt` to build automatically.',
}),
'No build output found at .bigcommerce/dist/. Run `catalyst build` first or remove `--prebuilt` to build automatically.',
);
expect(exitMock).toHaveBeenCalledWith(1);

Expand Down
21 changes: 11 additions & 10 deletions packages/catalyst/src/cli/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import yoctoSpinner from 'yocto-spinner';
import { z } from 'zod';

import { getDeploymentErrorMessage } from '../lib/deployment-errors';
import { withErrorHandler } from '../lib/error-handler';
import { consola } from '../lib/logger';
import { getProjectConfig } from '../lib/project-config';
import { Telemetry } from '../lib/telemetry';
import { getTelemetry } from '../lib/telemetry';

import { buildCatalystProject } from './build';

const telemetry = new Telemetry();

const stepsEnum = z.enum([
'initializing',
'downloading',
Expand Down Expand Up @@ -119,6 +118,7 @@ export const generateUploadSignature = async (
'X-Auth-Token': accessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
'X-Correlation-Id': getTelemetry().sessionId,
},
body: JSON.stringify({}),
},
Expand Down Expand Up @@ -195,6 +195,7 @@ export const createDeployment = async (
'X-Auth-Token': accessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
'X-Correlation-Id': getTelemetry().sessionId,
},
body: JSON.stringify({
project_uuid: projectUuid,
Expand Down Expand Up @@ -234,6 +235,7 @@ export const getDeploymentStatus = async (
'X-Auth-Token': accessToken,
Accept: 'text/event-stream',
Connection: 'keep-alive',
'X-Correlation-Id': getTelemetry().sessionId,
},
},
);
Expand Down Expand Up @@ -317,6 +319,7 @@ export const fetchProject = async (
'X-Auth-Token': accessToken,
'Content-Type': 'application/json',
Accept: 'application/json',
'X-Correlation-Id': getTelemetry().sessionId,
},
},
);
Expand Down Expand Up @@ -373,9 +376,10 @@ export const deploy = new Command('deploy')
'--prebuilt',
'Skip the build step. Requires .bigcommerce/dist/ to already contain build output.',
)
.action(async (options) => {
try {
.action(
withErrorHandler('deploy', async (options) => {
const config = getProjectConfig();
const telemetry = getTelemetry();

await telemetry.identify(options.storeHash);

Expand Down Expand Up @@ -448,8 +452,5 @@ export const deploy = new Command('deploy')
options.accessToken,
options.apiHost,
);
} catch (error) {
consola.error(error);
process.exit(1);
}
});
}),
);
13 changes: 5 additions & 8 deletions packages/catalyst/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { execa } from 'execa';
import { existsSync } from 'node:fs';
import { join } from 'node:path';

import { consola } from '../lib/logger';
import { withErrorHandler } from '../lib/error-handler';

export const dev = new Command('dev')
.description('Start the Catalyst development server.')
Expand All @@ -15,8 +15,8 @@ export const dev = new Command('dev')
'[options...]',
'Next.js `dev` options (see: https://nextjs.org/docs/app/api-reference/cli/next#next-dev-options)',
)
.action(async (options) => {
try {
.action(
withErrorHandler('dev', async (options) => {
const nextBin = join('node_modules', '.bin', 'next');

if (!existsSync(nextBin)) {
Expand All @@ -29,8 +29,5 @@ export const dev = new Command('dev')
stdio: 'inherit',
cwd: process.cwd(),
});
} catch (error) {
consola.error(error instanceof Error ? error.message : error);
process.exit(1);
}
});
}),
);
24 changes: 14 additions & 10 deletions packages/catalyst/src/cli/commands/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,21 @@ beforeAll(async () => {
consola.mockTypes(() => vi.fn());

vi.mock('../lib/telemetry', () => {
const instance = {
identify: mockIdentify,
isEnabled: vi.fn(() => true),
track: vi.fn(),
sessionId: 'test-session-uuid',
durationMs: vi.fn().mockReturnValue(0),
analytics: {
closeAndFlush: vi.fn().mockResolvedValue(undefined),
},
};

return {
Telemetry: vi.fn().mockImplementation(() => {
return {
identify: mockIdentify,
isEnabled: vi.fn(() => true),
track: vi.fn(),
analytics: {
closeAndFlush: vi.fn(),
},
};
}),
Telemetry: vi.fn().mockImplementation(() => instance),
getTelemetry: vi.fn(() => instance),
resetTelemetry: vi.fn(),
};
});

Expand Down
44 changes: 17 additions & 27 deletions packages/catalyst/src/cli/commands/project.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Command, Option } from 'commander';

import { withErrorHandler } from '../lib/error-handler';
import { consola } from '../lib/logger';
import { createProject, fetchProjects } from '../lib/project';
import { getProjectConfig } from '../lib/project-config';
import { Telemetry } from '../lib/telemetry';

const telemetry = new Telemetry();
import { getTelemetry } from '../lib/telemetry';

const list = new Command('list')
.description('List BigCommerce infrastructure projects for your store.')
Expand All @@ -26,8 +25,8 @@ const list = new Command('list')
.env('BIGCOMMERCE_API_HOST')
.default('api.bigcommerce.com'),
)
.action(async (options) => {
try {
.action(
withErrorHandler('project list', async (options) => {
if (!options.storeHash || !options.accessToken) {
consola.error('Insufficient information to list projects.');
consola.info(
Expand All @@ -38,7 +37,7 @@ const list = new Command('list')
return;
}

await telemetry.identify(options.storeHash);
await getTelemetry().identify(options.storeHash);

consola.start('Fetching projects...');

Expand All @@ -58,11 +57,8 @@ const list = new Command('list')
});

process.exit(0);
} catch (error) {
consola.error(error instanceof Error ? error.message : error);
process.exit(1);
}
});
}),
);

const create = new Command('create')
.description(
Expand Down Expand Up @@ -90,8 +86,8 @@ const create = new Command('create')
'Path to the root directory of your Catalyst project (default: current working directory).',
process.cwd(),
)
.action(async (options) => {
try {
.action(
withErrorHandler('project create', async (options) => {
if (!options.storeHash || !options.accessToken) {
consola.error('Insufficient information to create a project.');
consola.info(
Expand All @@ -102,7 +98,7 @@ const create = new Command('create')
return;
}

await telemetry.identify(options.storeHash);
await getTelemetry().identify(options.storeHash);

const newProjectName = await consola.prompt('Enter a name for the new project:', {
type: 'text',
Expand All @@ -125,11 +121,8 @@ const create = new Command('create')
consola.success('Project UUID written to .bigcommerce/project.json.');

process.exit(0);
} catch (error) {
consola.error(error instanceof Error ? error.message : error);
process.exit(1);
}
});
}),
);

export const link = new Command('link')
.description(
Expand Down Expand Up @@ -161,8 +154,8 @@ export const link = new Command('link')
'Path to the root directory of your Catalyst project (default: current working directory).',
process.cwd(),
)
.action(async (options) => {
try {
.action(
withErrorHandler('project link', async (options) => {
const config = getProjectConfig(options.rootDir);

const writeProjectConfig = (uuid: string) => {
Expand All @@ -179,7 +172,7 @@ export const link = new Command('link')
}

if (options.storeHash && options.accessToken) {
await telemetry.identify(options.storeHash);
await getTelemetry().identify(options.storeHash);

consola.start('Fetching projects...');

Expand Down Expand Up @@ -239,11 +232,8 @@ export const link = new Command('link')
consola.info('Provide a project UUID with --project-uuid, or');
consola.info('Provide both --store-hash and --access-token to fetch and select a project.');
process.exit(1);
} catch (error) {
consola.error(error instanceof Error ? error.message : error);
process.exit(1);
}
});
}),
);

export const project = new Command('project')
.description('Manage your BigCommerce infrastructure project.')
Expand Down
12 changes: 5 additions & 7 deletions packages/catalyst/src/cli/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { execa } from 'execa';
import { existsSync } from 'node:fs';
import { join } from 'node:path';

import { consola } from '../lib/logger';
import { withErrorHandler } from '../lib/error-handler';
import { getProjectConfig } from '../lib/project-config';

export const start = new Command('start')
Expand All @@ -20,8 +20,8 @@ export const start = new Command('start')
'nextjs',
]),
)
.action(async (startOptions, options) => {
try {
.action(
withErrorHandler('start', async (startOptions, options) => {
const config = getProjectConfig();
const framework = options.framework ?? config.get('framework');

Expand Down Expand Up @@ -55,7 +55,5 @@ export const start = new Command('start')
cwd: process.cwd(),
},
);
} catch (error) {
consola.error(error instanceof Error ? error.message : error);
}
});
}),
);
4 changes: 2 additions & 2 deletions packages/catalyst/src/cli/commands/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Argument, Command, Option } from 'commander';
import { colorize } from 'consola/utils';

import { consola } from '../lib/logger';
import { Telemetry } from '../lib/telemetry';
import { getTelemetry } from '../lib/telemetry';

const telemetryService = new Telemetry();
const telemetryService = getTelemetry();
let isEnabled = telemetryService.isEnabled();

export const telemetry = new Command('telemetry')
Expand Down
8 changes: 5 additions & 3 deletions packages/catalyst/src/cli/hooks/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Command } from '@commander-js/extra-typings';

import { Telemetry } from '../lib/telemetry';

const telemetry = new Telemetry();
import { getTelemetry } from '../lib/telemetry';

const allowlistArguments = ['--keep-temp-dir', '--api-host', '--project-uuid'];

Expand Down Expand Up @@ -36,6 +34,7 @@ function parseArguments(args: string[]) {
}

export const telemetryPreHook = async (command: Command) => {
const telemetry = getTelemetry();
const [commandName, ...args] = command.args;

// Return the await to get a proper stack trace.
Expand All @@ -46,5 +45,8 @@ export const telemetryPreHook = async (command: Command) => {
};

export const telemetryPostHook = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there arguments on this that we can determine if the command failed with an error? It would be cleaner than having to add a closure around each command and is more scalable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I was actually just playing around with this in a sandbox and here's what I found:

const { Command } = require('commander');

const p = new Command();

p.command('test').action(async () => { throw new Error('boom'); });
p.hook('postAction', () => { console.log('POST_ACTION_FIRED'); });
p.parseAsync(['node', 'test', 'test']).catch(e => console.log('CAUGHT:', e.message));

If you run that, you don't see POST_ACTION_FIRED. I guess hooks in Commander work like .then()?

TL;DR... to your original question, I don't think so... but open to your thoughts

const telemetry = getTelemetry();

await telemetry.track('command_completed', { durationMs: telemetry.durationMs() });
await telemetry.analytics.closeAndFlush();
};
Loading