kasy-cli 1.31.5 → 1.31.7
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 +20 -3
- package/docs/cli-reference.md +8 -6
- package/lib/commands/codemagic.js +110 -45
- package/lib/commands/new.js +5 -0
- package/lib/commands/reset.js +6 -6
- package/lib/commands/run.js +11 -11
- package/lib/scaffold/engine.js +5 -3
- package/lib/scaffold/generate.js +7 -0
- package/lib/utils/codemagic-release.js +122 -13
- package/lib/utils/env-tools.js +66 -0
- package/lib/utils/flutter-install.js +8 -0
- package/lib/utils/flutter-run.js +5 -25
- package/lib/utils/i18n/messages-en.js +18 -7
- package/lib/utils/i18n/messages-es.js +18 -7
- package/lib/utils/i18n/messages-pt.js +18 -7
- package/package.json +1 -1
- package/templates/firebase/codemagic.yaml +213 -0
- package/templates/firebase/docs/codemagic-release.en.md +45 -18
- package/templates/firebase/docs/codemagic-release.es.md +40 -14
- package/templates/firebase/docs/codemagic-release.pt.md +42 -15
package/lib/utils/env-tools.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
const os = require('node:os');
|
|
20
20
|
const path = require('node:path');
|
|
21
21
|
const fs = require('node:fs');
|
|
22
|
+
const { spawn, spawnSync } = require('node:child_process');
|
|
22
23
|
|
|
23
24
|
const isWindows = process.platform === 'win32';
|
|
24
25
|
const PATH_SEP = isWindows ? ';' : ':';
|
|
@@ -179,6 +180,68 @@ function augmentedEnv(extraDirs = []) {
|
|
|
179
180
|
return env;
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Spawn `flutter <args>` cross-platform, with the (possibly freshly-installed)
|
|
185
|
+
* SDK exposed on PATH via augmentedEnv().
|
|
186
|
+
*
|
|
187
|
+
* On Windows `flutter` is a .bat, which Node's spawn() refuses to run without a
|
|
188
|
+
* shell. But passing an ARGS ARRAY *together with* shell:true triggers Node's
|
|
189
|
+
* DEP0190 deprecation warning — and that warning, emitted asynchronously while a
|
|
190
|
+
* Clack prompt is on screen, corrupts its redraw (the device picker shows up
|
|
191
|
+
* duplicated). So on Windows we build ONE command STRING and pass shell:true with
|
|
192
|
+
* no args array (no array + shell ⇒ no warning); on Unix we pass the plain args.
|
|
193
|
+
*/
|
|
194
|
+
function flutterSpawn(spawnFn, args, options = {}) {
|
|
195
|
+
const base = { env: augmentedEnv(), ...options };
|
|
196
|
+
if (!isWindows) return spawnFn('flutter', args, base);
|
|
197
|
+
const quoted = args.map((a) => (/\s/.test(a) ? `"${a}"` : a));
|
|
198
|
+
return spawnFn(['flutter', ...quoted].join(' '), { ...base, shell: true });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const spawnFlutter = (args, options) => flutterSpawn(spawn, args, options);
|
|
202
|
+
const spawnSyncFlutter = (args, options) => flutterSpawn(spawnSync, args, options);
|
|
203
|
+
|
|
204
|
+
// Run the native-assets disable at most once per process — the setting is
|
|
205
|
+
// persisted in Flutter's global config, so re-running it every command would
|
|
206
|
+
// just add ~1s of cold flutter startup for nothing.
|
|
207
|
+
let nativeAssetsHandled = false;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Disable Flutter "native assets" compilation — Windows only.
|
|
211
|
+
*
|
|
212
|
+
* Why: `path_provider_foundation` (the Apple impl of `path_provider`, pulled in
|
|
213
|
+
* transitively by almost everything — google_fonts, home_widget,
|
|
214
|
+
* flutter_secure_storage…) ships a native-assets build hook for the
|
|
215
|
+
* `objective_c` package. On Windows, Dart's hook runner builds that compile
|
|
216
|
+
* command WITHOUT quoting the SDK/project path, so a username containing a
|
|
217
|
+
* SPACE (e.g. `C:\Users\John Silva`) breaks it — the shell reads only up to the
|
|
218
|
+
* space: `'C:\Users\John' is not recognized as a command`. It even fires for a
|
|
219
|
+
* Chrome/web run, where the Apple-only hook is pure dead weight.
|
|
220
|
+
*
|
|
221
|
+
* The hook is irrelevant on Windows (no iOS/macOS builds ever happen there), so
|
|
222
|
+
* turning native assets off removes the broken step at zero cost. `flutter
|
|
223
|
+
* config` is per-machine and global, so this never touches a Mac's iOS build.
|
|
224
|
+
*
|
|
225
|
+
* Idempotent and best-effort: it must never block `kasy run` / `kasy new`.
|
|
226
|
+
*
|
|
227
|
+
* @returns {{ ok: boolean, skipped?: boolean, error?: string }}
|
|
228
|
+
*/
|
|
229
|
+
function disableNativeAssetsWindows() {
|
|
230
|
+
if (!isWindows) return { ok: false, skipped: true };
|
|
231
|
+
if (nativeAssetsHandled) return { ok: true, skipped: true };
|
|
232
|
+
nativeAssetsHandled = true;
|
|
233
|
+
try {
|
|
234
|
+
const r = spawnSyncFlutter(['config', '--no-enable-native-assets'], {
|
|
235
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
236
|
+
encoding: 'utf8',
|
|
237
|
+
timeout: 120_000,
|
|
238
|
+
});
|
|
239
|
+
return { ok: r.status === 0 };
|
|
240
|
+
} catch (e) {
|
|
241
|
+
return { ok: false, error: e.message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
182
245
|
module.exports = {
|
|
183
246
|
isWindows,
|
|
184
247
|
homeDir,
|
|
@@ -186,4 +249,7 @@ module.exports = {
|
|
|
186
249
|
pubCacheBin,
|
|
187
250
|
keytoolBin,
|
|
188
251
|
augmentedEnv,
|
|
252
|
+
spawnFlutter,
|
|
253
|
+
spawnSyncFlutter,
|
|
254
|
+
disableNativeAssetsWindows,
|
|
189
255
|
};
|
|
@@ -106,6 +106,14 @@ if ($LASTEXITCODE -ne 0) {
|
|
|
106
106
|
throw 'Flutter was installed but its first run failed (Dart SDK bootstrap). Check your internet connection and run this again.'
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
# 3b. Disable native assets. path_provider_foundation (Apple impl of
|
|
110
|
+
# path_provider) ships an objective_c native-assets build hook whose compile
|
|
111
|
+
# command isn't quoted, so a username with a SPACE (C:\\Users\\John Silva)
|
|
112
|
+
# breaks every build — even Chrome/web, where the Apple hook is dead weight.
|
|
113
|
+
# No iOS/macOS builds happen on Windows, so turning it off is free. Best
|
|
114
|
+
# effort: never fail the install over it.
|
|
115
|
+
& (Join-Path $bin 'flutter.bat') config --no-enable-native-assets 2>&1 | Out-Null
|
|
116
|
+
|
|
109
117
|
# 4. Persist flutter\\bin on the User PATH so every future terminal finds it.
|
|
110
118
|
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
111
119
|
if (-not $userPath) { $userPath = '' }
|
package/lib/utils/flutter-run.js
CHANGED
|
@@ -28,29 +28,9 @@
|
|
|
28
28
|
|
|
29
29
|
const path = require('node:path');
|
|
30
30
|
const fs = require('node:fs');
|
|
31
|
-
const { spawn } = require('node:child_process');
|
|
32
31
|
const kleur = require('kleur');
|
|
33
32
|
const ui = require('./ui');
|
|
34
|
-
const {
|
|
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
|
-
}
|
|
33
|
+
const { spawnFlutter } = require('./env-tools');
|
|
54
34
|
|
|
55
35
|
// Markers that tell us the initial build is done and the app is running.
|
|
56
36
|
const FLUTTER_READY_RE = /Flutter run key commands\.|is listening on|VM Service|Dart VM service|To hot reload|Hot restart/i;
|
|
@@ -160,10 +140,10 @@ function spawnFlutterWithSpinner(args, projectDir, t, options = {}) {
|
|
|
160
140
|
*/
|
|
161
141
|
function spawnRaw(args, projectDir, t, log, onReady) {
|
|
162
142
|
return new Promise((resolve, reject) => {
|
|
163
|
-
const proc =
|
|
143
|
+
const proc = spawnFlutter(args, {
|
|
164
144
|
cwd: projectDir,
|
|
165
145
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
166
|
-
})
|
|
146
|
+
});
|
|
167
147
|
|
|
168
148
|
let readyFired = false;
|
|
169
149
|
const fireReady = () => {
|
|
@@ -212,10 +192,10 @@ function spawnRaw(args, projectDir, t, log, onReady) {
|
|
|
212
192
|
*/
|
|
213
193
|
function spawnWithSpinner(args, projectDir, t, log, onReady) {
|
|
214
194
|
return new Promise((resolve, reject) => {
|
|
215
|
-
const proc =
|
|
195
|
+
const proc = spawnFlutter(args, {
|
|
216
196
|
cwd: projectDir,
|
|
217
197
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
218
|
-
})
|
|
198
|
+
});
|
|
219
199
|
|
|
220
200
|
const spinner = ui.spinner();
|
|
221
201
|
const startTime = Date.now();
|
|
@@ -389,12 +389,18 @@ module.exports = {
|
|
|
389
389
|
'cli.command.ios.help.before': 'How to publish to the App Store:\n 1) kasy ios configure → do this once (saves Apple credentials)\n 2) kasy ios release → run for each new version (build + upload)\n\nUse "build" to only generate the IPA, "clean" if a build fails.\n',
|
|
390
390
|
'cli.command.codemagic.description': 'Build the app in the cloud (no Mac needed)',
|
|
391
391
|
'cli.command.codemagic.configure.description': 'Configure Codemagic API credentials',
|
|
392
|
-
'cli.command.codemagic.release.description': 'Start a
|
|
392
|
+
'cli.command.codemagic.release.description': 'Start a cloud build (iOS, Android, or both)',
|
|
393
|
+
'cli.command.codemagic.release.iosOption': 'Build only iOS',
|
|
394
|
+
'cli.command.codemagic.release.androidOption': 'Build only Android',
|
|
395
|
+
'cli.command.codemagic.release.platformPick': 'Which platform do you want to build?',
|
|
396
|
+
'cli.command.codemagic.release.platform.both': 'iOS + Android',
|
|
397
|
+
'cli.command.codemagic.release.platform.ios': 'iOS only',
|
|
398
|
+
'cli.command.codemagic.release.platform.android': 'Android only',
|
|
393
399
|
'cli.command.codemagic.status.description': 'Show Codemagic build status by ID',
|
|
394
400
|
'cli.command.codemagic.picker.intro': 'Build in the cloud with Codemagic',
|
|
395
401
|
'cli.command.codemagic.picker.message': 'What do you want to do?',
|
|
396
402
|
'cli.command.codemagic.picker.statusHint': 'Asks for the build ID',
|
|
397
|
-
'cli.command.codemagic.help.before': 'How to build with Codemagic (no Mac needed):\n 1) kasy codemagic configure → do this once (saves API token)\n 2) kasy codemagic release →
|
|
403
|
+
'cli.command.codemagic.help.before': 'How to build with Codemagic (no Mac needed):\n 1) kasy codemagic configure → do this once (saves API token + app)\n 2) kasy codemagic release → cloud build for each version\n kasy codemagic release --ios (iOS only)\n kasy codemagic release --android (Android only)\n\nUse "status <buildId>" to check the progress of a running build.\n',
|
|
398
404
|
|
|
399
405
|
'ios.configure.title': 'iOS App Store — setup',
|
|
400
406
|
'ios.configure.bundleId': 'Bundle ID',
|
|
@@ -456,19 +462,24 @@ module.exports = {
|
|
|
456
462
|
'codemagic.configure.title': 'Codemagic — setup',
|
|
457
463
|
'codemagic.configure.doc': 'Guide',
|
|
458
464
|
'codemagic.configure.checklist': 'Complete signing + App Store Connect in the Codemagic dashboard (see guide).',
|
|
459
|
-
'codemagic.configure.
|
|
465
|
+
'codemagic.configure.openingToken': 'Opening Codemagic settings — copy your API token…',
|
|
460
466
|
'codemagic.configure.q.token': 'Codemagic API token',
|
|
461
467
|
'codemagic.configure.q.appId': 'Codemagic App ID',
|
|
462
|
-
'codemagic.configure.q.workflowId': 'Workflow ID (from codemagic.yaml)',
|
|
463
468
|
'codemagic.configure.q.branch': 'Git branch to build',
|
|
464
469
|
'codemagic.configure.q.required': 'Required',
|
|
470
|
+
'codemagic.configure.validating': 'Checking token and loading your apps…',
|
|
471
|
+
'codemagic.configure.validated': 'Token OK',
|
|
472
|
+
'codemagic.configure.tokenInvalid': 'Could not validate the token',
|
|
473
|
+
'codemagic.configure.noApps': 'No apps found on this account. Connect your repository in the Codemagic dashboard first.',
|
|
474
|
+
'codemagic.configure.pickApp': 'Which app do you want to build?',
|
|
465
475
|
'codemagic.configure.cancelled': 'Setup cancelled',
|
|
466
476
|
'codemagic.configure.success': 'Codemagic credentials saved',
|
|
467
477
|
'codemagic.configure.next': 'Next',
|
|
468
478
|
'codemagic.release.title': 'Triggering Codemagic build…',
|
|
469
|
-
'codemagic.release.
|
|
470
|
-
'codemagic.release.
|
|
471
|
-
'codemagic.release.
|
|
479
|
+
'codemagic.release.spinPlatform': 'Starting {platform} build on Codemagic…',
|
|
480
|
+
'codemagic.release.triggeredPlatform': '{platform} build queued on Codemagic',
|
|
481
|
+
'codemagic.release.noIosKey': 'No iOS production RevenueCat key (RC_IOS_PROD_KEY) in .env — subscriptions may not load in the build.',
|
|
482
|
+
'codemagic.release.noAndroidKey': 'No Android production RevenueCat key (RC_ANDROID_PROD_KEY) in .env — subscriptions may not load in the build.',
|
|
472
483
|
'codemagic.status.title': 'Build status',
|
|
473
484
|
'codemagic.status.usage': 'Usage: kasy codemagic status <buildId>',
|
|
474
485
|
'codemagic.status.spin': 'Fetching build status…',
|
|
@@ -391,12 +391,18 @@ module.exports = {
|
|
|
391
391
|
'cli.command.ios.help.before': 'Cómo publicar en la App Store:\n 1) kasy ios configure → hazlo una vez (guarda las credenciales Apple)\n 2) kasy ios release → ejecuta en cada nueva versión (build + subida)\n\nUsa "build" si solo quieres generar el IPA, "clean" si un build falló.\n',
|
|
392
392
|
'cli.command.codemagic.description': 'Compila la app en la nube (sin necesitar Mac)',
|
|
393
393
|
'cli.command.codemagic.configure.description': 'Configurar credenciales API de Codemagic',
|
|
394
|
-
'cli.command.codemagic.release.description': 'Iniciar build
|
|
394
|
+
'cli.command.codemagic.release.description': 'Iniciar build en la nube (iOS, Android o ambos)',
|
|
395
|
+
'cli.command.codemagic.release.iosOption': 'Compilar solo iOS',
|
|
396
|
+
'cli.command.codemagic.release.androidOption': 'Compilar solo Android',
|
|
397
|
+
'cli.command.codemagic.release.platformPick': '¿Qué plataforma quieres compilar?',
|
|
398
|
+
'cli.command.codemagic.release.platform.both': 'iOS + Android',
|
|
399
|
+
'cli.command.codemagic.release.platform.ios': 'Solo iOS',
|
|
400
|
+
'cli.command.codemagic.release.platform.android': 'Solo Android',
|
|
395
401
|
'cli.command.codemagic.status.description': 'Estado del build Codemagic por ID',
|
|
396
402
|
'cli.command.codemagic.picker.intro': 'Compilar en la nube con Codemagic',
|
|
397
403
|
'cli.command.codemagic.picker.message': '¿Qué quieres hacer?',
|
|
398
404
|
'cli.command.codemagic.picker.statusHint': 'Pregunta el ID del build',
|
|
399
|
-
'cli.command.codemagic.help.before': 'Cómo compilar con Codemagic (sin necesitar Mac):\n 1) kasy codemagic configure → hazlo una vez (guarda el token de la API)\n 2) kasy codemagic release →
|
|
405
|
+
'cli.command.codemagic.help.before': 'Cómo compilar con Codemagic (sin necesitar Mac):\n 1) kasy codemagic configure → hazlo una vez (guarda el token de la API + app)\n 2) kasy codemagic release → build en la nube por versión\n kasy codemagic release --ios (solo iOS)\n kasy codemagic release --android (solo Android)\n\nUsa "status <buildId>" para seguir el progreso de un build.\n',
|
|
400
406
|
|
|
401
407
|
'ios.configure.title': 'App Store iOS — configuración',
|
|
402
408
|
'ios.configure.bundleId': 'Bundle ID',
|
|
@@ -458,19 +464,24 @@ module.exports = {
|
|
|
458
464
|
'codemagic.configure.title': 'Codemagic — configuración',
|
|
459
465
|
'codemagic.configure.doc': 'Guía',
|
|
460
466
|
'codemagic.configure.checklist': 'Complete firma + App Store Connect en el panel Codemagic (vea la guía).',
|
|
461
|
-
'codemagic.configure.
|
|
467
|
+
'codemagic.configure.openingToken': 'Abriendo la configuración de Codemagic — copia tu token de la API…',
|
|
462
468
|
'codemagic.configure.q.token': 'Token API de Codemagic',
|
|
463
469
|
'codemagic.configure.q.appId': 'App ID en Codemagic',
|
|
464
|
-
'codemagic.configure.q.workflowId': 'Workflow ID (de codemagic.yaml)',
|
|
465
470
|
'codemagic.configure.q.branch': 'Rama Git para build',
|
|
466
471
|
'codemagic.configure.q.required': 'Obligatorio',
|
|
472
|
+
'codemagic.configure.validating': 'Verificando el token y cargando tus apps…',
|
|
473
|
+
'codemagic.configure.validated': 'Token válido',
|
|
474
|
+
'codemagic.configure.tokenInvalid': 'No se pudo validar el token',
|
|
475
|
+
'codemagic.configure.noApps': 'No se encontraron apps en esta cuenta. Conecta tu repositorio en el panel de Codemagic primero.',
|
|
476
|
+
'codemagic.configure.pickApp': '¿Qué app quieres compilar?',
|
|
467
477
|
'codemagic.configure.cancelled': 'Configuración cancelada',
|
|
468
478
|
'codemagic.configure.success': 'Credenciales Codemagic guardadas',
|
|
469
479
|
'codemagic.configure.next': 'Siguiente paso',
|
|
470
480
|
'codemagic.release.title': 'Disparando build en Codemagic…',
|
|
471
|
-
'codemagic.release.
|
|
472
|
-
'codemagic.release.
|
|
473
|
-
'codemagic.release.
|
|
481
|
+
'codemagic.release.spinPlatform': 'Iniciando build de {platform} en Codemagic…',
|
|
482
|
+
'codemagic.release.triggeredPlatform': 'Build de {platform} encolado en Codemagic',
|
|
483
|
+
'codemagic.release.noIosKey': 'Sin clave de producción iOS de RevenueCat (RC_IOS_PROD_KEY) en .env — las suscripciones pueden no cargar en el build.',
|
|
484
|
+
'codemagic.release.noAndroidKey': 'Sin clave de producción Android de RevenueCat (RC_ANDROID_PROD_KEY) en .env — las suscripciones pueden no cargar en el build.',
|
|
474
485
|
'codemagic.status.title': 'Estado del build',
|
|
475
486
|
'codemagic.status.usage': 'Uso: kasy codemagic status <buildId>',
|
|
476
487
|
'codemagic.status.spin': 'Consultando estado del build…',
|
|
@@ -389,12 +389,18 @@ module.exports = {
|
|
|
389
389
|
'cli.command.ios.help.before': 'Como publicar na App Store:\n 1) kasy ios configure → faça isso uma vez (salva as credenciais Apple)\n 2) kasy ios release → rode em cada nova versão (build + envio)\n\nUse "build" se quiser só gerar o IPA, "clean" se um build falhou.\n',
|
|
390
390
|
'cli.command.codemagic.description': 'Compila o app na nuvem (sem precisar de Mac)',
|
|
391
391
|
'cli.command.codemagic.configure.description': 'Configurar credenciais da API Codemagic',
|
|
392
|
-
'cli.command.codemagic.release.description': 'Iniciar build
|
|
392
|
+
'cli.command.codemagic.release.description': 'Iniciar build na nuvem (iOS, Android ou os dois)',
|
|
393
|
+
'cli.command.codemagic.release.iosOption': 'Compilar só o iOS',
|
|
394
|
+
'cli.command.codemagic.release.androidOption': 'Compilar só o Android',
|
|
395
|
+
'cli.command.codemagic.release.platformPick': 'Qual plataforma você quer compilar?',
|
|
396
|
+
'cli.command.codemagic.release.platform.both': 'iOS + Android',
|
|
397
|
+
'cli.command.codemagic.release.platform.ios': 'Só iOS',
|
|
398
|
+
'cli.command.codemagic.release.platform.android': 'Só Android',
|
|
393
399
|
'cli.command.codemagic.status.description': 'Status do build Codemagic por ID',
|
|
394
400
|
'cli.command.codemagic.picker.intro': 'Compilar na nuvem com Codemagic',
|
|
395
401
|
'cli.command.codemagic.picker.message': 'O que você quer fazer?',
|
|
396
402
|
'cli.command.codemagic.picker.statusHint': 'Pergunta o ID do build',
|
|
397
|
-
'cli.command.codemagic.help.before': 'Como compilar com Codemagic (sem precisar de Mac):\n 1) kasy codemagic configure → faça isso uma vez (salva o token da API)\n 2) kasy codemagic release →
|
|
403
|
+
'cli.command.codemagic.help.before': 'Como compilar com Codemagic (sem precisar de Mac):\n 1) kasy codemagic configure → faça isso uma vez (salva o token da API + app)\n 2) kasy codemagic release → build na nuvem a cada versão\n kasy codemagic release --ios (só iOS)\n kasy codemagic release --android (só Android)\n\nUse "status <buildId>" para acompanhar o progresso de um build.\n',
|
|
398
404
|
|
|
399
405
|
'ios.configure.title': 'App Store iOS — configuração',
|
|
400
406
|
'ios.configure.bundleId': 'Bundle ID',
|
|
@@ -456,19 +462,24 @@ module.exports = {
|
|
|
456
462
|
'codemagic.configure.title': 'Codemagic — configuração',
|
|
457
463
|
'codemagic.configure.doc': 'Guia',
|
|
458
464
|
'codemagic.configure.checklist': 'Conclua assinatura + App Store Connect no painel Codemagic (veja o guia).',
|
|
459
|
-
'codemagic.configure.
|
|
465
|
+
'codemagic.configure.openingToken': 'Abrindo as configurações do Codemagic — copie o token da API…',
|
|
460
466
|
'codemagic.configure.q.token': 'Token da API Codemagic',
|
|
461
467
|
'codemagic.configure.q.appId': 'App ID no Codemagic',
|
|
462
|
-
'codemagic.configure.q.workflowId': 'Workflow ID (do codemagic.yaml)',
|
|
463
468
|
'codemagic.configure.q.branch': 'Branch Git para build',
|
|
464
469
|
'codemagic.configure.q.required': 'Obrigatório',
|
|
470
|
+
'codemagic.configure.validating': 'Verificando o token e carregando seus apps…',
|
|
471
|
+
'codemagic.configure.validated': 'Token válido',
|
|
472
|
+
'codemagic.configure.tokenInvalid': 'Não foi possível validar o token',
|
|
473
|
+
'codemagic.configure.noApps': 'Nenhum app encontrado nesta conta. Conecte seu repositório no painel do Codemagic primeiro.',
|
|
474
|
+
'codemagic.configure.pickApp': 'Qual app você quer compilar?',
|
|
465
475
|
'codemagic.configure.cancelled': 'Configuração cancelada',
|
|
466
476
|
'codemagic.configure.success': 'Credenciais Codemagic salvas',
|
|
467
477
|
'codemagic.configure.next': 'Próximo passo',
|
|
468
478
|
'codemagic.release.title': 'Disparando build no Codemagic…',
|
|
469
|
-
'codemagic.release.
|
|
470
|
-
'codemagic.release.
|
|
471
|
-
'codemagic.release.
|
|
479
|
+
'codemagic.release.spinPlatform': 'Iniciando build de {platform} no Codemagic…',
|
|
480
|
+
'codemagic.release.triggeredPlatform': 'Build de {platform} enfileirado no Codemagic',
|
|
481
|
+
'codemagic.release.noIosKey': 'Sem chave de produção iOS do RevenueCat (RC_IOS_PROD_KEY) no .env — as assinaturas podem não carregar no build.',
|
|
482
|
+
'codemagic.release.noAndroidKey': 'Sem chave de produção Android do RevenueCat (RC_ANDROID_PROD_KEY) no .env — as assinaturas podem não carregar no build.',
|
|
472
483
|
'codemagic.status.title': 'Status do build',
|
|
473
484
|
'codemagic.status.usage': 'Uso: kasy codemagic status <buildId>',
|
|
474
485
|
'codemagic.status.spin': 'Consultando status do build…',
|
package/package.json
CHANGED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Codemagic CI/CD — shipped by the Kasy `ci` feature.
|
|
2
|
+
#
|
|
3
|
+
# iOS and Android run as SEPARATE workflows (ios-workflow / android-workflow),
|
|
4
|
+
# so you can build and publish each platform on its own. `kasy codemagic release`
|
|
5
|
+
# triggers them: `--ios`, `--android`, or both.
|
|
6
|
+
#
|
|
7
|
+
# App keys (RevenueCat, backend URL, Sentry, Mixpanel, App Store id) arrive as
|
|
8
|
+
# ENVIRONMENT VARIABLES, two ways:
|
|
9
|
+
# 1. `kasy codemagic release` injects them automatically from your local .env, or
|
|
10
|
+
# 2. you store them in the Codemagic UI as variable groups.
|
|
11
|
+
# The "Generate .env" step below rebuilds the app's .env asset from those vars,
|
|
12
|
+
# so the build always has the production RevenueCat keys (appl_/goog_) — never
|
|
13
|
+
# the test_ key. ENV=prod is also passed as a dart-define (compile-time const).
|
|
14
|
+
#
|
|
15
|
+
# What you still set up ONCE in the Codemagic dashboard (UI-only, by design):
|
|
16
|
+
# - Connect your Git repository (Add application)
|
|
17
|
+
# - iOS: App Store Connect integration (the Apple API key)
|
|
18
|
+
# - Android: upload the keystore + the Google Play service account JSON
|
|
19
|
+
|
|
20
|
+
workflows:
|
|
21
|
+
android-workflow:
|
|
22
|
+
name: Android Workflow
|
|
23
|
+
instance_type: mac_mini_m1
|
|
24
|
+
max_build_duration: 120
|
|
25
|
+
environment:
|
|
26
|
+
android_signing:
|
|
27
|
+
- keystore_reference # <-- Reference name of the keystore you upload in the Codemagic UI
|
|
28
|
+
groups:
|
|
29
|
+
- google_play # <-- Group holding GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS (and any app keys)
|
|
30
|
+
vars:
|
|
31
|
+
PACKAGE_NAME: com.aicrus.firebase.kit # <-- Your Android applicationId
|
|
32
|
+
GOOGLE_PLAY_TRACK: "internal" # <-- internal | alpha | beta | production
|
|
33
|
+
flutter: stable
|
|
34
|
+
triggering:
|
|
35
|
+
cancel_previous_builds: true
|
|
36
|
+
events:
|
|
37
|
+
- push
|
|
38
|
+
branch_patterns:
|
|
39
|
+
- pattern: '*'
|
|
40
|
+
include: false
|
|
41
|
+
source: false
|
|
42
|
+
- pattern: 'main'
|
|
43
|
+
include: true
|
|
44
|
+
source: true
|
|
45
|
+
scripts:
|
|
46
|
+
- name: Set up local.properties
|
|
47
|
+
script: |
|
|
48
|
+
echo "flutter.sdk=$HOME/programs/flutter" > "$CM_BUILD_DIR/android/local.properties"
|
|
49
|
+
- name: Get Flutter packages
|
|
50
|
+
script: |
|
|
51
|
+
flutter packages pub get
|
|
52
|
+
- name: Generate .env from build environment
|
|
53
|
+
script: |
|
|
54
|
+
# The app reads its config from a bundled .env asset at runtime. That
|
|
55
|
+
# file is gitignored (never in the repo), so recreate it here from the
|
|
56
|
+
# environment variables provided by `kasy codemagic release` or by a
|
|
57
|
+
# Codemagic variable group. In a release build the app uses the
|
|
58
|
+
# production RevenueCat key (appl_/goog_) from RC_*_PROD_KEY.
|
|
59
|
+
cat > "$CM_BUILD_DIR/.env" <<EOF
|
|
60
|
+
ENV=prod
|
|
61
|
+
BACKEND_URL=${BACKEND_URL:-}
|
|
62
|
+
AI_CHAT_ENDPOINT=${AI_CHAT_ENDPOINT:-}
|
|
63
|
+
RC_TEST_KEY=${RC_TEST_KEY:-}
|
|
64
|
+
RC_IOS_PROD_KEY=${RC_IOS_PROD_KEY:-}
|
|
65
|
+
RC_ANDROID_PROD_KEY=${RC_ANDROID_PROD_KEY:-}
|
|
66
|
+
MIXPANEL_TOKEN=${MIXPANEL_TOKEN:-}
|
|
67
|
+
SENTRY_DSN=${SENTRY_DSN:-}
|
|
68
|
+
APP_STORE_ID=${APP_STORE_ID:-}
|
|
69
|
+
EOF
|
|
70
|
+
- name: Unit tests
|
|
71
|
+
script: |
|
|
72
|
+
mkdir -p test-results
|
|
73
|
+
flutter test --machine > test-results/flutter.json
|
|
74
|
+
test_report: test-results/flutter.json
|
|
75
|
+
- name: Build AAB with Flutter
|
|
76
|
+
script: |
|
|
77
|
+
BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name "$PACKAGE_NAME" --tracks="$GOOGLE_PLAY_TRACK") + 1))
|
|
78
|
+
flutter build appbundle --release \
|
|
79
|
+
--dart-define=ENV=prod \
|
|
80
|
+
--build-name=1.0.$BUILD_NUMBER \
|
|
81
|
+
--build-number=$BUILD_NUMBER
|
|
82
|
+
artifacts:
|
|
83
|
+
- build/**/outputs/**/*.aab
|
|
84
|
+
- build/**/outputs/**/mapping.txt
|
|
85
|
+
- flutter_drive.log
|
|
86
|
+
publishing:
|
|
87
|
+
email:
|
|
88
|
+
recipients:
|
|
89
|
+
- # <-- Put your email here or add others recipients
|
|
90
|
+
notify:
|
|
91
|
+
success: true
|
|
92
|
+
failure: true
|
|
93
|
+
google_play:
|
|
94
|
+
credentials: $GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS
|
|
95
|
+
track: $GOOGLE_PLAY_TRACK
|
|
96
|
+
submit_as_draft: true
|
|
97
|
+
ios-workflow:
|
|
98
|
+
name: iOS Workflow
|
|
99
|
+
instance_type: mac_mini_m1
|
|
100
|
+
max_build_duration: 120
|
|
101
|
+
integrations:
|
|
102
|
+
app_store_connect: codemagic # <-- Name of your App Store Connect integration (Codemagic UI)
|
|
103
|
+
environment:
|
|
104
|
+
groups:
|
|
105
|
+
- appstore_credentials # <-- Group holding APP_ID (and any app keys)
|
|
106
|
+
ios_signing:
|
|
107
|
+
distribution_type: app_store
|
|
108
|
+
bundle_identifier: com.aicrus.firebase.kit # <-- Your iOS bundle identifier
|
|
109
|
+
vars:
|
|
110
|
+
# APP_ID = the numeric App Store Connect app id. `kasy codemagic release`
|
|
111
|
+
# sends it automatically (read from APP_STORE_ID in your .env); or define
|
|
112
|
+
# it in the appstore_credentials group.
|
|
113
|
+
APP_ID: $APP_ID
|
|
114
|
+
flutter: stable
|
|
115
|
+
xcode: latest # <-- set to specific version e.g. 15.0 to avoid unexpected updates.
|
|
116
|
+
cocoapods: default
|
|
117
|
+
triggering:
|
|
118
|
+
cancel_previous_builds: true
|
|
119
|
+
events:
|
|
120
|
+
- push
|
|
121
|
+
branch_patterns:
|
|
122
|
+
- pattern: '*'
|
|
123
|
+
include: false
|
|
124
|
+
source: false
|
|
125
|
+
- pattern: 'main'
|
|
126
|
+
include: true
|
|
127
|
+
source: true
|
|
128
|
+
scripts:
|
|
129
|
+
- name: Set up code signing settings on Xcode project
|
|
130
|
+
script: |
|
|
131
|
+
xcode-project use-profiles
|
|
132
|
+
- name: Get Flutter packages
|
|
133
|
+
script: |
|
|
134
|
+
flutter packages pub get
|
|
135
|
+
- name: Install pods
|
|
136
|
+
script: |
|
|
137
|
+
find . -name "Podfile" -execdir pod install \;
|
|
138
|
+
- name: Validate Google Sign-In iOS URL scheme
|
|
139
|
+
script: |
|
|
140
|
+
GOOGLE_PLIST="ios/Runner/GoogleService-Info.plist"
|
|
141
|
+
INFO_PLIST="ios/Runner/Info.plist"
|
|
142
|
+
if [ -f "$GOOGLE_PLIST" ]; then
|
|
143
|
+
REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c "Print :REVERSED_CLIENT_ID" "$GOOGLE_PLIST" 2>/dev/null || true)
|
|
144
|
+
if [ -z "$REVERSED_CLIENT_ID" ]; then
|
|
145
|
+
echo "REVERSED_CLIENT_ID not found in $GOOGLE_PLIST"
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
if ! /usr/libexec/PlistBuddy -c "Print :CFBundleURLTypes" "$INFO_PLIST" 2>/dev/null | grep -Fq "$REVERSED_CLIENT_ID"; then
|
|
149
|
+
echo "Google Sign-In iOS URL scheme mismatch."
|
|
150
|
+
echo "Expected CFBundleURLSchemes to include: $REVERSED_CLIENT_ID"
|
|
151
|
+
echo "Run flutterfire configure/project setup before building."
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
- name: Generate .env from build environment
|
|
156
|
+
script: |
|
|
157
|
+
# See the Android workflow note above — recreate the bundled .env asset
|
|
158
|
+
# from environment variables so the release build gets the production
|
|
159
|
+
# RevenueCat key (appl_) instead of the test_ key.
|
|
160
|
+
cat > "$CM_BUILD_DIR/.env" <<EOF
|
|
161
|
+
ENV=prod
|
|
162
|
+
BACKEND_URL=${BACKEND_URL:-}
|
|
163
|
+
AI_CHAT_ENDPOINT=${AI_CHAT_ENDPOINT:-}
|
|
164
|
+
RC_TEST_KEY=${RC_TEST_KEY:-}
|
|
165
|
+
RC_IOS_PROD_KEY=${RC_IOS_PROD_KEY:-}
|
|
166
|
+
RC_ANDROID_PROD_KEY=${RC_ANDROID_PROD_KEY:-}
|
|
167
|
+
MIXPANEL_TOKEN=${MIXPANEL_TOKEN:-}
|
|
168
|
+
SENTRY_DSN=${SENTRY_DSN:-}
|
|
169
|
+
APP_STORE_ID=${APP_STORE_ID:-}
|
|
170
|
+
EOF
|
|
171
|
+
- name: Flutter analyze # <-- remove if you don't like flutter analyze
|
|
172
|
+
script: |
|
|
173
|
+
flutter analyze
|
|
174
|
+
- name: Flutter unit tests
|
|
175
|
+
script: |
|
|
176
|
+
flutter test
|
|
177
|
+
ignore_failure: false # You should never build an app that has failing tests.
|
|
178
|
+
- name: Flutter build ipa and automatic versioning
|
|
179
|
+
script: |
|
|
180
|
+
flutter build ipa --release \
|
|
181
|
+
--dart-define=ENV=prod \
|
|
182
|
+
--build-name=1.0.0 \
|
|
183
|
+
--build-number=$(($(app-store-connect get-latest-app-store-build-number "$APP_ID") + 1)) \
|
|
184
|
+
--export-options-plist=/Users/builder/export_options.plist
|
|
185
|
+
artifacts:
|
|
186
|
+
- build/ios/ipa/*.ipa
|
|
187
|
+
- /tmp/xcodebuild_logs/*.log
|
|
188
|
+
- flutter_drive.log
|
|
189
|
+
publishing:
|
|
190
|
+
# See the following link for details about email publishing - https://docs.codemagic.io/publishing-yaml/distribution/#email
|
|
191
|
+
email:
|
|
192
|
+
recipients:
|
|
193
|
+
- # <-- Put your email here or add others recipients
|
|
194
|
+
notify:
|
|
195
|
+
success: true
|
|
196
|
+
failure: true
|
|
197
|
+
app_store_connect:
|
|
198
|
+
# Use codemagic integration (easier)
|
|
199
|
+
auth: integration
|
|
200
|
+
# ====================================
|
|
201
|
+
## Or push all keys manually here
|
|
202
|
+
## ====================================
|
|
203
|
+
#api_key: $APP_STORE_CONNECT_PRIVATE_KEY
|
|
204
|
+
#key_id: $APP_STORE_CONNECT_KEY_IDENTIFIER
|
|
205
|
+
#issuer_id: $APP_STORE_CONNECT_ISSUER_ID
|
|
206
|
+
## ====================================
|
|
207
|
+
submit_to_app_store: false # Set true to send to App Store review automatically
|
|
208
|
+
release_type: MANUAL
|
|
209
|
+
# Configuration related to TestFlight (optional)
|
|
210
|
+
# Note: This action is performed during post-processing.
|
|
211
|
+
submit_to_testflight: true
|
|
212
|
+
beta_groups: # Specify the names of beta tester groups that will get access to the build once it has passed beta review.
|
|
213
|
+
- kasy # <-- Put your beta group name here
|
|
@@ -1,43 +1,64 @@
|
|
|
1
|
-
# Publish
|
|
1
|
+
# Publish to the cloud with Codemagic (no Mac)
|
|
2
|
+
|
|
3
|
+
Publishes **iOS** and **Android** through the Codemagic cloud. Each platform has
|
|
4
|
+
its own workflow, so you can ship them separately or both together.
|
|
2
5
|
|
|
3
6
|
## Prerequisites
|
|
4
7
|
|
|
5
|
-
- [Codemagic](https://codemagic.io) account
|
|
6
|
-
- Git repository connected to Codemagic
|
|
7
|
-
- Apple Developer account + app in App Store Connect
|
|
8
|
+
- A [Codemagic](https://codemagic.io) account
|
|
9
|
+
- A Git repository connected to Codemagic
|
|
10
|
+
- iOS: Apple Developer account + app in App Store Connect
|
|
11
|
+
- Android: app in Google Play Console + a signing keystore
|
|
8
12
|
|
|
9
|
-
## 1. Add CI to the project (if
|
|
13
|
+
## 1. Add CI to the project (if you don't have it yet)
|
|
10
14
|
|
|
11
15
|
```bash
|
|
12
16
|
kasy add ci
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
This creates `codemagic.yaml` at the project root (`ios-workflow` and
|
|
20
|
+
`android-workflow`).
|
|
21
|
+
|
|
22
|
+
## 2. Set up in the Codemagic dashboard (once)
|
|
23
|
+
|
|
24
|
+
These are secrets and can only be done in the dashboard — one time:
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
1. Open [codemagic.io/apps](https://codemagic.io/apps) and **connect your repository**.
|
|
27
|
+
2. **iOS** — *Integrations → App Store Connect*: connect the Apple API key. Its
|
|
28
|
+
name goes in `integrations: app_store_connect:` in `codemagic.yaml`.
|
|
29
|
+
3. **Android** — *Code signing identities → Android keystores*: upload the
|
|
30
|
+
keystore with **Reference name** `keystore_reference` (matching `codemagic.yaml`).
|
|
31
|
+
4. **Android** — upload the **Google Play service account** JSON as the secret
|
|
32
|
+
variable `GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS` (group `google_play`).
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `BACKEND_URL`, `SENTRY_DSN`, `RC_IOS_API_KEY`, `RC_ANDROID_API_KEY`, `MIXPANEL_TOKEN`
|
|
24
|
-
5. In `codemagic.yaml`, set `APP_ID` (numeric App Store Connect app ID from the app URL).
|
|
34
|
+
> The **app keys** (RevenueCat, backend, etc.) do **not** need to be filled in
|
|
35
|
+
> the dashboard: `kasy codemagic release` sends them automatically from your
|
|
36
|
+
> `.env`. If you'd rather trigger from the dashboard/push, add them to the
|
|
37
|
+
> variable groups instead.
|
|
25
38
|
|
|
26
|
-
## 3.
|
|
39
|
+
## 3. Set up in the terminal (once)
|
|
27
40
|
|
|
28
41
|
```bash
|
|
29
42
|
kasy codemagic configure
|
|
30
43
|
```
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
The wizard opens the **API token** page (Settings → Codemagic API), validates the
|
|
46
|
+
token and **lists your apps** to pick from — no IDs to type. It saves everything
|
|
47
|
+
to `.kasy/codemagic.env` (gitignored).
|
|
33
48
|
|
|
34
49
|
## 4. Trigger a build
|
|
35
50
|
|
|
36
51
|
```bash
|
|
37
|
-
kasy codemagic release
|
|
52
|
+
kasy codemagic release # iOS + Android
|
|
53
|
+
kasy codemagic release --ios # iOS only
|
|
54
|
+
kasy codemagic release --android # Android only
|
|
38
55
|
```
|
|
39
56
|
|
|
40
|
-
The
|
|
57
|
+
The command reads your `.env` and carries the production keys (including the
|
|
58
|
+
RevenueCat production key, `appl_`/`goog_`) with the trigger. A step in
|
|
59
|
+
`codemagic.yaml` recreates the `.env` in the cloud before building, so the build
|
|
60
|
+
ships with the right keys. Per `codemagic.yaml`, iOS goes to TestFlight and
|
|
61
|
+
Android to the configured track (`internal` by default).
|
|
41
62
|
|
|
42
63
|
## Build status
|
|
43
64
|
|
|
@@ -45,6 +66,12 @@ The cloud build runs and may upload to TestFlight per `codemagic.yaml`.
|
|
|
45
66
|
kasy codemagic status <buildId>
|
|
46
67
|
```
|
|
47
68
|
|
|
69
|
+
## Can I use the Mac and Codemagic at the same time?
|
|
70
|
+
|
|
71
|
+
Yes. They are two paths to the same store; they don't conflict. The build number
|
|
72
|
+
is computed by the cloud (latest in the store + 1), so it rarely collides. For
|
|
73
|
+
day-to-day, pick one main path.
|
|
74
|
+
|
|
48
75
|
## Local Mac
|
|
49
76
|
|
|
50
|
-
If you have a Mac: [ios-release.md](./ios-release.md) and `kasy ios release`.
|
|
77
|
+
If you have a Mac: see [ios-release.md](./ios-release.md) and `kasy ios release`.
|