kasy-cli 1.31.6 → 1.31.8
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/scaffold/backends/api/pubspec.yaml.tpl +7 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +7 -0
- 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/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 +211 -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/templates/firebase/pubspec.yaml +7 -0
package/bin/kasy.js
CHANGED
|
@@ -602,7 +602,22 @@ function buildProgram(language) {
|
|
|
602
602
|
],
|
|
603
603
|
});
|
|
604
604
|
if (sub === 'configure') await runCodemagicConfigure('.', { language });
|
|
605
|
-
else if (sub === 'release')
|
|
605
|
+
else if (sub === 'release') {
|
|
606
|
+
const platform = await ui.select({
|
|
607
|
+
message: t('cli.command.codemagic.release.platformPick'),
|
|
608
|
+
options: [
|
|
609
|
+
{ value: 'both', label: t('cli.command.codemagic.release.platform.both') },
|
|
610
|
+
{ value: 'ios', label: t('cli.command.codemagic.release.platform.ios') },
|
|
611
|
+
{ value: 'android', label: t('cli.command.codemagic.release.platform.android') },
|
|
612
|
+
],
|
|
613
|
+
});
|
|
614
|
+
if (typeof platform === 'string') {
|
|
615
|
+
const relOpts = { language };
|
|
616
|
+
if (platform === 'ios') relOpts.ios = true;
|
|
617
|
+
else if (platform === 'android') relOpts.android = true;
|
|
618
|
+
await runCodemagicRelease('.', relOpts);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
606
621
|
else if (sub === 'status') {
|
|
607
622
|
const buildId = await ui.text({
|
|
608
623
|
message: 'Build ID',
|
|
@@ -625,9 +640,11 @@ function buildProgram(language) {
|
|
|
625
640
|
codemagicCmd
|
|
626
641
|
.command('release')
|
|
627
642
|
.argument('[directory]', 'Project folder (default: current directory)', '.')
|
|
643
|
+
.option('--ios', t('cli.command.codemagic.release.iosOption'))
|
|
644
|
+
.option('--android', t('cli.command.codemagic.release.androidOption'))
|
|
628
645
|
.description(t('cli.command.codemagic.release.description'))
|
|
629
|
-
.action(async (directory) => {
|
|
630
|
-
await runCodemagicRelease(directory, { language });
|
|
646
|
+
.action(async (directory, opts) => {
|
|
647
|
+
await runCodemagicRelease(directory, { language, ios: opts.ios, android: opts.android });
|
|
631
648
|
}),
|
|
632
649
|
t
|
|
633
650
|
);
|
package/docs/cli-reference.md
CHANGED
|
@@ -121,15 +121,17 @@ Atalho: `make release-ios`
|
|
|
121
121
|
|
|
122
122
|
---
|
|
123
123
|
|
|
124
|
-
### `kasy codemagic` — Release iOS (nuvem)
|
|
124
|
+
### `kasy codemagic` — Release iOS/Android (nuvem)
|
|
125
125
|
|
|
126
|
-
Dispara build iOS no Codemagic (ideal sem Mac).
|
|
126
|
+
Dispara build **iOS e Android** no Codemagic (ideal sem Mac), levando as chaves do `.env` no disparo.
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
kasy add ci
|
|
130
|
-
kasy codemagic configure
|
|
131
|
-
kasy codemagic release
|
|
132
|
-
kasy codemagic
|
|
129
|
+
kasy add ci # entrega o codemagic.yaml (se necessário)
|
|
130
|
+
kasy codemagic configure # token API → valida e lista seus apps → branch
|
|
131
|
+
kasy codemagic release # iOS + Android na nuvem
|
|
132
|
+
kasy codemagic release --ios # só iOS
|
|
133
|
+
kasy codemagic release --android # só Android
|
|
134
|
+
kasy codemagic status <id> # status do build
|
|
133
135
|
```
|
|
134
136
|
|
|
135
137
|
Documentação: `docs/codemagic-release.md` (idioma da CLI)
|
|
@@ -11,18 +11,39 @@ const {
|
|
|
11
11
|
codemagicReleaseDocPath,
|
|
12
12
|
writeCodemagicEnv,
|
|
13
13
|
validateCodemagicSetup,
|
|
14
|
+
loadProjectEnv,
|
|
15
|
+
listApps,
|
|
16
|
+
workflowIdFor,
|
|
17
|
+
resolveReleaseVars,
|
|
14
18
|
triggerBuild,
|
|
15
19
|
getBuildStatus,
|
|
16
20
|
URL_CODEMAGIC_APPS,
|
|
17
21
|
URL_CODEMAGIC_API,
|
|
18
22
|
} = require('../utils/codemagic-release');
|
|
19
23
|
|
|
24
|
+
// Human label per platform (proper nouns — same across locales).
|
|
25
|
+
const PLATFORM_LABEL = { ios: 'iOS', android: 'Android' };
|
|
26
|
+
|
|
20
27
|
async function assertProject(projectDir, t) {
|
|
21
28
|
if (!(await isKasyFlutterProject(projectDir))) {
|
|
22
29
|
throw new Error(t('codemagic.error.notProject'));
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Decide which platforms to release. Explicit flags win; with no flag we build
|
|
35
|
+
* both. Returns an ordered list like ['ios', 'android'].
|
|
36
|
+
*/
|
|
37
|
+
function platformsFromOptions(options) {
|
|
38
|
+
const wantIos = !!options.ios;
|
|
39
|
+
const wantAndroid = !!options.android;
|
|
40
|
+
if (!wantIos && !wantAndroid) return ['ios', 'android'];
|
|
41
|
+
const platforms = [];
|
|
42
|
+
if (wantIos) platforms.push('ios');
|
|
43
|
+
if (wantAndroid) platforms.push('android');
|
|
44
|
+
return platforms;
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
async function runConfigure(directory, options = {}) {
|
|
27
48
|
const lang = options.language || detectDefaultLanguage();
|
|
28
49
|
const t = createTranslator(lang);
|
|
@@ -35,39 +56,62 @@ async function runConfigure(directory, options = {}) {
|
|
|
35
56
|
ui.log.message(kleur.dim(`${t('codemagic.configure.doc')}: ${codemagicReleaseDocPath(lang)}`));
|
|
36
57
|
ui.log.warn(t('codemagic.configure.checklist'));
|
|
37
58
|
|
|
38
|
-
ui.log.info(t('codemagic.configure.openingLinks'));
|
|
39
|
-
openUrl(URL_CODEMAGIC_API);
|
|
40
|
-
openUrl(URL_CODEMAGIC_APPS);
|
|
41
|
-
|
|
42
59
|
const cancel = () => { ui.cancel(t('codemagic.configure.cancelled')); process.exit(0); };
|
|
43
60
|
const required = (v) => (v && String(v).trim().length > 0 ? undefined : t('codemagic.configure.q.required'));
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
// 1. API token — open the settings page so the user can copy it.
|
|
63
|
+
ui.log.info(t('codemagic.configure.openingToken'));
|
|
64
|
+
openUrl(URL_CODEMAGIC_API);
|
|
65
|
+
const tokenRaw = await ui.password({
|
|
46
66
|
message: t('codemagic.configure.q.token'),
|
|
47
67
|
validate: required,
|
|
48
68
|
onCancel: cancel,
|
|
49
69
|
});
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
})
|
|
60
|
-
|
|
70
|
+
const token = String(tokenRaw).trim();
|
|
71
|
+
|
|
72
|
+
// 2. Validate the token by listing the user's apps.
|
|
73
|
+
const spinner = ui.spinner();
|
|
74
|
+
spinner.start(t('codemagic.configure.validating'));
|
|
75
|
+
let apps;
|
|
76
|
+
try {
|
|
77
|
+
apps = await listApps(token);
|
|
78
|
+
spinner.stop(t('codemagic.configure.validated'));
|
|
79
|
+
} catch (err) {
|
|
80
|
+
spinner.stop(`${t('codemagic.configure.tokenInvalid')} (${err.message})`, 2);
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 3. Pick the app (or guide the user to connect a repo first).
|
|
86
|
+
let appId;
|
|
87
|
+
if (!apps.length) {
|
|
88
|
+
ui.log.warn(t('codemagic.configure.noApps'));
|
|
89
|
+
openUrl(URL_CODEMAGIC_APPS);
|
|
90
|
+
const manual = await ui.text({
|
|
91
|
+
message: t('codemagic.configure.q.appId'),
|
|
92
|
+
validate: required,
|
|
93
|
+
onCancel: cancel,
|
|
94
|
+
});
|
|
95
|
+
appId = String(manual).trim();
|
|
96
|
+
} else {
|
|
97
|
+
appId = await ui.select({
|
|
98
|
+
message: t('codemagic.configure.pickApp'),
|
|
99
|
+
options: apps.map((a) => ({ value: a.id, label: a.name, hint: a.id })),
|
|
100
|
+
});
|
|
101
|
+
if (typeof appId === 'symbol') cancel();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 4. Branch that triggers the build.
|
|
105
|
+
const branchRaw = await ui.text({
|
|
61
106
|
message: t('codemagic.configure.q.branch'),
|
|
62
107
|
initialValue: 'main',
|
|
63
108
|
onCancel: cancel,
|
|
64
109
|
});
|
|
65
110
|
|
|
66
111
|
await writeCodemagicEnv(projectDir, {
|
|
67
|
-
token
|
|
68
|
-
appId
|
|
69
|
-
|
|
70
|
-
branch: String(branch).trim() || 'main',
|
|
112
|
+
token,
|
|
113
|
+
appId,
|
|
114
|
+
branch: String(branchRaw).trim() || 'main',
|
|
71
115
|
});
|
|
72
116
|
|
|
73
117
|
ui.log.success(t('codemagic.configure.success'));
|
|
@@ -87,44 +131,64 @@ async function runRelease(directory, options = {}) {
|
|
|
87
131
|
if (validation.issues.includes('missing_yaml')) {
|
|
88
132
|
ui.log.message(kleur.dim(t('codemagic.error.addCi')));
|
|
89
133
|
}
|
|
90
|
-
if (validation.issues.includes('missing_env')) {
|
|
134
|
+
if (validation.issues.includes('missing_env') || validation.issues.includes('missing_token') || validation.issues.includes('missing_app_id')) {
|
|
91
135
|
ui.log.message(kleur.dim(t('codemagic.error.runConfigure')));
|
|
92
136
|
}
|
|
93
137
|
process.exitCode = 1;
|
|
94
138
|
return;
|
|
95
139
|
}
|
|
96
140
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
141
|
+
const platforms = platformsFromOptions(options);
|
|
142
|
+
|
|
143
|
+
// iOS builds need the Google Sign-In URL scheme to be in sync.
|
|
144
|
+
if (platforms.includes('ios')) {
|
|
145
|
+
const googleScheme = await validateGoogleIosUrlScheme(projectDir);
|
|
146
|
+
if (!googleScheme.ok) {
|
|
147
|
+
printCompactHeader(t);
|
|
148
|
+
ui.log.error(t('codemagic.error.googleUrlScheme'));
|
|
149
|
+
ui.log.message(kleur.dim(googleScheme.error));
|
|
150
|
+
ui.log.info(t('ios.error.googleUrlSchemeHint'));
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
105
154
|
}
|
|
106
155
|
|
|
107
156
|
printCompactHeader(t);
|
|
108
157
|
ui.intro(t('codemagic.release.title'));
|
|
109
158
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
159
|
+
// Read the project's keys and carry them with the build.
|
|
160
|
+
const projectEnv = await loadProjectEnv(projectDir);
|
|
161
|
+
const variables = resolveReleaseVars(projectEnv);
|
|
162
|
+
|
|
163
|
+
if (platforms.includes('ios') && !variables.RC_IOS_PROD_KEY) {
|
|
164
|
+
ui.log.warn(t('codemagic.release.noIosKey'));
|
|
165
|
+
}
|
|
166
|
+
if (platforms.includes('android') && !variables.RC_ANDROID_PROD_KEY) {
|
|
167
|
+
ui.log.warn(t('codemagic.release.noAndroidKey'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let anyFailed = false;
|
|
171
|
+
for (const platform of platforms) {
|
|
172
|
+
const label = PLATFORM_LABEL[platform];
|
|
173
|
+
const workflowId = workflowIdFor(validation.env, platform);
|
|
174
|
+
const spinner = ui.spinner();
|
|
175
|
+
spinner.start(t('codemagic.release.spinPlatform', { platform: label }));
|
|
176
|
+
try {
|
|
177
|
+
const result = await triggerBuild(validation.env, { workflowId, variables });
|
|
178
|
+
const buildId = result.buildId || result._id;
|
|
179
|
+
spinner.stop(t('codemagic.release.triggeredPlatform', { platform: label }));
|
|
180
|
+
if (buildId) {
|
|
181
|
+
ui.log.info(`Build ID: ${kleur.cyan(buildId)}`);
|
|
182
|
+
ui.log.message(kleur.dim(`https://codemagic.io/app/${validation.env.CODEMAGIC_APP_ID}/build/${buildId}`));
|
|
183
|
+
}
|
|
184
|
+
} catch (err) {
|
|
185
|
+
anyFailed = true;
|
|
186
|
+
spinner.stop(`${label}: ${err.message}`, 2);
|
|
123
187
|
}
|
|
124
|
-
} catch (err) {
|
|
125
|
-
spinner.stop(err.message, 2);
|
|
126
|
-
process.exitCode = 1;
|
|
127
188
|
}
|
|
189
|
+
|
|
190
|
+
if (anyFailed) process.exitCode = 1;
|
|
191
|
+
ui.outro('');
|
|
128
192
|
}
|
|
129
193
|
|
|
130
194
|
async function runStatus(buildId, directory, options = {}) {
|
|
@@ -168,4 +232,5 @@ module.exports = {
|
|
|
168
232
|
runConfigure,
|
|
169
233
|
runRelease,
|
|
170
234
|
runStatus,
|
|
235
|
+
platformsFromOptions,
|
|
171
236
|
};
|
|
@@ -58,6 +58,13 @@ dependencies:
|
|
|
58
58
|
open_filex: ^4.7.0
|
|
59
59
|
package_info_plus: ^8.3.0
|
|
60
60
|
path_provider: ^2.1.5
|
|
61
|
+
# Pin the Apple impl of path_provider below 2.6.0/2.5.0. Those versions pull in
|
|
62
|
+
# objective_c via "native assets", whose build hook is unquoted and breaks every
|
|
63
|
+
# build on Windows when the username has a space (C:\Users\John Silva) — even a
|
|
64
|
+
# Chrome/web run, where the Apple-only hook is dead weight. 2.5.1 is the last
|
|
65
|
+
# release without objective_c (added in 2.5.0, reverted in 2.5.1, re-added in
|
|
66
|
+
# 2.6.0). Drop this pin once the upstream web-target regression is fixed.
|
|
67
|
+
path_provider_foundation: 2.5.1
|
|
61
68
|
permission_handler: ^12.0.1
|
|
62
69
|
provider: ^6.1.0
|
|
63
70
|
pub_semver: ^2.2.0
|
|
@@ -59,6 +59,13 @@ dependencies:
|
|
|
59
59
|
open_filex: ^4.7.0
|
|
60
60
|
package_info_plus: ^8.3.0
|
|
61
61
|
path_provider: ^2.1.5
|
|
62
|
+
# Pin the Apple impl of path_provider below 2.6.0/2.5.0. Those versions pull in
|
|
63
|
+
# objective_c via "native assets", whose build hook is unquoted and breaks every
|
|
64
|
+
# build on Windows when the username has a space (C:\Users\John Silva) — even a
|
|
65
|
+
# Chrome/web run, where the Apple-only hook is dead weight. 2.5.1 is the last
|
|
66
|
+
# release without objective_c (added in 2.5.0, reverted in 2.5.1, re-added in
|
|
67
|
+
# 2.6.0). Drop this pin once the upstream web-target regression is fixed.
|
|
68
|
+
path_provider_foundation: 2.5.1
|
|
62
69
|
permission_handler: ^12.0.1
|
|
63
70
|
provider: ^6.1.0
|
|
64
71
|
pub_semver: ^2.2.0
|
package/lib/scaffold/engine.js
CHANGED
|
@@ -27,12 +27,14 @@ const ALWAYS_EXCLUDE = new Set([
|
|
|
27
27
|
'.cursor',
|
|
28
28
|
'.idea',
|
|
29
29
|
'.claude', // AI assistant instructions — not for the client
|
|
30
|
-
//
|
|
31
|
-
// (these files live in features/ci/ and are applied as a feature patch)
|
|
30
|
+
// GitHub/GitLab CI pipelines are not shipped to generated projects yet.
|
|
32
31
|
'.github',
|
|
33
32
|
'.gitlab',
|
|
34
33
|
'.gitlab-ci.yml',
|
|
35
|
-
|
|
34
|
+
// NOTE: codemagic.yaml is intentionally NOT excluded here. It ships in the
|
|
35
|
+
// base template and generate.js removes it unless the 'ci' module is
|
|
36
|
+
// selected (see the "Phase B" gating). This is what lets `kasy codemagic`
|
|
37
|
+
// work in a generated project.
|
|
36
38
|
]);
|
|
37
39
|
|
|
38
40
|
// File basenames that should NEVER be copied, regardless of directory depth
|
package/lib/scaffold/generate.js
CHANGED
|
@@ -236,6 +236,13 @@ async function generateProject(targetDir, backend, options, hooks = {}) {
|
|
|
236
236
|
// Remove directories of modules that were NOT selected
|
|
237
237
|
await removeModuleDirs(targetDir, modules);
|
|
238
238
|
|
|
239
|
+
// codemagic.yaml ships in the base template (so `kasy codemagic` works out
|
|
240
|
+
// of the box). It belongs to the 'ci' module — strip it when ci was not
|
|
241
|
+
// selected, mirroring how the other optional modules are gated above.
|
|
242
|
+
if (!modules.includes('ci')) {
|
|
243
|
+
await fs.remove(path.join(targetDir, 'codemagic.yaml'));
|
|
244
|
+
}
|
|
245
|
+
|
|
239
246
|
// Write no-op analytics_api.dart if analytics not selected
|
|
240
247
|
if (!modules.includes('analytics')) {
|
|
241
248
|
await writeNoOpAnalyticsApi(targetDir, packageName);
|
|
@@ -8,18 +8,22 @@ const CODEMAGIC_ENV_REL = path.join('.kasy', 'codemagic.env');
|
|
|
8
8
|
const CODEMAGIC_API = 'https://api.codemagic.io';
|
|
9
9
|
|
|
10
10
|
const URL_CODEMAGIC_APPS = 'https://codemagic.io/apps';
|
|
11
|
-
|
|
11
|
+
// Codemagic API token lives in the account settings → Integrations page.
|
|
12
|
+
const URL_CODEMAGIC_API = 'https://codemagic.io/settings';
|
|
13
|
+
|
|
14
|
+
// Default workflow ids defined in the codemagic.yaml shipped by the `ci` feature.
|
|
15
|
+
const DEFAULT_IOS_WORKFLOW = 'ios-workflow';
|
|
16
|
+
const DEFAULT_ANDROID_WORKFLOW = 'android-workflow';
|
|
12
17
|
|
|
13
18
|
function codemagicReleaseDocPath() {
|
|
14
19
|
return 'docs/codemagic-release.md';
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const content = await fs.readFile(envPath, 'utf8');
|
|
22
|
+
/**
|
|
23
|
+
* Parse a simple KEY=VALUE env file into an object. Ignores blanks/comments.
|
|
24
|
+
* Values keep everything after the first '=' (so URLs with ':' survive).
|
|
25
|
+
*/
|
|
26
|
+
function parseEnvContent(content) {
|
|
23
27
|
const env = {};
|
|
24
28
|
for (const line of content.split('\n')) {
|
|
25
29
|
const trimmed = line.trim();
|
|
@@ -31,12 +35,32 @@ async function loadCodemagicEnv(projectDir) {
|
|
|
31
35
|
return env;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
async function
|
|
38
|
+
async function loadCodemagicEnv(projectDir) {
|
|
39
|
+
const envPath = path.join(projectDir, CODEMAGIC_ENV_REL);
|
|
40
|
+
if (!(await fs.pathExists(envPath))) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return parseEnvContent(await fs.readFile(envPath, 'utf8'));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Read the project's runtime `.env` (the app's config). Returns {} when absent.
|
|
48
|
+
*/
|
|
49
|
+
async function loadProjectEnv(projectDir) {
|
|
50
|
+
const envPath = path.join(projectDir, '.env');
|
|
51
|
+
if (!(await fs.pathExists(envPath))) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
return parseEnvContent(await fs.readFile(envPath, 'utf8'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function writeCodemagicEnv(projectDir, { token, appId, iosWorkflowId, androidWorkflowId, branch }) {
|
|
35
58
|
const lines = [
|
|
36
59
|
'# Generated by kasy codemagic configure — do not commit',
|
|
37
60
|
`CODEMAGIC_API_TOKEN=${token}`,
|
|
38
61
|
`CODEMAGIC_APP_ID=${appId}`,
|
|
39
|
-
`
|
|
62
|
+
`CODEMAGIC_IOS_WORKFLOW_ID=${iosWorkflowId || DEFAULT_IOS_WORKFLOW}`,
|
|
63
|
+
`CODEMAGIC_ANDROID_WORKFLOW_ID=${androidWorkflowId || DEFAULT_ANDROID_WORKFLOW}`,
|
|
40
64
|
`CODEMAGIC_BRANCH=${branch || 'main'}`,
|
|
41
65
|
'',
|
|
42
66
|
];
|
|
@@ -83,12 +107,92 @@ function httpsJson(method, urlPath, token, body) {
|
|
|
83
107
|
});
|
|
84
108
|
}
|
|
85
109
|
|
|
86
|
-
|
|
110
|
+
/**
|
|
111
|
+
* List the Codemagic applications the token can access.
|
|
112
|
+
* Returns [{ id, name }] so the configure wizard can let the user pick one
|
|
113
|
+
* instead of pasting a raw Mongo id.
|
|
114
|
+
*/
|
|
115
|
+
async function listApps(token) {
|
|
116
|
+
const result = await httpsJson('GET', '/apps', token);
|
|
117
|
+
const apps = Array.isArray(result) ? result : (result.applications || []);
|
|
118
|
+
return apps
|
|
119
|
+
.map((a) => ({ id: a._id || a.id, name: a.appName || a.name || a.repository?.name || '(unnamed app)' }))
|
|
120
|
+
.filter((a) => a.id);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Resolve which workflow id to trigger for a platform, honoring custom ids
|
|
125
|
+
* saved at configure time and falling back to the shipped defaults. The legacy
|
|
126
|
+
* single CODEMAGIC_WORKFLOW_ID (older configs) is treated as the iOS workflow.
|
|
127
|
+
*/
|
|
128
|
+
function workflowIdFor(env, platform) {
|
|
129
|
+
if (platform === 'android') {
|
|
130
|
+
return env.CODEMAGIC_ANDROID_WORKFLOW_ID || DEFAULT_ANDROID_WORKFLOW;
|
|
131
|
+
}
|
|
132
|
+
return env.CODEMAGIC_IOS_WORKFLOW_ID || env.CODEMAGIC_WORKFLOW_ID || DEFAULT_IOS_WORKFLOW;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Build the environment variables to inject at trigger time, read from the
|
|
137
|
+
* project's `.env`. These travel with the build so the cloud has the SAME
|
|
138
|
+
* production keys you use locally — the user does not have to fill the
|
|
139
|
+
* Codemagic variable groups by hand.
|
|
140
|
+
*
|
|
141
|
+
* RevenueCat SDK keys (appl_/goog_/test_) are public keys, safe to send this
|
|
142
|
+
* way. Real secrets (keystore, App Store key, service account) stay in the
|
|
143
|
+
* dashboard and are NOT sent here.
|
|
144
|
+
*/
|
|
145
|
+
function resolveReleaseVars(projectEnv) {
|
|
146
|
+
const vars = { ENV: 'prod' };
|
|
147
|
+
|
|
148
|
+
const passthrough = [
|
|
149
|
+
'BACKEND_URL',
|
|
150
|
+
'RC_TEST_KEY',
|
|
151
|
+
'RC_IOS_PROD_KEY',
|
|
152
|
+
'RC_ANDROID_PROD_KEY',
|
|
153
|
+
'MIXPANEL_TOKEN',
|
|
154
|
+
'SENTRY_DSN',
|
|
155
|
+
];
|
|
156
|
+
for (const key of passthrough) {
|
|
157
|
+
const value = projectEnv[key];
|
|
158
|
+
if (value && value.trim()) vars[key] = value.trim();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// LLM chat endpoint may be named either way across template versions; the app
|
|
162
|
+
// reads AI_CHAT_ENDPOINT.
|
|
163
|
+
const endpoint = projectEnv.AI_CHAT_ENDPOINT || projectEnv.LLM_CHAT_ENDPOINT;
|
|
164
|
+
if (endpoint && endpoint.trim()) vars.AI_CHAT_ENDPOINT = endpoint.trim();
|
|
165
|
+
|
|
166
|
+
// Legacy single-key projects (pre test/prod split): promote a non-test key
|
|
167
|
+
// into the prod slot so release builds still get a real key.
|
|
168
|
+
if (!vars.RC_IOS_PROD_KEY && projectEnv.RC_IOS_API_KEY && !projectEnv.RC_IOS_API_KEY.startsWith('test_')) {
|
|
169
|
+
vars.RC_IOS_PROD_KEY = projectEnv.RC_IOS_API_KEY.trim();
|
|
170
|
+
}
|
|
171
|
+
if (!vars.RC_ANDROID_PROD_KEY && projectEnv.RC_ANDROID_API_KEY && !projectEnv.RC_ANDROID_API_KEY.startsWith('test_')) {
|
|
172
|
+
vars.RC_ANDROID_PROD_KEY = projectEnv.RC_ANDROID_API_KEY.trim();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Numeric App Store id → APP_ID, used by the iOS workflow for build numbering.
|
|
176
|
+
if (projectEnv.APP_STORE_ID && projectEnv.APP_STORE_ID.trim()) {
|
|
177
|
+
vars.APP_ID = projectEnv.APP_STORE_ID.trim();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return vars;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Trigger one build. `variables` is injected via the build's environment so the
|
|
185
|
+
* cloud build carries the project's production keys.
|
|
186
|
+
*/
|
|
187
|
+
async function triggerBuild(env, { workflowId, branch, variables }) {
|
|
87
188
|
const body = {
|
|
88
189
|
appId: env.CODEMAGIC_APP_ID,
|
|
89
|
-
workflowId
|
|
90
|
-
branch: env.CODEMAGIC_BRANCH || 'main',
|
|
190
|
+
workflowId,
|
|
191
|
+
branch: branch || env.CODEMAGIC_BRANCH || 'main',
|
|
91
192
|
};
|
|
193
|
+
if (variables && Object.keys(variables).length > 0) {
|
|
194
|
+
body.environment = { variables };
|
|
195
|
+
}
|
|
92
196
|
return httpsJson('POST', '/builds', env.CODEMAGIC_API_TOKEN, body);
|
|
93
197
|
}
|
|
94
198
|
|
|
@@ -109,7 +213,6 @@ async function validateCodemagicSetup(projectDir) {
|
|
|
109
213
|
}
|
|
110
214
|
if (!env.CODEMAGIC_API_TOKEN) issues.push('missing_token');
|
|
111
215
|
if (!env.CODEMAGIC_APP_ID) issues.push('missing_app_id');
|
|
112
|
-
if (!env.CODEMAGIC_WORKFLOW_ID) issues.push('missing_workflow');
|
|
113
216
|
return { ok: issues.length === 0, issues, env };
|
|
114
217
|
}
|
|
115
218
|
|
|
@@ -118,9 +221,15 @@ module.exports = {
|
|
|
118
221
|
CODEMAGIC_API,
|
|
119
222
|
URL_CODEMAGIC_APPS,
|
|
120
223
|
URL_CODEMAGIC_API,
|
|
224
|
+
DEFAULT_IOS_WORKFLOW,
|
|
225
|
+
DEFAULT_ANDROID_WORKFLOW,
|
|
121
226
|
codemagicReleaseDocPath,
|
|
122
227
|
loadCodemagicEnv,
|
|
228
|
+
loadProjectEnv,
|
|
123
229
|
writeCodemagicEnv,
|
|
230
|
+
listApps,
|
|
231
|
+
workflowIdFor,
|
|
232
|
+
resolveReleaseVars,
|
|
124
233
|
triggerBuild,
|
|
125
234
|
getBuildStatus,
|
|
126
235
|
validateCodemagicSetup,
|
|
@@ -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,211 @@
|
|
|
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
|
+
ios_signing:
|
|
105
|
+
distribution_type: app_store
|
|
106
|
+
bundle_identifier: com.aicrus.firebase.kit # <-- Your iOS bundle identifier
|
|
107
|
+
# APP_ID (numeric App Store Connect id) and the app keys arrive as
|
|
108
|
+
# environment variables: `kasy codemagic release` sends them from your .env.
|
|
109
|
+
# No variable group is required for iOS (App Store auth uses the integration
|
|
110
|
+
# above). For dashboard/push-triggered builds, add the keys as environment
|
|
111
|
+
# variables in the Codemagic app settings and a `groups:` entry here.
|
|
112
|
+
flutter: stable
|
|
113
|
+
xcode: latest # <-- set to specific version e.g. 15.0 to avoid unexpected updates.
|
|
114
|
+
cocoapods: default
|
|
115
|
+
triggering:
|
|
116
|
+
cancel_previous_builds: true
|
|
117
|
+
events:
|
|
118
|
+
- push
|
|
119
|
+
branch_patterns:
|
|
120
|
+
- pattern: '*'
|
|
121
|
+
include: false
|
|
122
|
+
source: false
|
|
123
|
+
- pattern: 'main'
|
|
124
|
+
include: true
|
|
125
|
+
source: true
|
|
126
|
+
scripts:
|
|
127
|
+
- name: Set up code signing settings on Xcode project
|
|
128
|
+
script: |
|
|
129
|
+
xcode-project use-profiles
|
|
130
|
+
- name: Get Flutter packages
|
|
131
|
+
script: |
|
|
132
|
+
flutter packages pub get
|
|
133
|
+
- name: Install pods
|
|
134
|
+
script: |
|
|
135
|
+
find . -name "Podfile" -execdir pod install \;
|
|
136
|
+
- name: Validate Google Sign-In iOS URL scheme
|
|
137
|
+
script: |
|
|
138
|
+
GOOGLE_PLIST="ios/Runner/GoogleService-Info.plist"
|
|
139
|
+
INFO_PLIST="ios/Runner/Info.plist"
|
|
140
|
+
if [ -f "$GOOGLE_PLIST" ]; then
|
|
141
|
+
REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c "Print :REVERSED_CLIENT_ID" "$GOOGLE_PLIST" 2>/dev/null || true)
|
|
142
|
+
if [ -z "$REVERSED_CLIENT_ID" ]; then
|
|
143
|
+
echo "REVERSED_CLIENT_ID not found in $GOOGLE_PLIST"
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
if ! /usr/libexec/PlistBuddy -c "Print :CFBundleURLTypes" "$INFO_PLIST" 2>/dev/null | grep -Fq "$REVERSED_CLIENT_ID"; then
|
|
147
|
+
echo "Google Sign-In iOS URL scheme mismatch."
|
|
148
|
+
echo "Expected CFBundleURLSchemes to include: $REVERSED_CLIENT_ID"
|
|
149
|
+
echo "Run flutterfire configure/project setup before building."
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
fi
|
|
153
|
+
- name: Generate .env from build environment
|
|
154
|
+
script: |
|
|
155
|
+
# See the Android workflow note above — recreate the bundled .env asset
|
|
156
|
+
# from environment variables so the release build gets the production
|
|
157
|
+
# RevenueCat key (appl_) instead of the test_ key.
|
|
158
|
+
cat > "$CM_BUILD_DIR/.env" <<EOF
|
|
159
|
+
ENV=prod
|
|
160
|
+
BACKEND_URL=${BACKEND_URL:-}
|
|
161
|
+
AI_CHAT_ENDPOINT=${AI_CHAT_ENDPOINT:-}
|
|
162
|
+
RC_TEST_KEY=${RC_TEST_KEY:-}
|
|
163
|
+
RC_IOS_PROD_KEY=${RC_IOS_PROD_KEY:-}
|
|
164
|
+
RC_ANDROID_PROD_KEY=${RC_ANDROID_PROD_KEY:-}
|
|
165
|
+
MIXPANEL_TOKEN=${MIXPANEL_TOKEN:-}
|
|
166
|
+
SENTRY_DSN=${SENTRY_DSN:-}
|
|
167
|
+
APP_STORE_ID=${APP_STORE_ID:-}
|
|
168
|
+
EOF
|
|
169
|
+
- name: Flutter analyze # <-- remove if you don't like flutter analyze
|
|
170
|
+
script: |
|
|
171
|
+
flutter analyze
|
|
172
|
+
- name: Flutter unit tests
|
|
173
|
+
script: |
|
|
174
|
+
flutter test
|
|
175
|
+
ignore_failure: false # You should never build an app that has failing tests.
|
|
176
|
+
- name: Flutter build ipa and automatic versioning
|
|
177
|
+
script: |
|
|
178
|
+
flutter build ipa --release \
|
|
179
|
+
--dart-define=ENV=prod \
|
|
180
|
+
--build-name=1.0.0 \
|
|
181
|
+
--build-number=$(($(app-store-connect get-latest-app-store-build-number "$APP_ID") + 1)) \
|
|
182
|
+
--export-options-plist=/Users/builder/export_options.plist
|
|
183
|
+
artifacts:
|
|
184
|
+
- build/ios/ipa/*.ipa
|
|
185
|
+
- /tmp/xcodebuild_logs/*.log
|
|
186
|
+
- flutter_drive.log
|
|
187
|
+
publishing:
|
|
188
|
+
# See the following link for details about email publishing - https://docs.codemagic.io/publishing-yaml/distribution/#email
|
|
189
|
+
email:
|
|
190
|
+
recipients:
|
|
191
|
+
- # <-- Put your email here or add others recipients
|
|
192
|
+
notify:
|
|
193
|
+
success: true
|
|
194
|
+
failure: true
|
|
195
|
+
app_store_connect:
|
|
196
|
+
# Use codemagic integration (easier)
|
|
197
|
+
auth: integration
|
|
198
|
+
# ====================================
|
|
199
|
+
## Or push all keys manually here
|
|
200
|
+
## ====================================
|
|
201
|
+
#api_key: $APP_STORE_CONNECT_PRIVATE_KEY
|
|
202
|
+
#key_id: $APP_STORE_CONNECT_KEY_IDENTIFIER
|
|
203
|
+
#issuer_id: $APP_STORE_CONNECT_ISSUER_ID
|
|
204
|
+
## ====================================
|
|
205
|
+
submit_to_app_store: false # Set true to send to App Store review automatically
|
|
206
|
+
release_type: MANUAL
|
|
207
|
+
# Configuration related to TestFlight (optional)
|
|
208
|
+
# Note: This action is performed during post-processing.
|
|
209
|
+
submit_to_testflight: true
|
|
210
|
+
beta_groups: # Specify the names of beta tester groups that will get access to the build once it has passed beta review.
|
|
211
|
+
- 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`.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
# Publicar
|
|
1
|
+
# Publicar en la nube con Codemagic (sin Mac)
|
|
2
|
+
|
|
3
|
+
Publica **iOS** y **Android** por la nube de Codemagic. Cada plataforma tiene su
|
|
4
|
+
propio workflow, así que puedes enviarlas por separado o las dos juntas.
|
|
2
5
|
|
|
3
6
|
## Requisitos
|
|
4
7
|
|
|
5
8
|
- Cuenta [Codemagic](https://codemagic.io)
|
|
6
9
|
- Repositorio Git conectado a Codemagic
|
|
7
|
-
-
|
|
10
|
+
- iOS: cuenta Apple Developer + app en App Store Connect
|
|
11
|
+
- Android: app en Google Play Console + un keystore de firma
|
|
8
12
|
|
|
9
13
|
## 1. Agregar CI al proyecto (si falta)
|
|
10
14
|
|
|
@@ -12,32 +16,48 @@
|
|
|
12
16
|
kasy add ci
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
Crea `codemagic.yaml` en la raíz del proyecto
|
|
19
|
+
Crea `codemagic.yaml` en la raíz del proyecto (workflows `ios-workflow` y
|
|
20
|
+
`android-workflow`).
|
|
21
|
+
|
|
22
|
+
## 2. Configurar en el panel Codemagic (una vez)
|
|
23
|
+
|
|
24
|
+
Estos elementos son secretos y solo se hacen en el panel — una vez:
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
1. Abre [codemagic.io/apps](https://codemagic.io/apps) y **conecta el repositorio**.
|
|
27
|
+
2. **iOS** — *Integrations → App Store Connect*: conecta la clave de Apple. Su
|
|
28
|
+
nombre va en `integrations: app_store_connect:` en `codemagic.yaml`.
|
|
29
|
+
3. **Android** — *Code signing identities → Android keystores*: sube el keystore
|
|
30
|
+
con el **Reference name** `keystore_reference` (igual que en `codemagic.yaml`).
|
|
31
|
+
4. **Android** — sube el JSON de la **service account de Google Play** como
|
|
32
|
+
variable secreta `GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS` (grupo `google_play`).
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
4. **Environment variables:** en el grupo del app (ej. `appstore_credentials`):
|
|
23
|
-
- `BACKEND_URL`, `SENTRY_DSN`, `RC_IOS_API_KEY`, `RC_ANDROID_API_KEY`, `MIXPANEL_TOKEN`
|
|
24
|
-
5. En `codemagic.yaml`, completa `APP_ID` (ID numérico del app en App Store Connect).
|
|
34
|
+
> Las **claves del app** (RevenueCat, backend, etc.) **no** hace falta cargarlas
|
|
35
|
+
> en el panel: `kasy codemagic release` las envía automáticamente desde tu `.env`.
|
|
36
|
+
> Si prefieres disparar desde el panel/push, agrégalas a los grupos de variables.
|
|
25
37
|
|
|
26
|
-
## 3. Configurar en la terminal
|
|
38
|
+
## 3. Configurar en la terminal (una vez)
|
|
27
39
|
|
|
28
40
|
```bash
|
|
29
41
|
kasy codemagic configure
|
|
30
42
|
```
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
El asistente abre la página del **API token** (Settings → Codemagic API), valida
|
|
45
|
+
el token y **lista tus apps** para elegir — sin escribir IDs. Guarda todo en
|
|
46
|
+
`.kasy/codemagic.env` (no versionado).
|
|
33
47
|
|
|
34
48
|
## 4. Disparar build
|
|
35
49
|
|
|
36
50
|
```bash
|
|
37
|
-
kasy codemagic release
|
|
51
|
+
kasy codemagic release # iOS + Android
|
|
52
|
+
kasy codemagic release --ios # solo iOS
|
|
53
|
+
kasy codemagic release --android # solo Android
|
|
38
54
|
```
|
|
39
55
|
|
|
40
|
-
El
|
|
56
|
+
El comando lee tu `.env` y lleva las claves de producción (incluida la clave de
|
|
57
|
+
producción de RevenueCat, `appl_`/`goog_`) junto al disparo. Un paso del
|
|
58
|
+
`codemagic.yaml` recrea el `.env` en la nube antes de compilar, así el build sale
|
|
59
|
+
con las claves correctas. Según `codemagic.yaml`, iOS va a TestFlight y Android al
|
|
60
|
+
track configurado (`internal` por defecto).
|
|
41
61
|
|
|
42
62
|
## Estado del build
|
|
43
63
|
|
|
@@ -45,6 +65,12 @@ El build en la nube puede subir a TestFlight según `codemagic.yaml`.
|
|
|
45
65
|
kasy codemagic status <buildId>
|
|
46
66
|
```
|
|
47
67
|
|
|
68
|
+
## ¿Puedo usar el Mac y Codemagic a la vez?
|
|
69
|
+
|
|
70
|
+
Sí. Son dos caminos hacia la misma tienda; no entran en conflicto. El número de
|
|
71
|
+
build lo calcula la nube (último en la tienda + 1), por lo que rara vez choca.
|
|
72
|
+
Para el día a día, elige un camino principal.
|
|
73
|
+
|
|
48
74
|
## Mac local
|
|
49
75
|
|
|
50
76
|
Si tienes Mac: [ios-release.md](./ios-release.md) y `kasy ios release`.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
# Publicar
|
|
1
|
+
# Publicar na nuvem com Codemagic (sem Mac)
|
|
2
|
+
|
|
3
|
+
Publica **iOS** e **Android** pela nuvem do Codemagic. Cada plataforma tem seu
|
|
4
|
+
próprio workflow, então você envia separado ou os dois juntos.
|
|
2
5
|
|
|
3
6
|
## Pré-requisitos
|
|
4
7
|
|
|
5
8
|
- Conta [Codemagic](https://codemagic.io)
|
|
6
9
|
- Repositório Git conectado ao Codemagic
|
|
7
|
-
-
|
|
10
|
+
- iOS: conta Apple Developer + app no App Store Connect
|
|
11
|
+
- Android: app no Google Play Console + keystore de assinatura
|
|
8
12
|
|
|
9
13
|
## 1. Adicionar CI ao projeto (se ainda não tiver)
|
|
10
14
|
|
|
@@ -12,32 +16,49 @@
|
|
|
12
16
|
kasy add ci
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
Isso cria `codemagic.yaml` na raiz do projeto
|
|
19
|
+
Isso cria o `codemagic.yaml` na raiz do projeto (workflows `ios-workflow` e
|
|
20
|
+
`android-workflow`).
|
|
21
|
+
|
|
22
|
+
## 2. Configurar no painel Codemagic (uma vez)
|
|
23
|
+
|
|
24
|
+
Estes itens são secretos e só podem ser feitos no painel — uma vez:
|
|
16
25
|
|
|
17
|
-
|
|
26
|
+
1. Abra [codemagic.io/apps](https://codemagic.io/apps) e **conecte o repositório**.
|
|
27
|
+
2. **iOS** — *Integrations → App Store Connect*: conecte a chave da Apple. No
|
|
28
|
+
`codemagic.yaml`, o nome dela vai em `integrations: app_store_connect:`.
|
|
29
|
+
3. **Android** — *Code signing identities → Android keystores*: suba o keystore
|
|
30
|
+
com o **Reference name** `keystore_reference` (o mesmo usado no `codemagic.yaml`).
|
|
31
|
+
4. **Android** — suba o JSON da **service account do Google Play** como variável
|
|
32
|
+
secreta `GOOGLE_PLAY_SERVICE_ACCOUNT_CREDENTIALS` (grupo `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. No `codemagic.yaml`, preencha `APP_ID` (ID numérico do app no App Store Connect — aparece na URL do app).
|
|
34
|
+
> As **chaves do app** (RevenueCat, backend, etc.) **não** precisam ser
|
|
35
|
+
> preenchidas no painel: o `kasy codemagic release` as envia automaticamente a
|
|
36
|
+
> partir do seu `.env`. Se preferir disparar pelo painel/push, aí sim cadastre-as
|
|
37
|
+
> nos grupos de variáveis.
|
|
25
38
|
|
|
26
|
-
## 3. Configurar no terminal
|
|
39
|
+
## 3. Configurar no terminal (uma vez)
|
|
27
40
|
|
|
28
41
|
```bash
|
|
29
42
|
kasy codemagic configure
|
|
30
43
|
```
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
O assistente abre a página do **token da API** (Settings → Codemagic API), valida
|
|
46
|
+
o token e **lista os seus apps** para você escolher — sem digitar IDs. Salva tudo
|
|
47
|
+
em `.kasy/codemagic.env` (não versionado).
|
|
33
48
|
|
|
34
49
|
## 4. Disparar build
|
|
35
50
|
|
|
36
51
|
```bash
|
|
37
|
-
kasy codemagic release
|
|
52
|
+
kasy codemagic release # iOS + Android
|
|
53
|
+
kasy codemagic release --ios # só iOS
|
|
54
|
+
kasy codemagic release --android # só Android
|
|
38
55
|
```
|
|
39
56
|
|
|
40
|
-
O
|
|
57
|
+
O comando lê o seu `.env` e leva as chaves de produção (incluindo a chave de
|
|
58
|
+
produção do RevenueCat, `appl_`/`goog_`) junto no disparo. Um passo do
|
|
59
|
+
`codemagic.yaml` recria o `.env` na nuvem antes de compilar, então o build sai
|
|
60
|
+
com as chaves certas. Conforme o `codemagic.yaml`, o iOS vai para o TestFlight e
|
|
61
|
+
o Android para o track configurado (`internal` por padrão).
|
|
41
62
|
|
|
42
63
|
## Status do build
|
|
43
64
|
|
|
@@ -45,6 +66,12 @@ O build roda na nuvem e, conforme o `codemagic.yaml`, pode enviar para TestFligh
|
|
|
45
66
|
kasy codemagic status <buildId>
|
|
46
67
|
```
|
|
47
68
|
|
|
69
|
+
## Posso usar o Mac e o Codemagic ao mesmo tempo?
|
|
70
|
+
|
|
71
|
+
Pode. São dois caminhos para a mesma loja, não conflitam. O número do build é
|
|
72
|
+
calculado pela própria nuvem (último na loja + 1), então dificilmente colide.
|
|
73
|
+
No dia a dia, escolha um caminho principal.
|
|
74
|
+
|
|
48
75
|
## Mac local
|
|
49
76
|
|
|
50
|
-
Se tiver Mac: [ios-release.md](./ios-release.md) e `kasy ios release`.
|
|
77
|
+
Se tiver Mac: veja [ios-release.md](./ios-release.md) e `kasy ios release`.
|
|
@@ -79,6 +79,13 @@ dependencies:
|
|
|
79
79
|
open_filex: ^4.7.0
|
|
80
80
|
package_info_plus: ^8.3.0
|
|
81
81
|
path_provider: ^2.1.5
|
|
82
|
+
# Pin the Apple impl of path_provider below 2.6.0/2.5.0. Those versions pull in
|
|
83
|
+
# objective_c via "native assets", whose build hook is unquoted and breaks every
|
|
84
|
+
# build on Windows when the username has a space (C:\Users\John Silva) — even a
|
|
85
|
+
# Chrome/web run, where the Apple-only hook is dead weight. 2.5.1 is the last
|
|
86
|
+
# release without objective_c (added in 2.5.0, reverted in 2.5.1, re-added in
|
|
87
|
+
# 2.6.0). Drop this pin once the upstream web-target regression is fixed.
|
|
88
|
+
path_provider_foundation: 2.5.1
|
|
82
89
|
permission_handler: ^12.0.1
|
|
83
90
|
provider: ^6.1.0
|
|
84
91
|
pub_semver: ^2.2.0
|