Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/cli-missing-wrangler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@mieweb/cli": patch
---

Don't crash with a stack trace when wrangler isn't installed: wrangler-delegated commands on the cloudflare target now print "wrangler binary not found…" and exit 1, and `mieweb --version` prints the mieweb version and exits 0 on every target (it no longer errors on non-cloudflare targets).
14 changes: 11 additions & 3 deletions packages/cli/src/cloudflare.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ import { runWrangler } from '@mieweb/deploy-wrangler';
* reference provider does (MIEWEB_REAL_WRANGLER, else the project's wrangler
* via the active package manager).
*
* Never throws: a missing wrangler (or a failed spawn) is reported on stderr
* and returned as exit code 1.
*
* @param {string[]} args arguments to forward to wrangler
* @param {{ cwd?: string }} [opts]
* @returns {Promise<number>} wrangler's exit code (1 if killed by a signal)
* @returns {Promise<number>} wrangler's exit code (1 if it couldn't run or was killed)
*/
export async function delegateToWrangler(args, opts = {}) {
const { code } = await runWrangler(args, { cwd: opts.cwd ?? process.cwd() });
return code ?? 1;
try {
const { code } = await runWrangler(args, { cwd: opts.cwd ?? process.cwd() });
return code ?? 1;
} catch (err) {
console.error(`mieweb: ${err instanceof Error ? err.message : String(err)}`);
return 1;
}
}
10 changes: 8 additions & 2 deletions packages/cli/src/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ async function main(argv) {

// Version flags: always show the mieweb version first. On cloudflare we then
// let wrangler print its own version below; other targets show mieweb only.
if (args[0] === '--version' || args[0] === '-v' || args[0] === '-V') {
const versionFlag = args[0] === '--version' || args[0] === '-v' || args[0] === '-V';
if (versionFlag) {
process.stdout.write(`mieweb ${miewebVersion()}\n`);
}

Expand All @@ -81,6 +82,9 @@ async function main(argv) {

const config = loadConfig({ overrideTarget });

// Only cloudflare has a second version to show (wrangler's, below).
if (versionFlag && config.target !== 'cloudflare') return 0;

// Container image plumbing (build once, skopeo copy — container-plan.md M2).
// `images push` on the cloudflare target delegates to wrangler's managed
// registry; every other target goes through skopeo to its own registry.
Expand Down Expand Up @@ -126,7 +130,9 @@ async function main(argv) {

if (config.target === 'cloudflare') {
// Reference path for non-provider commands: hand to wrangler untouched.
return delegateToWrangler(args, { cwd: config.root });
const code = await delegateToWrangler(args, { cwd: config.root });
// `--version` already printed mieweb's version; wrangler's is a bonus.
return versionFlag ? 0 : code;
}

// Node "host" targets run the unchanged worker via the host harness. `local`
Expand Down
42 changes: 42 additions & 0 deletions packages/cli/test/provider-core.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,45 @@ test('delegateToWrangler returns wrangler’s exit code', async (t) => {
t.after(() => delete process.env.MIEWEB_REAL_WRANGLER);
assert.equal(await delegateToWrangler(['d1', 'migrations', 'apply'], { cwd: dir }), 3);
});

test('delegateToWrangler reports a missing wrangler instead of throwing', async (t) => {
const dir = tmp(t);
writeFileSync(join(dir, 'package.json'), '{"name":"app"}');
const saved = process.env.MIEWEB_REAL_WRANGLER;
delete process.env.MIEWEB_REAL_WRANGLER;
t.after(() => {
if (saved !== undefined) process.env.MIEWEB_REAL_WRANGLER = saved;
});
const errors = [];
t.mock.method(console, 'error', (m) => errors.push(m));
assert.equal(await delegateToWrangler(['d1', 'list'], { cwd: dir }), 1);
assert.match(errors.join('\n'), /wrangler binary not found/);
});

test('`mieweb --version` works without wrangler installed', async (t) => {
const { execFileSync } = await import('node:child_process');
const dir = tmp(t);
writeFileSync(join(dir, 'package.json'), '{"name":"app"}');
const env = { ...process.env };
delete env.MIEWEB_REAL_WRANGLER;
const out = execFileSync(process.execPath, [new URL('../src/index.mjs', import.meta.url).pathname, '--version'], {
cwd: dir,
env,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
assert.match(out, /^mieweb \d+\.\d+\.\d+/);
});

test('`mieweb --version` on a non-cloudflare target prints only the version', async (t) => {
const { execFileSync } = await import('node:child_process');
const dir = tmp(t);
writeFileSync(join(dir, 'package.json'), '{"name":"app"}');
writeFileSync(join(dir, 'mieweb.jsonc'), '{"target":"mieweb"}');
const out = execFileSync(process.execPath, [new URL('../src/index.mjs', import.meta.url).pathname, '--version'], {
cwd: dir,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
assert.match(out, /^mieweb \d+\.\d+\.\d+\n$/);
});
Loading