Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ jobs:
run: yarn install --frozen-lockfile
- name: Build
run: yarn build --define:process.env.CRAFT_BUILD_SHA='"'${{ github.sha }}'"'
- name: Smoke Test
run: ./dist/craft --help
- name: NPM Pack
run: npm pack
- name: Docs
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ name: Release
on:
workflow_dispatch:
inputs:
craft_version:
description: Craft version to release
required: true
default: "latest"
version:
description: Version to release
required: false
Expand Down Expand Up @@ -33,3 +37,4 @@ jobs:
with:
version: ${{ github.event.inputs.version }}
force: ${{ github.event.inputs.force }}
craft_version: ${{ github.event.inputs.craft_version }}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@types/shell-quote": "^1.6.0",
"@types/tar": "^4.0.0",
"@types/tmp": "^0.0.33",
"@types/yargs": "^15.0.3",
"@types/yargs": "^17",
"@typescript-eslint/eslint-plugin": "^5.19.0",
"@typescript-eslint/parser": "^5.19.0",
"ajv": "6.12.6",
Expand Down Expand Up @@ -86,12 +86,12 @@
"tmp": "0.2.4",
"ts-jest": "^29.1.1",
"typescript": "^5.1.6",
"yargs": "15.4.1"
"yargs": "^18"
},
"scripts": {
"build:fat": "yarn run compile-config-schema && tsc -p tsconfig.build.json",
"build:watch": "yarn run compile-config-schema && tsc -p tsconfig.build.json --watch",
"build": "yarn compile-config-schema && esbuild src/index.ts --sourcemap --bundle --platform=node --target=node20 --inject:./src/utils/import-meta-url.js --define:import.meta.url=import_meta_url --outfile=dist/craft --minify",
"build": "yarn compile-config-schema && esbuild src/index.ts --sourcemap --bundle --platform=node --target=node22 --inject:./src/utils/import-meta-url.js --define:import.meta.url=import_meta_url --outfile=dist/craft",
"precli": "yarn build",
"cli": "node -r source-map-support/register dist/craft",
"clean": "rimraf dist coverage",
Expand Down
80 changes: 78 additions & 2 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,79 @@
test('it works', () => {
expect(true).toBeTruthy();
import { execFile } from 'child_process';
import { promisify } from 'util';
import { resolve } from 'path';

const execFileAsync = promisify(execFile);

// Path to the TypeScript source file - we use ts-node to run it directly
const CLI_ENTRY = resolve(__dirname, '../index.ts');

describe('CLI smoke tests', () => {
// Increase timeout for CLI tests as they spawn processes
jest.setTimeout(30000);

test('CLI starts and shows help without runtime errors', async () => {
// This catches issues like:
// - Missing dependencies
// - Syntax errors
// - Runtime initialization errors (e.g., yargs singleton usage in v18)
const { stdout, stderr } = await execFileAsync(
'npx',
['ts-node', '--transpile-only', CLI_ENTRY, '--help'],
{ env: { ...process.env, NODE_ENV: 'test' } }
);

expect(stdout).toMatch(/<command>/);
expect(stdout).toContain('prepare NEW-VERSION');
expect(stdout).toContain('publish NEW-VERSION');
expect(stdout).toContain('--help');
// Ensure no error output (warnings are acceptable)
expect(stderr).not.toContain('Error');
expect(stderr).not.toContain('TypeError');
});

test('CLI shows version without errors', async () => {
const { stdout } = await execFileAsync(
'npx',
['ts-node', '--transpile-only', CLI_ENTRY, '--version'],
{ env: { ...process.env, NODE_ENV: 'test' } }
);

// Version should be a semver-like string
expect(stdout.trim()).toMatch(/^\d+\.\d+\.\d+/);
});

test('CLI exits with error for unknown command', async () => {
// This ensures yargs command parsing works and async handlers are awaited
await expect(
execFileAsync(
'npx',
['ts-node', '--transpile-only', CLI_ENTRY, 'nonexistent-command'],
{ env: { ...process.env, NODE_ENV: 'test' } }
)
).rejects.toMatchObject({
code: 1,
});
});

test('async command handler completes properly', async () => {
// The 'targets' command has an async handler and requires a .craft.yml
// Without proper await on parse(), this would exit before completing
// We expect it to fail due to missing config, but it should fail gracefully
// not due to premature exit
try {
await execFileAsync(
'npx',
['ts-node', '--transpile-only', CLI_ENTRY, 'targets'],
{
env: { ...process.env, NODE_ENV: 'test' },
cwd: '/tmp', // No .craft.yml here
}
);
} catch (error: any) {
// Should fail with a config error, not a silent exit or unhandled promise
expect(error.stderr || error.stdout).toMatch(
/Cannot find configuration file|craft\.yml|config/i
);
}
});
});
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function fixGlobalBooleanFlags(argv: string[]): string[] {
/**
* Main entrypoint
*/
function main(): void {
async function main(): Promise<void> {
printVersion();

readEnvironmentConfig();
Expand All @@ -74,7 +74,7 @@ function main(): void {

const argv = fixGlobalBooleanFlags(process.argv.slice(2));

yargs
await yargs()
.parserConfiguration({
'boolean-negation': false,
})
Expand Down
Loading
Loading