kasy-cli 1.21.0 → 1.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/utils/checks.js
CHANGED
|
@@ -32,7 +32,11 @@ function getInstallGuide(tool) {
|
|
|
32
32
|
const guides = {
|
|
33
33
|
gcloud: {
|
|
34
34
|
darwin: { cmd: 'brew install --cask google-cloud-sdk', after: 'gcloud auth login', url: 'https://cloud.google.com/sdk/docs/install' },
|
|
35
|
-
|
|
35
|
+
// The accept flags are required for a NON-interactive winget run — without
|
|
36
|
+
// them winget stalls waiting for the user to accept the source/package
|
|
37
|
+
// agreements (which is why the silent auto-install failed). They're also
|
|
38
|
+
// harmless when the user runs the command by hand.
|
|
39
|
+
win32: { cmd: 'winget install --id Google.CloudSDK -e --accept-source-agreements --accept-package-agreements', after: 'gcloud auth login', url: 'https://cloud.google.com/sdk/docs/install-sdk#windows' },
|
|
36
40
|
linux: { cmd: 'curl https://sdk.cloud.google.com | bash', after: 'gcloud auth login', url: 'https://cloud.google.com/sdk/docs/install' },
|
|
37
41
|
},
|
|
38
42
|
flutter: {
|
|
@@ -231,6 +235,7 @@ async function runSingleCheck(check, options = {}) {
|
|
|
231
235
|
// Lightweight auto-install (npm / pub global). Heavy tools (gcloud, flutter)
|
|
232
236
|
// use confirmInstall and are handled interactively after the spinner.
|
|
233
237
|
if (check.tryInstall) {
|
|
238
|
+
if (typeof options.onInstalling === 'function') options.onInstalling(check);
|
|
234
239
|
const installed = await execTool(check.tryInstall, INSTALL_TIMEOUT);
|
|
235
240
|
if (installed.ok) {
|
|
236
241
|
const verified = await verifyTool(check);
|
|
@@ -267,8 +272,7 @@ async function revalidate(check, t) {
|
|
|
267
272
|
* Returns true if the tool is present by the end.
|
|
268
273
|
*/
|
|
269
274
|
async function recoverCheckInteractively(check, t) {
|
|
270
|
-
|
|
271
|
-
|
|
275
|
+
// The step list already flagged this tool as missing; go straight to fixing it.
|
|
272
276
|
const guide = typeof check.installGuide === 'function' ? check.installGuide() : null;
|
|
273
277
|
|
|
274
278
|
// 1) Offer auto-install for heavy tools that have a package-manager command.
|
|
@@ -278,12 +282,17 @@ async function recoverCheckInteractively(check, t) {
|
|
|
278
282
|
initialValue: true,
|
|
279
283
|
});
|
|
280
284
|
if (doInstall) {
|
|
281
|
-
const spinner = ui.
|
|
285
|
+
const spinner = ui.timedSpinner();
|
|
282
286
|
spinner.start(t('checks.install.running', { name: check.name }));
|
|
283
287
|
const installed = await execTool(guide.cmd, INSTALL_TIMEOUT);
|
|
284
288
|
spinner.stop(t('checks.install.running', { name: check.name }));
|
|
285
289
|
if (installed.ok && (await revalidate(check, t))) return true;
|
|
286
290
|
ui.log.warn(t('checks.install.failedManual', { name: check.name }));
|
|
291
|
+
// Surface the installer's own last line — it usually says WHY (needs admin,
|
|
292
|
+
// agreement not accepted, package not found), which beats a generic failure.
|
|
293
|
+
const reason = (installed.stderr || installed.error || '')
|
|
294
|
+
.split('\n').map((l) => l.trim()).filter(Boolean).pop();
|
|
295
|
+
if (reason) ui.log.message(kleur.dim(reason.slice(0, 200)));
|
|
287
296
|
}
|
|
288
297
|
}
|
|
289
298
|
|
|
@@ -350,32 +359,38 @@ async function runChecks(checks, title, options = {}) {
|
|
|
350
359
|
// command never blocks waiting for input that will never come.
|
|
351
360
|
const interactive = options.interactive !== false && Boolean(process.stdout.isTTY);
|
|
352
361
|
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
362
|
+
// One step per tool, each with its own running clock — so the user always
|
|
363
|
+
// sees WHAT is being checked or installed, and that it's still moving. This
|
|
364
|
+
// replaces the single frozen "…" spinner that looked stuck during long
|
|
365
|
+
// installs (e.g. FlutterFire). Same stepper the kasy-new flow uses, so the
|
|
366
|
+
// environment setup feels as guided as the project generation, on every OS.
|
|
367
|
+
const stepper = ui.makeTimedStepper();
|
|
359
368
|
const results = [];
|
|
369
|
+
|
|
360
370
|
for (const check of checks) {
|
|
361
|
-
|
|
371
|
+
stepper.next(t('checks.checking', { name: check.name }));
|
|
372
|
+
const result = await runSingleCheck({ ...check, t }, {
|
|
373
|
+
showVersion,
|
|
374
|
+
// Switch the line to "Installing X…" while the auto-install runs, so the
|
|
375
|
+
// clock keeps ticking against a message that explains the wait.
|
|
376
|
+
onInstalling: (c) => stepper.update(
|
|
377
|
+
c.tryInstallMessageKey ? t(c.tryInstallMessageKey) : t('setup.installingNamed', { name: c.name }),
|
|
378
|
+
),
|
|
379
|
+
});
|
|
380
|
+
results.push(result);
|
|
381
|
+
|
|
382
|
+
if (result.ok) {
|
|
383
|
+
stepper.succeed(result.version ? `${result.name} — ${result.version}` : result.name);
|
|
384
|
+
} else if (result.required) {
|
|
385
|
+
const detail = result.autoInstallFailed ? ` — ${t('checks.install.failed')}` : '';
|
|
386
|
+
stepper.fail(`${t('checks.missing', { name: result.name })}${detail}`);
|
|
387
|
+
} else {
|
|
388
|
+
stepper.warn(t('checks.notFound', { name: result.name }));
|
|
389
|
+
}
|
|
362
390
|
}
|
|
363
391
|
|
|
364
392
|
const failures = results.filter((r) => !r.ok);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (failures.length === 0) {
|
|
368
|
-
spinner.stop(doneLabel);
|
|
369
|
-
return results;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Close the spinner reflecting what actually happened: red ▲ if a required
|
|
373
|
-
// check failed, default green ✦ if only optional checks failed (warnings).
|
|
374
|
-
if (requiredFailures.length > 0) {
|
|
375
|
-
spinner.error(doneLabel);
|
|
376
|
-
} else {
|
|
377
|
-
spinner.stop(doneLabel);
|
|
378
|
-
}
|
|
393
|
+
if (failures.length === 0) return results;
|
|
379
394
|
|
|
380
395
|
for (const result of failures) {
|
|
381
396
|
if (interactive && canRecover(result)) {
|
|
@@ -383,8 +398,10 @@ async function runChecks(checks, title, options = {}) {
|
|
|
383
398
|
if (recovered) {
|
|
384
399
|
const idx = results.indexOf(result);
|
|
385
400
|
if (idx >= 0) results[idx] = { ...result, ok: true, autoInstallFailed: false };
|
|
386
|
-
continue;
|
|
387
401
|
}
|
|
402
|
+
// recoverCheckInteractively already printed the outcome/guidance — don't
|
|
403
|
+
// duplicate it. The step line above already showed the missing status.
|
|
404
|
+
continue;
|
|
388
405
|
}
|
|
389
406
|
printCheckFailure(result, t);
|
|
390
407
|
}
|
package/lib/utils/env-tools.js
CHANGED
|
@@ -70,14 +70,31 @@ function pubCacheBin(name) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
73
|
+
* Well-known install dirs of tools that may not be on the frozen session PATH
|
|
74
|
+
* yet — e.g. the Google Cloud SDK right after it's installed mid-run. Only
|
|
75
|
+
* returns dirs that actually exist. Windows-focused (on Unix these tools land
|
|
76
|
+
* on PATH via the shell profile, which a new terminal already picks up).
|
|
77
|
+
*/
|
|
78
|
+
function extraToolDirs() {
|
|
79
|
+
if (!isWindows) return [];
|
|
80
|
+
const candidates = [
|
|
81
|
+
process.env.LOCALAPPDATA && path.win32.join(process.env.LOCALAPPDATA, 'Google', 'Cloud SDK', 'google-cloud-sdk', 'bin'),
|
|
82
|
+
process.env.ProgramFiles && path.win32.join(process.env.ProgramFiles, 'Google', 'Cloud SDK', 'google-cloud-sdk', 'bin'),
|
|
83
|
+
process.env['ProgramFiles(x86)'] && path.win32.join(process.env['ProgramFiles(x86)'], 'Google', 'Cloud SDK', 'google-cloud-sdk', 'bin'),
|
|
84
|
+
].filter(Boolean);
|
|
85
|
+
return candidates.filter((dir) => fs.existsSync(dir));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A copy of process.env with `extraDirs` (plus the pub-cache bin and known tool
|
|
90
|
+
* dirs) prepended to PATH, so child processes can find tools installed earlier
|
|
91
|
+
* in this same run. On Windows we set both `PATH` and `Path` because Node reads
|
|
92
|
+
* the original key.
|
|
76
93
|
*
|
|
77
94
|
* @param {string[]} [extraDirs] additional directories to expose
|
|
78
95
|
*/
|
|
79
96
|
function augmentedEnv(extraDirs = []) {
|
|
80
|
-
const dirs = [pubCacheBinDir(), ...extraDirs].filter(Boolean);
|
|
97
|
+
const dirs = [pubCacheBinDir(), ...extraToolDirs(), ...extraDirs].filter(Boolean);
|
|
81
98
|
const env = { ...process.env };
|
|
82
99
|
// Windows env keys are case-insensitive; find whichever key holds PATH.
|
|
83
100
|
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === 'path') || 'PATH';
|
|
@@ -73,7 +73,8 @@ module.exports = {
|
|
|
73
73
|
'setup.checks.backend': 'Backend checks ({backend})',
|
|
74
74
|
'setup.firebase.installing': 'Installing Firebase CLI...',
|
|
75
75
|
'setup.supabase.installing': 'Installing Supabase CLI...',
|
|
76
|
-
'setup.flutterfire.installing': 'Installing FlutterFire CLI
|
|
76
|
+
'setup.flutterfire.installing': 'Installing FlutterFire CLI… (may take 1-2 min)',
|
|
77
|
+
'setup.installingNamed': 'Installing {name}…',
|
|
77
78
|
'setup.warn.hang': 'If setup stalls, run manually: flutterfire --version',
|
|
78
79
|
'setup.warn.supabase': 'Using Supabase or custom API? Firebase still helps with push notifications and remote config.',
|
|
79
80
|
'doctor.title': 'Kasy Doctor',
|
|
@@ -73,7 +73,8 @@ module.exports = {
|
|
|
73
73
|
'setup.checks.backend': 'Verificaciones de backend ({backend})',
|
|
74
74
|
'setup.firebase.installing': 'Instalando Firebase CLI...',
|
|
75
75
|
'setup.supabase.installing': 'Instalando Supabase CLI...',
|
|
76
|
-
'setup.flutterfire.installing': 'Instalando FlutterFire CLI
|
|
76
|
+
'setup.flutterfire.installing': 'Instalando FlutterFire CLI… (puede tardar 1-2 min)',
|
|
77
|
+
'setup.installingNamed': 'Instalando {name}…',
|
|
77
78
|
'setup.warn.hang': 'Si se cuelga, ejecuta manualmente: flutterfire --versión',
|
|
78
79
|
'setup.warn.supabase': '¿Usas Supabase o API propia? Firebase sigue siendo útil para push y remote config.',
|
|
79
80
|
'doctor.title': 'Kasy Doctor',
|
|
@@ -73,7 +73,8 @@ module.exports = {
|
|
|
73
73
|
'setup.checks.backend': 'Verificações de backend ({backend})',
|
|
74
74
|
'setup.firebase.installing': 'Instalando Firebase CLI...',
|
|
75
75
|
'setup.supabase.installing': 'Instalando Supabase CLI...',
|
|
76
|
-
'setup.flutterfire.installing': 'Instalando FlutterFire CLI
|
|
76
|
+
'setup.flutterfire.installing': 'Instalando FlutterFire CLI… (pode levar 1-2 min)',
|
|
77
|
+
'setup.installingNamed': 'Instalando {name}…',
|
|
77
78
|
'setup.warn.hang': 'Se travar, execute manualmente: flutterfire --version',
|
|
78
79
|
'setup.warn.supabase': 'Usando Supabase ou API propria? O Firebase ainda ajuda com push e remote config.',
|
|
79
80
|
'doctor.title': 'Kasy Doctor',
|