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

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 (27) hide show
  1. package/dist/{cli-Br5ApIgR.mjs → cli-DDY3jfTt.mjs} +3 -3
  2. package/dist/{cli-Br5ApIgR.mjs.map → cli-DDY3jfTt.mjs.map} +1 -1
  3. package/dist/{eas-BkLA60P2.cjs → eas-Cne743nQ.cjs} +21 -3
  4. package/dist/eas-Cne743nQ.cjs.map +1 -0
  5. package/dist/{eas-6ezdJtry.mjs → eas-DEpwISrQ.mjs} +25 -7
  6. package/dist/eas-DEpwISrQ.mjs.map +1 -0
  7. package/dist/{googlePlay-DNgybe5t.cjs → googlePlay-B6uiHi65.cjs} +2 -2
  8. package/dist/{googlePlay-DNgybe5t.cjs.map → googlePlay-B6uiHi65.cjs.map} +1 -1
  9. package/dist/{googlePlay-DY9vZQ1p.mjs → googlePlay-BYZ-J5sC.mjs} +4 -4
  10. package/dist/{googlePlay-DY9vZQ1p.mjs.map → googlePlay-BYZ-J5sC.mjs.map} +1 -1
  11. package/dist/index.cjs +155 -73
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.mjs +164 -84
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/{migrate-B9GswIAr.mjs → migrate-xHYaeBkd.mjs} +2 -2
  16. package/dist/{migrate-B9GswIAr.mjs.map → migrate-xHYaeBkd.mjs.map} +1 -1
  17. package/dist/{publish-B-EIjfQ0.mjs → publish-CSctCNf4.mjs} +2 -2
  18. package/dist/{publish-B-EIjfQ0.mjs.map → publish-CSctCNf4.mjs.map} +1 -1
  19. package/dist/{publish-CPgSbtcs.cjs → publish-SDG1WfYn.cjs} +2 -2
  20. package/dist/{publish-CPgSbtcs.cjs.map → publish-SDG1WfYn.cjs.map} +1 -1
  21. package/dist/{stage-DjxR_7C0.mjs → stage-ClKTHQoM.mjs} +2 -2
  22. package/dist/{stage-DjxR_7C0.mjs.map → stage-ClKTHQoM.mjs.map} +1 -1
  23. package/dist/{terminal-mXM-Lqc6.mjs → terminal-CybB-cjk.mjs} +2 -2
  24. package/dist/{terminal-mXM-Lqc6.mjs.map → terminal-CybB-cjk.mjs.map} +1 -1
  25. package/package.json +15 -15
  26. package/dist/eas-6ezdJtry.mjs.map +0 -1
  27. package/dist/eas-BkLA60P2.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { r as storedCredentials } from "./credentials-B5N5yKk6.mjs";
2
2
  import { t as deploy } from "./deploy-DLq9-m6H.mjs";
3
- import { a as formatError } from "./index.mjs";
3
+ import { s as formatError } from "./index.mjs";
4
4
  import { n as targetForProvider } from "./provider-CEOKk8NS.mjs";
5
- import { terminalCredentials } from "./terminal-mXM-Lqc6.mjs";
5
+ import { terminalCredentials } from "./terminal-CybB-cjk.mjs";
6
6
  //#region src/deploy/cli.ts
