@geekmidas/cli 10.0.0-alpha.110 → 10.0.0-alpha.111

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.
Files changed (117) hide show
  1. package/dist/{GoDaddyProvider-CjeOduia.mjs → GoDaddyProvider-CPabc3XW.mjs} +2 -2
  2. package/dist/{GoDaddyProvider-CjeOduia.mjs.map → GoDaddyProvider-CPabc3XW.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-qNUzTXI7.cjs → GoDaddyProvider-DWrDIXzQ.cjs} +2 -2
  4. package/dist/{GoDaddyProvider-qNUzTXI7.cjs.map → GoDaddyProvider-DWrDIXzQ.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-pgscYsiG.mjs → LegacyStateStore-BdXoAlYY.mjs} +3 -3
  6. package/dist/{LegacyStateStore-pgscYsiG.mjs.map → LegacyStateStore-BdXoAlYY.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-MxiSu7KJ.cjs → LegacyStateStore-fsyPGBWu.cjs} +3 -3
  8. package/dist/{LegacyStateStore-MxiSu7KJ.cjs.map → LegacyStateStore-fsyPGBWu.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-Bm8Vmwjb.cjs → LocalStateStore-AtviUBky.cjs} +2 -2
  10. package/dist/{LocalStateStore-Bm8Vmwjb.cjs.map → LocalStateStore-AtviUBky.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-BcvyKbAw.mjs → LocalStateStore-BrAX5yy7.mjs} +2 -2
  12. package/dist/{LocalStateStore-BcvyKbAw.mjs.map → LocalStateStore-BrAX5yy7.mjs.map} +1 -1
  13. package/dist/{S3StateStore-H2XsAzpJ.mjs → S3StateStore-Cx7njkBz.mjs} +2 -2
  14. package/dist/{S3StateStore-H2XsAzpJ.mjs.map → S3StateStore-Cx7njkBz.mjs.map} +1 -1
  15. package/dist/{S3StateStore-DXHSBVsV.cjs → S3StateStore-tn5g80Sx.cjs} +2 -2
  16. package/dist/{S3StateStore-DXHSBVsV.cjs.map → S3StateStore-tn5g80Sx.cjs.map} +1 -1
  17. package/dist/{SSMStateStore-BLPSNl-U.cjs → SSMStateStore-CAFde-Fq.cjs} +2 -2
  18. package/dist/{SSMStateStore-BLPSNl-U.cjs.map → SSMStateStore-CAFde-Fq.cjs.map} +1 -1
  19. package/dist/{SSMStateStore-CENciQEM.mjs → SSMStateStore-sqZ3oDmn.mjs} +2 -2
  20. package/dist/{SSMStateStore-CENciQEM.mjs.map → SSMStateStore-sqZ3oDmn.mjs.map} +1 -1
  21. package/dist/{add-Cdg1kEqN.cjs → add-Cs8yT5iH.cjs} +3 -3
  22. package/dist/{add-Cdg1kEqN.cjs.map → add-Cs8yT5iH.cjs.map} +1 -1
  23. package/dist/{add-snhdVmR-.mjs → add-DJE7zqJ4.mjs} +3 -3
  24. package/dist/{add-snhdVmR-.mjs.map → add-DJE7zqJ4.mjs.map} +1 -1
  25. package/dist/{bundler-Wd9H4QLJ.mjs → bundler-DrySVbXF.mjs} +5 -5
  26. package/dist/{bundler-Wd9H4QLJ.mjs.map → bundler-DrySVbXF.mjs.map} +1 -1
  27. package/dist/{bundler-B4GDxImV.cjs → bundler-NrtItVBp.cjs} +5 -5
  28. package/dist/{bundler-B4GDxImV.cjs.map → bundler-NrtItVBp.cjs.map} +1 -1
  29. package/dist/{cli-BzrDSNSg.mjs → cli-Br5ApIgR.mjs} +4 -4
  30. package/dist/{cli-BzrDSNSg.mjs.map → cli-Br5ApIgR.mjs.map} +1 -1
  31. package/dist/{cli-BZYz55tO.cjs → cli-CHzpFBh-.cjs} +2 -2
  32. package/dist/{cli-BZYz55tO.cjs.map → cli-CHzpFBh-.cjs.map} +1 -1
  33. package/dist/{compose-DN9zySvQ.cjs → compose-ByIbIrzs.cjs} +20 -5
  34. package/dist/compose-ByIbIrzs.cjs.map +1 -0
  35. package/dist/{compose-Gsd1Nq7R.mjs → compose-CDC2bCme.mjs} +20 -5
  36. package/dist/compose-CDC2bCme.mjs.map +1 -0
  37. package/dist/deploy/public.cjs +6 -6
  38. package/dist/deploy/public.mjs +6 -6
  39. package/dist/{deploy-H2S8_mTx.mjs → deploy-DLq9-m6H.mjs} +5 -5
  40. package/dist/{deploy-H2S8_mTx.mjs.map → deploy-DLq9-m6H.mjs.map} +1 -1
  41. package/dist/{deploy-CFvq8daZ.cjs → deploy-Dl_NavYE.cjs} +5 -5
  42. package/dist/{deploy-CFvq8daZ.cjs.map → deploy-Dl_NavYE.cjs.map} +1 -1
  43. package/dist/{dns-BdETzP10.cjs → dns-C2TGXUF4.cjs} +2 -2
  44. package/dist/{dns-BdETzP10.cjs.map → dns-C2TGXUF4.cjs.map} +1 -1
  45. package/dist/{dns-pFMShDqW.mjs → dns-pzftumxg.mjs} +2 -2
  46. package/dist/{dns-pFMShDqW.mjs.map → dns-pzftumxg.mjs.map} +1 -1
  47. package/dist/{dokploy-oDJbnvCb.mjs → dokploy-B7-OwY2s.mjs} +2 -2
  48. package/dist/{dokploy-oDJbnvCb.mjs.map → dokploy-B7-OwY2s.mjs.map} +1 -1
  49. package/dist/{dokploy-HioAjP7K.cjs → dokploy-OqBh8G4g.cjs} +2 -2
  50. package/dist/{dokploy-HioAjP7K.cjs.map → dokploy-OqBh8G4g.cjs.map} +1 -1
  51. package/dist/eas-6ezdJtry.mjs +316 -0
  52. package/dist/eas-6ezdJtry.mjs.map +1 -0
  53. package/dist/eas-BkLA60P2.cjs +319 -0
  54. package/dist/eas-BkLA60P2.cjs.map +1 -0
  55. package/dist/{encryption-D5pDJyp2.mjs → encryption-DJv_jEXi.mjs} +2 -2
  56. package/dist/{encryption-D5pDJyp2.mjs.map → encryption-DJv_jEXi.mjs.map} +1 -1
  57. package/dist/{encryption-DVCxD-gV.cjs → encryption-DoVnVU82.cjs} +2 -2
  58. package/dist/{encryption-DVCxD-gV.cjs.map → encryption-DoVnVU82.cjs.map} +1 -1
  59. package/dist/{engine-BaJGPzte.cjs → engine-CJhQb50j.cjs} +3 -3
  60. package/dist/{engine-BaJGPzte.cjs.map → engine-CJhQb50j.cjs.map} +1 -1
  61. package/dist/{engine-CfAKgr2_.mjs → engine-Db71-OZL.mjs} +3 -3
  62. package/dist/{engine-CfAKgr2_.mjs.map → engine-Db71-OZL.mjs.map} +1 -1
  63. package/dist/{github-E-ZxqFPq.mjs → github-D2eABlxM.mjs} +4 -4
  64. package/dist/{github-E-ZxqFPq.mjs.map → github-D2eABlxM.mjs.map} +1 -1
  65. package/dist/{github-D902Vwr9.cjs → github-DDOHjdS9.cjs} +4 -4
  66. package/dist/{github-D902Vwr9.cjs.map → github-DDOHjdS9.cjs.map} +1 -1
  67. package/dist/googlePlay-DNgybe5t.cjs +931 -0
  68. package/dist/googlePlay-DNgybe5t.cjs.map +1 -0
  69. package/dist/googlePlay-DY9vZQ1p.mjs +931 -0
  70. package/dist/googlePlay-DY9vZQ1p.mjs.map +1 -0
  71. package/dist/index.cjs +157 -65
  72. package/dist/index.cjs.map +1 -1
  73. package/dist/index.mjs +158 -67
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/{migrate-DCXwHcWX.mjs → migrate-B9GswIAr.mjs} +3 -3
  76. package/dist/{migrate-DCXwHcWX.mjs.map → migrate-B9GswIAr.mjs.map} +1 -1
  77. package/dist/{migrate-BZ0GnSZQ.mjs → migrate-CQUGbptv.mjs} +2 -2
  78. package/dist/{migrate-BZ0GnSZQ.mjs.map → migrate-CQUGbptv.mjs.map} +1 -1
  79. package/dist/{migrate-Df81ew0k.cjs → migrate-GLBZlrlJ.cjs} +2 -2
  80. package/dist/{migrate-Df81ew0k.cjs.map → migrate-GLBZlrlJ.cjs.map} +1 -1
  81. package/dist/{migrate-CTqhWtev.cjs → migrate-bDQzFvBN.cjs} +2 -2
  82. package/dist/{migrate-CTqhWtev.cjs.map → migrate-bDQzFvBN.cjs.map} +1 -1
  83. package/dist/publish-B-EIjfQ0.mjs +123 -0
  84. package/dist/publish-B-EIjfQ0.mjs.map +1 -0
  85. package/dist/publish-CPgSbtcs.cjs +124 -0
  86. package/dist/publish-CPgSbtcs.cjs.map +1 -0
  87. package/dist/{resolve-BMpPd8x3.mjs → resolve-CxDqDmCD.mjs} +3 -3
  88. package/dist/{resolve-BMpPd8x3.mjs.map → resolve-CxDqDmCD.mjs.map} +1 -1
  89. package/dist/{resolve-D043YmW9.cjs → resolve-wG49Woex.cjs} +3 -3
  90. package/dist/{resolve-D043YmW9.cjs.map → resolve-wG49Woex.cjs.map} +1 -1
  91. package/dist/{rollback-CDs_XNb0.mjs → rollback-BQgEdr_p.mjs} +4 -4
  92. package/dist/{rollback-CDs_XNb0.mjs.map → rollback-BQgEdr_p.mjs.map} +1 -1
  93. package/dist/{rollback-CBiYsQ3T.cjs → rollback-Cw-Z0mWn.cjs} +4 -4
  94. package/dist/{rollback-CBiYsQ3T.cjs.map → rollback-Cw-Z0mWn.cjs.map} +1 -1
  95. package/dist/{seeds-BjcO4DDa.mjs → seeds-CIk0l4SC.mjs} +13 -10
  96. package/dist/seeds-CIk0l4SC.mjs.map +1 -0
  97. package/dist/{seeds-DNqRA1aQ.cjs → seeds-Dk8YkLkM.cjs} +18 -15
  98. package/dist/seeds-Dk8YkLkM.cjs.map +1 -0
  99. package/dist/{sniffer-DipSS8r8.cjs → sniffer-2aK7cB2u.cjs} +6 -6
  100. package/dist/{sniffer-DipSS8r8.cjs.map → sniffer-2aK7cB2u.cjs.map} +1 -1
  101. package/dist/{sniffer-b0XPmSyT.mjs → sniffer-CABMO-Bh.mjs} +6 -6
  102. package/dist/{sniffer-b0XPmSyT.mjs.map → sniffer-CABMO-Bh.mjs.map} +1 -1
  103. package/dist/{stage-DrDmZusd.mjs → stage-DjxR_7C0.mjs} +69 -33
  104. package/dist/stage-DjxR_7C0.mjs.map +1 -0
  105. package/dist/{stage-C_y2UovO.cjs → stage-DrCS0vKD.cjs} +79 -32
  106. package/dist/stage-DrCS0vKD.cjs.map +1 -0
  107. package/dist/target/public.cjs +1 -1
  108. package/dist/target/public.mjs +1 -1
  109. package/dist/{terminal-CvRLxz6g.mjs → terminal-mXM-Lqc6.mjs} +2 -2
  110. package/dist/{terminal-CvRLxz6g.mjs.map → terminal-mXM-Lqc6.mjs.map} +1 -1
  111. package/package.json +15 -15
  112. package/dist/compose-DN9zySvQ.cjs.map +0 -1
  113. package/dist/compose-Gsd1Nq7R.mjs.map +0 -1
  114. package/dist/seeds-BjcO4DDa.mjs.map +0 -1
  115. package/dist/seeds-DNqRA1aQ.cjs.map +0 -1
  116. package/dist/stage-C_y2UovO.cjs.map +0 -1
  117. package/dist/stage-DrDmZusd.mjs.map +0 -1
