kasy-cli 1.21.0 → 1.21.2
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: {
|
|
@@ -88,7 +92,16 @@ const PLATFORM_CHECKS = {
|
|
|
88
92
|
linux: []
|
|
89
93
|
};
|
|
90
94
|
|
|
91
|
-
/**
|
|
95
|
+
/**
|
|
96
|
+
* Firebase CLI + FlutterFire — required for push notifications (FCM) on EVERY
|
|
97
|
+
* backend (Firebase, Supabase, API), since FCM is the shared push layer.
|
|
98
|
+
*
|
|
99
|
+
* gcloud is deliberately NOT in this list. It's only needed to create a Firebase
|
|
100
|
+
* project FROM SCRATCH (an opt-in path), so checking it for every backend made
|
|
101
|
+
* the Supabase/API setup offer to install gcloud for no reason — and the user
|
|
102
|
+
* doesn't even need it there. The create-from-scratch flow verifies gcloud on
|
|
103
|
+
* its own (checkGcloudAuth in new.js), exactly when it's actually required.
|
|
104
|
+
*/
|
|
92
105
|
const FIREBASE_CHECKS = [
|
|
93
106
|
{
|
|
94
107
|
name: 'Firebase CLI',
|
|
@@ -109,22 +122,6 @@ const FIREBASE_CHECKS = [
|
|
|
109
122
|
pubGlobalBin: 'flutterfire',
|
|
110
123
|
tryInstallMessageKey: 'setup.flutterfire.installing',
|
|
111
124
|
},
|
|
112
|
-
{
|
|
113
|
-
name: 'gcloud CLI (create-from-scratch)',
|
|
114
|
-
command: 'gcloud --version',
|
|
115
|
-
required: false,
|
|
116
|
-
installGuide: () => getInstallGuide('gcloud'),
|
|
117
|
-
confirmInstall: true,
|
|
118
|
-
waitPromptKey: 'checks.waitPrompt.gcloud.install',
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: 'gcloud auth (create-from-scratch)',
|
|
122
|
-
command: 'gcloud auth print-access-token',
|
|
123
|
-
required: false,
|
|
124
|
-
showVersion: false,
|
|
125
|
-
failHint: 'gcloud auth login',
|
|
126
|
-
waitPromptKey: 'checks.waitPrompt.gcloud.auth',
|
|
127
|
-
},
|
|
128
125
|
];
|
|
129
126
|
|
|
130
127
|
const BACKEND_CHECKS = {
|
|
@@ -231,6 +228,7 @@ async function runSingleCheck(check, options = {}) {
|
|
|
231
228
|
// Lightweight auto-install (npm / pub global). Heavy tools (gcloud, flutter)
|
|
232
229
|
// use confirmInstall and are handled interactively after the spinner.
|
|
233
230
|
if (check.tryInstall) {
|
|
231
|
+
if (typeof options.onInstalling === 'function') options.onInstalling(check);
|
|
234
232
|
const installed = await execTool(check.tryInstall, INSTALL_TIMEOUT);
|
|
235
233
|
if (installed.ok) {
|
|
236
234
|
const verified = await verifyTool(check);
|
|
@@ -267,8 +265,7 @@ async function revalidate(check, t) {
|
|
|
267
265
|
* Returns true if the tool is present by the end.
|
|
268
266
|
*/
|
|
269
267
|
async function recoverCheckInteractively(check, t) {
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
// The step list already flagged this tool as missing; go straight to fixing it.
|
|
272
269
|
const guide = typeof check.installGuide === 'function' ? check.installGuide() : null;
|
|
273
270
|
|
|
274
271
|
// 1) Offer auto-install for heavy tools that have a package-manager command.
|
|
@@ -278,12 +275,17 @@ async function recoverCheckInteractively(check, t) {
|
|
|
278
275
|
initialValue: true,
|
|
279
276
|
});
|
|
280
277
|
if (doInstall) {
|
|
281
|
-
const spinner = ui.
|
|
278
|
+
const spinner = ui.timedSpinner();
|
|
282
279
|
spinner.start(t('checks.install.running', { name: check.name }));
|
|
283
280
|
const installed = await execTool(guide.cmd, INSTALL_TIMEOUT);
|
|
284
281
|
spinner.stop(t('checks.install.running', { name: check.name }));
|
|
285
282
|
if (installed.ok && (await revalidate(check, t))) return true;
|
|
286
283
|
ui.log.warn(t('checks.install.failedManual', { name: check.name }));
|
|
284
|
+
// Surface the installer's own last line — it usually says WHY (needs admin,
|
|
285
|
+
// agreement not accepted, package not found), which beats a generic failure.
|
|
286
|
+
const reason = (installed.stderr || installed.error || '')
|
|
287
|
+
.split('\n').map((l) => l.trim()).filter(Boolean).pop();
|
|
288
|
+
if (reason) ui.log.message(kleur.dim(reason.slice(0, 200)));
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
291
|
|
|
@@ -350,32 +352,38 @@ async function runChecks(checks, title, options = {}) {
|
|
|
350
352
|
// command never blocks waiting for input that will never come.
|
|
351
353
|
const interactive = options.interactive !== false && Boolean(process.stdout.isTTY);
|
|
352
354
|
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
355
|
+
// One step per tool, each with its own running clock — so the user always
|
|
356
|
+
// sees WHAT is being checked or installed, and that it's still moving. This
|
|
357
|
+
// replaces the single frozen "…" spinner that looked stuck during long
|
|
358
|
+
// installs (e.g. FlutterFire). Same stepper the kasy-new flow uses, so the
|
|
359
|
+
// environment setup feels as guided as the project generation, on every OS.
|
|
360
|
+
const stepper = ui.makeTimedStepper();
|
|
359
361
|
const results = [];
|
|
362
|
+
|
|
360
363
|
for (const check of checks) {
|
|
361
|
-
|
|
364
|
+
stepper.next(t('checks.checking', { name: check.name }));
|
|
365
|
+
const result = await runSingleCheck({ ...check, t }, {
|
|
366
|
+
showVersion,
|
|
367
|
+
// Switch the line to "Installing X…" while the auto-install runs, so the
|
|
368
|
+
// clock keeps ticking against a message that explains the wait.
|
|
369
|
+
onInstalling: (c) => stepper.update(
|
|
370
|
+
c.tryInstallMessageKey ? t(c.tryInstallMessageKey) : t('setup.installingNamed', { name: c.name }),
|
|
371
|
+
),
|
|
372
|
+
});
|
|
373
|
+
results.push(result);
|
|
374
|
+
|
|
375
|
+
if (result.ok) {
|
|
376
|
+
stepper.succeed(result.version ? `${result.name} — ${result.version}` : result.name);
|
|
377
|
+
} else if (result.required) {
|
|
378
|
+
const detail = result.autoInstallFailed ? ` — ${t('checks.install.failed')}` : '';
|
|
379
|
+
stepper.fail(`${t('checks.missing', { name: result.name })}${detail}`);
|
|
380
|
+
} else {
|
|
381
|
+
stepper.warn(t('checks.notFound', { name: result.name }));
|
|
382
|
+
}
|
|
362
383
|
}
|
|
363
384
|
|
|
364
385
|
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
|
-
}
|
|
386
|
+
if (failures.length === 0) return results;
|
|
379
387
|
|
|
380
388
|
for (const result of failures) {
|
|
381
389
|
if (interactive && canRecover(result)) {
|
|
@@ -383,8 +391,10 @@ async function runChecks(checks, title, options = {}) {
|
|
|
383
391
|
if (recovered) {
|
|
384
392
|
const idx = results.indexOf(result);
|
|
385
393
|
if (idx >= 0) results[idx] = { ...result, ok: true, autoInstallFailed: false };
|
|
386
|
-
continue;
|
|
387
394
|
}
|
|
395
|
+
// recoverCheckInteractively already printed the outcome/guidance — don't
|
|
396
|
+
// duplicate it. The step line above already showed the missing status.
|
|
397
|
+
continue;
|
|
388
398
|
}
|
|
389
399
|
printCheckFailure(result, t);
|
|
390
400
|
}
|
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',
|