7
7
  /**
8
8
  * `gkm deploy`: the terminal around `deploy()`. CLI layer only.
@@ -68,4 +68,4 @@ async function deployCli(options, streams = { stdout: process.stdout }) {
68
68
  //#endregion
69
69
  export { deployCli };
70
70
 
71
- //# sourceMappingURL=cli-Br5ApIgR.mjs.map
71
+ //# sourceMappingURL=cli-DDY3jfTt.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-Br5ApIgR.mjs","names":[],"sources":["../src/deploy/cli.ts"],"sourcesContent":["/**\n * `gkm deploy`: the terminal around `deploy()`. CLI layer only.\n *\n * Everything that belongs to a person at a terminal lives here and nowhere\n * below it: prompting for a missing login (and storing it), printing progress,\n * and turning the outcome into an exit code. The deploy itself is the same\n * `deploy()` a host calls.\n */\n\nimport { formatError } from '../debug';\nimport { targetForProvider } from '../target/provider';\nimport { storedCredentials } from './credentials';\nimport { deploy } from './deploy';\nimport { terminalCredentials } from './terminal';\n\nexport interface DeployCliOptions {\n\t/** The project directory: `--cwd`, else where the command was run. */\n\tcwd: string;\n\t/** `--target`: where to deploy. Defaults to `deploy.default`. */\n\ttarget?: string;\n\t/** `--provider`, deprecated: `dokploy` means `--target dokploy`. */\n\tprovider?: string;\n\tstage: string;\n\ttag?: string;\n\t/**\n\t * Print each event as one JSON object per line on stdout, instead of the\n\t * progress lines. Nothing prompts: a missing credential fails the run.\n\t */\n\tjson?: boolean;\n\t/** Plan only: create, change, build and push nothing. */\n\tdryRun?: boolean;\n\t/** On a failed release, roll back every app, not only the failed ones. */\n\tatomic?: boolean;\n\t/** `--allow-dev-services`. */\n\tallowDevServices?: boolean;\n\t/** `--skip-dns`. */\n\tskipDns?: boolean;\n\t/** `--rotate-keys`. */\n\trotateKeys?: boolean;\n\t/** `--retire-old-keys`. */\n\tretireOldKeys?: boolean;\n}\n\n/** Where the command writes. The process's own streams, outside tests. */\nexport interface DeployCliStreams {\n\tstdout: Pick<NodeJS.WriteStream, 'write'>;\n}\n\n/**\n * Run `gkm deploy` and resolve with its exit code: 0 when the run finished,\n * 1 when anything stopped it.\n */\nexport async function deployCli(\n\toptions: DeployCliOptions,\n\tstreams: DeployCliStreams = { stdout: process.stdout },\n): Promise<number> {\n\tlet target = options.target;\n\tif (options.provider !== undefined && target === undefined) {\n\t\ttry {\n\t\t\t// On stderr, so a `--json` run's stdout still carries only events.\n\t\t\ttarget = targetForProvider(options.provider, (message) =>\n\t\t\t\tconsole.warn(message),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tconst input = {\n\t\tcwd: options.cwd,\n\t\tstage: options.stage,\n\t\t...(target ? { target } : {}),\n\t\t...(options.tag ? { tag: options.tag } : {}),\n\t\t...(options.dryRun ? { dryRun: true } : {}),\n\t\t...(options.atomic ? { atomic: true } : {}),\n\t\t...(options.allowDevServices ? { allowDevServices: true } : {}),\n\t\t...(options.skipDns ? { skipDns: true } : {}),\n\t\t...(options.rotateKeys ? { rotateKeys: true } : {}),\n\t\t...(options.retireOldKeys ? { retireOldKeys: true } : {}),\n\t};\n\n\tif (options.json) {\n\t\t// stdout carries the events and nothing else: docker's own output goes\n\t\t// to stderr, and nobody is asked anything — a prompt would be written\n\t\t// into the stream a program is parsing.\n\t\tconst run = deploy({\n\t\t\t...input,\n\t\t\tcredentials: storedCredentials(),\n\t\t\tchildOutput: 'stderr',\n\t\t});\n\t\tfor await (const event of run) {\n\t\t\tstreams.stdout.write(`${JSON.stringify(event)}\\n`);\n\t\t}\n\t\treturn run.result.then(\n\t\t\t() => 0,\n\t\t\t(error) => {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\treturn 1;\n\t\t\t},\n\t\t);\n\t}\n\n\t// The lines arrive as they are written, rather than as events read back a\n\t// tick later, so they interleave with a prompt exactly as they always did.\n\tconst run = deploy({\n\t\t...input,\n\t\tcredentials: terminalCredentials(storedCredentials()),\n\t\tlogger: {\n\t\t\tinfo: (message) => console.log(message),\n\t\t\twarn: (message) => console.warn(message),\n\t\t\terror: (message) => console.error(message),\n\t\t},\n\t});\n\n\ttry {\n\t\tawait run.result;\n\t\treturn 0;\n\t} catch (error) {\n\t\tconsole.error(formatError(error));\n\t\treturn 1;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoDA,eAAsB,UACrB,SACA,UAA4B,EAAE,QAAQ,QAAQ,OAAO,GACnC;CAClB,IAAI,SAAS,QAAQ;CACrB,IAAI,QAAQ,aAAa,KAAA,KAAa,WAAW,KAAA,GAChD,IAAI;EAEH,SAAS,kBAAkB,QAAQ,WAAW,YAC7C,QAAQ,KAAK,OAAO,CACrB;CACD,SAAS,OAAO;EACf,QAAQ,MAAM,YAAY,KAAK,CAAC;EAChC,OAAO;CACR;CAGD,MAAM,QAAQ;EACb,KAAK,QAAQ;EACb,OAAO,QAAQ;EACf,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;EACzC,GAAI,QAAQ,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;EACzC,GAAI,QAAQ,mBAAmB,EAAE,kBAAkB,KAAK,IAAI,CAAC;EAC7D,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;EAC3C,GAAI,QAAQ,aAAa,EAAE,YAAY,KAAK,IAAI,CAAC;EACjD,GAAI,QAAQ,gBAAgB,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;CAEA,IAAI,QAAQ,MAAM;EAIjB,MAAM,MAAM,OAAO;GAClB,GAAG;GACH,aAAa,kBAAkB;GAC/B,aAAa;EACd,CAAC;EACD,WAAW,MAAM,SAAS,KACzB,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;EAElD,OAAO,IAAI,OAAO,WACX,IACL,UAAU;GACV,QAAQ,MAAM,YAAY,KAAK,CAAC;GAChC,OAAO;EACR,CACD;CACD;CAIA,MAAM,MAAM,OAAO;EAClB,GAAG;EACH,aAAa,oBAAoB,kBAAkB,CAAC;EACpD,QAAQ;GACP,OAAO,YAAY,QAAQ,IAAI,OAAO;GACtC,OAAO,YAAY,QAAQ,KAAK,OAAO;GACvC,QAAQ,YAAY,QAAQ,MAAM,OAAO;EAC1C;CACD,CAAC;CAED,IAAI;EACH,MAAM,IAAI;EACV,OAAO;CACR,SAAS,OAAO;EACf,QAAQ,MAAM,YAAY,KAAK,CAAC;EAChC,OAAO;CACR;AACD"}
1
+ {"version":3,"file":"cli-DDY3jfTt.mjs","names":[],"sources":["../src/deploy/cli.ts"],"sourcesContent":["/**\n * `gkm deploy`: the terminal around `deploy()`. CLI layer only.\n *\n * Everything that belongs to a person at a terminal lives here and nowhere\n * below it: prompting for a missing login (and storing it), printing progress,\n * and turning the outcome into an exit code. The deploy itself is the same\n * `deploy()` a host calls.\n */\n\nimport { formatError } from '../debug';\nimport { targetForProvider } from '../target/provider';\nimport { storedCredentials } from './credentials';\nimport { deploy } from './deploy';\nimport { terminalCredentials } from './terminal';\n\nexport interface DeployCliOptions {\n\t/** The project directory: `--cwd`, else where the command was run. */\n\tcwd: string;\n\t/** `--target`: where to deploy. Defaults to `deploy.default`. */\n\ttarget?: string;\n\t/** `--provider`, deprecated: `dokploy` means `--target dokploy`. */\n\tprovider?: string;\n\tstage: string;\n\ttag?: string;\n\t/**\n\t * Print each event as one JSON object per line on stdout, instead of the\n\t * progress lines. Nothing prompts: a missing credential fails the run.\n\t */\n\tjson?: boolean;\n\t/** Plan only: create, change, build and push nothing. */\n\tdryRun?: boolean;\n\t/** On a failed release, roll back every app, not only the failed ones. */\n\tatomic?: boolean;\n\t/** `--allow-dev-services`. */\n\tallowDevServices?: boolean;\n\t/** `--skip-dns`. */\n\tskipDns?: boolean;\n\t/** `--rotate-keys`. */\n\trotateKeys?: boolean;\n\t/** `--retire-old-keys`. */\n\tretireOldKeys?: boolean;\n}\n\n/** Where the command writes. The process's own streams, outside tests. */\nexport interface DeployCliStreams {\n\tstdout: Pick<NodeJS.WriteStream, 'write'>;\n}\n\n/**\n * Run `gkm deploy` and resolve with its exit code: 0 when the run finished,\n * 1 when anything stopped it.\n */\nexport async function deployCli(\n\toptions: DeployCliOptions,\n\tstreams: DeployCliStreams = { stdout: process.stdout },\n): Promise<number> {\n\tlet target = options.target;\n\tif (options.provider !== undefined && target === undefined) {\n\t\ttry {\n\t\t\t// On stderr, so a `--json` run's stdout still carries only events.\n\t\t\ttarget = targetForProvider(options.provider, (message) =>\n\t\t\t\tconsole.warn(message),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(formatError(error));\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tconst input = {\n\t\tcwd: options.cwd,\n\t\tstage: options.stage,\n\t\t...(target ? { target } : {}),\n\t\t...(options.tag ? { tag: options.tag } : {}),\n\t\t...(options.dryRun ? { dryRun: true } : {}),\n\t\t...(options.atomic ? { atomic: true } : {}),\n\t\t...(options.allowDevServices ? { allowDevServices: true } : {}),\n\t\t...(options.skipDns ? { skipDns: true } : {}),\n\t\t...(options.rotateKeys ? { rotateKeys: true } : {}),\n\t\t...(options.retireOldKeys ? { retireOldKeys: true } : {}),\n\t};\n\n\tif (options.json) {\n\t\t// stdout carries the events and nothing else: docker's own output goes\n\t\t// to stderr, and nobody is asked anything — a prompt would be written\n\t\t// into the stream a program is parsing.\n\t\tconst run = deploy({\n\t\t\t...input,\n\t\t\tcredentials: storedCredentials(),\n\t\t\tchildOutput: 'stderr',\n\t\t});\n\t\tfor await (const event of run) {\n\t\t\tstreams.stdout.write(`${JSON.stringify(event)}\\n`);\n\t\t}\n\t\treturn run.result.then(\n\t\t\t() => 0,\n\t\t\t(error) => {\n\t\t\t\tconsole.error(formatError(error));\n\t\t\t\treturn 1;\n\t\t\t},\n\t\t);\n\t}\n\n\t// The lines arrive as they are written, rather than as events read back a\n\t// tick later, so they interleave with a prompt exactly as they always did.\n\tconst run = deploy({\n\t\t...input,\n\t\tcredentials: terminalCredentials(storedCredentials()),\n\t\tlogger: {\n\t\t\tinfo: (message) => console.log(message),\n\t\t\twarn: (message) => console.warn(message),\n\t\t\terror: (message) => console.error(message),\n\t\t},\n\t});\n\n\ttry {\n\t\tawait run.result;\n\t\treturn 0;\n\t} catch (error) {\n\t\tconsole.error(formatError(error));\n\t\treturn 1;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoDA,eAAsB,UACrB,SACA,UAA4B,EAAE,QAAQ,QAAQ,OAAO,GACnC;CAClB,IAAI,SAAS,QAAQ;CACrB,IAAI,QAAQ,aAAa,KAAA,KAAa,WAAW,KAAA,GAChD,IAAI;EAEH,SAAS,kBAAkB,QAAQ,WAAW,YAC7C,QAAQ,KAAK,OAAO,CACrB;CACD,SAAS,OAAO;EACf,QAAQ,MAAM,YAAY,KAAK,CAAC;EAChC,OAAO;CACR;CAGD,MAAM,QAAQ;EACb,KAAK,QAAQ;EACb,OAAO,QAAQ;EACf,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;EACzC,GAAI,QAAQ,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;EACzC,GAAI,QAAQ,mBAAmB,EAAE,kBAAkB,KAAK,IAAI,CAAC;EAC7D,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;EAC3C,GAAI,QAAQ,aAAa,EAAE,YAAY,KAAK,IAAI,CAAC;EACjD,GAAI,QAAQ,gBAAgB,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;CAEA,IAAI,QAAQ,MAAM;EAIjB,MAAM,MAAM,OAAO;GAClB,GAAG;GACH,aAAa,kBAAkB;GAC/B,aAAa;EACd,CAAC;EACD,WAAW,MAAM,SAAS,KACzB,QAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;EAElD,OAAO,IAAI,OAAO,WACX,IACL,UAAU;GACV,QAAQ,MAAM,YAAY,KAAK,CAAC;GAChC,OAAO;EACR,CACD;CACD;CAIA,MAAM,MAAM,OAAO;EAClB,GAAG;EACH,aAAa,oBAAoB,kBAAkB,CAAC;EACpD,QAAQ;GACP,OAAO,YAAY,QAAQ,IAAI,OAAO;GACtC,OAAO,YAAY,QAAQ,KAAK,OAAO;GACvC,QAAQ,YAAY,QAAQ,MAAM,OAAO;EAC1C;CACD,CAAC;CAED,IAAI;EACH,MAAM,IAAI;EACV,OAAO;CACR,SAAS,OAAO;EACf,QAAQ,MAAM,YAAY,KAAK,CAAC;EAChC,OAAO;CACR;AACD"}
@@ -273,10 +273,21 @@ async function easSync(options, eas) {
273
273
  const before = (0, node_fs.existsSync)(path) ? JSON.parse(await (0, node_fs_promises.readFile)(path, "utf8")) : {};
274
274
  const after = withStageProfiles(before, stage);
275
275
  const easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);
276
+ const workflowPath = require_index.easWorkflowPath(stage.stage);
277
+ const workflowFile = (0, node_path.join)(dir, workflowPath);
278
+ const workflowYaml = require_index.easWorkflow(stage, {
279
+ release: workspace.stages.protected?.includes(stage.stage) ?? false,
280
+ iosSubmits: Boolean((after.submit?.[stage.stage]?.ios)?.ascAppId)
281
+ });
282
+ const workflow = {
283
+ path: workflowPath,
284
+ changed: !(0, node_fs.existsSync)(workflowFile) || await (0, node_fs_promises.readFile)(workflowFile, "utf8") !== workflowYaml
285
+ };
276
286
  const where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;
277
287
  if (options.dryRun) {
278
288
  logger.log(`📱 ${where} (dry run)`);
279
289
  if (easJsonChanged) logger.log(` would write build.${stage.stage} and submit.${stage.stage} in ${path}`);
290
+ if (workflow.changed) logger.log(` would write ${workflowFile}`);
280
291
  for (const [key, value] of Object.entries(values)) logger.log(` would set ${key}=${value}`);
281
292
  return {
282
293
  app: name,
@@ -284,7 +295,8 @@ async function easSync(options, eas) {
284
295
  dir,
285
296
  stage,
286
297
  values,
287
- easJsonChanged
298
+ easJsonChanged,
299
+ workflow
288
300
  };
289
301
  }
290
302
  logger.log(`📱 ${where}`);
@@ -292,6 +304,11 @@ async function easSync(options, eas) {
292
304
  await (0, node_fs_promises.writeFile)(path, `${JSON.stringify(after, null, 2)}\n`);
293
305
  logger.log(` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`);
294
306
  }
307
+ if (workflow.changed) {
308
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(workflowFile), { recursive: true });
309
+ await (0, node_fs_promises.writeFile)(workflowFile, workflowYaml);
310
+ logger.log(` wrote ${workflowFile}`);
311
+ }
295
312
  const cli = eas ?? easCli(workspace.root, values);
296
313
  for (const [key, value] of Object.entries(values)) {
297
314
  await cli.setEnv(dir, {
@@ -307,7 +324,8 @@ async function easSync(options, eas) {
307
324
  dir,
308
325
  stage,
309
326
  values,
310
- easJsonChanged
327
+ easJsonChanged,
328
+ workflow
311
329
  };
312
330
  }
313
331
  //#endregion
@@ -316,4 +334,4 @@ exports.easCli = easCli;
316
334
  exports.easSync = easSync;
317
335
  exports.withStageProfiles = withStageProfiles;
318
336
 
319
- //# sourceMappingURL=eas-BkLA60P2.cjs.map
337
+ //# sourceMappingURL=eas-Cne743nQ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eas-Cne743nQ.cjs","names":["GkmError","spawn","join","delimiter","appKey","loadStage","mobileStage","existsSync","readFile","easWorkflowPath","easWorkflow","writeFile","mkdir","dirname"],"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 { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, 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 { easWorkflow, easWorkflowPath } from './easWorkflow.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\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: 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 workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\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\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\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\tworkflow,\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\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\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\tworkflow,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,SAAS;;AAGf,IAAa,cAAb,cAAiCA,eAAAA,SAAS;CACzC,cAAc;EACb,MACC,4JAED;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,qBAAb,cAAwCA,eAAAA,SAAS;CAC3B;CAArB,YAAY,MAAkC;EAC7C,MACC,0BAA0B,KAAK,OAAO,gBAAgB,KAAK,KAAK,IAAI,EAAE,iDACtB,KAAK,GAAG,EACzD;EAJoB,KAAA,OAAA;EAKpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,mBAAb,cAAsCA,eAAAA,SAAS;CAEpC;CACA;CAFV,YACC,KACA,MACC;EACD,MACC,IAAI,IAAI,+CAA+C,KAAK,KAAK,IAAI,EAAE,GACxE;EALS,KAAA,MAAA;EACA,KAAA,OAAA;EAKT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,gBAAb,cAAmCA,eAAAA,SAAS;CACtB;CAArB,YAAY,KAAsB;EACjC,MACC,wBAAwB,IAAI,2FAE7B;EAJoB,KAAA,MAAA;EAKpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,mBAAb,cAAsCA,eAAAA,SAAS;CAEpC;CACA;CACA;CAHV,YACC,SACA,MACA,QACC;EACD,MACC,SAAS,QAAQ,kBAAkB,KAAK,KAAK,OAAO,KAAK,KAAK,kBAAkB,iEAEjF;EAPS,KAAA,UAAA;EACA,KAAA,OAAA;EACA,KAAA,SAAA;EAMT,KAAK,OAAO;CACb;AACD;;;;;;AAoDA,SAAS,OACR,MACA,KACA,MACA,UAAyE,CAAC,GACxD;CAClB,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,MAAM,SAAA,GAAQC,mBAAAA,MAAAA,CAAM,OAAO,CAAC,GAAG,MAAM,mBAAmB,GAAG;GAC1D,KAAK;GAEL,OAAO;IAAC;IAAU,QAAQ,UAAU,YAAY;IAAQ;GAAM;GAC9D,KAAK;IACJ,GAAG,QAAQ;IACX,GAAG,QAAQ;IACX,MAAM;MACLC,GAAAA,UAAAA,KAAAA,CAAK,KAAK,gBAAgB,MAAM;MAChCA,GAAAA,UAAAA,KAAAA,CAAK,MAAM,gBAAgB,MAAM;KACjC,QAAQ,IAAI;IACb,CAAC,CAAC,KAAKC,UAAAA,SAAS;GACjB;EACD,CAAC;EACD,IAAI,SAAS;EACb,IAAI,SAAS;EACb,MAAM,QAAQ,GAAG,SAAS,UAAkB;GAC3C,UAAU,MAAM,SAAS;EAC1B,CAAC;EACD,MAAM,QAAQ,GAAG,SAAS,UAAkB;GAC3C,UAAU,MAAM,SAAS;GACzB,IAAI,QAAQ,SAAS,QAAQ,OAAO,MAAM,KAAK;EAChD,CAAC;EACD,MAAM,UAAU,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG;EACzC,MAAM,GAAG,UAAU,UAClB,OACC,MAAM,SAAS,WACZ,IAAI,cAAc,GAAG,IACrB,IAAI,iBAAiB,SAAS,MAAM,MAAM,OAAO,CACrD,CACD;EACA,MAAM,GAAG,UAAU,SAClB,SAAS,IACN,QAAQ,MAAM,IACd,OAAO,IAAI,iBAAiB,SAAS,MAAM,MAAM,CAAC,CACtD;CACD,CAAC;AACF;;AAGA,SAAS,QAAQ,QAAgB,MAAuB;CACvD,IAAI;EACH,OAAO,KAAK,MAAM,MAAM;CACzB,QAAQ;EACP,MAAM,IAAI,iBAAiB,MAAM,GAAG,aAAa,OAAO,MAAM,GAAG,GAAG,GAAG;CACxE;AACD;;;;;;;;AASA,SAAgB,OACf,MACA,MAAwC,CAAC,GAChC;CACT,MAAM,OACL,KACA,MACA,UAAiC,CAAC,MAC9B,OAAO,MAAM,KAAK,MAAM;EAAE,GAAG;EAAS;CAAI,CAAC;CAChD,OAAO;EACN,MAAM,OAAO,KAAK,EAAE,aAAa,MAAM,SAAS;GAC/C,MAAM,IAAI,KAAK;IACd;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACD,CAAC;EACF;EAEA,MAAM,YAAY,KAAK,EAAE,UAAU,WAAW;GAS7C,MAAM,OACL,QAAQ,MATY,IAAI,KAAK;IAC7B;IACA;IACA;IACA;IACA;IACA;GACD,CAAC,GAEgB,sBAAsB,CAAC,CACtC;GACF,IAAI,OAAO,SAAS,UACnB,MAAM,IAAI,iBAAiB,wBAAwB,GAAG,SAAS;GAEhE,OAAO;EACR;EAEA,MAAM,WAAW,KAAK,EAAE,UAAU,SAAS,eAAe;GAiBzD,MAAM,SAAS,QAAQ,MAhBF,IAAI,KAAK;IAC7B;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACD,CAAC,GAC8B,YAAY;GAC3C,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,OAAO,KAAA;GACnC,MAAM,KAAM,OAAO,EAAE,EAAmC;GACxD,OAAO,OAAO,OAAO,WAAW,KAAK,KAAA;EACtC;EAEA,MAAM,MAAM,KAAK,EAAE,UAAU,SAAS,QAAQ,QAAQ;GACrD,MAAM,IACL,KACA;IACC;IACA;IACA;IACA;IACA;IACA,GAAI,SAAS,CAAC,eAAe,IAAI,CAAC;IAClC,GAAI,OAAO,CAAC,IAAI,CAAC,WAAW;GAC7B,GACA,EAAE,SAAS,KAAK,CACjB;EACD;EAEA,MAAM,OAAO,KAAK,EAAE,UAAU,SAAS,aAAa,WAAW;GAC9D,MAAM,IACL,KACA;IACC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;GACD,GACA,EAAE,SAAS,KAAK,CACjB;EACD;CACD;AACD;;;;;;;AAQA,SAAgB,gBACf,UACA,KACA,MACuB;CACvB,MAAM,eAAe,OAAO,OAAO,QAAQ,CAAC,CAAC,QAC3C,gBACA,YAAY,SAAS,YACvB;CACA,MAAM,QAAQ,aAAa,KAAK,gBAAgBC,eAAAA,OAAO,YAAY,EAAE,CAAC;CACtE,IAAI,aAAa,WAAW,GAAG,MAAM,IAAI,YAAY;CACrD,MAAM,OACL,QACC,QAAQ,MAAM,SAAS,IAAI,IACzB,OACA,MAAM,WAAW,IAChB,MAAM,KACN,KAAA;CACL,IAAI,CAAC,MAAM,MAAM,IAAI,mBAAmB,KAAK;CAC7C,MAAM,cAAc,aAAa,MAAM,MAAMA,eAAAA,OAAO,EAAE,EAAE,MAAM,IAAI;CAClE,IAAI,CAAC,aAAa,MAAM,IAAI,iBAAiB,MAAM,KAAK;CACxD,OAAO;AACR;;;;;AAaA,SAAgB,kBACf,SACA,OACU;CACV,MAAM,QAAQ,QAAQ,QAAQ,MAAM,UAAU,CAAC;CAC/C,MAAM,SAAS,QAAQ,SAAS,MAAM,UAAU,CAAC;CACjD,MAAM,UAAW,OAAO,WAAmD,CAAC;CAC5E,OAAO;EACN,GAAG;EACH,OAAO;GACN,GAAG,QAAQ;IACV,MAAM,QAAQ;IACd,GAAG;IACH,aAAa,MAAM;IACnB,SAAS,MAAM;GAChB;EACD;EACA,QAAQ;GACP,GAAG,QAAQ;IACV,MAAM,QAAQ;IACd,GAAG;IACH,SAAS;KACR,GAAG;KACH,OAAO,MAAM,QAAQ;KACrB,eAAe,MAAM,QAAQ;IAC9B;GACD;EACD;CACD;AACD;;;;;;;;;AAmCA,eAAsB,QACrB,SACA,KACyB;CACzB,MAAM,SAAS,MAAMC,cAAAA,UAAU,OAAO;CACtC,MAAM,EAAE,WAAW,UAAU,UAAU;CAEvC,MAAM,cAAc,gBAAgB,UAAU,QAAQ,KAAK,OAAO,OAAO;CACzE,MAAM,OAAOD,eAAAA,OAAO,YAAY,EAAE;CAElC,MAAM,QAAQE,cAAAA,YAAY,WAAW,aAAa,QAAQ,KAAK;CAC/D,MAAM,SACL,MAAM,WAAW,MAAM,cAAc,UAAU,SAAS,IAAI,CAAC,EAAE,UAAU,CAAC;CAC3E,MAAM,OAAA,GAAMJ,UAAAA,KAAAA,CAAK,UAAU,MAAM,YAAY,IAAI,IAAI;CAErD,MAAM,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,UAAU;CACjC,MAAM,UAAA,GAASK,QAAAA,WAAAA,CAAW,IAAI,IAC1B,KAAK,MAAM,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,MAAM,MAAM,CAAC,IACxC,CAAC;CACJ,MAAM,QAAQ,kBAAkB,QAAQ,KAAK;CAC7C,MAAM,iBAAiB,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,KAAK;CAEtE,MAAM,eAAeC,cAAAA,gBAAgB,MAAM,KAAK;CAChD,MAAM,gBAAA,GAAeP,UAAAA,KAAAA,CAAK,KAAK,YAAY;CAC3C,MAAM,eAAeQ,cAAAA,YAAY,OAAO;EACvC,SAAS,UAAU,OAAO,WAAW,SAAS,MAAM,KAAK,KAAK;EAC9D,YAAY,SACV,MAAM,SAAS,MAAM,MAAM,EAAE,IAAA,EAC3B,QACJ;CACD,CAAC;CACD,MAAM,WAAW;EAChB,MAAM;EACN,SACC,EAAA,GAACH,QAAAA,WAAAA,CAAW,YAAY,KACvB,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,cAAc,MAAM,MAAO;CAC7C;CAEA,MAAM,QAAQ,GAAG,KAAK,OAAO,MAAM,MAAM,sBAAsB,MAAM,YAAY,cAAc,MAAM,QAAQ,iBAAiB,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,cAAc;CACnL,IAAI,QAAQ,QAAQ;EACnB,OAAO,IAAI,MAAM,MAAM,WAAW;EAClC,IAAI,gBACH,OAAO,IACN,wBAAwB,MAAM,MAAM,cAAc,MAAM,MAAM,MAAM,MACrE;EAED,IAAI,SAAS,SAAS,OAAO,IAAI,kBAAkB,cAAc;EACjE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC/C,OAAO,IAAI,gBAAgB,IAAI,GAAG,OAAO;EAE1C,OAAO;GACN,KAAK;GACL,MAAM,UAAU;GAChB;GACA;GACA;GACA;GACA;EACD;CACD;CAEA,OAAO,IAAI,MAAM,OAAO;CACxB,IAAI,gBAAgB;EACnB,OAAA,GAAMG,iBAAAA,UAAAA,CAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;EAC3D,OAAO,IACN,kBAAkB,MAAM,MAAM,cAAc,MAAM,MAAM,MAAM,MAC/D;CACD;CACA,IAAI,SAAS,SAAS;EACrB,OAAA,GAAMC,iBAAAA,MAAAA,EAAAA,GAAMC,UAAAA,QAAAA,CAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;EACtD,OAAA,GAAMF,iBAAAA,UAAAA,CAAU,cAAc,YAAY;EAC1C,OAAO,IAAI,YAAY,cAAc;CACtC;CACA,MAAM,MAAM,OAAO,OAAO,UAAU,MAAM,MAAM;CAChD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;EAClD,MAAM,IAAI,OAAO,KAAK;GAAE,aAAa,MAAM;GAAa,MAAM;GAAK;EAAM,CAAC;EAC1E,OAAO,IAAI,UAAU,KAAK;CAC3B;CACA,OAAO;EACN,KAAK;EACL,MAAM,UAAU;EAChB;EACA;EACA;EACA;EACA;CACD;AACD"}
@@ -1,10 +1,10 @@
1
1
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
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";
3
+ import { i as easWorkflowPath, n as mobileStage, r as easWorkflow } from "./index.mjs";
4
+ import { t as loadStage } from "./stage-ClKTHQoM.mjs";
5
+ import { delimiter, dirname, join } from "node:path";
6
6
  import { existsSync } from "node:fs";
7
- import { readFile, writeFile } from "node:fs/promises";
7
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
8
8
  import { spawn } from "node:child_process";
9
9
  //#region src/mobile/eas.ts
10
10
  /**
@@ -273,10 +273,21 @@ async function easSync(options, eas) {
273
273
  const before = existsSync(path) ? JSON.parse(await readFile(path, "utf8")) : {};
274
274
  const after = withStageProfiles(before, stage);
275
275
  const easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);
276
+ const workflowPath = easWorkflowPath(stage.stage);
277
+ const workflowFile = join(dir, workflowPath);
278
+ const workflowYaml = easWorkflow(stage, {
279
+ release: workspace.stages.protected?.includes(stage.stage) ?? false,
280
+ iosSubmits: Boolean((after.submit?.[stage.stage]?.ios)?.ascAppId)
281
+ });
282
+ const workflow = {
283
+ path: workflowPath,
284
+ changed: !existsSync(workflowFile) || await readFile(workflowFile, "utf8") !== workflowYaml
285
+ };
276
286
  const where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;
277
287
  if (options.dryRun) {
278
288
  logger.log(`📱 ${where} (dry run)`);
279
289
  if (easJsonChanged) logger.log(` would write build.${stage.stage} and submit.${stage.stage} in ${path}`);
290
+ if (workflow.changed) logger.log(` would write ${workflowFile}`);
280
291
  for (const [key, value] of Object.entries(values)) logger.log(` would set ${key}=${value}`);
281
292
  return {
282
293
  app: name,
@@ -284,7 +295,8 @@ async function easSync(options, eas) {
284
295
  dir,
285
296
  stage,
286
297
  values,
287
- easJsonChanged
298
+ easJsonChanged,
299
+ workflow
288
300
  };
289
301
  }
290
302
  logger.log(`📱 ${where}`);
@@ -292,6 +304,11 @@ async function easSync(options, eas) {
292
304
  await writeFile(path, `${JSON.stringify(after, null, 2)}\n`);
293
305
  logger.log(` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`);
294
306
  }
307
+ if (workflow.changed) {
308
+ await mkdir(dirname(workflowFile), { recursive: true });
309
+ await writeFile(workflowFile, workflowYaml);
310
+ logger.log(` wrote ${workflowFile}`);
311
+ }
295
312
  const cli = eas ?? easCli(workspace.root, values);
296
313
  for (const [key, value] of Object.entries(values)) {
297
314
  await cli.setEnv(dir, {
@@ -307,10 +324,11 @@ async function easSync(options, eas) {
307
324
  dir,
308
325
  stage,
309
326
  values,
310
- easJsonChanged
327
+ easJsonChanged,
328
+ workflow
311
329
  };
312
330
  }
313
331
  //#endregion
314
332
  export { chooseMobileApp, easCli, easSync, withStageProfiles };
315
333
 
316
- //# sourceMappingURL=eas-6ezdJtry.mjs.map
334
+ //# sourceMappingURL=eas-DEpwISrQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eas-DEpwISrQ.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 { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, 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 { easWorkflow, easWorkflowPath } from './easWorkflow.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\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: 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 workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\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\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\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\tworkflow,\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\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\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\tworkflow,\n\t};\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ const require_run = require("./run-fVZ-rLnc.cjs");
3
3
  const require_config = require("./config.cjs");
4
4
  const require_index = require("./index.cjs");
5
5
  const require_stage = require("./stage-DrCS0vKD.cjs");
6
- const require_eas = require("./eas-BkLA60P2.cjs");
6
+ const require_eas = require("./eas-Cne743nQ.cjs");
7
7
  let node_path = require("node:path");
8
8
  let node_fs = require("node:fs");
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -928,4 +928,4 @@ async function deployGooglePlay(options, deps = defaultGooglePlayDeps) {
928
928
  //#endregion
929
929
  exports.deployGooglePlay = deployGooglePlay;
930
930
 
931
- //# sourceMappingURL=googlePlay-DNgybe5t.cjs.map
931
+ //# sourceMappingURL=googlePlay-B6uiHi65.cjs.map