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
71 changes: 70 additions & 1 deletion packages/catalyst/src/cli/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Command } from '@commander-js/extra-typings';
import { describe, expect, test, vi } from 'vitest';
import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { afterEach, describe, expect, test, vi } from 'vitest';

vi.mock('./hooks/telemetry', () => ({
telemetryPreHook: vi.fn().mockResolvedValue(undefined),
telemetryPostHook: vi.fn().mockResolvedValue(undefined),
}));

import { telemetryPostHook, telemetryPreHook } from './hooks/telemetry';
import { mkTempDir } from './lib/mk-temp-dir';
import { program } from './program';

describe('CLI program', () => {
Expand Down Expand Up @@ -53,3 +56,69 @@ describe('CLI program', () => {
expect(telemetryPreHook).toHaveBeenCalledWith(expect.any(Command), expect.any(Command));
});
});

describe('--env-path option', () => {
afterEach(() => {
delete process.env.CATALYST_STORE_HASH;
delete process.env.CATALYST_ACCESS_TOKEN;
});

test('loads environment variables from file when --env-path points to existing file', async () => {
const [tmpDir, cleanup] = await mkTempDir('catalyst-env-path-');
const envPath = join(tmpDir, '.env');

await writeFile(
envPath,
'CATALYST_STORE_HASH=test-store-hash\nCATALYST_ACCESS_TOKEN=test-access-token',
'utf-8',
);

try {
await program.parseAsync(['--env-path', envPath, 'version'], { from: 'user' });

expect(process.env.CATALYST_STORE_HASH).toBe('test-store-hash');
expect(process.env.CATALYST_ACCESS_TOKEN).toBe('test-access-token');
} finally {
await cleanup();
}
});

test('loads environment variables when --env-path is relative to cwd', async () => {
const [tmpDir, cleanup] = await mkTempDir('catalyst-env-path-');
const envFileName = '.env.catalyst-test';
const envPath = join(tmpDir, envFileName);

await writeFile(
envPath,
'CATALYST_STORE_HASH=test-store-hash\nCATALYST_ACCESS_TOKEN=test-access-token',
'utf-8',
);

const originalCwd = process.cwd();

process.chdir(tmpDir);

try {
await program.parseAsync(['--env-path', envFileName, 'version'], { from: 'user' });

expect(process.env.CATALYST_STORE_HASH).toBe('test-store-hash');
expect(process.env.CATALYST_ACCESS_TOKEN).toBe('test-access-token');
} finally {
process.chdir(originalCwd);
await cleanup();
}
});

test('throws when --env-path points to non-existent file', async () => {
const [tmpDir, cleanup] = await mkTempDir('catalyst-env-path-');
const nonExistentPath = join(tmpDir, '.env.missing');

try {
await expect(
program.parseAsync(['--env-path', nonExistentPath, 'version'], { from: 'user' }),
).rejects.toThrow(/Env file not found/);
} finally {
await cleanup();
}
});
});
35 changes: 35 additions & 0 deletions packages/catalyst/src/cli/program.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Option } from '@commander-js/extra-typings';
import { Command } from 'commander';
import { colorize } from 'consola/utils';
import { config } from 'dotenv';
import { resolve } from 'node:path';

import PACKAGE_INFO from '../../package.json';

Expand All @@ -22,6 +25,38 @@ program
.name(PACKAGE_INFO.name)
.version(PACKAGE_INFO.version)
.description('CLI tool for Catalyst development')
.addOption(
new Option(
'--env-path <path>',
'Path to environment file to load (relative to current working directory)',
// We are using argParser, because commander loads in environment variables before executing hooks.
).argParser((value) => {
if (value) {
const envFilePath = resolve(process.cwd(), value);
const result = config({
path: envFilePath,
override: true,
});

if (result.error) {
const errCode =
'code' in result.error && typeof result.error.code === 'string'
? result.error.code
: undefined;
const message =
errCode === 'ENOENT'
? `Env file not found: ${envFilePath}`
: `Failed to load --env-path ${value}: ${result.error.message}`;

throw new Error(message);
}

consola.log(colorize('cyanBright', `Loaded environment variables from ${envFilePath}\n`));
}

return value;
}),
)
.addCommand(version)
.addCommand(dev)
.addCommand(start)
Expand Down
1 change: 1 addition & 0 deletions packages/catalyst/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export default defineConfig((options: Options) => ({
CLI_SEGMENT_WRITE_KEY: process.env.CLI_SEGMENT_WRITE_KEY ?? 'not-a-valid-segment-write-key',
CONSOLA_LEVEL: process.env.NODE_ENV === 'production' ? '2' : '3',
},
external: ['commander', '@commander-js/extra-typings'],
...options,
}));