kasy-cli 1.13.0 → 1.15.0
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/bin/kasy.js +140 -12
- package/lib/commands/add.js +2 -2
- package/lib/commands/codemagic.js +11 -4
- package/lib/commands/deploy.js +3 -3
- package/lib/commands/favicon.js +115 -0
- package/lib/commands/icon.js +143 -0
- package/lib/commands/ios.js +28 -7
- package/lib/commands/new.js +8 -20
- package/lib/commands/remove.js +1 -1
- package/lib/commands/reset.js +385 -0
- package/lib/commands/run.js +24 -17
- package/lib/commands/splash.js +14 -4
- package/lib/commands/update.js +1 -1
- package/lib/scaffold/backends/api/patch/README.md +1 -1
- package/lib/scaffold/backends/api/patch/android/app/src/main/AndroidManifest.xml +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +53 -0
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +11 -1
- package/lib/scaffold/backends/firebase/tokens.js +2 -2
- package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -2
- package/lib/scaffold/backends/supabase/migrations/20240101000011_dedupe_device_tokens.sql +34 -0
- package/lib/scaffold/backends/supabase/patch/README.md +1 -1
- package/lib/scaffold/backends/supabase/patch/android/app/src/main/AndroidManifest.xml +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +43 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +11 -1
- package/lib/utils/apple-release.js +115 -16
- package/lib/utils/checks.js +45 -107
- package/lib/utils/debug.js +75 -0
- package/lib/utils/flutter-run.js +173 -0
- package/lib/utils/friendly-error.js +91 -0
- package/lib/utils/i18n/messages-en.js +970 -0
- package/lib/utils/i18n/messages-es.js +968 -0
- package/lib/utils/i18n/messages-pt.js +968 -0
- package/lib/utils/i18n.js +21 -2483
- package/lib/utils/mobile-identity.js +35 -0
- package/lib/utils/png-padding.js +120 -0
- package/lib/utils/ui.js +114 -0
- package/package.json +8 -4
- package/templates/firebase/README.en.md +1 -1
- package/templates/firebase/README.es.md +1 -1
- package/templates/firebase/README.md +1 -1
- package/templates/firebase/android/app/build.gradle.kts +10 -1
- package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +161 -11
- package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
- package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +53 -0
- package/templates/firebase/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
- package/templates/firebase/assets/images/favicon.png +0 -0
- package/templates/firebase/assets/images/icon.png +0 -0
- package/templates/firebase/assets/images/icon_android.png +0 -0
- package/templates/firebase/assets/images/icon_foreground_empty.png +0 -0
- package/templates/firebase/assets/images/splash_logo_dark_android12.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light_android12.png +0 -0
- package/templates/firebase/firestore.indexes.json +10 -0
- package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
- package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
- package/templates/firebase/functions/src/index.ts +1 -0
- package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
- package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
- package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
- package/templates/firebase/ios/Runner/Info.plist +2 -2
- package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/lib/components/components.dart +1 -0
- package/templates/firebase/lib/components/kasy_avatar.dart +88 -57
- package/templates/firebase/lib/components/kasy_avatar_presets.dart +116 -74
- package/templates/firebase/lib/components/kasy_button.dart +8 -0
- package/templates/firebase/lib/components/kasy_tabs.dart +431 -0
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +73 -19
- package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
- package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
- package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
- package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
- package/templates/firebase/lib/features/home/home_components_page.dart +1 -1
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +316 -93
- package/templates/firebase/lib/features/home/home_page.dart +0 -6
- package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
- package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
- package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
- package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
- package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
- package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
- package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
- package/templates/firebase/lib/i18n/en.i18n.json +4 -1
- package/templates/firebase/lib/i18n/es.i18n.json +4 -1
- package/templates/firebase/lib/i18n/pt.i18n.json +4 -1
- package/templates/firebase/pubspec.yaml +10 -3
- package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
- package/templates/firebase/web/favicon.png +0 -0
- package/templates/firebase/web/icons/Icon-192.png +0 -0
- package/templates/firebase/web/icons/Icon-512.png +0 -0
- package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
- package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
- package/templates/firebase/web/index.html +9 -0
- package/templates/firebase/web/manifest.json +3 -3
- package/templates/firebase/assets/images/app_icon.png +0 -0
- package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
- package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
- package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
package/bin/kasy.js
CHANGED
|
@@ -10,12 +10,15 @@ const { runValidate } = require('../lib/commands/validate');
|
|
|
10
10
|
const { runDeployCommand } = require('../lib/commands/deploy');
|
|
11
11
|
const { runCheck } = require('../lib/commands/check');
|
|
12
12
|
const { runRun } = require('../lib/commands/run');
|
|
13
|
+
const { runReset } = require('../lib/commands/reset');
|
|
13
14
|
const { runAdd } = require('../lib/commands/add');
|
|
14
15
|
const { runRemove } = require('../lib/commands/remove');
|
|
15
16
|
const { runUpdate } = require('../lib/commands/update');
|
|
16
17
|
const { runDocs } = require('../lib/commands/docs');
|
|
17
18
|
const { runNotificationsText } = require('../lib/commands/notifications');
|
|
18
19
|
const { runSplash } = require('../lib/commands/splash');
|
|
20
|
+
const { runIcon } = require('../lib/commands/icon');
|
|
21
|
+
const { runFavicon } = require('../lib/commands/favicon');
|
|
19
22
|
const {
|
|
20
23
|
runConfigure: runIosConfigure,
|
|
21
24
|
runBuild: runIosBuild,
|
|
@@ -37,6 +40,9 @@ const { promptLanguage } = require('../lib/utils/prompts');
|
|
|
37
40
|
const { ensureLicenseKey, shouldRequireLicenseForArgv } = require('../lib/utils/license-gate');
|
|
38
41
|
const { checkForUpdates } = require('../lib/utils/updates');
|
|
39
42
|
const { printCompactHeader } = require('../lib/utils/brand');
|
|
43
|
+
const ui = require('../lib/utils/ui');
|
|
44
|
+
const { stripVerboseFlag, isVerbose, printVerboseError } = require('../lib/utils/debug');
|
|
45
|
+
const { formatError } = require('../lib/utils/friendly-error');
|
|
40
46
|
|
|
41
47
|
function createLocalizedHelpConfig(t) {
|
|
42
48
|
return {
|
|
@@ -131,11 +137,15 @@ function createLocalizedHelpConfig(t) {
|
|
|
131
137
|
// Group root commands by intent for easier scanning by non-devs.
|
|
132
138
|
const groups = [
|
|
133
139
|
{ id: 'start', ids: ['new', 'doctor', 'features'] },
|
|
134
|
-
{ id: 'work', ids: ['add', 'remove', 'update', 'run'] },
|
|
140
|
+
{ id: 'work', ids: ['add', 'remove', 'update', 'run', 'reset', 'splash', 'icon', 'favicon', 'notifications'] },
|
|
135
141
|
{ id: 'publish', ids: ['deploy', 'check', 'ios', 'codemagic'] },
|
|
136
|
-
{ id: 'maintenance', ids: ['upgrade', 'version', 'uninstall', 'docs'
|
|
142
|
+
{ id: 'maintenance', ids: ['upgrade', 'version', 'uninstall', 'docs'] },
|
|
137
143
|
{ id: 'advanced', ids: ['setup', 'validate'] },
|
|
138
144
|
];
|
|
145
|
+
// Commander auto-generates a `help` subcommand. Hide it from the
|
|
146
|
+
// "Other" section — the tip line at the bottom already teaches users
|
|
147
|
+
// how to get per-command help.
|
|
148
|
+
const HIDDEN_FROM_OTHER = new Set(['help']);
|
|
139
149
|
const knownIds = new Set(groups.flatMap((g) => g.ids));
|
|
140
150
|
const byName = new Map(visibleCommands.map((c) => [c.name(), c]));
|
|
141
151
|
for (const group of groups) {
|
|
@@ -157,7 +167,7 @@ function createLocalizedHelpConfig(t) {
|
|
|
157
167
|
]);
|
|
158
168
|
}
|
|
159
169
|
const otherItems = visibleCommands
|
|
160
|
-
.filter((c) => !knownIds.has(c.name()))
|
|
170
|
+
.filter((c) => !knownIds.has(c.name()) && !HIDDEN_FROM_OTHER.has(c.name()))
|
|
161
171
|
.map((sub) =>
|
|
162
172
|
formatItem(
|
|
163
173
|
localizeSubcommandTerm(helper.subcommandTerm(sub)),
|
|
@@ -221,7 +231,7 @@ function buildProgram(language) {
|
|
|
221
231
|
program.addHelpText('beforeAll', `${kleur.bold().cyan(t('cli.tagline'))}\n`);
|
|
222
232
|
program.addHelpText(
|
|
223
233
|
'after',
|
|
224
|
-
|
|
234
|
+
`${t('cli.help.quickStart')} ${kleur.cyan('kasy new')}\n`
|
|
225
235
|
);
|
|
226
236
|
|
|
227
237
|
applyLocalizedHelp(
|
|
@@ -343,6 +353,23 @@ function buildProgram(language) {
|
|
|
343
353
|
t
|
|
344
354
|
);
|
|
345
355
|
|
|
356
|
+
applyLocalizedHelp(
|
|
357
|
+
program
|
|
358
|
+
.command('reset')
|
|
359
|
+
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
360
|
+
.option('--ios', 'Reset on iOS simulator/device only')
|
|
361
|
+
.option('--android', 'Reset on Android emulator/device only')
|
|
362
|
+
.option('--web', 'Show web reset instructions only')
|
|
363
|
+
.option('-d, --device <id>', 'Reset specific device ID')
|
|
364
|
+
.option('--no-reinstall', 'Only uninstall, skip reinstall')
|
|
365
|
+
.option('--no-clear-launcher', 'Android only: skip clearing the launcher cache (widget previews may stay gray)')
|
|
366
|
+
.description(t('cli.command.reset.description'))
|
|
367
|
+
.action(async (directory, options) => {
|
|
368
|
+
await runReset(directory, { language, ...options });
|
|
369
|
+
}),
|
|
370
|
+
t
|
|
371
|
+
);
|
|
372
|
+
|
|
346
373
|
applyLocalizedHelp(
|
|
347
374
|
program
|
|
348
375
|
.command('add')
|
|
@@ -422,7 +449,27 @@ function buildProgram(language) {
|
|
|
422
449
|
t
|
|
423
450
|
);
|
|
424
451
|
|
|
425
|
-
const iosCmd = program
|
|
452
|
+
const iosCmd = program
|
|
453
|
+
.command('ios')
|
|
454
|
+
.description(t('cli.command.ios.description'))
|
|
455
|
+
.addHelpText('before', `\n${t('cli.command.ios.help.before')}`)
|
|
456
|
+
.action(async () => {
|
|
457
|
+
printCompactHeader(t);
|
|
458
|
+
ui.intro(kleur.cyan(t('cli.command.ios.picker.intro')));
|
|
459
|
+
const sub = await ui.select({
|
|
460
|
+
message: t('cli.command.ios.picker.message'),
|
|
461
|
+
options: [
|
|
462
|
+
{ value: 'configure', label: t('cli.command.ios.configure.description') },
|
|
463
|
+
{ value: 'release', label: t('cli.command.ios.release.description') },
|
|
464
|
+
{ value: 'build', label: t('cli.command.ios.build.description') },
|
|
465
|
+
{ value: 'clean', label: t('cli.command.ios.clean.description') },
|
|
466
|
+
],
|
|
467
|
+
});
|
|
468
|
+
if (sub === 'configure') await runIosConfigure('.', { language });
|
|
469
|
+
else if (sub === 'release') await runIosRelease('.', { language });
|
|
470
|
+
else if (sub === 'build') await runIosBuild('.', { language });
|
|
471
|
+
else if (sub === 'clean') await runIosClean('.', { language });
|
|
472
|
+
});
|
|
426
473
|
applyLocalizedHelp(
|
|
427
474
|
iosCmd
|
|
428
475
|
.command('configure')
|
|
@@ -468,7 +515,31 @@ function buildProgram(language) {
|
|
|
468
515
|
t
|
|
469
516
|
);
|
|
470
517
|
|
|
471
|
-
const codemagicCmd = program
|
|
518
|
+
const codemagicCmd = program
|
|
519
|
+
.command('codemagic')
|
|
520
|
+
.description(t('cli.command.codemagic.description'))
|
|
521
|
+
.addHelpText('before', `\n${t('cli.command.codemagic.help.before')}`)
|
|
522
|
+
.action(async () => {
|
|
523
|
+
printCompactHeader(t);
|
|
524
|
+
ui.intro(kleur.cyan(t('cli.command.codemagic.picker.intro')));
|
|
525
|
+
const sub = await ui.select({
|
|
526
|
+
message: t('cli.command.codemagic.picker.message'),
|
|
527
|
+
options: [
|
|
528
|
+
{ value: 'configure', label: t('cli.command.codemagic.configure.description') },
|
|
529
|
+
{ value: 'release', label: t('cli.command.codemagic.release.description') },
|
|
530
|
+
{ value: 'status', label: t('cli.command.codemagic.status.description'), hint: t('cli.command.codemagic.picker.statusHint') },
|
|
531
|
+
],
|
|
532
|
+
});
|
|
533
|
+
if (sub === 'configure') await runCodemagicConfigure('.', { language });
|
|
534
|
+
else if (sub === 'release') await runCodemagicRelease('.', { language });
|
|
535
|
+
else if (sub === 'status') {
|
|
536
|
+
const buildId = await ui.text({
|
|
537
|
+
message: 'Build ID',
|
|
538
|
+
validate: (v) => (v && v.trim() ? undefined : 'Build ID is required'),
|
|
539
|
+
});
|
|
540
|
+
await runCodemagicStatus(buildId?.trim(), '.', { language });
|
|
541
|
+
}
|
|
542
|
+
});
|
|
472
543
|
applyLocalizedHelp(
|
|
473
544
|
codemagicCmd
|
|
474
545
|
.command('configure')
|
|
@@ -531,9 +602,56 @@ function buildProgram(language) {
|
|
|
531
602
|
t
|
|
532
603
|
);
|
|
533
604
|
|
|
605
|
+
applyLocalizedHelp(
|
|
606
|
+
program
|
|
607
|
+
.command('icon')
|
|
608
|
+
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
609
|
+
.requiredOption('--image <path>', 'PNG with the app icon (square, 1024x1024 recommended)')
|
|
610
|
+
.option('--skip-generate', 'Only copy file, do not run flutter_launcher_icons', false)
|
|
611
|
+
.description(t('cli.command.icon.description'))
|
|
612
|
+
.action(async (directory, options) => {
|
|
613
|
+
const dir = directory || '.';
|
|
614
|
+
await runIcon(dir, {
|
|
615
|
+
language,
|
|
616
|
+
image: options.image,
|
|
617
|
+
skipGenerate: options.skipGenerate,
|
|
618
|
+
});
|
|
619
|
+
}),
|
|
620
|
+
t
|
|
621
|
+
);
|
|
622
|
+
|
|
623
|
+
applyLocalizedHelp(
|
|
624
|
+
program
|
|
625
|
+
.command('favicon')
|
|
626
|
+
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
627
|
+
.requiredOption('--image <path>', 'PNG with the web favicon / PWA icon (square, 512x512+ recommended)')
|
|
628
|
+
.option('--skip-generate', 'Only copy file, do not run flutter_launcher_icons', false)
|
|
629
|
+
.description(t('cli.command.favicon.description'))
|
|
630
|
+
.action(async (directory, options) => {
|
|
631
|
+
const dir = directory || '.';
|
|
632
|
+
await runFavicon(dir, {
|
|
633
|
+
language,
|
|
634
|
+
image: options.image,
|
|
635
|
+
skipGenerate: options.skipGenerate,
|
|
636
|
+
});
|
|
637
|
+
}),
|
|
638
|
+
t
|
|
639
|
+
);
|
|
640
|
+
|
|
534
641
|
const notificationsCmd = program
|
|
535
642
|
.command('notifications')
|
|
536
|
-
.description(t('cli.command.notifications.description'))
|
|
643
|
+
.description(t('cli.command.notifications.description'))
|
|
644
|
+
.action(async () => {
|
|
645
|
+
printCompactHeader(t);
|
|
646
|
+
ui.intro(kleur.cyan(t('cli.command.notifications.picker.intro')));
|
|
647
|
+
const sub = await ui.select({
|
|
648
|
+
message: t('cli.command.notifications.picker.message'),
|
|
649
|
+
options: [
|
|
650
|
+
{ value: 'text', label: t('cli.command.notifications.text.description') },
|
|
651
|
+
],
|
|
652
|
+
});
|
|
653
|
+
if (sub === 'text') await runNotificationsText('.', { language, directory: '.', lang: 'all' });
|
|
654
|
+
});
|
|
537
655
|
applyLocalizedHelp(
|
|
538
656
|
notificationsCmd
|
|
539
657
|
.command('text')
|
|
@@ -603,18 +721,28 @@ async function resolveLanguage(argv) {
|
|
|
603
721
|
return detectDefaultLanguage();
|
|
604
722
|
}
|
|
605
723
|
|
|
724
|
+
// Track the resolved language so the global catch can localize hints.
|
|
725
|
+
let activeLanguage = null;
|
|
726
|
+
|
|
606
727
|
async function main() {
|
|
607
|
-
|
|
608
|
-
|
|
728
|
+
// Strip --verbose before commander sees it — that flag is handled out-of-band
|
|
729
|
+
// by lib/utils/debug.js (reads process.argv directly).
|
|
730
|
+
const argv = stripVerboseFlag(process.argv.slice(2));
|
|
731
|
+
activeLanguage = await resolveLanguage(argv);
|
|
609
732
|
if (shouldRequireLicenseForArgv(argv)) {
|
|
610
|
-
await ensureLicenseKey({ language, argv });
|
|
733
|
+
await ensureLicenseKey({ language: activeLanguage, argv });
|
|
611
734
|
}
|
|
612
735
|
await checkForUpdates();
|
|
613
|
-
const program = buildProgram(
|
|
736
|
+
const program = buildProgram(activeLanguage);
|
|
614
737
|
await program.parseAsync([process.argv[0], process.argv[1], ...argv]);
|
|
615
738
|
}
|
|
616
739
|
|
|
617
740
|
main().catch((error) => {
|
|
618
|
-
|
|
741
|
+
const t = activeLanguage ? createTranslator(activeLanguage) : null;
|
|
742
|
+
console.error(formatError(error, t));
|
|
743
|
+
printVerboseError(error);
|
|
744
|
+
if (!isVerbose()) {
|
|
745
|
+
console.error(kleur.dim(' Run again with --verbose (or set KASY_DEBUG=1) for full output.'));
|
|
746
|
+
}
|
|
619
747
|
process.exitCode = 1;
|
|
620
748
|
});
|
package/lib/commands/add.js
CHANGED
|
@@ -460,7 +460,7 @@ async function postAddLlmChat(projectDir, kitSetup, answers, t) {
|
|
|
460
460
|
// 3. Deploy the LLM function automatically
|
|
461
461
|
if (backend === 'api') return { deployOk: false, deployAttempted: false };
|
|
462
462
|
|
|
463
|
-
const deploySpinner = ui.
|
|
463
|
+
const deploySpinner = ui.timedSpinner();
|
|
464
464
|
deploySpinner.start(t('add.llm_chat.deploying'));
|
|
465
465
|
try {
|
|
466
466
|
if (backend === 'firebase') {
|
|
@@ -761,7 +761,7 @@ async function runAdd(module, options = {}) {
|
|
|
761
761
|
// 11. build_runner (only when needed: features with codegen)
|
|
762
762
|
const needsBuildRunner = ['revenuecat', 'analytics', 'sentry', 'onboarding', 'llm_chat', 'feedback'].includes(normalized);
|
|
763
763
|
if (needsBuildRunner) {
|
|
764
|
-
const spinner = ui.
|
|
764
|
+
const spinner = ui.timedSpinner();
|
|
765
765
|
spinner.start(t('add.buildRunner'));
|
|
766
766
|
try {
|
|
767
767
|
await execAsync('dart run build_runner build --delete-conflicting-outputs', { cwd: projectDir, timeout: 600_000 });
|
|
@@ -107,8 +107,11 @@ async function runRelease(directory, options = {}) {
|
|
|
107
107
|
printCompactHeader(t);
|
|
108
108
|
ui.intro(t('codemagic.release.title'));
|
|
109
109
|
|
|
110
|
+
const spinner = ui.spinner();
|
|
111
|
+
spinner.start(t('codemagic.release.spin'));
|
|
110
112
|
try {
|
|
111
113
|
const result = await triggerBuild(projectDir, validation.env);
|
|
114
|
+
spinner.stop(t('codemagic.release.spinDone'));
|
|
112
115
|
const buildId = result.buildId || result._id;
|
|
113
116
|
if (buildId) {
|
|
114
117
|
ui.log.success(t('codemagic.release.triggered'));
|
|
@@ -119,7 +122,7 @@ async function runRelease(directory, options = {}) {
|
|
|
119
122
|
ui.outro(t('codemagic.release.triggered'));
|
|
120
123
|
}
|
|
121
124
|
} catch (err) {
|
|
122
|
-
|
|
125
|
+
spinner.stop(err.message, 2);
|
|
123
126
|
process.exitCode = 1;
|
|
124
127
|
}
|
|
125
128
|
}
|
|
@@ -145,14 +148,18 @@ async function runStatus(buildId, directory, options = {}) {
|
|
|
145
148
|
return;
|
|
146
149
|
}
|
|
147
150
|
|
|
151
|
+
printCompactHeader(t);
|
|
152
|
+
ui.intro(`${t('codemagic.status.title')}: ${buildId}`);
|
|
153
|
+
|
|
154
|
+
const spinner = ui.spinner();
|
|
155
|
+
spinner.start(t('codemagic.status.spin'));
|
|
148
156
|
try {
|
|
149
157
|
const result = await getBuildStatus(buildId, validation.env.CODEMAGIC_API_TOKEN);
|
|
150
|
-
|
|
151
|
-
ui.intro(`${t('codemagic.status.title')}: ${buildId}`);
|
|
158
|
+
spinner.stop(t('codemagic.status.spinDone'));
|
|
152
159
|
ui.note(JSON.stringify(result, null, 2));
|
|
153
160
|
ui.outro('');
|
|
154
161
|
} catch (err) {
|
|
155
|
-
|
|
162
|
+
spinner.stop(err.message, 2);
|
|
156
163
|
process.exitCode = 1;
|
|
157
164
|
}
|
|
158
165
|
}
|
package/lib/commands/deploy.js
CHANGED
|
@@ -122,7 +122,7 @@ async function deployFirebase(projectDir, options, tr) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
const spinner = ui.
|
|
125
|
+
const spinner = ui.timedSpinner();
|
|
126
126
|
spinner.start(tr('deploy.firebase.spin'));
|
|
127
127
|
let steps;
|
|
128
128
|
try {
|
|
@@ -173,7 +173,7 @@ async function deploySupabase(projectDir, tr) {
|
|
|
173
173
|
const firebaseProjectId = await readFirebaseProjectId(projectDir);
|
|
174
174
|
|
|
175
175
|
if (firebaseProjectId) {
|
|
176
|
-
const fcmSpinner = ui.
|
|
176
|
+
const fcmSpinner = ui.timedSpinner();
|
|
177
177
|
fcmSpinner.start(tr('deploy.supabase.sakSpin'));
|
|
178
178
|
const fcmResult = await createFcmServiceAccountKey(firebaseProjectId);
|
|
179
179
|
fcmSpinner.stop(tr('deploy.supabase.sakSpinDone'));
|
|
@@ -194,7 +194,7 @@ async function deploySupabase(projectDir, tr) {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// ── 3. Deploy edge functions ────────────────────────────────────────────
|
|
197
|
-
const fnSpinner = ui.
|
|
197
|
+
const fnSpinner = ui.timedSpinner();
|
|
198
198
|
fnSpinner.start(tr('deploy.supabase.fnSpin'));
|
|
199
199
|
const fnResult = await deployFunctions(projectDir);
|
|
200
200
|
fnSpinner.stop(tr('deploy.supabase.fnSpinDone'));
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const kleur = require('kleur');
|
|
4
|
+
const ui = require('../utils/ui');
|
|
5
|
+
const { printCompactHeader } = require('../utils/brand');
|
|
6
|
+
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
7
|
+
const { inspectPng } = require('./splash');
|
|
8
|
+
const { runFlutterLauncherIcons } = require('./icon');
|
|
9
|
+
|
|
10
|
+
const ASSETS_DIR = path.join('assets', 'images');
|
|
11
|
+
const FAVICON_NAME = 'favicon.png';
|
|
12
|
+
|
|
13
|
+
async function assertKasyProject(projectDir, t) {
|
|
14
|
+
const kitSetupPath = path.join(projectDir, 'kit_setup.json');
|
|
15
|
+
const pubspecPath = path.join(projectDir, 'pubspec.yaml');
|
|
16
|
+
if (!(await fs.pathExists(kitSetupPath)) && !(await fs.pathExists(pubspecPath))) {
|
|
17
|
+
throw new Error(t('favicon.error.notKasyProject'));
|
|
18
|
+
}
|
|
19
|
+
const assetsDir = path.join(projectDir, ASSETS_DIR);
|
|
20
|
+
await fs.ensureDir(assetsDir);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveInputPath(flagValue) {
|
|
24
|
+
if (!flagValue) return null;
|
|
25
|
+
const expanded = flagValue.startsWith('~')
|
|
26
|
+
? path.join(require('node:os').homedir(), flagValue.slice(1))
|
|
27
|
+
: flagValue;
|
|
28
|
+
return path.resolve(expanded);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function runFavicon(projectDir, options = {}) {
|
|
32
|
+
const language = options.language || detectDefaultLanguage();
|
|
33
|
+
const t = createTranslator(language);
|
|
34
|
+
|
|
35
|
+
printCompactHeader();
|
|
36
|
+
ui.intro(kleur.bold().cyan(t('favicon.intro')));
|
|
37
|
+
|
|
38
|
+
await assertKasyProject(projectDir, t);
|
|
39
|
+
|
|
40
|
+
const imagePath = resolveInputPath(options.image);
|
|
41
|
+
|
|
42
|
+
if (!imagePath) {
|
|
43
|
+
ui.log.error(t('favicon.error.imageRequired'));
|
|
44
|
+
ui.log.message(kleur.dim('kasy favicon --image <favicon.png>'));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!(await fs.pathExists(imagePath))) {
|
|
49
|
+
ui.log.error(t('favicon.error.fileNotFound', { path: imagePath }));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const inspectSpinner = ui.spinner();
|
|
54
|
+
inspectSpinner.start(t('favicon.validating'));
|
|
55
|
+
|
|
56
|
+
let info;
|
|
57
|
+
try {
|
|
58
|
+
info = await inspectPng(imagePath);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
inspectSpinner.stop(`✖ ${err.message || t('favicon.error.notPng')}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!info.valid) {
|
|
65
|
+
inspectSpinner.stop(`✖ ${t('favicon.error.notPng')}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
inspectSpinner.stop(t('favicon.validated'));
|
|
70
|
+
|
|
71
|
+
const warnings = [];
|
|
72
|
+
if (info.width !== info.height) {
|
|
73
|
+
warnings.push(t('favicon.warn.notSquare', { w: info.width, h: info.height }));
|
|
74
|
+
}
|
|
75
|
+
if (info.width < 512 || info.height < 512) {
|
|
76
|
+
warnings.push(t('favicon.warn.small', { w: info.width, h: info.height }));
|
|
77
|
+
}
|
|
78
|
+
if (warnings.length > 0) {
|
|
79
|
+
ui.note(warnings.map((w) => `${kleur.yellow('⚠')} ${w}`).join('\n'), t('favicon.warn.title'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const dest = path.join(projectDir, ASSETS_DIR, FAVICON_NAME);
|
|
83
|
+
|
|
84
|
+
const copySpinner = ui.spinner();
|
|
85
|
+
copySpinner.start(t('favicon.copying'));
|
|
86
|
+
await fs.copy(imagePath, dest, { overwrite: true });
|
|
87
|
+
copySpinner.stop(t('favicon.copied'));
|
|
88
|
+
|
|
89
|
+
if (options.skipGenerate) {
|
|
90
|
+
ui.note(t('favicon.skipGenerate.hint'), t('favicon.skipGenerate.title'));
|
|
91
|
+
ui.outro(t('favicon.done'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const genSpinner = ui.spinner();
|
|
96
|
+
genSpinner.start(t('favicon.generating'));
|
|
97
|
+
const result = await runFlutterLauncherIcons(projectDir);
|
|
98
|
+
if (result.ok) {
|
|
99
|
+
genSpinner.stop(t('favicon.generated'));
|
|
100
|
+
} else {
|
|
101
|
+
genSpinner.stop(`⚠ ${t('favicon.error.generateFailed')}`);
|
|
102
|
+
if (result.stderr) {
|
|
103
|
+
ui.log.message(kleur.dim(result.stderr.split('\n').slice(0, 8).join('\n')));
|
|
104
|
+
}
|
|
105
|
+
ui.log.message(kleur.dim('dart run flutter_launcher_icons'));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const summary = `${kleur.bold(t('favicon.summary.favicon'))}: ${kleur.white(FAVICON_NAME)} (${info.width}x${info.height})`;
|
|
110
|
+
ui.note(summary, t('favicon.summary.title'));
|
|
111
|
+
|
|
112
|
+
ui.outro(t('favicon.done'));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { runFavicon };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const { exec } = require('node:child_process');
|
|
4
|
+
const { promisify } = require('node:util');
|
|
5
|
+
const kleur = require('kleur');
|
|
6
|
+
const ui = require('../utils/ui');
|
|
7
|
+
const { printCompactHeader } = require('../utils/brand');
|
|
8
|
+
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
9
|
+
const { inspectPng } = require('./splash');
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
const ASSETS_DIR = path.join('assets', 'images');
|
|
14
|
+
const ICON_NAME = 'icon.png';
|
|
15
|
+
|
|
16
|
+
async function assertKasyProject(projectDir, t) {
|
|
17
|
+
const kitSetupPath = path.join(projectDir, 'kit_setup.json');
|
|
18
|
+
const pubspecPath = path.join(projectDir, 'pubspec.yaml');
|
|
19
|
+
if (!(await fs.pathExists(kitSetupPath)) && !(await fs.pathExists(pubspecPath))) {
|
|
20
|
+
throw new Error(t('icon.error.notKasyProject'));
|
|
21
|
+
}
|
|
22
|
+
const assetsDir = path.join(projectDir, ASSETS_DIR);
|
|
23
|
+
await fs.ensureDir(assetsDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveInputPath(flagValue) {
|
|
27
|
+
if (!flagValue) return null;
|
|
28
|
+
const expanded = flagValue.startsWith('~')
|
|
29
|
+
? path.join(require('node:os').homedir(), flagValue.slice(1))
|
|
30
|
+
: flagValue;
|
|
31
|
+
return path.resolve(expanded);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function runIcon(projectDir, options = {}) {
|
|
35
|
+
const language = options.language || detectDefaultLanguage();
|
|
36
|
+
const t = createTranslator(language);
|
|
37
|
+
|
|
38
|
+
printCompactHeader();
|
|
39
|
+
ui.intro(kleur.bold().cyan(t('icon.intro')));
|
|
40
|
+
|
|
41
|
+
await assertKasyProject(projectDir, t);
|
|
42
|
+
|
|
43
|
+
const imagePath = resolveInputPath(options.image);
|
|
44
|
+
|
|
45
|
+
if (!imagePath) {
|
|
46
|
+
ui.log.error(t('icon.error.imageRequired'));
|
|
47
|
+
ui.log.message(kleur.dim('kasy icon --image <icon.png>'));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!(await fs.pathExists(imagePath))) {
|
|
52
|
+
ui.log.error(t('icon.error.fileNotFound', { path: imagePath }));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const inspectSpinner = ui.spinner();
|
|
57
|
+
inspectSpinner.start(t('icon.validating'));
|
|
58
|
+
|
|
59
|
+
let info;
|
|
60
|
+
try {
|
|
61
|
+
info = await inspectPng(imagePath);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
inspectSpinner.stop(`✖ ${err.message || t('icon.error.notPng')}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!info.valid) {
|
|
68
|
+
inspectSpinner.stop(`✖ ${t('icon.error.notPng')}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
inspectSpinner.stop(t('icon.validated'));
|
|
73
|
+
|
|
74
|
+
const warnings = [];
|
|
75
|
+
if (info.width !== info.height) {
|
|
76
|
+
warnings.push(t('icon.warn.notSquare', { w: info.width, h: info.height }));
|
|
77
|
+
}
|
|
78
|
+
if (info.width < 1024 || info.height < 1024) {
|
|
79
|
+
warnings.push(t('icon.warn.small', { w: info.width, h: info.height }));
|
|
80
|
+
}
|
|
81
|
+
if (info.hasAlpha) {
|
|
82
|
+
warnings.push(t('icon.warn.hasAlpha'));
|
|
83
|
+
}
|
|
84
|
+
if (warnings.length > 0) {
|
|
85
|
+
ui.note(warnings.map((w) => `${kleur.yellow('⚠')} ${w}`).join('\n'), t('icon.warn.title'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const dest = path.join(projectDir, ASSETS_DIR, ICON_NAME);
|
|
89
|
+
|
|
90
|
+
const copySpinner = ui.spinner();
|
|
91
|
+
copySpinner.start(t('icon.copying'));
|
|
92
|
+
await fs.copy(imagePath, dest, { overwrite: true });
|
|
93
|
+
copySpinner.stop(t('icon.copied'));
|
|
94
|
+
|
|
95
|
+
if (options.skipGenerate) {
|
|
96
|
+
ui.note(t('icon.skipGenerate.hint'), t('icon.skipGenerate.title'));
|
|
97
|
+
ui.outro(t('icon.done'));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const genSpinner = ui.spinner();
|
|
102
|
+
genSpinner.start(t('icon.generating'));
|
|
103
|
+
const result = await runFlutterLauncherIcons(projectDir);
|
|
104
|
+
if (result.ok) {
|
|
105
|
+
genSpinner.stop(t('icon.generated'));
|
|
106
|
+
} else {
|
|
107
|
+
genSpinner.stop(`⚠ ${t('icon.error.generateFailed')}`);
|
|
108
|
+
if (result.stderr) {
|
|
109
|
+
ui.log.message(kleur.dim(result.stderr.split('\n').slice(0, 8).join('\n')));
|
|
110
|
+
}
|
|
111
|
+
ui.log.message(kleur.dim('dart run flutter_launcher_icons'));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const summary = `${kleur.bold(t('icon.summary.icon'))}: ${kleur.white(ICON_NAME)} (${info.width}x${info.height})`;
|
|
116
|
+
ui.note(summary, t('icon.summary.title'));
|
|
117
|
+
ui.note(t('icon.reinstall.hint'), t('icon.reinstall.title'));
|
|
118
|
+
|
|
119
|
+
ui.outro(t('icon.done'));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function runFlutterLauncherIcons(projectDir) {
|
|
123
|
+
try {
|
|
124
|
+
const { stdout, stderr } = await execAsync(
|
|
125
|
+
'dart run flutter_launcher_icons',
|
|
126
|
+
{
|
|
127
|
+
cwd: projectDir,
|
|
128
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
129
|
+
timeout: 240_000,
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
return { ok: true, stdout, stderr };
|
|
133
|
+
} catch (err) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
error: err.message,
|
|
137
|
+
stdout: err.stdout || '',
|
|
138
|
+
stderr: err.stderr || '',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { runIcon, runFlutterLauncherIcons };
|
package/lib/commands/ios.js
CHANGED
|
@@ -19,7 +19,9 @@ const {
|
|
|
19
19
|
checkIosSigning,
|
|
20
20
|
checkDiskSpaceForIosBuild,
|
|
21
21
|
isXcodeCacheBuildError,
|
|
22
|
+
isPodNetworkError,
|
|
22
23
|
printBuildFailureHints,
|
|
24
|
+
printPodNetworkHints,
|
|
23
25
|
runIosClean,
|
|
24
26
|
MIN_DISK_GB_FOR_IOS_BUILD,
|
|
25
27
|
URL_APP_STORE_CONNECT_APPS,
|
|
@@ -52,6 +54,8 @@ async function runConfigure(directory, options = {}) {
|
|
|
52
54
|
openUrl(URL_APP_STORE_CONNECT_API);
|
|
53
55
|
openUrl(URL_APP_STORE_CONNECT_APPS);
|
|
54
56
|
|
|
57
|
+
ui.note(t('ios.configure.note.body'), t('ios.configure.note.title'));
|
|
58
|
+
|
|
55
59
|
const cancel = () => { ui.cancel(t('ios.configure.cancelled')); process.exit(0); };
|
|
56
60
|
const required = (v) => (v && String(v).trim().length > 0 ? undefined : t('ios.configure.q.required'));
|
|
57
61
|
|
|
@@ -126,12 +130,25 @@ async function runBuildOrRelease(directory, options = {}, mode) {
|
|
|
126
130
|
const validation = await validateAppleSetup(projectDir);
|
|
127
131
|
if (!validation.ok) {
|
|
128
132
|
printCompactHeader(t);
|
|
129
|
-
ui.log.
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
ui.log.warn(t('ios.error.notConfigured'));
|
|
134
|
+
const shouldConfigure = await ui.confirm({
|
|
135
|
+
message: t('ios.release.askConfigure'),
|
|
136
|
+
initialValue: true,
|
|
137
|
+
});
|
|
138
|
+
if (!shouldConfigure) {
|
|
139
|
+
if (validation.issues.includes('missing_env')) {
|
|
140
|
+
ui.log.message(kleur.dim(t('ios.error.runConfigure')));
|
|
141
|
+
}
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
await runConfigure(directory, options);
|
|
146
|
+
const recheck = await validateAppleSetup(projectDir);
|
|
147
|
+
if (!recheck.ok) {
|
|
148
|
+
ui.log.error(t('ios.error.notConfigured'));
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
132
151
|
}
|
|
133
|
-
process.exitCode = 1;
|
|
134
|
-
return;
|
|
135
152
|
}
|
|
136
153
|
}
|
|
137
154
|
|
|
@@ -165,9 +182,13 @@ async function runBuildOrRelease(directory, options = {}, mode) {
|
|
|
165
182
|
ui.outro(mode === 'build' ? t('ios.build.success') : t('ios.release.success'));
|
|
166
183
|
} catch (err) {
|
|
167
184
|
const output = err.buildOutput || err.message;
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
if (isPodNetworkError(output)) {
|
|
186
|
+
printPodNetworkHints(t);
|
|
187
|
+
} else if (isXcodeCacheBuildError(output)) {
|
|
188
|
+
ui.log.error(output.slice(-4000));
|
|
170
189
|
printBuildFailureHints(t, projectDir);
|
|
190
|
+
} else {
|
|
191
|
+
ui.log.error(output.slice(-4000));
|
|
171
192
|
}
|
|
172
193
|
process.exitCode = 1;
|
|
173
194
|
}
|