@@ -0,0 +1,316 @@
1
+ import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
+ import { t as appKey } from "./derive-B4vDpzgP.mjs";
3
+ import { n as mobileStage } from "./index.mjs";
4
+ import { t as loadStage } from "./stage-DjxR_7C0.mjs";
5
+ import { delimiter, join } from "node:path";
6
+ import { existsSync } from "node:fs";
7
+ import { readFile, writeFile } from "node:fs/promises";
8
+ import { spawn } from "node:child_process";
9
+ //#region src/mobile/eas.ts
10
+ /**
11
+ * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a
12
+ * deployed stage, from what the workspace declares — so a store build, which
13
+ * EAS runs on its own machines, outside gkm, is built with the same values
14
+ * the stage's servers were deployed with.
15
+ *
16
+ * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS
17
+ * environment and listens on its channel; its submit profile
18
+ * (`submit.<stage>.android`) has its Play track and release status — see
19
+ * `mobileStage`. Every other field is left as it is.
20
+ * - **The EAS environment**: each value the app's build reads for the stage
21
+ * (`StackMobileApp.values`) — its scheme, its edges' public URLs under
22
+ * `EXPO_PUBLIC_`, and every stage secret already named so — set with
23
+ * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.
24
+ * Nothing else is written: no server key reaches EAS.
25
+ */
26
+ const logger = console;
27
+ /** The workspace declares no `MobileApp`: nothing to set up. */
28
+ var NoMobileApp = class extends GkmError {
29
+ constructor() {
30
+ super("The workspace declares no MobileApp, so there is no EAS project to set up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).");
31
+ this.name = "NoMobileApp";
32
+ }
33
+ };
34
+ /** Several `MobileApp`s, and nothing to say which. */
35
+ var MobileAppNotChosen = class extends GkmError {
36
+ apps;
37
+ constructor(apps) {
38
+ super(`The workspace declares ${apps.length} mobile apps (${apps.join(", ")}). Run from one's directory, or name it: --app ${apps[0]}.`);
39
+ this.apps = apps;
40
+ this.name = "MobileAppNotChosen";
41
+ }
42
+ };
43
+ /** `--app` names no `MobileApp`. */
44
+ var MobileAppUnknown = class extends GkmError {
45
+ app;
46
+ apps;
47
+ constructor(app, apps) {
48
+ super(`'${app}' is not one of the workspace's mobile apps (${apps.join(", ")}).`);
49
+ this.app = app;
50
+ this.apps = apps;
51
+ this.name = "MobileAppUnknown";
52
+ }
53
+ };
54
+ /** `eas` is not installed where the app is. */
55
+ var EasCliMissing = class extends GkmError {
56
+ dir;
57
+ constructor(dir) {
58
+ super(`No \`eas\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli (or install it globally: npm install -g eas-cli).`);
59
+ this.dir = dir;
60
+ this.name = "EasCliMissing";
61
+ }
62
+ };
63
+ /** `eas` refused a command. */
64
+ var EasCommandFailed = class extends GkmError {
65
+ command;
66
+ code;
67
+ stderr;
68
+ constructor(command, code, stderr) {
69
+ super(`\`eas ${command}\` failed (exit ${code}): ${stderr.trim() || "it said nothing"}. In CI, EXPO_TOKEN must be set; on this machine, \`eas login\`.`);
70
+ this.command = command;
71
+ this.code = code;
72
+ this.stderr = stderr;
73
+ this.name = "EasCommandFailed";
74
+ }
75
+ };
76
+ /**
77
+ * Run `eas` in `dir`: the app's own, else the workspace's, else the one on
78
+ * PATH. What it prints on stdout is answered; a failure is
79
+ * {@link EasCommandFailed} with what it said.
80
+ */
81
+ function runEas(root, dir, args, options = {}) {
82
+ return new Promise((resolve, reject) => {
83
+ const child = spawn("eas", [...args, "--non-interactive"], {
84
+ cwd: dir,
85
+ stdio: [
86
+ "ignore",
87
+ options.inherit ? "inherit" : "pipe",
88
+ "pipe"
89
+ ],
90
+ env: {
91
+ ...process.env,
92
+ ...options.env,
93
+ PATH: [
94
+ join(dir, "node_modules", ".bin"),
95
+ join(root, "node_modules", ".bin"),
96
+ process.env.PATH
97
+ ].join(delimiter)
98
+ }
99
+ });
100
+ let stdout = "";
101
+ let stderr = "";
102
+ child.stdout?.on("data", (chunk) => {
103
+ stdout += chunk.toString();
104
+ });
105
+ child.stderr?.on("data", (chunk) => {
106
+ stderr += chunk.toString();
107
+ if (options.inherit) process.stderr.write(chunk);
108
+ });
109
+ const command = args.slice(0, 3).join(" ");
110
+ child.on("error", (error) => reject(error.code === "ENOENT" ? new EasCliMissing(dir) : new EasCommandFailed(command, null, error.message)));
111
+ child.on("close", (code) => code === 0 ? resolve(stdout) : reject(new EasCommandFailed(command, code, stderr)));
112
+ });
113
+ }
114
+ /** The JSON `eas … --json` printed, read for `what`. */
115
+ function easJson(output, what) {
116
+ try {
117
+ return JSON.parse(output);
118
+ } catch {
119
+ throw new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);
120
+ }
121
+ }
122
+ /**
123
+ * The real one — never a prompt (`--non-interactive`). `env` is what the
124
+ * app's `app.config.ts` reads for the stage: every `eas` command evaluates
125
+ * it, and one that reads a value it lacks fails before it starts — and a
126
+ * fingerprint computed here must match the one EAS computes with the
127
+ * stage's values.
128
+ */
129
+ function easCli(root, env = {}) {
130
+ const run = (dir, args, options = {}) => runEas(root, dir, args, {
131
+ ...options,
132
+ env
133
+ });
134
+ return {
135
+ async setEnv(dir, { environment, name, value }) {
136
+ await run(dir, [
137
+ "env:set",
138
+ "--environment",
139
+ environment,
140
+ "--name",
141
+ name,
142
+ "--value",
143
+ value,
144
+ "--visibility",
145
+ "plaintext",
146
+ "--scope",
147
+ "project"
148
+ ]);
149
+ },
150
+ async fingerprint(dir, { platform, profile }) {
151
+ const hash = easJson(await run(dir, [
152
+ "fingerprint:generate",
153
+ "--platform",
154
+ platform,
155
+ "--build-profile",
156
+ profile,
157
+ "--json"
158
+ ]), "fingerprint:generate").hash;
159
+ if (typeof hash !== "string") throw new EasCommandFailed("fingerprint:generate", 0, "no hash");
160
+ return hash;
161
+ },
162
+ async storeBuild(dir, { platform, profile, fingerprint }) {
163
+ const builds = easJson(await run(dir, [
164
+ "build:list",
165
+ "--platform",
166
+ platform,
167
+ "--build-profile",
168
+ profile,
169
+ "--fingerprint-hash",
170
+ fingerprint,
171
+ "--status",
172
+ "finished",
173
+ "--distribution",
174
+ "store",
175
+ "--limit",
176
+ "1",
177
+ "--json"
178
+ ]), "build:list");
179
+ if (!Array.isArray(builds)) return void 0;
180
+ const id = builds[0]?.id;
181
+ return typeof id === "string" ? id : void 0;
182
+ },
183
+ async build(dir, { platform, profile, submit, wait }) {
184
+ await run(dir, [
185
+ "build",
186
+ "--platform",
187
+ platform,
188
+ "--profile",
189
+ profile,
190
+ ...submit ? ["--auto-submit"] : [],
191
+ ...wait ? [] : ["--no-wait"]
192
+ ], { inherit: true });
193
+ },
194
+ async update(dir, { platform, channel, environment, message }) {
195
+ await run(dir, [
196
+ "update",
197
+ "--platform",
198
+ platform,
199
+ "--channel",
200
+ channel,
201
+ "--environment",
202
+ environment,
203
+ "--message",
204
+ message
205
+ ], { inherit: true });
206
+ }
207
+ };
208
+ }
209
+ /**
210
+ * The mobile app a command is about: the one `--app` names, else the one
211
+ * this directory is, else the only one.
212
+ *
213
+ * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}
214
+ */
215
+ function chooseMobileApp(manifest, app, here) {
216
+ const declarations = Object.values(manifest).filter((declaration) => declaration.kind === "mobile-app");
217
+ const names = declarations.map((declaration) => appKey(declaration.id));
218
+ if (declarations.length === 0) throw new NoMobileApp();
219
+ const name = app ?? (here && names.includes(here) ? here : names.length === 1 ? names[0] : void 0);
220
+ if (!name) throw new MobileAppNotChosen(names);
221
+ const declaration = declarations.find((d) => appKey(d.id) === name);
222
+ if (!declaration) throw new MobileAppUnknown(name, names);
223
+ return declaration;
224
+ }
225
+ /**
226
+ * `eas.json` with the stage's build and submit profiles as `stage` says:
227
+ * every field gkm does not decide is kept.
228
+ */
229
+ function withStageProfiles(easJson, stage) {
230
+ const build = easJson.build?.[stage.stage] ?? {};
231
+ const submit = easJson.submit?.[stage.stage] ?? {};
232
+ const android = submit.android ?? {};
233
+ return {
234
+ ...easJson,
235
+ build: {
236
+ ...easJson.build,
237
+ [stage.stage]: {
238
+ ...build,
239
+ environment: stage.environment,
240
+ channel: stage.channel
241
+ }
242
+ },
243
+ submit: {
244
+ ...easJson.submit,
245
+ [stage.stage]: {
246
+ ...submit,
247
+ android: {
248
+ ...android,
249
+ track: stage.android.track,
250
+ releaseStatus: stage.android.releaseStatus
251
+ }
252
+ }
253
+ }
254
+ };
255
+ }
256
+ /**
257
+ * Set up a mobile app's EAS project for a deployed stage.
258
+ *
259
+ * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},
260
+ * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and
261
+ * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},
262
+ * {ExecStageNotDeployed}
263
+ */
264
+ async function easSync(options, eas) {
265
+ const loaded = await loadStage(options);
266
+ const { workspace, manifest, stack } = loaded;
267
+ const declaration = chooseMobileApp(manifest, options.app, loaded.appName);
268
+ const name = appKey(declaration.id);
269
+ const stage = mobileStage(workspace, declaration, options.stage);
270
+ const values = stack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};
271
+ const dir = join(workspace.root, declaration.app.path);
272
+ const path = join(dir, "eas.json");
273
+ const before = existsSync(path) ? JSON.parse(await readFile(path, "utf8")) : {};
274
+ const after = withStageProfiles(before, stage);
275
+ const easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);
276
+ const where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;
277
+ if (options.dryRun) {
278
+ logger.log(`📱 ${where} (dry run)`);
279
+ if (easJsonChanged) logger.log(` would write build.${stage.stage} and submit.${stage.stage} in ${path}`);
280
+ for (const [key, value] of Object.entries(values)) logger.log(` would set ${key}=${value}`);
281
+ return {
282
+ app: name,
283
+ root: workspace.root,
284
+ dir,
285
+ stage,
286
+ values,
287
+ easJsonChanged
288
+ };
289
+ }
290
+ logger.log(`📱 ${where}`);
291
+ if (easJsonChanged) {
292
+ await writeFile(path, `${JSON.stringify(after, null, 2)}\n`);
293
+ logger.log(` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`);
294
+ }
295
+ const cli = eas ?? easCli(workspace.root, values);
296
+ for (const [key, value] of Object.entries(values)) {
297
+ await cli.setEnv(dir, {
298
+ environment: stage.environment,
299
+ name: key,
300
+ value
301
+ });
302
+ logger.log(` set ${key}`);
303
+ }
304
+ return {
305
+ app: name,
306
+ root: workspace.root,
307
+ dir,
308
+ stage,
309
+ values,
310
+ easJsonChanged
311
+ };
312
+ }
313
+ //#endregion
314
+ export { chooseMobileApp, easCli, easSync, withStageProfiles };
315
+
316
+ //# sourceMappingURL=eas-6ezdJtry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eas-6ezdJtry.mjs","names":[],"sources":["../src/mobile/eas.ts"],"sourcesContent":["/**\n * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a\n * deployed stage, from what the workspace declares — so a store build, which\n * EAS runs on its own machines, outside gkm, is built with the same values\n * the stage's servers were deployed with.\n *\n * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS\n * environment and listens on its channel; its submit profile\n * (`submit.<stage>.android`) has its Play track and release status — see\n * `mobileStage`. Every other field is left as it is.\n * - **The EAS environment**: each value the app's build reads for the stage\n * (`StackMobileApp.values`) — its scheme, its edges' public URLs under\n * `EXPO_PUBLIC_`, and every stage secret already named so — set with\n * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.\n * Nothing else is written: no server key reaches EAS.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, join } from 'node:path';\nimport type {\n\tConstructManifest,\n\tMobileAppDeclaration,\n} from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { loadStage } from '../exec/stage.js';\nimport { appKey } from '../workspace/derive.js';\nimport { type MobileStage, mobileStage } from './stage.js';\n\nconst logger = console;\n\n/** The workspace declares no `MobileApp`: nothing to set up. */\nexport class NoMobileApp extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'The workspace declares no MobileApp, so there is no EAS project to set ' +\n\t\t\t\t\"up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).\",\n\t\t);\n\t\tthis.name = 'NoMobileApp';\n\t}\n}\n\n/** Several `MobileApp`s, and nothing to say which. */\nexport class MobileAppNotChosen extends GkmError {\n\tconstructor(readonly apps: readonly string[]) {\n\t\tsuper(\n\t\t\t`The workspace declares ${apps.length} mobile apps (${apps.join(', ')}). ` +\n\t\t\t\t`Run from one's directory, or name it: --app ${apps[0]}.`,\n\t\t);\n\t\tthis.name = 'MobileAppNotChosen';\n\t}\n}\n\n/** `--app` names no `MobileApp`. */\nexport class MobileAppUnknown extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${app}' is not one of the workspace's mobile apps (${apps.join(', ')}).`,\n\t\t);\n\t\tthis.name = 'MobileAppUnknown';\n\t}\n}\n\n/** `eas` is not installed where the app is. */\nexport class EasCliMissing extends GkmError {\n\tconstructor(readonly dir: string) {\n\t\tsuper(\n\t\t\t`No \\`eas\\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli ` +\n\t\t\t\t'(or install it globally: npm install -g eas-cli).',\n\t\t);\n\t\tthis.name = 'EasCliMissing';\n\t}\n}\n\n/** `eas` refused a command. */\nexport class EasCommandFailed extends GkmError {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly code: number | null,\n\t\treadonly stderr: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`eas ${command}\\` failed (exit ${code}): ${stderr.trim() || 'it said nothing'}. ` +\n\t\t\t\t'In CI, EXPO_TOKEN must be set; on this machine, `eas login`.',\n\t\t);\n\t\tthis.name = 'EasCommandFailed';\n\t}\n}\n\n/** A platform EAS builds for. */\nexport type EasPlatform = 'ios' | 'android';\n\n/** What gkm asks of EAS, so tests can stand in. */\nexport interface EasCli {\n\t/** Create or update a variable in one of the project's environments. */\n\tsetEnv(\n\t\tdir: string,\n\t\tvariable: { environment: string; name: string; value: string },\n\t): Promise<void>;\n\t/**\n\t * The fingerprint of the app's native code, as a build with `profile`\n\t * would compute it — what its runtime version is.\n\t */\n\tfingerprint(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string },\n\t): Promise<string>;\n\t/** A finished store build of `profile` with that fingerprint, if any. */\n\tstoreBuild(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string; fingerprint: string },\n\t): Promise<string | undefined>;\n\t/** Start a store build of `profile`, submitted to the store when asked. */\n\tbuild(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tprofile: string;\n\t\t\tsubmit: boolean;\n\t\t\twait: boolean;\n\t\t},\n\t): Promise<void>;\n\t/** Publish an update of the app's JavaScript on a channel. */\n\tupdate(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tchannel: string;\n\t\t\tenvironment: string;\n\t\t\tmessage: string;\n\t\t},\n\t): Promise<void>;\n}\n\n/**\n * Run `eas` in `dir`: the app's own, else the workspace's, else the one on\n * PATH. What it prints on stdout is answered; a failure is\n * {@link EasCommandFailed} with what it said.\n */\nfunction runEas(\n\troot: string,\n\tdir: string,\n\targs: readonly string[],\n\toptions: { inherit?: boolean; env?: Readonly<Record<string, string>> } = {},\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('eas', [...args, '--non-interactive'], {\n\t\t\tcwd: dir,\n\t\t\t// A build's or an update's progress is the user's to read.\n\t\t\tstdio: ['ignore', options.inherit ? 'inherit' : 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...options.env,\n\t\t\t\tPATH: [\n\t\t\t\t\tjoin(dir, 'node_modules', '.bin'),\n\t\t\t\t\tjoin(root, 'node_modules', '.bin'),\n\t\t\t\t\tprocess.env.PATH,\n\t\t\t\t].join(delimiter),\n\t\t\t},\n\t\t});\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.stderr?.on('data', (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString();\n\t\t\tif (options.inherit) process.stderr.write(chunk);\n\t\t});\n\t\tconst command = args.slice(0, 3).join(' ');\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t? new EasCliMissing(dir)\n\t\t\t\t\t: new EasCommandFailed(command, null, error.message),\n\t\t\t),\n\t\t);\n\t\tchild.on('close', (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolve(stdout)\n\t\t\t\t: reject(new EasCommandFailed(command, code, stderr)),\n\t\t);\n\t});\n}\n\n/** The JSON `eas … --json` printed, read for `what`. */\nfunction easJson(output: string, what: string): unknown {\n\ttry {\n\t\treturn JSON.parse(output);\n\t} catch {\n\t\tthrow new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);\n\t}\n}\n\n/**\n * The real one — never a prompt (`--non-interactive`). `env` is what the\n * app's `app.config.ts` reads for the stage: every `eas` command evaluates\n * it, and one that reads a value it lacks fails before it starts — and a\n * fingerprint computed here must match the one EAS computes with the\n * stage's values.\n */\nexport function easCli(\n\troot: string,\n\tenv: Readonly<Record<string, string>> = {},\n): EasCli {\n\tconst run = (\n\t\tdir: string,\n\t\targs: readonly string[],\n\t\toptions: { inherit?: boolean } = {},\n\t) => runEas(root, dir, args, { ...options, env });\n\treturn {\n\t\tasync setEnv(dir, { environment, name, value }) {\n\t\t\tawait run(dir, [\n\t\t\t\t'env:set',\n\t\t\t\t'--environment',\n\t\t\t\tenvironment,\n\t\t\t\t'--name',\n\t\t\t\tname,\n\t\t\t\t'--value',\n\t\t\t\tvalue,\n\t\t\t\t'--visibility',\n\t\t\t\t'plaintext',\n\t\t\t\t'--scope',\n\t\t\t\t'project',\n\t\t\t]);\n\t\t},\n\n\t\tasync fingerprint(dir, { platform, profile }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'fingerprint:generate',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst hash = (\n\t\t\t\teasJson(output, 'fingerprint:generate') as { hash?: unknown }\n\t\t\t).hash;\n\t\t\tif (typeof hash !== 'string') {\n\t\t\t\tthrow new EasCommandFailed('fingerprint:generate', 0, 'no hash');\n\t\t\t}\n\t\t\treturn hash;\n\t\t},\n\n\t\tasync storeBuild(dir, { platform, profile, fingerprint }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'build:list',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--fingerprint-hash',\n\t\t\t\tfingerprint,\n\t\t\t\t'--status',\n\t\t\t\t'finished',\n\t\t\t\t'--distribution',\n\t\t\t\t'store',\n\t\t\t\t'--limit',\n\t\t\t\t'1',\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst builds = easJson(output, 'build:list');\n\t\t\tif (!Array.isArray(builds)) return undefined;\n\t\t\tconst id = (builds[0] as { id?: unknown } | undefined)?.id;\n\t\t\treturn typeof id === 'string' ? id : undefined;\n\t\t},\n\n\t\tasync build(dir, { platform, profile, submit, wait }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'build',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--profile',\n\t\t\t\t\tprofile,\n\t\t\t\t\t...(submit ? ['--auto-submit'] : []),\n\t\t\t\t\t...(wait ? [] : ['--no-wait']),\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\n\t\tasync update(dir, { platform, channel, environment, message }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'update',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--channel',\n\t\t\t\t\tchannel,\n\t\t\t\t\t'--environment',\n\t\t\t\t\tenvironment,\n\t\t\t\t\t'--message',\n\t\t\t\t\tmessage,\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * The mobile app a command is about: the one `--app` names, else the one\n * this directory is, else the only one.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}\n */\nexport function chooseMobileApp(\n\tmanifest: ConstructManifest,\n\tapp: string | undefined,\n\there: string | undefined,\n): MobileAppDeclaration {\n\tconst declarations = Object.values(manifest).filter(\n\t\t(declaration): declaration is MobileAppDeclaration =>\n\t\t\tdeclaration.kind === 'mobile-app',\n\t);\n\tconst names = declarations.map((declaration) => appKey(declaration.id));\n\tif (declarations.length === 0) throw new NoMobileApp();\n\tconst name =\n\t\tapp ??\n\t\t(here && names.includes(here)\n\t\t\t? here\n\t\t\t: names.length === 1\n\t\t\t\t? names[0]\n\t\t\t\t: undefined);\n\tif (!name) throw new MobileAppNotChosen(names);\n\tconst declaration = declarations.find((d) => appKey(d.id) === name);\n\tif (!declaration) throw new MobileAppUnknown(name, names);\n\treturn declaration;\n}\n\n/** `eas.json`, as far as gkm writes it. */\ninterface EasJson {\n\tbuild?: Record<string, Record<string, unknown>>;\n\tsubmit?: Record<string, Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * `eas.json` with the stage's build and submit profiles as `stage` says:\n * every field gkm does not decide is kept.\n */\nexport function withStageProfiles(\n\teasJson: EasJson,\n\tstage: MobileStage,\n): EasJson {\n\tconst build = easJson.build?.[stage.stage] ?? {};\n\tconst submit = easJson.submit?.[stage.stage] ?? {};\n\tconst android = (submit.android as Record<string, unknown> | undefined) ?? {};\n\treturn {\n\t\t...easJson,\n\t\tbuild: {\n\t\t\t...easJson.build,\n\t\t\t[stage.stage]: {\n\t\t\t\t...build,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tchannel: stage.channel,\n\t\t\t},\n\t\t},\n\t\tsubmit: {\n\t\t\t...easJson.submit,\n\t\t\t[stage.stage]: {\n\t\t\t\t...submit,\n\t\t\t\tandroid: {\n\t\t\t\t\t...android,\n\t\t\t\t\ttrack: stage.android.track,\n\t\t\t\t\treleaseStatus: stage.android.releaseStatus,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport interface EasSyncOptions {\n\tstage: string;\n\t/** The mobile app, by its app key; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n\t/** Say what would be written, and write nothing. */\n\tdryRun?: boolean;\n}\n\n/** What a sync wrote — or, dry, would. */\nexport interface EasSyncResult {\n\tapp: string;\n\t/** The workspace's root, where its `eas` may be installed. */\n\troot: string;\n\tdir: string;\n\tstage: MobileStage;\n\tvalues: Record<string, string>;\n\t/** Whether `eas.json` changed. */\n\teasJsonChanged: boolean;\n}\n\n/**\n * Set up a mobile app's EAS project for a deployed stage.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},\n * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and\n * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},\n * {ExecStageNotDeployed}\n */\nexport async function easSync(\n\toptions: EasSyncOptions,\n\teas?: EasCli,\n): Promise<EasSyncResult> {\n\tconst loaded = await loadStage(options);\n\tconst { workspace, manifest, stack } = loaded;\n\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = appKey(declaration.id);\n\n\tconst stage = mobileStage(workspace, declaration, options.stage);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};\n\tconst dir = join(workspace.root, declaration.app.path);\n\n\tconst path = join(dir, 'eas.json');\n\tconst before = existsSync(path)\n\t\t? (JSON.parse(await readFile(path, 'utf8')) as EasJson)\n\t\t: {};\n\tconst after = withStageProfiles(before, stage);\n\tconst easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);\n\n\tconst where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;\n\tif (options.dryRun) {\n\t\tlogger.log(`📱 ${where} (dry run)`);\n\t\tif (easJsonChanged) {\n\t\t\tlogger.log(\n\t\t\t\t` would write build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t\t);\n\t\t}\n\t\tfor (const [key, value] of Object.entries(values)) {\n\t\t\tlogger.log(` would set ${key}=${value}`);\n\t\t}\n\t\treturn {\n\t\t\tapp: name,\n\t\t\troot: workspace.root,\n\t\t\tdir,\n\t\t\tstage,\n\t\t\tvalues,\n\t\t\teasJsonChanged,\n\t\t};\n\t}\n\n\tlogger.log(`📱 ${where}`);\n\tif (easJsonChanged) {\n\t\tawait writeFile(path, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(\n\t\t\t` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t);\n\t}\n\tconst cli = eas ?? easCli(workspace.root, values);\n\tfor (const [key, value] of Object.entries(values)) {\n\t\tawait cli.setEnv(dir, { environment: stage.environment, name: key, value });\n\t\tlogger.log(` set ${key}`);\n\t}\n\treturn {\n\t\tapp: name,\n\t\troot: workspace.root,\n\t\tdir,\n\t\tstage,\n\t\tvalues,\n\t\teasJsonChanged,\n\t};\n}\n"],"mappings":"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"}
@@ -0,0 +1,319 @@
1
+ const require_errors = require("./errors-DMC4Umsb.cjs");
2
+ const require_derive = require("./derive-DAbo49sm.cjs");
3
+ const require_index = require("./index.cjs");
4
+ const require_stage = require("./stage-DrCS0vKD.cjs");
5
+ let node_path = require("node:path");
6
+ let node_fs = require("node:fs");
7
+ let node_fs_promises = require("node:fs/promises");
8
+ let node_child_process = require("node:child_process");
9
+ //#region src/mobile/eas.ts
10
+ /**
11
+ * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a
12
+ * deployed stage, from what the workspace declares — so a store build, which
13
+ * EAS runs on its own machines, outside gkm, is built with the same values
14
+ * the stage's servers were deployed with.
15
+ *
16
+ * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS
17
+ * environment and listens on its channel; its submit profile
18
+ * (`submit.<stage>.android`) has its Play track and release status — see
19
+ * `mobileStage`. Every other field is left as it is.
20
+ * - **The EAS environment**: each value the app's build reads for the stage
21
+ * (`StackMobileApp.values`) — its scheme, its edges' public URLs under
22
+ * `EXPO_PUBLIC_`, and every stage secret already named so — set with
23
+ * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.
24
+ * Nothing else is written: no server key reaches EAS.
25
+ */
26
+ const logger = console;
27
+ /** The workspace declares no `MobileApp`: nothing to set up. */
28
+ var NoMobileApp = class extends require_errors.GkmError {
29
+ constructor() {
30
+ super("The workspace declares no MobileApp, so there is no EAS project to set up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).");
31
+ this.name = "NoMobileApp";
32
+ }
33
+ };
34
+ /** Several `MobileApp`s, and nothing to say which. */
35
+ var MobileAppNotChosen = class extends require_errors.GkmError {
36
+ apps;
37
+ constructor(apps) {
38
+ super(`The workspace declares ${apps.length} mobile apps (${apps.join(", ")}). Run from one's directory, or name it: --app ${apps[0]}.`);
39
+ this.apps = apps;
40
+ this.name = "MobileAppNotChosen";
41
+ }
42
+ };
43
+ /** `--app` names no `MobileApp`. */
44
+ var MobileAppUnknown = class extends require_errors.GkmError {
45
+ app;
46
+ apps;
47
+ constructor(app, apps) {
48
+ super(`'${app}' is not one of the workspace's mobile apps (${apps.join(", ")}).`);
49
+ this.app = app;
50
+ this.apps = apps;
51
+ this.name = "MobileAppUnknown";
52
+ }
53
+ };
54
+ /** `eas` is not installed where the app is. */
55
+ var EasCliMissing = class extends require_errors.GkmError {
56
+ dir;
57
+ constructor(dir) {
58
+ super(`No \`eas\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli (or install it globally: npm install -g eas-cli).`);
59
+ this.dir = dir;
60
+ this.name = "EasCliMissing";
61
+ }
62
+ };
63
+ /** `eas` refused a command. */
64
+ var EasCommandFailed = class extends require_errors.GkmError {
65
+ command;
66
+ code;
67
+ stderr;
68
+ constructor(command, code, stderr) {
69
+ super(`\`eas ${command}\` failed (exit ${code}): ${stderr.trim() || "it said nothing"}. In CI, EXPO_TOKEN must be set; on this machine, \`eas login\`.`);
70
+ this.command = command;
71
+ this.code = code;
72
+ this.stderr = stderr;
73
+ this.name = "EasCommandFailed";
74
+ }
75
+ };
76
+ /**
77
+ * Run `eas` in `dir`: the app's own, else the workspace's, else the one on
78
+ * PATH. What it prints on stdout is answered; a failure is
79
+ * {@link EasCommandFailed} with what it said.
80
+ */
81
+ function runEas(root, dir, args, options = {}) {
82
+ return new Promise((resolve, reject) => {
83
+ const child = (0, node_child_process.spawn)("eas", [...args, "--non-interactive"], {
84
+ cwd: dir,
85
+ stdio: [
86
+ "ignore",
87
+ options.inherit ? "inherit" : "pipe",
88
+ "pipe"
89
+ ],
90
+ env: {
91
+ ...process.env,
92
+ ...options.env,
93
+ PATH: [
94
+ (0, node_path.join)(dir, "node_modules", ".bin"),
95
+ (0, node_path.join)(root, "node_modules", ".bin"),
96
+ process.env.PATH
97
+ ].join(node_path.delimiter)
98
+ }
99
+ });
100
+ let stdout = "";
101
+ let stderr = "";
102
+ child.stdout?.on("data", (chunk) => {
103
+ stdout += chunk.toString();
104
+ });
105
+ child.stderr?.on("data", (chunk) => {
106
+ stderr += chunk.toString();
107
+ if (options.inherit) process.stderr.write(chunk);
108
+ });
109
+ const command = args.slice(0, 3).join(" ");
110
+ child.on("error", (error) => reject(error.code === "ENOENT" ? new EasCliMissing(dir) : new EasCommandFailed(command, null, error.message)));
111
+ child.on("close", (code) => code === 0 ? resolve(stdout) : reject(new EasCommandFailed(command, code, stderr)));
112
+ });
113
+ }
114
+ /** The JSON `eas … --json` printed, read for `what`. */
115
+ function easJson(output, what) {
116
+ try {
117
+ return JSON.parse(output);
118
+ } catch {
119
+ throw new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);
120
+ }
121
+ }
122
+ /**
123
+ * The real one — never a prompt (`--non-interactive`). `env` is what the
124
+ * app's `app.config.ts` reads for the stage: every `eas` command evaluates
125
+ * it, and one that reads a value it lacks fails before it starts — and a
126
+ * fingerprint computed here must match the one EAS computes with the
127
+ * stage's values.
128
+ */
129
+ function easCli(root, env = {}) {
130
+ const run = (dir, args, options = {}) => runEas(root, dir, args, {
131
+ ...options,
132
+ env
133
+ });
134
+ return {
135
+ async setEnv(dir, { environment, name, value }) {
136
+ await run(dir, [
137
+ "env:set",
138
+ "--environment",
139
+ environment,
140
+ "--name",
141
+ name,
142
+ "--value",
143
+ value,
144
+ "--visibility",
145
+ "plaintext",
146
+ "--scope",
147
+ "project"
148
+ ]);
149
+ },
150
+ async fingerprint(dir, { platform, profile }) {
151
+ const hash = easJson(await run(dir, [
152
+ "fingerprint:generate",
153
+ "--platform",
154
+ platform,
155
+ "--build-profile",
156
+ profile,
157
+ "--json"
158
+ ]), "fingerprint:generate").hash;
159
+ if (typeof hash !== "string") throw new EasCommandFailed("fingerprint:generate", 0, "no hash");
160
+ return hash;
161
+ },
162
+ async storeBuild(dir, { platform, profile, fingerprint }) {
163
+ const builds = easJson(await run(dir, [
164
+ "build:list",
165
+ "--platform",
166
+ platform,
167
+ "--build-profile",
168
+ profile,
169
+ "--fingerprint-hash",
170
+ fingerprint,
171
+ "--status",
172
+ "finished",
173
+ "--distribution",
174
+ "store",
175
+ "--limit",
176
+ "1",
177
+ "--json"
178
+ ]), "build:list");
179
+ if (!Array.isArray(builds)) return void 0;
180
+ const id = builds[0]?.id;
181
+ return typeof id === "string" ? id : void 0;
182
+ },
183
+ async build(dir, { platform, profile, submit, wait }) {
184
+ await run(dir, [
185
+ "build",
186
+ "--platform",
187
+ platform,
188
+ "--profile",
189
+ profile,
190
+ ...submit ? ["--auto-submit"] : [],
191
+ ...wait ? [] : ["--no-wait"]
192
+ ], { inherit: true });
193
+ },
194
+ async update(dir, { platform, channel, environment, message }) {
195
+ await run(dir, [
196
+ "update",
197
+ "--platform",
198
+ platform,
199
+ "--channel",
200
+ channel,
201
+ "--environment",
202
+ environment,
203
+ "--message",
204
+ message
205
+ ], { inherit: true });
206
+ }
207
+ };
208
+ }
209
+ /**
210
+ * The mobile app a command is about: the one `--app` names, else the one
211
+ * this directory is, else the only one.
212
+ *
213
+ * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}
214
+ */
215
+ function chooseMobileApp(manifest, app, here) {
216
+ const declarations = Object.values(manifest).filter((declaration) => declaration.kind === "mobile-app");
217
+ const names = declarations.map((declaration) => require_derive.appKey(declaration.id));
218
+ if (declarations.length === 0) throw new NoMobileApp();
219
+ const name = app ?? (here && names.includes(here) ? here : names.length === 1 ? names[0] : void 0);
220
+ if (!name) throw new MobileAppNotChosen(names);
221
+ const declaration = declarations.find((d) => require_derive.appKey(d.id) === name);
222
+ if (!declaration) throw new MobileAppUnknown(name, names);
223
+ return declaration;
224
+ }
225
+ /**
226
+ * `eas.json` with the stage's build and submit profiles as `stage` says:
227
+ * every field gkm does not decide is kept.
228
+ */
229
+ function withStageProfiles(easJson, stage) {
230
+ const build = easJson.build?.[stage.stage] ?? {};
231
+ const submit = easJson.submit?.[stage.stage] ?? {};
232
+ const android = submit.android ?? {};
233
+ return {
234
+ ...easJson,
235
+ build: {
236
+ ...easJson.build,
237
+ [stage.stage]: {
238
+ ...build,
239
+ environment: stage.environment,
240
+ channel: stage.channel
241
+ }
242
+ },
243
+ submit: {
244
+ ...easJson.submit,
245
+ [stage.stage]: {
246
+ ...submit,
247
+ android: {
248
+ ...android,
249
+ track: stage.android.track,
250
+ releaseStatus: stage.android.releaseStatus
251
+ }
252
+ }
253
+ }
254
+ };
255
+ }
256
+ /**
257
+ * Set up a mobile app's EAS project for a deployed stage.
258
+ *
259
+ * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},
260
+ * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and
261
+ * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},
262
+ * {ExecStageNotDeployed}
263
+ */
264
+ async function easSync(options, eas) {
265
+ const loaded = await require_stage.loadStage(options);
266
+ const { workspace, manifest, stack } = loaded;
267
+ const declaration = chooseMobileApp(manifest, options.app, loaded.appName);
268
+ const name = require_derive.appKey(declaration.id);
269
+ const stage = require_index.mobileStage(workspace, declaration, options.stage);
270
+ const values = stack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};
271
+ const dir = (0, node_path.join)(workspace.root, declaration.app.path);
272
+ const path = (0, node_path.join)(dir, "eas.json");
273
+ const before = (0, node_fs.existsSync)(path) ? JSON.parse(await (0, node_fs_promises.readFile)(path, "utf8")) : {};
274
+ const after = withStageProfiles(before, stage);
275
+ const easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);
276
+ const where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;
277
+ if (options.dryRun) {
278
+ logger.log(`📱 ${where} (dry run)`);
279
+ if (easJsonChanged) logger.log(` would write build.${stage.stage} and submit.${stage.stage} in ${path}`);
280
+ for (const [key, value] of Object.entries(values)) logger.log(` would set ${key}=${value}`);
281
+ return {
282
+ app: name,
283
+ root: workspace.root,
284
+ dir,
285
+ stage,
286
+ values,
287
+ easJsonChanged
288
+ };
289
+ }
290
+ logger.log(`📱 ${where}`);
291
+ if (easJsonChanged) {
292
+ await (0, node_fs_promises.writeFile)(path, `${JSON.stringify(after, null, 2)}\n`);
293
+ logger.log(` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`);
294
+ }
295
+ const cli = eas ?? easCli(workspace.root, values);
296
+ for (const [key, value] of Object.entries(values)) {
297
+ await cli.setEnv(dir, {
298
+ environment: stage.environment,
299
+ name: key,
300
+ value
301
+ });
302
+ logger.log(` set ${key}`);
303
+ }
304
+ return {
305
+ app: name,
306
+ root: workspace.root,
307
+ dir,
308
+ stage,
309
+ values,
310
+ easJsonChanged
311
+ };
312
+ }
313
+ //#endregion
314
+ exports.chooseMobileApp = chooseMobileApp;
315
+ exports.easCli = easCli;
316
+ exports.easSync = easSync;
317
+ exports.withStageProfiles = withStageProfiles;
318
+
319
+ //# sourceMappingURL=eas-BkLA60P2.cjs.map