kasy-cli 1.31.4 → 1.31.5
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/commands/add.js +2 -1
- package/lib/commands/remove.js +2 -1
- package/lib/commands/reset.js +5 -0
- package/lib/commands/run.js +9 -0
- package/lib/commands/update.js +4 -3
- package/lib/utils/flutter-run.js +24 -4
- package/package.json +1 -1
- package/templates/firebase/lib/core/config/app_env.dart +77 -5
- package/templates/firebase/pubspec.yaml +1 -1
package/lib/commands/add.js
CHANGED
|
@@ -8,6 +8,7 @@ const kleur = require('kleur');
|
|
|
8
8
|
const ui = require('../utils/ui');
|
|
9
9
|
const { printCompactHeader, paintLime } = require('../utils/brand');
|
|
10
10
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
11
|
+
const { augmentedEnv } = require('../utils/env-tools');
|
|
11
12
|
const {
|
|
12
13
|
AVAILABLE_FEATURES,
|
|
13
14
|
FEATURES_PATCH_DIR,
|
|
@@ -797,7 +798,7 @@ async function runAdd(module, options = {}) {
|
|
|
797
798
|
const spinner = ui.spinner({ color: paintLime });
|
|
798
799
|
spinner.start(t('add.pubGet'));
|
|
799
800
|
try {
|
|
800
|
-
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000 });
|
|
801
|
+
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000, env: augmentedEnv() });
|
|
801
802
|
spinner.stop(t('add.pubGetDone'));
|
|
802
803
|
} catch {
|
|
803
804
|
spinner.stop(`⚠ ${t('add.pubGetFailed')}`);
|
package/lib/commands/remove.js
CHANGED
|
@@ -9,6 +9,7 @@ const kleur = require('kleur');
|
|
|
9
9
|
const ui = require('../utils/ui');
|
|
10
10
|
const { printCompactHeader } = require('../utils/brand');
|
|
11
11
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
12
|
+
const { augmentedEnv } = require('../utils/env-tools');
|
|
12
13
|
const {
|
|
13
14
|
AVAILABLE_FEATURES,
|
|
14
15
|
normalizeFeature,
|
|
@@ -471,7 +472,7 @@ async function runRemove(module, options = {}) {
|
|
|
471
472
|
const spinner = ui.spinner();
|
|
472
473
|
spinner.start(t('remove.pubGet'));
|
|
473
474
|
try {
|
|
474
|
-
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000 });
|
|
475
|
+
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000, env: augmentedEnv() });
|
|
475
476
|
spinner.stop(t('remove.pubGetDone'));
|
|
476
477
|
} catch {
|
|
477
478
|
spinner.stop(`⚠ ${t('remove.pubGetFailed')}`);
|
package/lib/commands/reset.js
CHANGED
|
@@ -7,6 +7,9 @@ const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
|
7
7
|
const { printCompactHeader } = require('../utils/brand');
|
|
8
8
|
const { readBundleId, readPackageName } = require('../utils/mobile-identity');
|
|
9
9
|
const { spawnFlutterWithSpinner } = require('../utils/flutter-run');
|
|
10
|
+
const { augmentedEnv } = require('../utils/env-tools');
|
|
11
|
+
|
|
12
|
+
const isWindows = process.platform === 'win32';
|
|
10
13
|
|
|
11
14
|
function runCmd(cmd, args) {
|
|
12
15
|
const res = spawnSync(cmd, args, { encoding: 'utf8' });
|
|
@@ -21,6 +24,8 @@ async function listFlutterDevices(projectDir) {
|
|
|
21
24
|
const res = spawnSync('flutter', ['devices', '--machine'], {
|
|
22
25
|
cwd: projectDir,
|
|
23
26
|
encoding: 'utf8',
|
|
27
|
+
env: augmentedEnv(),
|
|
28
|
+
shell: isWindows,
|
|
24
29
|
});
|
|
25
30
|
if (res.status !== 0) return [];
|
|
26
31
|
try {
|
package/lib/commands/run.js
CHANGED
|
@@ -6,11 +6,20 @@ const ui = require('../utils/ui');
|
|
|
6
6
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
7
7
|
const { printCompactHeader, paintLime } = require('../utils/brand');
|
|
8
8
|
const { spawnFlutterWithSpinner } = require('../utils/flutter-run');
|
|
9
|
+
const { augmentedEnv } = require('../utils/env-tools');
|
|
10
|
+
|
|
11
|
+
const isWindows = process.platform === 'win32';
|
|
9
12
|
|
|
10
13
|
function listFlutterDevices(projectDir) {
|
|
14
|
+
// Same as `kasy run`'s launcher: expose a freshly-installed Flutter SDK via
|
|
15
|
+
// augmentedEnv() (the terminal's PATH may not have it yet), and use a shell on
|
|
16
|
+
// Windows so the flutter.bat is found. Without this, a machine that just ran
|
|
17
|
+
// `kasy new` reports "Flutter not found" here even though Flutter is installed.
|
|
11
18
|
const res = spawnSync('flutter', ['devices', '--machine'], {
|
|
12
19
|
cwd: projectDir,
|
|
13
20
|
encoding: 'utf8',
|
|
21
|
+
env: augmentedEnv(),
|
|
22
|
+
shell: isWindows,
|
|
14
23
|
});
|
|
15
24
|
if (res.status !== 0) return [];
|
|
16
25
|
try {
|
package/lib/commands/update.js
CHANGED
|
@@ -9,6 +9,7 @@ const kleur = require('kleur');
|
|
|
9
9
|
const ui = require('../utils/ui');
|
|
10
10
|
const { printCompactHeader, paintLime } = require('../utils/brand');
|
|
11
11
|
const { createTranslator, detectDefaultLanguage } = require('../utils/i18n');
|
|
12
|
+
const { augmentedEnv } = require('../utils/env-tools');
|
|
12
13
|
const {
|
|
13
14
|
AVAILABLE_FEATURES,
|
|
14
15
|
BASE_COMPONENT_FILES,
|
|
@@ -204,7 +205,7 @@ async function runUpdate(module, options = {}) {
|
|
|
204
205
|
const spinnerPubGet = ui.spinner({ color: paintLime });
|
|
205
206
|
spinnerPubGet.start(t('update.pubGet'));
|
|
206
207
|
try {
|
|
207
|
-
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000 });
|
|
208
|
+
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000, env: augmentedEnv() });
|
|
208
209
|
spinnerPubGet.stop(t('update.pubGetDone'));
|
|
209
210
|
} catch {
|
|
210
211
|
spinnerPubGet.stop(`⚠ ${t('update.pubGetFailed')}`);
|
|
@@ -252,7 +253,7 @@ async function runUpdate(module, options = {}) {
|
|
|
252
253
|
const spinnerPubGet = ui.spinner({ color: paintLime });
|
|
253
254
|
spinnerPubGet.start(t('update.pubGet'));
|
|
254
255
|
try {
|
|
255
|
-
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000 });
|
|
256
|
+
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000, env: augmentedEnv() });
|
|
256
257
|
spinnerPubGet.stop(t('update.pubGetDone'));
|
|
257
258
|
} catch {
|
|
258
259
|
spinnerPubGet.stop(`⚠ ${t('update.pubGetFailed')}`);
|
|
@@ -365,7 +366,7 @@ async function runUpdate(module, options = {}) {
|
|
|
365
366
|
const spinner = ui.spinner({ color: paintLime });
|
|
366
367
|
spinner.start(t('update.pubGet'));
|
|
367
368
|
try {
|
|
368
|
-
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000 });
|
|
369
|
+
await execAsync('flutter pub get', { cwd: projectDir, timeout: 300_000, env: augmentedEnv() });
|
|
369
370
|
spinner.stop(t('update.pubGetDone'));
|
|
370
371
|
} catch {
|
|
371
372
|
spinner.stop(`⚠ ${t('update.pubGetFailed')}`);
|
package/lib/utils/flutter-run.js
CHANGED
|
@@ -31,6 +31,26 @@ const fs = require('node:fs');
|
|
|
31
31
|
const { spawn } = require('node:child_process');
|
|
32
32
|
const kleur = require('kleur');
|
|
33
33
|
const ui = require('./ui');
|
|
34
|
+
const { augmentedEnv } = require('./env-tools');
|
|
35
|
+
|
|
36
|
+
const isWindows = process.platform === 'win32';
|
|
37
|
+
|
|
38
|
+
// `flutter` runs from an SDK that may have been installed by `kasy new` in this
|
|
39
|
+
// same session — and Windows only refreshes PATH for NEW terminals, so the
|
|
40
|
+
// current one doesn't see it yet. augmentedEnv() exposes %LOCALAPPDATA%\flutter\bin
|
|
41
|
+
// (and the pub-cache bin) so `kasy run` finds flutter exactly like `kasy new` did.
|
|
42
|
+
// On Windows `flutter` is a .bat, which Node's spawn() refuses to run without a
|
|
43
|
+
// shell — hence shell:true there. With shell:true the args become a command line,
|
|
44
|
+
// so any arg with a space must be quoted (our flutter args normally have none,
|
|
45
|
+
// but a dart-define value could).
|
|
46
|
+
function flutterSpawnOptions(extra = {}) {
|
|
47
|
+
return { env: augmentedEnv(), shell: isWindows, ...extra };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function safeArgs(args) {
|
|
51
|
+
if (!isWindows) return args;
|
|
52
|
+
return args.map((a) => (/\s/.test(a) ? `"${a}"` : a));
|
|
53
|
+
}
|
|
34
54
|
|
|
35
55
|
// Markers that tell us the initial build is done and the app is running.
|
|
36
56
|
const FLUTTER_READY_RE = /Flutter run key commands\.|is listening on|VM Service|Dart VM service|To hot reload|Hot restart/i;
|
|
@@ -140,10 +160,10 @@ function spawnFlutterWithSpinner(args, projectDir, t, options = {}) {
|
|
|
140
160
|
*/
|
|
141
161
|
function spawnRaw(args, projectDir, t, log, onReady) {
|
|
142
162
|
return new Promise((resolve, reject) => {
|
|
143
|
-
const proc = spawn('flutter', args, {
|
|
163
|
+
const proc = spawn('flutter', safeArgs(args), flutterSpawnOptions({
|
|
144
164
|
cwd: projectDir,
|
|
145
165
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
146
|
-
});
|
|
166
|
+
}));
|
|
147
167
|
|
|
148
168
|
let readyFired = false;
|
|
149
169
|
const fireReady = () => {
|
|
@@ -192,10 +212,10 @@ function spawnRaw(args, projectDir, t, log, onReady) {
|
|
|
192
212
|
*/
|
|
193
213
|
function spawnWithSpinner(args, projectDir, t, log, onReady) {
|
|
194
214
|
return new Promise((resolve, reject) => {
|
|
195
|
-
const proc = spawn('flutter', args, {
|
|
215
|
+
const proc = spawn('flutter', safeArgs(args), flutterSpawnOptions({
|
|
196
216
|
cwd: projectDir,
|
|
197
217
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
198
|
-
});
|
|
218
|
+
}));
|
|
199
219
|
|
|
200
220
|
const spinner = ui.spinner();
|
|
201
221
|
const startTime = Date.now();
|
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'package:flutter/foundation.dart' show kReleaseMode;
|
|
1
2
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
2
3
|
|
|
3
4
|
const String _kEnvFromDefine = String.fromEnvironment(
|
|
@@ -16,6 +17,15 @@ const String _kRcAndroidApiKeyFromDefine = String.fromEnvironment(
|
|
|
16
17
|
const String _kRcIosApiKeyFromDefine = String.fromEnvironment(
|
|
17
18
|
'RC_IOS_API_KEY',
|
|
18
19
|
);
|
|
20
|
+
const String _kRcTestKeyFromDefine = String.fromEnvironment(
|
|
21
|
+
'RC_TEST_KEY',
|
|
22
|
+
);
|
|
23
|
+
const String _kRcIosProdKeyFromDefine = String.fromEnvironment(
|
|
24
|
+
'RC_IOS_PROD_KEY',
|
|
25
|
+
);
|
|
26
|
+
const String _kRcAndroidProdKeyFromDefine = String.fromEnvironment(
|
|
27
|
+
'RC_ANDROID_PROD_KEY',
|
|
28
|
+
);
|
|
19
29
|
const String _kMixpanelTokenFromDefine = String.fromEnvironment(
|
|
20
30
|
'MIXPANEL_TOKEN',
|
|
21
31
|
);
|
|
@@ -57,13 +67,43 @@ class AppEnv {
|
|
|
57
67
|
dartDefineValue: _kSupabaseTokenFromDefine,
|
|
58
68
|
);
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
/// RevenueCat "Test Store" public key (`test_…`). Works only against the
|
|
71
|
+
/// RevenueCat fake store (simulator/emulator); the native SDK rejects it in
|
|
72
|
+
/// release builds.
|
|
73
|
+
static String get rcTestKey =>
|
|
74
|
+
_resolve(key: 'RC_TEST_KEY', dartDefineValue: _kRcTestKeyFromDefine);
|
|
75
|
+
|
|
76
|
+
/// RevenueCat production/sandbox iOS key (`appl_…`) — the App Store key used
|
|
77
|
+
/// on physical devices, TestFlight and production.
|
|
78
|
+
static String get rcIosProdKey => _resolve(
|
|
79
|
+
key: 'RC_IOS_PROD_KEY',
|
|
80
|
+
dartDefineValue: _kRcIosProdKeyFromDefine,
|
|
63
81
|
);
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
/// RevenueCat production/sandbox Android key (`goog_…`) — the Play Store key
|
|
84
|
+
/// used on physical devices, internal testing and production.
|
|
85
|
+
static String get rcAndroidProdKey => _resolve(
|
|
86
|
+
key: 'RC_ANDROID_PROD_KEY',
|
|
87
|
+
dartDefineValue: _kRcAndroidProdKeyFromDefine,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
/// Effective RevenueCat key for Android, resolved for the current build.
|
|
91
|
+
static String get rcAndroidApiKey => _resolveRcKey(
|
|
92
|
+
explicit: _resolve(
|
|
93
|
+
key: 'RC_ANDROID_API_KEY',
|
|
94
|
+
dartDefineValue: _kRcAndroidApiKeyFromDefine,
|
|
95
|
+
),
|
|
96
|
+
prodKey: rcAndroidProdKey,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/// Effective RevenueCat key for iOS, resolved for the current build.
|
|
100
|
+
static String get rcIosApiKey => _resolveRcKey(
|
|
101
|
+
explicit: _resolve(
|
|
102
|
+
key: 'RC_IOS_API_KEY',
|
|
103
|
+
dartDefineValue: _kRcIosApiKeyFromDefine,
|
|
104
|
+
),
|
|
105
|
+
prodKey: rcIosProdKey,
|
|
106
|
+
);
|
|
67
107
|
|
|
68
108
|
static String get mixpanelToken => _resolve(
|
|
69
109
|
key: 'MIXPANEL_TOKEN',
|
|
@@ -91,4 +131,36 @@ class AppEnv {
|
|
|
91
131
|
if (dartDefineValue.isNotEmpty) return dartDefineValue;
|
|
92
132
|
return defaultValue;
|
|
93
133
|
}
|
|
134
|
+
|
|
135
|
+
/// Resolve the RevenueCat key that actually works for the current build.
|
|
136
|
+
///
|
|
137
|
+
/// A Test Store key (`test_…`) only works against RevenueCat's fake store
|
|
138
|
+
/// (simulator/emulator); the native SDK refuses it in release builds and the
|
|
139
|
+
/// paywall comes up empty. So:
|
|
140
|
+
///
|
|
141
|
+
/// - Release builds (TestFlight / App Store / Play Store) always use the
|
|
142
|
+
/// production key (`appl_…` / `goog_…`), read from the bundled `.env`
|
|
143
|
+
/// (`RC_IOS_PROD_KEY` / `RC_ANDROID_PROD_KEY`). Because `.env` ships as an
|
|
144
|
+
/// asset, this holds no matter how the IPA/AAB was built (`kasy ios
|
|
145
|
+
/// release`, Xcode archive, Codemagic, `flutter build …`). An explicit
|
|
146
|
+
/// non-test key (injected by `kasy run` on a device, or by Codemagic
|
|
147
|
+
/// variable groups) still wins.
|
|
148
|
+
/// - Debug builds honor whatever `kasy run` injected (`test_` for
|
|
149
|
+
/// simulators, the prod key for physical devices) and fall back to the
|
|
150
|
+
/// Test Store key.
|
|
151
|
+
static String _resolveRcKey({
|
|
152
|
+
required String explicit,
|
|
153
|
+
required String prodKey,
|
|
154
|
+
}) {
|
|
155
|
+
final bool explicitIsTest = explicit.startsWith('test_');
|
|
156
|
+
if (kReleaseMode) {
|
|
157
|
+
if (explicit.isNotEmpty && !explicitIsTest) return explicit;
|
|
158
|
+
if (prodKey.isNotEmpty) return prodKey;
|
|
159
|
+
return explicit;
|
|
160
|
+
}
|
|
161
|
+
if (explicit.isNotEmpty) return explicit;
|
|
162
|
+
final String test = rcTestKey;
|
|
163
|
+
if (test.isNotEmpty) return test;
|
|
164
|
+
return prodKey;
|
|
165
|
+
}
|
|
94
166
|
}
|
|
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|
|
16
16
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
|
17
17
|
# In Windows, build-name is used as the major, minor, and patch parts
|
|
18
18
|
# of the product and file versions while build-number is used as the build suffix.
|
|
19
|
-
version: 1.0.0+
|
|
19
|
+
version: 1.0.0+36
|
|
20
20
|
|
|
21
21
|
environment:
|
|
22
22
|
sdk: ^3.11.0
|