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

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 (37) 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/{github-D2eABlxM.mjs → github-BMakI-k3.mjs} +5 -4
  8. package/dist/github-BMakI-k3.mjs.map +1 -0
  9. package/dist/{github-DDOHjdS9.cjs → github-C3gblUvr.cjs} +5 -3
  10. package/dist/github-C3gblUvr.cjs.map +1 -0
  11. package/dist/{googlePlay-DNgybe5t.cjs → googlePlay-B6uiHi65.cjs} +2 -2
  12. package/dist/{googlePlay-DNgybe5t.cjs.map → googlePlay-B6uiHi65.cjs.map} +1 -1
  13. package/dist/{googlePlay-DY9vZQ1p.mjs → googlePlay-BYZ-J5sC.mjs} +4 -4
  14. package/dist/{googlePlay-DY9vZQ1p.mjs.map → googlePlay-BYZ-J5sC.mjs.map} +1 -1
  15. package/dist/index.cjs +167 -74
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.mjs +176 -85
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{migrate-B9GswIAr.mjs → migrate-xHYaeBkd.mjs} +2 -2
  20. package/dist/{migrate-B9GswIAr.mjs.map → migrate-xHYaeBkd.mjs.map} +1 -1
  21. package/dist/{publish-B-EIjfQ0.mjs → publish-CSctCNf4.mjs} +2 -2
  22. package/dist/{publish-B-EIjfQ0.mjs.map → publish-CSctCNf4.mjs.map} +1 -1
  23. package/dist/{publish-CPgSbtcs.cjs → publish-SDG1WfYn.cjs} +2 -2
  24. package/dist/{publish-CPgSbtcs.cjs.map → publish-SDG1WfYn.cjs.map} +1 -1
  25. package/dist/serverSetup--O61SRvg.mjs +558 -0
  26. package/dist/serverSetup--O61SRvg.mjs.map +1 -0
  27. package/dist/serverSetup-BCbD8ksJ.cjs +558 -0
  28. package/dist/serverSetup-BCbD8ksJ.cjs.map +1 -0
  29. package/dist/{stage-DjxR_7C0.mjs → stage-ClKTHQoM.mjs} +2 -2
  30. package/dist/{stage-DjxR_7C0.mjs.map → stage-ClKTHQoM.mjs.map} +1 -1
  31. package/dist/{terminal-mXM-Lqc6.mjs → terminal-CybB-cjk.mjs} +2 -2
  32. package/dist/{terminal-mXM-Lqc6.mjs.map → terminal-CybB-cjk.mjs.map} +1 -1
  33. package/package.json +15 -15
  34. package/dist/eas-6ezdJtry.mjs.map +0 -1
  35. package/dist/eas-BkLA60P2.cjs.map +0 -1
  36. package/dist/github-D2eABlxM.mjs.map +0 -1
  37. package/dist/github-DDOHjdS9.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-C3gblUvr.cjs","names":["GkmError","projectBucketName","deployIdentity","PROJECT_BUCKET_CREATE_ACTIONS","s3SecretsLocation","s3SecretsKey","secretsParameterName","secretsManagerSecretName","stageProvider","bucketName","iamUserName","IAM_USER_PATH","stageBackups","backupsUserName","PROJECT_BUCKET_LIFECYCLE_ACTIONS","stageDnsDomains","execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","ListRoleTagsCommand","CreateRoleCommand","DetachRolePolicyCommand","DeleteRolePolicyCommand","AttachRolePolicyCommand","TagRoleCommand","PutRolePolicyCommand","UntagRoleCommand","ListAttachedRolePoliciesCommand","IAMClient","loadWorkspaceConfig","discover","constructGlobs","readKey","keystoreProject","getKeyPath","isRemoteStore","secretsStoreFor","GkmError"],"sources":["../src/deploy/githubOidc.ts","../src/deploy/githubPolicy.ts","../src/deploy/github.ts"],"sourcesContent":["/**\n * The `sub` GitHub puts in a deploy job's OIDC token — what the deploy role's\n * trust policy has to match exactly.\n *\n * It is not always `repo:<owner>/<name>:environment:<stage>`. A repository\n * (or its organisation) decides the format, and says which through\n * `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`:\n *\n * - the default: `repo:<owner>/<name>:environment:<stage>`;\n * - the immutable subject — the default for repositories created after\n * 15 July 2026, opt-in before — puts the owner's and the repository's ids in\n * the `repo` segment: `repo:<owner>@<ownerId>/<name>@<repoId>:environment:<stage>`.\n * The API hands that segment back as `sub_claim_prefix`;\n * - a custom template (`use_default: false`) builds the `sub` from\n * `include_claim_keys`, in order, each as `<key>:<value>` joined by `:`,\n * where `context` is `environment:<stage>` in an environment job.\n *\n * A `:` inside a value is sent as `%3A`.\n *\n * See https://docs.github.com/en/actions/reference/security/oidc.\n */\n\nimport { GkmError } from '../errors';\nimport type { Gh } from './github.js';\n\n/** `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`. */\nexport interface OidcSubjectCustomization {\n\tuse_default: boolean;\n\tinclude_claim_keys?: string[];\n\tuse_immutable_subject?: boolean;\n\t/** The `repo` segment of the subject, as GitHub will send it. */\n\tsub_claim_prefix?: string;\n}\n\n/** What `GET /repos/{owner}/{repo}` says that a subject may include. */\nexport interface RepoFacts {\n\townerId: number;\n\trepoId: number;\n\tvisibility: string;\n}\n\n/**\n * Template keys whose values are known for a repository's environment job\n * before the job runs. Anything else — `job_workflow_ref`, `ref`, `sha`,\n * `run_id`, `workflow`, `actor`, a custom property — depends on the run.\n */\nexport const SUPPORTED_CLAIM_KEYS = [\n\t'repo',\n\t'context',\n\t'environment',\n\t'repository',\n\t'repository_owner',\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n] as const;\n\nconst NEEDS_FACTS = new Set([\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n]);\n\n/** A `:` inside a claim value is sent as `%3A`. */\nfunction claim(value: string): string {\n\treturn value.replaceAll(':', '%3A');\n}\n\n/** Which kind of subject the repository uses, for the output. */\nexport type OidcSubjectKind = 'default' | 'immutable' | 'custom' | 'assumed';\n\n/** Whether {@link oidcSubject} needs the repository's ids or visibility. */\nexport function needsRepoFacts(\n\tcustomization: OidcSubjectCustomization,\n): boolean {\n\tconst immutableWithoutPrefix =\n\t\tcustomization.use_immutable_subject === true &&\n\t\t!customization.sub_claim_prefix;\n\tif (customization.use_default) return immutableWithoutPrefix;\n\n\tconst keys = customization.include_claim_keys ?? [];\n\treturn keys.some(\n\t\t(key) => NEEDS_FACTS.has(key) || (key === 'repo' && immutableWithoutPrefix),\n\t);\n}\n\n/**\n * The exact `sub` of a job in `repo`'s `stage` environment.\n *\n * @throws OidcSubjectNotSupported when a custom template includes a claim\n * whose value depends on the run, so no one subject can be trusted.\n */\nexport function oidcSubject(\n\trepo: string,\n\tstage: string,\n\tcustomization: OidcSubjectCustomization,\n\tfacts?: RepoFacts,\n): { subject: string; kind: OidcSubjectKind } {\n\tconst [owner = '', name = ''] = repo.split('/');\n\tconst environment = `environment:${claim(stage)}`;\n\n\tconst repoSegment = (): string => {\n\t\tif (customization.sub_claim_prefix) return customization.sub_claim_prefix;\n\t\tif (customization.use_immutable_subject) {\n\t\t\tif (!facts) throw new RepoFactsRequired(repo);\n\t\t\treturn `repo:${owner}@${facts.ownerId}/${name}@${facts.repoId}`;\n\t\t}\n\t\treturn `repo:${repo}`;\n\t};\n\tconst immutable =\n\t\tcustomization.use_immutable_subject === true ||\n\t\tcustomization.sub_claim_prefix !== undefined;\n\n\tif (customization.use_default) {\n\t\treturn {\n\t\t\tsubject: `${repoSegment()}:${environment}`,\n\t\t\tkind: immutable ? 'immutable' : 'default',\n\t\t};\n\t}\n\n\tconst keys = customization.include_claim_keys ?? [];\n\tconst unsupported = keys.filter(\n\t\t(key) => !(SUPPORTED_CLAIM_KEYS as readonly string[]).includes(key),\n\t);\n\tif (keys.length === 0 || unsupported.length > 0) {\n\t\tthrow new OidcSubjectNotSupported(repo, keys, unsupported);\n\t}\n\tif (keys.some((key) => NEEDS_FACTS.has(key)) && !facts) {\n\t\tthrow new RepoFactsRequired(repo);\n\t}\n\n\tconst parts = keys.map((key) => {\n\t\tswitch (key) {\n\t\t\tcase 'repo':\n\t\t\t\treturn repoSegment();\n\t\t\tcase 'context':\n\t\t\tcase 'environment':\n\t\t\t\treturn environment;\n\t\t\tcase 'repository':\n\t\t\t\treturn `repository:${claim(repo)}`;\n\t\t\tcase 'repository_owner':\n\t\t\t\treturn `repository_owner:${claim(owner)}`;\n\t\t\tcase 'repository_owner_id':\n\t\t\t\treturn `repository_owner_id:${facts!.ownerId}`;\n\t\t\tcase 'repository_id':\n\t\t\t\treturn `repository_id:${facts!.repoId}`;\n\t\t\tdefault:\n\t\t\t\treturn `repository_visibility:${claim(facts!.visibility)}`;\n\t\t}\n\t});\n\treturn { subject: parts.join(':'), kind: 'custom' };\n}\n\n/** What {@link resolveOidcSubject} found. */\nexport interface ResolvedOidcSubject {\n\tsubject: string;\n\tkind: OidcSubjectKind;\n\t/** Set when the format could not be read and the default was assumed. */\n\twarning?: string;\n}\n\n/** The endpoint a repository's subject format is read from. */\nexport function customizationEndpoint(repo: string): string {\n\treturn `repos/${repo}/actions/oidc/customization/sub`;\n}\n\n/**\n * The `sub` GitHub will send for `repo`'s `stage` environment, read from the\n * repository's OIDC settings through `gh`.\n *\n * A repository on a custom template that names no keys of its own uses its\n * organisation's, read from `orgs/{owner}/actions/oidc/customization/sub`.\n * When the settings cannot be read at all — no permission, or a GitHub\n * Enterprise Server without the endpoint — the default format is assumed, with\n * a warning saying where to check it.\n */\nexport function resolveOidcSubject(\n\tgh: Gh,\n\trepo: string,\n\tstage: string,\n): ResolvedOidcSubject {\n\tconst endpoint = customizationEndpoint(repo);\n\tlet customization: OidcSubjectCustomization;\n\ttry {\n\t\tcustomization = JSON.parse(gh(['api', endpoint]));\n\t} catch {\n\t\treturn {\n\t\t\tsubject: `repo:${repo}:environment:${claim(stage)}`,\n\t\t\tkind: 'assumed',\n\t\t\twarning: `Could not read ${endpoint}, so the default subject format is assumed. Check it with: gh api ${endpoint} — a repository on the immutable subject or a custom template sends a different sub, and the role would not be assumable.`,\n\t\t};\n\t}\n\n\tif (\n\t\t!customization.use_default &&\n\t\t(customization.include_claim_keys ?? []).length === 0\n\t) {\n\t\t// The repository follows its organisation's template.\n\t\tconst [owner = ''] = repo.split('/');\n\t\tconst orgEndpoint = `orgs/${owner}/actions/oidc/customization/sub`;\n\t\tlet org: { include_claim_keys?: string[]; use_immutable_subject?: boolean };\n\t\ttry {\n\t\t\torg = JSON.parse(gh(['api', orgEndpoint]));\n\t\t} catch {\n\t\t\tthrow new OrgOidcTemplateUnreadable(repo, orgEndpoint);\n\t\t}\n\t\tcustomization = {\n\t\t\t...customization,\n\t\t\tinclude_claim_keys: org.include_claim_keys ?? [],\n\t\t\tuse_immutable_subject:\n\t\t\t\tcustomization.use_immutable_subject ?? org.use_immutable_subject,\n\t\t};\n\t}\n\n\tconst facts = needsRepoFacts(customization)\n\t\t? readRepoFacts(gh, repo)\n\t\t: undefined;\n\treturn oidcSubject(repo, stage, customization, facts);\n}\n\nfunction readRepoFacts(gh: Gh, repo: string): RepoFacts {\n\tconst raw = JSON.parse(gh(['api', `repos/${repo}`])) as {\n\t\tid: number;\n\t\towner: { id: number };\n\t\tvisibility: string;\n\t};\n\treturn {\n\t\townerId: raw.owner.id,\n\t\trepoId: raw.id,\n\t\tvisibility: raw.visibility,\n\t};\n}\n\n/**\n * The repository's custom subject template includes a claim whose value\n * depends on the run — or names none — so no one subject can be trusted.\n */\nexport class OidcSubjectNotSupported extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly includeClaimKeys: string[],\n\t\treadonly unsupportedKeys: string[],\n\t) {\n\t\tsuper(\n\t\t\t`${repo} builds its OIDC subject from a custom template, ${JSON.stringify(includeClaimKeys)}, and ${\n\t\t\t\tunsupportedKeys.length > 0\n\t\t\t\t\t? `${unsupportedKeys.join(', ')} depend${unsupportedKeys.length === 1 ? 's' : ''} on the run`\n\t\t\t\t\t: 'it names no claims'\n\t\t\t}, so the deploy role cannot trust one exact subject. Set the repository back to the default subject — gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true — (or keep the immutable one by also passing -F use_immutable_subject=true), or use a template built only from ${SUPPORTED_CLAIM_KEYS.join(', ')}; then run this again.`,\n\t\t);\n\t\tthis.name = 'OidcSubjectNotSupported';\n\t}\n}\n\n/** The subject needs the repository's ids, and none were read. */\nexport class RepoFactsRequired extends GkmError {\n\tconstructor(readonly repo: string) {\n\t\tsuper(\n\t\t\t`The OIDC subject of ${repo} includes its owner and repository ids; read them with gh api repos/${repo} and pass them in.`,\n\t\t);\n\t\tthis.name = 'RepoFactsRequired';\n\t}\n}\n\n/**\n * The repository uses its organisation's subject template, and that template\n * could not be read — so what GitHub sends is unknown, and is not guessed.\n */\nexport class OrgOidcTemplateUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly endpoint: string,\n\t) {\n\t\tsuper(\n\t\t\t`${repo} uses its organisation's OIDC subject template, and ${endpoint} could not be read. Run this as an organisation owner (gh auth refresh -s admin:org), or set the repository back to the default subject with gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true; then run this again.`,\n\t\t);\n\t\tthis.name = 'OrgOidcTemplateUnreadable';\n\t}\n}\n","/**\n * What the deploy role may do in the stage's account.\n *\n * An SST stage creates its own stack, which is most of AWS, so its role gets\n * `AdministratorAccess`. A stage deployed with the `compose` target builds and\n * runs containers on a server; from AWS its deploy job needs only the stage's\n * secrets — read, and written back when a deploy generates a new one: the one\n * object in the project bucket, or the SSM parameter or Secrets Manager secret\n * — and,\n * when the deploy state is kept in AWS, the stage's state, and, when the\n * stage's domain is in Route53, the records of its hosted zone, and, when an\n * `s3` provider backs its buckets, those buckets and the IAM user each is\n * reached with — by the names the provider gives them — and, when it runs\n * Postgres, the project bucket its backups go to and the backups user. So a\n * compose stage gets an inline policy naming exactly those resources and\n * nothing else.\n *\n * `--policy-arn` replaces either.\n */\n\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { stageBackups } from '../backups/config.js';\nimport { backupsUserName } from '../backups/provision.js';\nimport { stageDnsDomains } from '../compose/dns.js';\nimport { stageProvider } from '../providers/config.js';\nimport {\n\tPROJECT_BUCKET_CREATE_ACTIONS,\n\tPROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\tprojectBucketName,\n} from '../providers/projectBucket.js';\nimport {\n\tbucketName,\n\tIAM_USER_PATH,\n\tiamUserName,\n\tSUFFIX_LENGTH,\n} from '../providers/s3/naming.js';\nimport { secretsParameterName } from '../secrets/aws.js';\nimport { s3SecretsKey, s3SecretsLocation } from '../secrets/providers.js';\nimport { secretsManagerSecretName } from '../secrets/secretsManager.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { deployIdentity } from './identity.js';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default\n * outside compose — said out loud in the output, and replaceable with\n * `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\n/** The name of the inline policy a compose stage's role is given. */\nexport const SCOPED_POLICY_NAME = 'gkm-deploy';\n\n/** One statement of an IAM policy document. */\nexport interface PolicyStatement {\n\tSid: string;\n\tEffect: 'Allow';\n\tAction: string[];\n\tResource: string | string[];\n\tCondition?: Record<string, Record<string, string>>;\n}\n\n/** What the role is given. */\nexport type DeployAccess =\n\t| {\n\t\t\tkind: 'managed';\n\t\t\tpolicyArn: string;\n\t\t\t/** Why, for the output. */\n\t\t\treason: string;\n\t }\n\t| {\n\t\t\tkind: 'scoped';\n\t\t\t/** The statements, in the account the role is in. */\n\t\t\tstatements(account: string): PolicyStatement[];\n\t\t\t/** What each statement is for, for the output. */\n\t\t\tdescribe: string[];\n\t };\n\n/** Every target the stage's apps deploy through. */\nexport function deployTargets(workspace: NormalizedWorkspace): string[] {\n\tconst targets = new Set<string>([workspace.deploy?.default ?? 'dokploy']);\n\tfor (const app of Object.values(workspace.apps)) {\n\t\ttargets.add(app.resolvedDeployTarget);\n\t}\n\treturn [...targets].sort();\n}\n\n/**\n * The access the stage's deploy role is given: `--policy-arn` when passed,\n * the scoped policy for a stage only the compose target deploys, and\n * `AdministratorAccess` otherwise.\n */\nexport function deployAccess(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tpolicyArn?: string,\n\t/** The stage's constructs: its buckets, and whether it runs Postgres. */\n\tmanifest: ConstructManifest = {},\n): DeployAccess {\n\tconst buckets = Object.entries(manifest)\n\t\t.filter(([, d]) => d.kind === 'objects')\n\t\t.map(([id]) => id);\n\tif (policyArn) {\n\t\treturn { kind: 'managed', policyArn, reason: '--policy-arn' };\n\t}\n\n\tconst targets = deployTargets(workspace);\n\tif (targets.length !== 1 || targets[0] !== 'compose') {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `the ${targets.join(', ')} target${targets.length === 1 ? '' : 's'} deploy${targets.length === 1 ? 's' : ''} infrastructure; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst project = workspace.name;\n\tconst store = workspace.secrets.store ?? 'file';\n\tconst state = workspace.state;\n\tconst custom =\n\t\t(typeof store === 'object' && typeof store.provider === 'object') ||\n\t\t(state !== undefined && typeof state.provider === 'object');\n\tif (custom) {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `a custom ${typeof store === 'object' && typeof store.provider === 'object' ? 'secrets store' : 'state provider'} cannot be scoped; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst describe: string[] = [];\n\tconst build: ((account: string) => PolicyStatement[])[] = [];\n\n\t// The project bucket's name is fixed by the account, so the role is given\n\t// exactly it, and the actions that create it for whichever run is first.\n\tconst projectBucketIn = (account: string) =>\n\t\tprojectBucketName(deployIdentity(workspace, stage).scope, account);\n\tconst projectBucketStatement = (account: string): PolicyStatement => ({\n\t\tSid: 'ProjectBucket',\n\t\tEffect: 'Allow',\n\t\tAction: [...PROJECT_BUCKET_CREATE_ACTIONS],\n\t\tResource: `arn:aws:s3:::${projectBucketIn(account)}`,\n\t});\n\t// Whether the state's statements already cover the secrets' bucket.\n\tconst stateInSecretsBucket =\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 's3' &&\n\t\tstate?.provider === 's3' &&\n\t\tstate.bucket === (store as { bucket?: string }).bucket;\n\n\tif (typeof store === 'object' && store.provider === 's3') {\n\t\tconst location = s3SecretsLocation(store, state);\n\t\tconst key = s3SecretsKey(location.prefix, project, stage);\n\t\tconst named = location.bucket;\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and update s3://${named}/${key}`\n\t\t\t\t: `read and update ${key} in the project bucket ${projectBucketIn('<account>')}${stateInSecretsBucket ? '' : ', creating it'}`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['s3:GetObject', 's3:PutObject'],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}/${key}`,\n\t\t\t},\n\t\t\t// The state's statements give these when it is in the same bucket.\n\t\t\t...(stateInSecretsBucket\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// HeadBucket, and secrets not written yet a 404, not a 403.\n\t\t\t\t\t\t\tSid: 'StageSecretsBucket',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['s3:ListBucket'],\n\t\t\t\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t\t\t\t]),\n\t\t]);\n\t} else if (typeof store === 'object' && store.provider === 'ssm') {\n\t\tconst { region } = store;\n\t\tconst name = secretsParameterName(project, stage);\n\t\tdescribe.push(`read and update ${name} (SSM, ${region})`);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${name}`,\n\t\t\t},\n\t\t]);\n\t} else if (\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 'secrets-manager'\n\t) {\n\t\tconst { region, kmsKeyId } = store as { region: string; kmsKeyId?: string };\n\t\tconst name = secretsManagerSecretName(project, stage);\n\t\tdescribe.push(`read and update ${name} (Secrets Manager, ${region})`);\n\t\tbuild.push((account) => {\n\t\t\tconst statements: PolicyStatement[] = [\n\t\t\t\t{\n\t\t\t\t\tSid: 'StageSecrets',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: [\n\t\t\t\t\t\t'secretsmanager:GetSecretValue',\n\t\t\t\t\t\t'secretsmanager:PutSecretValue',\n\t\t\t\t\t\t'secretsmanager:CreateSecret',\n\t\t\t\t\t],\n\t\t\t\t\t// Secrets Manager appends six random characters to a secret's ARN.\n\t\t\t\t\tResource: `arn:aws:secretsmanager:${region}:${account}:secret:${name}-??????`,\n\t\t\t\t},\n\t\t\t];\n\t\t\tif (kmsKeyId) {\n\t\t\t\tstatements.push({\n\t\t\t\t\tSid: 'StageSecretsKey',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: ['kms:Decrypt', 'kms:GenerateDataKey'],\n\t\t\t\t\tResource: kmsKeyId.startsWith('arn:') ? kmsKeyId : '*',\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t\t'kms:ViaService': `secretsmanager.${region}.amazonaws.com`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn statements;\n\t\t});\n\t}\n\n\tif (state?.provider === 'ssm') {\n\t\tconst { region } = state;\n\t\tconst path = `/gkm/${project}/${stage}/`;\n\t\tdescribe.push(\n\t\t\t`read and write the deploy state under ${path} (SSM, ${region})`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageState',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter', 'ssm:DeleteParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${path}*`,\n\t\t\t},\n\t\t]);\n\t} else if (state?.provider === 's3') {\n\t\tconst prefix = (state.prefix ?? 'gkm').replace(/\\/+$/, '');\n\t\tconst path = `${prefix ? `${prefix}/` : ''}${project}/${stage}/`;\n\t\tconst named = state.bucket;\n\t\t// No bucket named: the project bucket, which the first deploy creates.\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and write the deploy state under s3://${named}/${path}`\n\t\t\t\t: `create the project bucket ${projectBucketIn('<account>')}, and read and write the deploy state under ${path} in it`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t...stateBucketStatements(bucketIn(account), path),\n\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t]);\n\t}\n\n\t// The deploy creates each bucket an s3 provider backs, and the IAM user\n\t// and key it is reached with: by their names — the good one, or the good\n\t// one with a clash's six-character suffix — and nothing else.\n\tconst objects = stageProvider(workspace, 'objects', stage);\n\tif (objects.mode === 'provider' && objects.name === 's3' && buckets.length) {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst names = [...buckets].sort().map((id) => ({\n\t\t\tbucket: bucketName({ scope, stage, id }),\n\t\t\tuser: iamUserName({ scope, stage, id }),\n\t\t}));\n\t\tconst suffix = '?'.repeat(SUFFIX_LENGTH - 1);\n\t\tdescribe.push(\n\t\t\t`create and configure the buckets ${names.map((n) => n.bucket).join(', ')} and their IAM users (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBuckets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:CreateBucket',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketTagging',\n\t\t\t\t\t's3:PutBucketTagging',\n\t\t\t\t\t's3:GetBucketPublicAccessBlock',\n\t\t\t\t\t's3:PutBucketPublicAccessBlock',\n\t\t\t\t\t's3:GetBucketPolicy',\n\t\t\t\t\t's3:PutBucketPolicy',\n\t\t\t\t\t's3:GetEncryptionConfiguration',\n\t\t\t\t\t's3:PutEncryptionConfiguration',\n\t\t\t\t\t's3:GetBucketVersioning',\n\t\t\t\t\t's3:PutBucketVersioning',\n\t\t\t\t\t's3:GetBucketCORS',\n\t\t\t\t\t's3:PutBucketCORS',\n\t\t\t\t],\n\t\t\t\tResource: names.flatMap((n) => [\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}`,\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}-${suffix}`,\n\t\t\t\t]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBucketUsers',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: names.map(\n\t\t\t\t\t(n) => `arn:aws:iam::${account}:user${IAM_USER_PATH}${n.user}`,\n\t\t\t\t),\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The stage's backups: the deploy creates the project bucket if it is not\n\t// there, keeps its lifecycle, and creates the backups user and its key.\n\t// The role never reads the backups — `gkm backup:list` and `:restore`\n\t// run with a person's own credentials.\n\tif (stageBackups(workspace, manifest, stage).mode === 'on') {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst user = backupsUserName(scope, stage);\n\t\tconst bucketIn = (account: string) => projectBucketName(scope, account);\n\t\tdescribe.push(\n\t\t\t`create the project bucket ${bucketIn('<account>')} and keep its lifecycle, and create the backups user ${user} and its key (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsBucket',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t...new Set([\n\t\t\t\t\t\t...PROJECT_BUCKET_CREATE_ACTIONS,\n\t\t\t\t\t\t...PROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\t\t\t\t\t]),\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsUser',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:iam::${account}:user${IAM_USER_PATH}${user}`,\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The deploy writes the stage's hosts' records: in Route53, only that\n\t// zone's — and, with no hostedZoneId to name it, the zone is looked up\n\t// by name, which IAM cannot scope.\n\tfor (const { domain, config } of stageDnsDomains(\n\t\tstage,\n\t\tworkspace.domains,\n\t\tworkspace.dns,\n\t)) {\n\t\tif (config.provider !== 'route53') continue;\n\t\tconst zoneId = (config as { hostedZoneId?: string }).hostedZoneId?.replace(\n\t\t\t/^\\/?hostedzone\\//,\n\t\t\t'',\n\t\t);\n\t\tdescribe.push(\n\t\t\t`write the A, AAAA and CNAME records of ${domain} (Route53${zoneId ? `, ${zoneId}` : ''})`,\n\t\t);\n\t\tbuild.push(() => [\n\t\t\t{\n\t\t\t\tSid: 'StageDnsRecords',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'route53:ChangeResourceRecordSets',\n\t\t\t\t\t'route53:ListResourceRecordSets',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:route53:::hostedzone/${zoneId ?? '*'}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageDnsChanges',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['route53:GetChange'],\n\t\t\t\tResource: 'arn:aws:route53:::change/*',\n\t\t\t},\n\t\t\t...(zoneId\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'StageDnsZones',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['route53:ListHostedZonesByName'],\n\t\t\t\t\t\t\tResource: '*',\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\treturn {\n\t\tkind: 'scoped',\n\t\tstatements: (account) => build.flatMap((statements) => statements(account)),\n\t\tdescribe,\n\t};\n}\n\n/**\n * Reading and writing one stage's deploy state in `bucket`, as the deploy\n * role is given it.\n */\nexport function stateBucketStatements(\n\tbucket: string,\n\tpath: string,\n): PolicyStatement[] {\n\treturn [\n\t\t{\n\t\t\tSid: 'StageState',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n\t\t\tResource: `arn:aws:s3:::${bucket}/${path}*`,\n\t\t},\n\t\t// HeadBucket, and a state not written yet a 404 rather than a 403.\n\t\t{\n\t\t\tSid: 'StageStateBucket',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:ListBucket'],\n\t\t\tResource: `arn:aws:s3:::${bucket}`,\n\t\t},\n\t];\n}\n\n/** The scoped access as a policy document, or null when it allows nothing. */\nexport function scopedPolicyDocument(\n\tstatements: PolicyStatement[],\n): string | null {\n\tif (statements.length === 0) return null;\n\treturn JSON.stringify({ Version: '2012-10-17', Statement: statements });\n}\n\n/** The account an IAM ARN is in: `arn:aws:iam::<account>:…`. */\nexport function accountOf(arn: string): string {\n\treturn arn.split(':')[4] ?? '';\n}\n","/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * S3, SSM or Secrets Manager the deploy job reads them with the role instead, so\n * no key is handed to GitHub, and this checks the stage's store in the same\n * account, with the same profile. Re-running it converges rather than duplicating.\n *\n * The role trusts the exact `sub` GitHub sends for the environment, read from\n * the repository's OIDC subject settings (`githubOidc.ts`) — the default, the\n * immutable subject with the owner's and repository's ids, or a custom\n * template. A re-run rewrites an existing role's trust to it, so running this\n * again repairs a role that trusted the wrong format. What the role may do is\n * `githubPolicy.ts`: `AdministratorAccess` for SST, and for a compose stage\n * only its own secrets (and deploy state, when that is in AWS).\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tDeleteRolePolicyCommand,\n\tDetachRolePolicyCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListAttachedRolePoliciesCommand,\n\tListOpenIDConnectProvidersCommand,\n\tListRoleTagsCommand,\n\tPutRolePolicyCommand,\n\tTagRoleCommand,\n\tUntagRoleCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { GkmError } from '../errors';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { resolveOidcSubject } from './githubOidc.js';\nimport {\n\taccountOf,\n\tdeployAccess,\n\tSCOPED_POLICY_NAME,\n\tscopedPolicyDocument,\n} from './githubPolicy.js';\n\nexport { DEFAULT_POLICY_ARN, SCOPED_POLICY_NAME } from './githubPolicy.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * The tag recording which managed policy gkm attached to the role — so a\n * re-run detaches only what gkm put there, never a policy someone else did.\n */\nexport const POLICY_TAG = 'gkm:policy-arn';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets, in place of the default. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\n/** The system's `gh`. */\nexport const ghCli: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t// stderr captured, so a read that fails (a 404) is not printed as noise.\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t...(input === undefined ? {} : { input }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job whose OIDC token carries exactly\n * `subject` — the repository's `<stage>` environment, in the format the\n * repository sends (see `githubOidc.ts`), and nothing else. Not another\n * branch's job, not another environment's: that is what keeps a staging\n * deploy from using the production role.\n */\nexport function trustPolicy(providerArn: string, subject: string): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: subject,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * The subject a trust policy trusts, or undefined when it names none. IAM\n * hands documents back URL-encoded.\n */\nexport function trustedSubject(\n\tdocument: string | undefined,\n): string | undefined {\n\tif (!document) return undefined;\n\tlet parsed: {\n\t\tStatement?: { Condition?: Record<string, Record<string, unknown>> }[];\n\t};\n\ttry {\n\t\tparsed = JSON.parse(safeDecode(document));\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const statement of parsed.Statement ?? []) {\n\t\tfor (const operator of Object.values(statement.Condition ?? {})) {\n\t\t\tconst value = operator[`${GITHUB_OIDC_HOST}:sub`];\n\t\t\tif (typeof value === 'string') return value;\n\t\t\tif (Array.isArray(value)) return value.join(', ');\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction safeDecode(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\n\t}\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/**\n * What the role is given: a managed policy, attached; or an inline policy\n * document — null for none at all.\n */\nexport type RoleAccess = { managed: string } | { inline: string | null };\n\n/** What {@link ensureRole} did. */\nexport interface RoleOutcome {\n\tarn: string;\n\tcreated: boolean;\n\t/** The subject the role trusted before, when it existed. */\n\tpreviousSubject?: string;\n\t/** Managed policies gkm had attached and detached now. */\n\tdetached: string[];\n\t/**\n\t * Managed policies still attached that the role no longer needs, which gkm\n\t * did not record attaching — left alone, for a person to detach.\n\t */\n\tleftAttached: string[];\n}\n\nfunction isNoSuchEntity(error: unknown): boolean {\n\treturn (error as { name?: string }).name === 'NoSuchEntityException';\n}\n\n/**\n * The deploy role, created or brought back to this trust and access.\n *\n * Converges: re-running rewrites the trust, and moves the role from one access\n * to another — detaching a managed policy only when the role's\n * {@link POLICY_TAG} says gkm attached it.\n */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\taccess: RoleAccess,\n): Promise<RoleOutcome> {\n\tlet arn: string;\n\tlet created = false;\n\tlet previousSubject: string | undefined;\n\tlet recorded: string | undefined;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tpreviousSubject = trustedSubject(Role!.AssumeRolePolicyDocument);\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tconst { Tags = [] } = await iam.send(\n\t\t\tnew ListRoleTagsCommand({ RoleName: name }),\n\t\t);\n\t\trecorded = Tags.find((tag) => tag.Key === POLICY_TAG)?.Value;\n\t} catch (error) {\n\t\tif (!isNoSuchEntity(error)) throw error;\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tcreated = true;\n\t}\n\n\tconst detached: string[] = [];\n\tconst leftAttached: string[] = [];\n\tconst detach = async (policyArn: string) => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DetachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t\t\t);\n\t\t\tdetached.push(policyArn);\n\t\t} catch (error) {\n\t\t\t// Already gone — by hand, or a run that stopped halfway.\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\tconst deleteInline = async () => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DeleteRolePolicyCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\n\tif ('managed' in access) {\n\t\t// Attaching an attached policy is a no-op, so this converges too.\n\t\tawait iam.send(\n\t\t\tnew AttachRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyArn: access.managed,\n\t\t\t}),\n\t\t);\n\t\tif (recorded && recorded !== access.managed) await detach(recorded);\n\t\tif (recorded !== access.managed) {\n\t\t\tawait iam.send(\n\t\t\t\tnew TagRoleCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tTags: [{ Key: POLICY_TAG, Value: access.managed }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!created) await deleteInline();\n\t\treturn { arn, created, previousSubject, detached, leftAttached };\n\t}\n\n\tif (access.inline) {\n\t\tawait iam.send(\n\t\t\tnew PutRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\tPolicyDocument: access.inline,\n\t\t\t}),\n\t\t);\n\t} else if (!created) {\n\t\tawait deleteInline();\n\t}\n\tif (created) return { arn, created, previousSubject, detached, leftAttached };\n\n\tif (recorded) {\n\t\tawait detach(recorded);\n\t\tawait iam.send(\n\t\t\tnew UntagRoleCommand({ RoleName: name, TagKeys: [POLICY_TAG] }),\n\t\t);\n\t}\n\t// A role from before gkm recorded what it attached: what is attached may\n\t// be gkm's or someone's own, so it is named, not detached.\n\tconst { AttachedPolicies = [] } = await iam.send(\n\t\tnew ListAttachedRolePoliciesCommand({ RoleName: name }),\n\t);\n\tfor (const policy of AttachedPolicies) {\n\t\tif (policy.PolicyArn && !detached.includes(policy.PolicyArn)) {\n\t\t\tleftAttached.push(policy.PolicyArn);\n\t\t}\n\t}\n\treturn { arn, created, previousSubject, detached, leftAttached };\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string; subject: string }> {\n\tconst gh = deps.gh ?? ghCli;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\t// The exact subject GitHub will send — read from the repository's OIDC\n\t// settings, before anything is changed, so a format no role can trust\n\t// fails here.\n\tconst oidc = resolveOidcSubject(gh, repo, options.stage);\n\t// The buckets an s3 provider creates are named after their constructs,\n\t// and a stage with a database is backed up: the role is scoped to\n\t// exactly those.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t});\n\tconst access = deployAccess(\n\t\tworkspace,\n\t\toptions.stage,\n\t\toptions.policyArn,\n\t\tmanifest,\n\t);\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tif (access.kind === 'managed') {\n\t\tlogger.log(` Policy: ${access.policyArn} (${access.reason})`);\n\t} else if (access.describe.length > 0) {\n\t\tlogger.log(\n\t\t\t` Policy: inline \"${SCOPED_POLICY_NAME}\", scoped to the compose deploy — only:`,\n\t\t);\n\t\tfor (const line of access.describe) logger.log(` ${line}`);\n\t} else {\n\t\tlogger.log(\n\t\t\t' Policy: none — a compose deploy with file secrets and local state reads nothing from AWS',\n\t\t);\n\t}\n\tlogger.log(\n\t\t` Trusted by: ${oidc.subject} only (${SUBJECT_KINDS[oidc.kind]})`,\n\t);\n\tif (oidc.warning) logger.log(` ⚠ ${oidc.warning}`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tif (access.kind === 'scoped') {\n\t\t\tconst document = scopedPolicyDocument(access.statements('<account>'));\n\t\t\tif (document) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`\\n Inline policy:\\n${JSON.stringify(JSON.parse(document), null, 2).replace(/^/gm, ' ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo, subject: oidc.subject };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst outcome = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, oidc.subject),\n\t\taccess.kind === 'managed'\n\t\t\t? { managed: access.policyArn }\n\t\t\t: {\n\t\t\t\t\tinline: scopedPolicyDocument(\n\t\t\t\t\t\taccess.statements(accountOf(providerArn)),\n\t\t\t\t\t),\n\t\t\t\t},\n\t);\n\tconst roleArn = outcome.arn;\n\tlogger.log(`\\n ✓ AWS: ${roleArn}${outcome.created ? ' (created)' : ''}`);\n\tif (!outcome.created) {\n\t\tlogger.log(\n\t\t\toutcome.previousSubject === oidc.subject\n\t\t\t\t? ` ✓ Trust: unchanged, ${oidc.subject}`\n\t\t\t\t: ` ✓ Trust: ${outcome.previousSubject ?? '(no subject)'} → ${oidc.subject}`,\n\t\t);\n\t}\n\tfor (const detached of outcome.detached) {\n\t\tlogger.log(` ✓ Detached ${detached} (attached by gkm, no longer needed)`);\n\t}\n\tif (access.kind === 'scoped') {\n\t\tfor (const attached of outcome.leftAttached) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ ${attached} is still attached to ${role}. gkm has no record of attaching it, so it is left; the compose deploy does not need it. Detach it with: aws iam detach-role-policy --role-name ${role} --policy-arn ${attached}${options.profile ? ` --profile ${options.profile}` : ''}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo, subject: oidc.subject };\n}\n\nconst SUBJECT_KINDS = {\n\tdefault: \"the repository's default subject\",\n\timmutable: \"the repository's immutable subject\",\n\tcustom: \"the repository's custom subject template\",\n\tassumed: 'assumed: the default format',\n} as const;\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends GkmError {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\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
@@ -1 +1 @@
1
- {"version":3,"file":"googlePlay-DNgybe5t.cjs","names":["GkmError","GkmError","join","gkmHome","spawn","GkmError","GkmError","createSign","join","existsSync","readFileSync","readdirSync","statSync","extname","GkmError","spawn","join","delimiter","canPrompt","loadWorkspaceConfig","loadStage","chooseMobileApp","mobileStage","existsSync","readFile","withStageProfiles","writeFile"],"sources":["../src/mobile/googlePlay/easApi.ts","../src/mobile/googlePlay/google.ts","../src/mobile/googlePlay/gcp.ts","../src/mobile/googlePlay/play.ts","../src/mobile/googlePlay/index.ts"],"sourcesContent":["/**\n * EAS, as `gkm deploy:google-play` reaches it: the GraphQL API eas-cli itself\n * calls, with an `EXPO_TOKEN` — to upload a Google service account key and\n * assign it to the app's Android credentials for EAS Submit, which is what\n * `eas credentials` does interactively.\n *\n * The documents are eas-cli's own (24.x), each selecting only what gkm reads:\n * eas-cli's credentials fragment also brings back the app's keystore and its\n * passwords, which gkm has no reason to hold.\n */\n\nimport { GkmError } from '../../errors';\n\nexport const EAS_GRAPHQL = 'https://api.expo.dev/graphql';\n\n/** No `EXPO_TOKEN` to call EAS with. */\nexport class ExpoTokenMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm uploads the key to EAS with an access token, and EXPO_TOKEN is not ' +\n\t\t\t\t'set. Create one at https://expo.dev/settings/access-tokens and export ' +\n\t\t\t\t'it as EXPO_TOKEN.',\n\t\t);\n\t\tthis.name = 'ExpoTokenMissing';\n\t}\n}\n\n/** EAS refused a call. */\nexport class EasApiError extends GkmError {\n\tconstructor(\n\t\treadonly operation: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`EAS refused ${operation}: ${detail}`);\n\t\tthis.name = 'EasApiError';\n\t}\n}\n\n/** A Google service account key, as EAS holds it. */\nexport interface EasGoogleKey {\n\tid: string;\n\tclientEmail: string;\n\tprivateKeyIdentifier: string;\n}\n\nconst KEY_FIELDS = 'id clientEmail privateKeyIdentifier';\n\nexport interface EasApi {\n\t/** The project's owner account. */\n\tproject(projectId: string): Promise<{\n\t\taccountId: string;\n\t\taccountName: string;\n\t\tfullName: string;\n\t}>;\n\t/** The app's Android credentials for a package, created when absent. */\n\tandroidCredentials(\n\t\tproject: { projectId: string; fullName: string },\n\t\tpackageName: string,\n\t): Promise<{ id: string; submissionsKey?: EasGoogleKey }>;\n\t/** Upload a key file's JSON to the account. */\n\tcreateKey(\n\t\taccountId: string,\n\t\tjson: Record<string, unknown>,\n\t): Promise<EasGoogleKey>;\n\t/** Make a key the one EAS Submit uses for the credentials. */\n\tuseForSubmissions(credentialsId: string, keyId: string): Promise<void>;\n\t/** Every key on the account. */\n\tkeys(accountName: string): Promise<EasGoogleKey[]>;\n\tdeleteKey(id: string): Promise<void>;\n}\n\nexport function easApi(\n\ttoken: string | undefined = process.env.EXPO_TOKEN,\n\tendpoint: string = EAS_GRAPHQL,\n): EasApi {\n\tconst call = async <T>(\n\t\toperation: string,\n\t\tquery: string,\n\t\tvariables: Record<string, unknown>,\n\t): Promise<T> => {\n\t\tif (!token) throw new ExpoTokenMissing();\n\t\tconst response = await fetch(endpoint, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t'content-type': 'application/json',\n\t\t\t},\n\t\t\tbody: JSON.stringify({ query, variables, operationName: operation }),\n\t\t});\n\t\tconst body = (await response.json().catch(() => ({}))) as {\n\t\t\tdata?: T;\n\t\t\terrors?: { message: string }[];\n\t\t};\n\t\tif (!response.ok || body.errors?.length || !body.data) {\n\t\t\tthrow new EasApiError(\n\t\t\t\toperation,\n\t\t\t\tbody.errors?.map((error) => error.message).join('; ') ||\n\t\t\t\t\t`HTTP ${response.status}`,\n\t\t\t);\n\t\t}\n\t\treturn body.data;\n\t};\n\n\tconst credentialsFields = `id googleServiceAccountKeyForSubmissions { ${KEY_FIELDS} }`;\n\ttype Credentials = {\n\t\tid: string;\n\t\tgoogleServiceAccountKeyForSubmissions: EasGoogleKey | null;\n\t};\n\tconst asCredentials = (credentials: Credentials) => ({\n\t\tid: credentials.id,\n\t\t...(credentials.googleServiceAccountKeyForSubmissions\n\t\t\t? { submissionsKey: credentials.googleServiceAccountKeyForSubmissions }\n\t\t\t: {}),\n\t});\n\n\treturn {\n\t\tasync project(projectId) {\n\t\t\tconst data = await call<{\n\t\t\t\tapp: {\n\t\t\t\t\tbyId: {\n\t\t\t\t\t\tfullName: string;\n\t\t\t\t\t\townerAccount: { id: string; name: string };\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'AppByIdQuery',\n\t\t\t\t`query AppByIdQuery($appId: String!) {\n app { byId(appId: $appId) { id fullName ownerAccount { id name } } }\n}`,\n\t\t\t\t{ appId: projectId },\n\t\t\t);\n\t\t\tconst app = data.app.byId;\n\t\t\treturn {\n\t\t\t\taccountId: app.ownerAccount.id,\n\t\t\t\taccountName: app.ownerAccount.name,\n\t\t\t\tfullName: app.fullName,\n\t\t\t};\n\t\t},\n\n\t\tasync androidCredentials(project, packageName) {\n\t\t\tconst found = await call<{\n\t\t\t\tapp: { byFullName: { androidAppCredentials: Credentials[] } };\n\t\t\t}>(\n\t\t\t\t'AndroidAppCredentialsByApplicationIdentifierQuery',\n\t\t\t\t`query AndroidAppCredentialsByApplicationIdentifierQuery(\n $projectFullName: String!\n $applicationIdentifier: String\n $legacyOnly: Boolean\n) {\n app {\n byFullName(fullName: $projectFullName) {\n id\n androidAppCredentials(\n filter: { applicationIdentifier: $applicationIdentifier, legacyOnly: $legacyOnly }\n ) { ${credentialsFields} }\n }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tprojectFullName: project.fullName,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t\tlegacyOnly: false,\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst existing = found.app.byFullName.androidAppCredentials[0];\n\t\t\tif (existing) return asCredentials(existing);\n\n\t\t\tconst created = await call<{\n\t\t\t\tandroidAppCredentials: { createAndroidAppCredentials: Credentials };\n\t\t\t}>(\n\t\t\t\t'CreateAndroidAppCredentialsMutation',\n\t\t\t\t`mutation CreateAndroidAppCredentialsMutation(\n $androidAppCredentialsInput: AndroidAppCredentialsInput!\n $appId: ID!\n $applicationIdentifier: String!\n) {\n androidAppCredentials {\n createAndroidAppCredentials(\n androidAppCredentialsInput: $androidAppCredentialsInput\n appId: $appId\n applicationIdentifier: $applicationIdentifier\n ) { ${credentialsFields} }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsInput: {},\n\t\t\t\t\tappId: project.projectId,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn asCredentials(\n\t\t\t\tcreated.androidAppCredentials.createAndroidAppCredentials,\n\t\t\t);\n\t\t},\n\n\t\tasync createKey(accountId, json) {\n\t\t\tconst data = await call<{\n\t\t\t\tgoogleServiceAccountKey: {\n\t\t\t\t\tcreateGoogleServiceAccountKey: EasGoogleKey;\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'CreateGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation CreateGoogleServiceAccountKeyMutation(\n $googleServiceAccountKeyInput: GoogleServiceAccountKeyInput!\n $accountId: ID!\n) {\n googleServiceAccountKey {\n createGoogleServiceAccountKey(\n googleServiceAccountKeyInput: $googleServiceAccountKeyInput\n accountId: $accountId\n ) { ${KEY_FIELDS} }\n }\n}`,\n\t\t\t\t{ googleServiceAccountKeyInput: { jsonKey: json }, accountId },\n\t\t\t);\n\t\t\treturn data.googleServiceAccountKey.createGoogleServiceAccountKey;\n\t\t},\n\n\t\tasync useForSubmissions(credentialsId, keyId) {\n\t\t\tawait call(\n\t\t\t\t'SetGoogleServiceAccountKeyForSubmissionsMutation',\n\t\t\t\t`mutation SetGoogleServiceAccountKeyForSubmissionsMutation(\n $androidAppCredentialsId: ID!\n $googleServiceAccountKeyId: ID!\n) {\n androidAppCredentials {\n setGoogleServiceAccountKeyForSubmissions(\n id: $androidAppCredentialsId\n googleServiceAccountKeyId: $googleServiceAccountKeyId\n ) { id }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsId: credentialsId,\n\t\t\t\t\tgoogleServiceAccountKeyId: keyId,\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync keys(accountName) {\n\t\t\tconst keys: EasGoogleKey[] = [];\n\t\t\tlet after: string | null = null;\n\t\t\tdo {\n\t\t\t\tconst data: {\n\t\t\t\t\taccount: {\n\t\t\t\t\t\tbyName: {\n\t\t\t\t\t\t\tgoogleServiceAccountKeysPaginated: {\n\t\t\t\t\t\t\t\tedges: { node: EasGoogleKey }[];\n\t\t\t\t\t\t\t\tpageInfo: { hasNextPage: boolean; endCursor: string | null };\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t} = await call(\n\t\t\t\t\t'GoogleServiceAccountKeysPaginatedByAccountQuery',\n\t\t\t\t\t`query GoogleServiceAccountKeysPaginatedByAccountQuery(\n $accountName: String!\n $after: String\n $first: Int\n) {\n account {\n byName(accountName: $accountName) {\n id\n googleServiceAccountKeysPaginated(after: $after, first: $first) {\n edges { node { ${KEY_FIELDS} } }\n pageInfo { hasNextPage endCursor }\n }\n }\n }\n}`,\n\t\t\t\t\t{ accountName, after, first: 50 },\n\t\t\t\t);\n\t\t\t\tconst page = data.account.byName.googleServiceAccountKeysPaginated;\n\t\t\t\tkeys.push(...page.edges.map((edge) => edge.node));\n\t\t\t\tafter = page.pageInfo.hasNextPage ? page.pageInfo.endCursor : null;\n\t\t\t} while (after);\n\t\t\treturn keys;\n\t\t},\n\n\t\tasync deleteKey(id) {\n\t\t\tawait call(\n\t\t\t\t'DeleteGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation DeleteGoogleServiceAccountKeyMutation($googleServiceAccountKeyId: ID!) {\n googleServiceAccountKey { deleteGoogleServiceAccountKey(id: $googleServiceAccountKeyId) { id } }\n}`,\n\t\t\t\t{ googleServiceAccountKeyId: id },\n\t\t\t);\n\t\t},\n\t};\n}\n","/**\n * Google, as `gkm deploy:google-play` reaches it: a token for the person\n * running it, and the Cloud APIs that make a project, a service account and\n * its key.\n *\n * The token is gcloud's own application-default credential, with the\n * `cloud-platform` and `androidpublisher` scopes — no OAuth client of gkm's —\n * kept in gkm's own gcloud config (`<gkm home>/gcloud`), so the person's own\n * gcloud setup is never read or changed. Every call carries the project as\n * its quota project (`x-goog-user-project`), which the Play Developer API\n * requires of a user credential.\n */\n\nimport { spawn } from 'node:child_process';\nimport { join } from 'node:path';\nimport { GkmError } from '../../errors';\nimport { gkmHome } from '../../home.js';\n\n/** The scopes the sign-in asks for. */\nexport const GOOGLE_SCOPES = [\n\t'https://www.googleapis.com/auth/cloud-platform',\n\t'https://www.googleapis.com/auth/androidpublisher',\n];\n\n/** `gcloud` is not installed. */\nexport class GcloudMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm signs in to Google with gcloud, and there is no `gcloud` here. ' +\n\t\t\t\t'Install the Google Cloud CLI: https://cloud.google.com/sdk/docs/install.',\n\t\t);\n\t\tthis.name = 'GcloudMissing';\n\t}\n}\n\n/** Not signed in, and no terminal to sign in from. */\nexport class GoogleSignInRequired extends GkmError {\n\tconstructor(readonly config: string) {\n\t\tsuper(\n\t\t\t'Signing in to Google opens a browser, and this run has no terminal. ' +\n\t\t\t\t'Run it once where you can sign in, as a Play Console admin: ' +\n\t\t\t\t`CLOUDSDK_CONFIG=${config} gcloud auth application-default login ` +\n\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}.`,\n\t\t);\n\t\tthis.name = 'GoogleSignInRequired';\n\t}\n}\n\n/** A Google API refused a call. */\nexport class GoogleApiError extends GkmError {\n\tconstructor(\n\t\treadonly method: string,\n\t\treadonly url: string,\n\t\treadonly status: number,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`Google answered ${status} to ${method} ${url}: ${detail}`);\n\t\tthis.name = 'GoogleApiError';\n\t}\n}\n\n/** A long-running Google operation did not finish in time. */\nexport class GoogleOperationTimedOut extends GkmError {\n\tconstructor(readonly operation: string) {\n\t\tsuper(\n\t\t\t`Google's operation ${operation} had not finished after a minute. ` +\n\t\t\t\t'Run the command again: what it finished is kept.',\n\t\t);\n\t\tthis.name = 'GoogleOperationTimedOut';\n\t}\n}\n\n/** Where gkm keeps its gcloud sign-in. */\nexport function gcloudConfig(env: NodeJS.ProcessEnv = process.env): string {\n\treturn join(gkmHome(env), 'gcloud');\n}\n\n/** How a token is had: the real one runs gcloud. */\nexport interface GoogleSignIn {\n\t/** An access token for the signed-in person, signing in if allowed. */\n\ttoken(options: { interactive: boolean }): Promise<string>;\n}\n\nfunction gcloud(\n\targs: readonly string[],\n\tconfig: string,\n\tinherit: boolean,\n): Promise<{ code: number | null; stdout: string }> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('gcloud', [...args], {\n\t\t\tstdio: [\n\t\t\t\t'inherit',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t],\n\t\t\tenv: { ...process.env, CLOUDSDK_CONFIG: config },\n\t\t});\n\t\tlet stdout = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(error.code === 'ENOENT' ? new GcloudMissing() : error),\n\t\t);\n\t\tchild.on('close', (code) => resolve({ code, stdout }));\n\t});\n}\n\n/** gcloud's application-default credential, in gkm's own gcloud config. */\nexport function gcloudSignIn(config: string = gcloudConfig()): GoogleSignIn {\n\treturn {\n\t\tasync token({ interactive }) {\n\t\t\tconst print = () =>\n\t\t\t\tgcloud(\n\t\t\t\t\t['auth', 'application-default', 'print-access-token'],\n\t\t\t\t\tconfig,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\tlet printed = await print();\n\t\t\tif (printed.code !== 0) {\n\t\t\t\tif (!interactive) throw new GoogleSignInRequired(config);\n\t\t\t\tawait gcloud(\n\t\t\t\t\t[\n\t\t\t\t\t\t'auth',\n\t\t\t\t\t\t'application-default',\n\t\t\t\t\t\t'login',\n\t\t\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}`,\n\t\t\t\t\t],\n\t\t\t\t\tconfig,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tprinted = await print();\n\t\t\t\tif (printed.code !== 0) throw new GoogleSignInRequired(config);\n\t\t\t}\n\t\t\treturn printed.stdout.trim();\n\t\t},\n\t};\n}\n\n/** One authenticated Google API caller, for one quota project. */\nexport interface GoogleClient {\n\trequest<T>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody?: unknown,\n\t\toptions?: { raw?: { contentType: string; data: Uint8Array } },\n\t): Promise<T>;\n}\n\nexport function googleClient(\n\ttoken: string,\n\tquotaProject?: string,\n): GoogleClient {\n\treturn {\n\t\tasync request(method, url, body, options) {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t...(quotaProject ? { 'x-goog-user-project': quotaProject } : {}),\n\t\t\t};\n\t\t\tlet payload: string | Uint8Array | undefined;\n\t\t\tif (options?.raw) {\n\t\t\t\theaders['content-type'] = options.raw.contentType;\n\t\t\t\tpayload = options.raw.data;\n\t\t\t} else if (body !== undefined) {\n\t\t\t\theaders['content-type'] = 'application/json';\n\t\t\t\tpayload = JSON.stringify(body);\n\t\t\t}\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\t...(payload !== undefined ? { body: payload } : {}),\n\t\t\t});\n\t\t\tconst text = await response.text();\n\t\t\tif (!response.ok) {\n\t\t\t\tlet detail = text;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(text) as { error?: { message?: string } };\n\t\t\t\t\tdetail = parsed.error?.message ?? text;\n\t\t\t\t} catch {\n\t\t\t\t\t// Not JSON: the text is the detail.\n\t\t\t\t}\n\t\t\t\tthrow new GoogleApiError(method, url, response.status, detail);\n\t\t\t}\n\t\t\treturn (text ? JSON.parse(text) : {}) as never;\n\t\t},\n\t};\n}\n\n/** A long-running operation, polled until it is done. */\nexport async function finished(\n\tclient: GoogleClient,\n\tbase: string,\n\toperation: { name?: string; done?: boolean; error?: { message?: string } },\n\toptions: { intervalMs?: number; attempts?: number } = {},\n): Promise<void> {\n\tlet current = operation;\n\tfor (let attempt = 0; !current.done; attempt++) {\n\t\tif (attempt >= (options.attempts ?? 30)) {\n\t\t\tthrow new GoogleOperationTimedOut(current.name ?? '(unnamed)');\n\t\t}\n\t\tawait new Promise((resolve) =>\n\t\t\tsetTimeout(resolve, options.intervalMs ?? 2_000),\n\t\t);\n\t\tcurrent = await client.request('GET', `${base}/${current.name}`);\n\t}\n\tif (current.error) {\n\t\tthrow new GoogleApiError(\n\t\t\t'GET',\n\t\t\t`${base}/${current.name}`,\n\t\t\t500,\n\t\t\tcurrent.error.message ?? 'the operation failed',\n\t\t);\n\t}\n}\n","/**\n * The Google Cloud side of publishing to Play: a project with the Android\n * Publisher API on, and a service account in it whose JSON key EAS Submit\n * uses. Each step finds what is there before it creates anything, so a\n * second run reuses the project and the account and only makes a new key.\n */\n\nimport { GkmError } from '../../errors';\nimport { finished, GoogleApiError, type GoogleClient } from './google.js';\n\nexport const CLOUD_RESOURCE_MANAGER =\n\t'https://cloudresourcemanager.googleapis.com/v3';\nexport const SERVICE_USAGE = 'https://serviceusage.googleapis.com/v1';\nexport const IAM = 'https://iam.googleapis.com/v1';\n\n/** The API EAS Submit and gkm call Play through. */\nexport const ANDROID_PUBLISHER_API = 'androidpublisher.googleapis.com';\n\n/** A project id or service account id gkm would derive is not a valid one. */\nexport class GoogleIdInvalid extends GkmError {\n\tconstructor(\n\t\treadonly kind: 'project' | 'service account',\n\t\treadonly id: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is not a valid Google ${kind} id: 6 to 30 lowercase letters, ` +\n\t\t\t\t'digits and hyphens, starting with a letter and not ending in a hyphen. ' +\n\t\t\t\t(kind === 'project' ? 'Name one with --project.' : ''),\n\t\t);\n\t\tthis.name = 'GoogleIdInvalid';\n\t}\n}\n\n/** The project could not be created — most often, its id is someone else's. */\nexport class GoogleProjectUnavailable extends GkmError {\n\tconstructor(\n\t\treadonly project: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not create the Google Cloud project '${project}' (${detail}). ` +\n\t\t\t\t'Project ids are global: name another, or one you already have, with --project.',\n\t\t);\n\t\tthis.name = 'GoogleProjectUnavailable';\n\t}\n}\n\nconst ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;\n\n/**\n * An id Google accepts, from a workspace's name: `shop` → `shop-play`.\n * Lowercased, anything else made a hyphen, cut to 30.\n */\nexport function playId(workspace: string): string {\n\tconst base = workspace\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^[^a-z]+|-+$/g, '');\n\treturn `${base.slice(0, 25).replace(/-+$/, '')}-play`;\n}\n\n/** Throws unless `id` is a valid project or service account id. */\nexport function assertGoogleId(\n\tkind: 'project' | 'service account',\n\tid: string,\n): void {\n\tif (!ID.test(id)) throw new GoogleIdInvalid(kind, id);\n}\n\n/**\n * The project, created when it is not there. Answers whether it was made.\n *\n * @throws {GoogleProjectUnavailable}\n */\nexport async function ensureProject(\n\tclient: GoogleClient,\n\tproject: string,\n\tdisplayName: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<boolean> {\n\tassertGoogleId('project', project);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects/${project}`,\n\t\t);\n\t\treturn false;\n\t} catch (error) {\n\t\t// Absent, or not ours to see — either way, try to create it.\n\t\tif (\n\t\t\t!(error instanceof GoogleApiError) ||\n\t\t\t![403, 404].includes(error.status)\n\t\t) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\ttry {\n\t\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t\t'POST',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects`,\n\t\t\t{ projectId: project, displayName: displayName.slice(0, 30) },\n\t\t);\n\t\tawait finished(client, CLOUD_RESOURCE_MANAGER, operation, options);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new GoogleProjectUnavailable(project, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n\treturn true;\n}\n\n/** The Android Publisher API, on in the project. */\nexport async function enablePublisherApi(\n\tclient: GoogleClient,\n\tproject: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<void> {\n\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t'POST',\n\t\t`${SERVICE_USAGE}/projects/${project}/services/${ANDROID_PUBLISHER_API}:enable`,\n\t\t{},\n\t);\n\tawait finished(client, SERVICE_USAGE, operation, options);\n}\n\n/** The service account's email, which is what Play and EAS know it by. */\nexport function serviceAccountEmail(project: string, account: string): string {\n\treturn `${account}@${project}.iam.gserviceaccount.com`;\n}\n\n/** The service account, created when it is not there. Answers its email. */\nexport async function ensureServiceAccount(\n\tclient: GoogleClient,\n\tproject: string,\n\taccount: string,\n\tdisplayName: string,\n): Promise<string> {\n\tassertGoogleId('service account', account);\n\tconst email = serviceAccountEmail(project, account);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${IAM}/projects/${project}/serviceAccounts/${email}`,\n\t\t);\n\t\treturn email;\n\t} catch (error) {\n\t\tif (!(error instanceof GoogleApiError) || error.status !== 404) throw error;\n\t}\n\tawait client.request('POST', `${IAM}/projects/${project}/serviceAccounts`, {\n\t\taccountId: account,\n\t\tserviceAccount: { displayName: displayName.slice(0, 100) },\n\t});\n\treturn email;\n}\n\n/** A service account key: its id, and — only when just made — its JSON. */\nexport interface ServiceAccountKey {\n\t/** `projects/<p>/serviceAccounts/<email>/keys/<id>`. */\n\tname: string;\n\t/** The key file's JSON, held in memory and never written anywhere. */\n\tjson?: string;\n}\n\n/** A new key for the account. Its JSON exists only in what this returns. */\nexport async function createKey(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey & { json: string }> {\n\tconst key = await client.request<{ name: string; privateKeyData: string }>(\n\t\t'POST',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys`,\n\t\t{},\n\t);\n\treturn {\n\t\tname: key.name,\n\t\tjson: Buffer.from(key.privateKeyData, 'base64').toString('utf8'),\n\t};\n}\n\n/** The account's keys that someone made — not Google's own. */\nexport async function listKeys(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey[]> {\n\tconst { keys = [] } = await client.request<{ keys?: { name: string }[] }>(\n\t\t'GET',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys?keyTypes=USER_MANAGED`,\n\t);\n\treturn keys.map(({ name }) => ({ name }));\n}\n\nexport async function deleteKey(\n\tclient: GoogleClient,\n\tkey: ServiceAccountKey,\n): Promise<void> {\n\tawait client.request('DELETE', `${IAM}/${key.name}`);\n}\n\n/** A key's id: the last part of its name. */\nexport function keyId(key: Pick<ServiceAccountKey, 'name'>): string {\n\treturn key.name.split('/').pop()!;\n}\n","/**\n * Play Console, through the Play Developer API (androidpublisher v3): the\n * service account's access to the app, the store listing as code, and a\n * check that the new key can act on the app.\n */\n\nimport { createSign } from 'node:crypto';\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { extname, join } from 'node:path';\nimport type { PlayTrack } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\nimport { GoogleApiError, type GoogleClient, googleClient } from './google.js';\n\nexport const PLAY =\n\t'https://androidpublisher.googleapis.com/androidpublisher/v3';\nexport const PLAY_UPLOAD =\n\t'https://androidpublisher.googleapis.com/upload/androidpublisher/v3';\n\n/** What Play answers about a package no app in the account has. */\nconst PACKAGE_MISSING = /packages are not available/i;\n\n/** The app is not in Play Console yet, and Play has no API that makes one. */\nexport class PlayAppMissing extends GkmError {\n\tconstructor(\n\t\treadonly packageName: string,\n\t\treadonly developer: string,\n\t) {\n\t\tsuper(\n\t\t\t`Play Console has no app '${packageName}', and Play has no API that ` +\n\t\t\t\t`creates one. Create it at https://play.google.com/console/developers/` +\n\t\t\t\t`${developer}/create-new-app, with ${packageName} as its package, then ` +\n\t\t\t\t'run this again.',\n\t\t);\n\t\tthis.name = 'PlayAppMissing';\n\t}\n}\n\n/** The new key could not open an edit on the app. */\nexport class PlayKeyUnverified extends GkmError {\n\tconstructor(\n\t\treadonly email: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`The new key for ${email} could not open an edit on the app (${detail}). ` +\n\t\t\t\t'Its access can take a few minutes to reach Play: run this again shortly.',\n\t\t);\n\t\tthis.name = 'PlayKeyUnverified';\n\t}\n}\n\n/**\n * The app-level permissions the service account needs: releasing to the\n * testing tracks, to production only where a stage submits there, and the\n * store listing where the workspace keeps one.\n */\nexport function playPermissions(options: {\n\ttracks: readonly PlayTrack[];\n\tlisting: boolean;\n}): string[] {\n\treturn [\n\t\t'CAN_MANAGE_TRACK_APKS',\n\t\t...(options.tracks.includes('production')\n\t\t\t? ['CAN_MANAGE_PUBLIC_APKS']\n\t\t\t: []),\n\t\t...(options.listing ? ['CAN_MANAGE_PUBLIC_LISTING'] : []),\n\t].sort();\n}\n\ninterface PlayUser {\n\temail: string;\n\tgrants?: { packageName: string; appLevelPermissions?: string[] }[];\n}\n\n/**\n * The service account in Play Console with exactly `permissions` on the\n * app: invited with the grant in one call — Play refuses a user with no\n * permission, and a grant for a user not yet there — and, already there,\n * its grant made what it should be. Answers what changed.\n *\n * @throws {PlayAppMissing}\n */\nexport async function ensurePlayAccess(\n\tclient: GoogleClient,\n\toptions: {\n\t\tdeveloper: string;\n\t\temail: string;\n\t\tpackageName: string;\n\t\tpermissions: readonly string[];\n\t},\n): Promise<'invited' | 'granted' | 'updated' | 'unchanged'> {\n\tconst { developer, email, packageName } = options;\n\tconst permissions = [...options.permissions].sort();\n\tconst users = `${PLAY}/developers/${developer}/users`;\n\tconst missing = (error: unknown) =>\n\t\terror instanceof GoogleApiError && PACKAGE_MISSING.test(error.detail)\n\t\t\t? new PlayAppMissing(packageName, developer)\n\t\t\t: error;\n\n\t// Listing takes no paging: Play says \"Pagination is not currently\n\t// available\" unless asked for every user at once.\n\tconst { users: listed = [] } = await client.request<{ users?: PlayUser[] }>(\n\t\t'GET',\n\t\t`${users}?pageSize=-1`,\n\t);\n\tconst user = listed.find((candidate) => candidate.email === email);\n\n\tif (!user) {\n\t\tawait client\n\t\t\t.request('POST', users, {\n\t\t\t\temail,\n\t\t\t\tgrants: [{ packageName, appLevelPermissions: permissions }],\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'invited';\n\t}\n\n\tconst grant = user.grants?.find((g) => g.packageName === packageName);\n\tconst grantPath = `${users}/${encodeURIComponent(email)}/grants`;\n\tif (!grant) {\n\t\tawait client\n\t\t\t.request('POST', grantPath, {\n\t\t\t\tpackageName,\n\t\t\t\tappLevelPermissions: permissions,\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'granted';\n\t}\n\tconst had = [...(grant.appLevelPermissions ?? [])].sort();\n\tif (had.join() === permissions.join()) return 'unchanged';\n\tawait client.request(\n\t\t'PATCH',\n\t\t`${grantPath}/${packageName}?updateMask=appLevelPermissions`,\n\t\t{ appLevelPermissions: permissions },\n\t);\n\treturn 'updated';\n}\n\n/** A service account key file, as Google writes it. */\nexport interface ServiceAccountJson {\n\tclient_email: string;\n\tprivate_key: string;\n\ttoken_uri?: string;\n}\n\n/** An access token for the key itself: a signed JWT, exchanged. */\nexport async function keyToken(key: ServiceAccountJson): Promise<string> {\n\tconst tokenUri = key.token_uri ?? 'https://oauth2.googleapis.com/token';\n\tconst now = Math.floor(Date.now() / 1000);\n\tconst encode = (value: object) =>\n\t\tBuffer.from(JSON.stringify(value)).toString('base64url');\n\tconst unsigned = `${encode({ alg: 'RS256', typ: 'JWT' })}.${encode({\n\t\tiss: key.client_email,\n\t\tscope: 'https://www.googleapis.com/auth/androidpublisher',\n\t\taud: tokenUri,\n\t\tiat: now,\n\t\texp: now + 600,\n\t})}`;\n\tconst signature = createSign('RSA-SHA256')\n\t\t.update(unsigned)\n\t\t.sign(key.private_key)\n\t\t.toString('base64url');\n\tconst response = await fetch(tokenUri, {\n\t\tmethod: 'POST',\n\t\theaders: { 'content-type': 'application/x-www-form-urlencoded' },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n\t\t\tassertion: `${unsigned}.${signature}`,\n\t\t}),\n\t});\n\tconst body = (await response.json().catch(() => ({}))) as {\n\t\taccess_token?: string;\n\t\terror_description?: string;\n\t};\n\tif (!response.ok || !body.access_token) {\n\t\tthrow new PlayKeyUnverified(\n\t\t\tkey.client_email,\n\t\t\tbody.error_description ?? `token exchange answered ${response.status}`,\n\t\t);\n\t}\n\treturn body.access_token;\n}\n\n/**\n * Proof the key and its grant work: as the key, open an edit on the app and\n * discard it — what EAS Submit's first call is.\n *\n * @throws {PlayKeyUnverified}\n */\nexport async function verifyKey(\n\tkey: ServiceAccountJson,\n\tpackageName: string,\n): Promise<void> {\n\tconst client = googleClient(await keyToken(key));\n\ttry {\n\t\tconst edit = await client.request<{ id: string }>(\n\t\t\t'POST',\n\t\t\t`${PLAY}/applications/${packageName}/edits`,\n\t\t\t{},\n\t\t);\n\t\tawait client.request(\n\t\t\t'DELETE',\n\t\t\t`${PLAY}/applications/${packageName}/edits/${edit.id}`,\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new PlayKeyUnverified(key.client_email, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** The image kinds Play's listing takes, as fastlane supply names them. */\nconst IMAGE_TYPES = [\n\t'icon',\n\t'featureGraphic',\n\t'promoGraphic',\n\t'tvBanner',\n\t'phoneScreenshots',\n\t'sevenInchScreenshots',\n\t'tenInchScreenshots',\n\t'tvScreenshots',\n\t'wearScreenshots',\n] as const;\n\n/** One locale's listing, read from `store/play/listings/<locale>/`. */\nexport interface PlayListing {\n\tlanguage: string;\n\ttitle?: string;\n\tshortDescription?: string;\n\tfullDescription?: string;\n\tvideo?: string;\n\t/** Each image kind's files, in order. */\n\timages: Partial<Record<(typeof IMAGE_TYPES)[number], string[]>>;\n}\n\n/** The store listing kept beside the app, in fastlane supply's layout. */\nexport interface PlayStore {\n\tlistings: PlayListing[];\n\t/** `store/play/data-safety.csv`: Play Console's data safety export. */\n\tdataSafety?: string;\n}\n\n/**\n * `<app>/store/play`, read: undefined where there is none.\n *\n * ```\n * store/play/\n * data-safety.csv\n * listings/en-US/title.txt, short_description.txt, full_description.txt, video.txt\n * listings/en-US/images/icon.png, featureGraphic.png\n * listings/en-US/images/phoneScreenshots/1.png, 2.png …\n * ```\n */\nexport function readPlayStore(appDir: string): PlayStore | undefined {\n\tconst root = join(appDir, 'store', 'play');\n\tif (!existsSync(root)) return undefined;\n\tconst text = (path: string) =>\n\t\texistsSync(path) ? readFileSync(path, 'utf8').trim() : undefined;\n\n\tconst listings: PlayListing[] = [];\n\tconst listingsDir = join(root, 'listings');\n\tfor (const language of existsSync(listingsDir)\n\t\t? readdirSync(listingsDir).sort()\n\t\t: []) {\n\t\tconst dir = join(listingsDir, language);\n\t\tif (!statSync(dir).isDirectory()) continue;\n\t\tconst images: PlayListing['images'] = {};\n\t\tconst imagesDir = join(dir, 'images');\n\t\tfor (const type of IMAGE_TYPES) {\n\t\t\tconst single = ['.png', '.jpg', '.jpeg']\n\t\t\t\t.map((ext) => join(imagesDir, `${type}${ext}`))\n\t\t\t\t.find(existsSync);\n\t\t\tconst many = join(imagesDir, type);\n\t\t\tconst files = single\n\t\t\t\t? [single]\n\t\t\t\t: existsSync(many)\n\t\t\t\t\t? readdirSync(many)\n\t\t\t\t\t\t\t.filter((f) => /\\.(png|jpe?g)$/i.test(f))\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.map((f) => join(many, f))\n\t\t\t\t\t: [];\n\t\t\tif (files.length > 0) images[type] = files;\n\t\t}\n\t\tconst listing: PlayListing = { language, images };\n\t\tconst title = text(join(dir, 'title.txt'));\n\t\tconst shortDescription = text(join(dir, 'short_description.txt'));\n\t\tconst fullDescription = text(join(dir, 'full_description.txt'));\n\t\tconst video = text(join(dir, 'video.txt'));\n\t\tif (title) listing.title = title;\n\t\tif (shortDescription) listing.shortDescription = shortDescription;\n\t\tif (fullDescription) listing.fullDescription = fullDescription;\n\t\tif (video) listing.video = video;\n\t\tlistings.push(listing);\n\t}\n\n\tconst dataSafety = text(join(root, 'data-safety.csv'));\n\treturn { listings, ...(dataSafety ? { dataSafety } : {}) };\n}\n\n/**\n * Push the listing through one edit, committed: each locale's text, and each\n * image kind replaced by the files kept. Then the data safety form, which\n * Play takes outside an edit. Answers whether the listing was pushed: an\n * app that has never had an upload may refuse an edit, and that is left for\n * the next run, not failed.\n */\nexport async function pushPlayStore(\n\tclient: GoogleClient,\n\tpackageName: string,\n\tstore: PlayStore,\n): Promise<{ listing: boolean; dataSafety: boolean; reason?: string }> {\n\tconst app = `${PLAY}/applications/${packageName}`;\n\tlet listing = false;\n\tlet reason: string | undefined;\n\tif (store.listings.length > 0) {\n\t\tlet edit: { id: string } | undefined;\n\t\ttry {\n\t\t\tedit = await client.request<{ id: string }>('POST', `${app}/edits`, {});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof GoogleApiError)) throw error;\n\t\t\treason = error.detail;\n\t\t}\n\t\tif (edit) {\n\t\t\tconst base = `${app}/edits/${edit.id}/listings`;\n\t\t\tfor (const entry of store.listings) {\n\t\t\t\tconst { images, ...text } = entry;\n\t\t\t\tif (text.title || text.shortDescription || text.fullDescription) {\n\t\t\t\t\tawait client.request('PUT', `${base}/${entry.language}`, text);\n\t\t\t\t}\n\t\t\t\tfor (const [type, files] of Object.entries(images)) {\n\t\t\t\t\tawait client.request('DELETE', `${base}/${entry.language}/${type}`);\n\t\t\t\t\tfor (const file of files ?? []) {\n\t\t\t\t\t\tawait client.request(\n\t\t\t\t\t\t\t'POST',\n\t\t\t\t\t\t\t`${PLAY_UPLOAD}/applications/${packageName}/edits/${edit.id}/listings/${entry.language}/${type}?uploadType=media`,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\traw: {\n\t\t\t\t\t\t\t\t\tcontentType:\n\t\t\t\t\t\t\t\t\t\textname(file).toLowerCase() === '.png'\n\t\t\t\t\t\t\t\t\t\t\t? 'image/png'\n\t\t\t\t\t\t\t\t\t\t\t: 'image/jpeg',\n\t\t\t\t\t\t\t\t\tdata: readFileSync(file),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait client.request('POST', `${app}/edits/${edit.id}:commit`);\n\t\t\tlisting = true;\n\t\t}\n\t}\n\n\tlet dataSafety = false;\n\tif (store.dataSafety) {\n\t\tawait client.request('POST', `${app}/dataSafety`, {\n\t\t\tsafetyLabels: store.dataSafety,\n\t\t});\n\t\tdataSafety = true;\n\t}\n\treturn { listing, dataSafety, ...(reason ? { reason } : {}) };\n}\n\n/** What Play Console only takes by hand, for the person to tick off. */\nexport const PLAY_CONSOLE_CHECKLIST = [\n\t'App content → Privacy policy: the URL of the published policy',\n\t'App content → App access: how a reviewer signs in',\n\t'App content → Ads: whether the app shows ads',\n\t'App content → Content rating: the questionnaire',\n\t'App content → Target audience and content',\n\t'App content → Health apps / Health Connect, where the app reads health data',\n\t'App content → Sensitive permissions, where the app asks for any',\n\t'Release → the first upload of a new app, by hand',\n];\n","/**\n * `gkm deploy:google-play`: what EAS Submit needs to publish a `MobileApp`\n * to Google Play, set up in one run — the counterpart of `gkm deploy:github`\n * for the AWS side.\n *\n * 1. Each deployed stage's track and release status into `eas.json`\n * (`submit.<stage>.android`), from `mobileStage`.\n * 2. Google: the project (`--project`, else `<workspace>-play`, made when\n * absent) with the Android Publisher API on, and the service account\n * `<workspace>-play` in it, found or made.\n * 3. Play Console: the account invited with a grant on the app alone, its\n * permissions following the stages' tracks — production releases only\n * when a stage submits there. An app not in Play Console yet is a step\n * only a person can take; interactively, the run says so and waits.\n * 4. A new JSON key, held in memory: uploaded to EAS, assigned to the app's\n * Android credentials for EAS Submit, and proved by opening and\n * discarding an edit with it. Only then are the account's older keys\n * deleted, from EAS and from Google — a failed run leaves the old key\n * working.\n * 5. The store listing kept beside the app (`store/play/`), pushed.\n * 6. What Play Console only takes by hand, as a checklist.\n *\n * Running it again rotates the key and pushes the listing.\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 { loadWorkspaceConfig } from '../../config.js';\nimport { GkmError } from '../../errors';\nimport { loadStage } from '../../exec/stage.js';\nimport { canPrompt } from '../../prompt.js';\nimport { chooseMobileApp, withStageProfiles } from '../eas.js';\nimport { mobileStage } from '../stage.js';\nimport { type EasApi, easApi } from './easApi.js';\nimport {\n\tassertGoogleId,\n\tcreateKey,\n\tdeleteKey,\n\tenablePublisherApi,\n\tensureProject,\n\tensureServiceAccount,\n\tkeyId,\n\tlistKeys,\n\tplayId,\n} from './gcp.js';\nimport {\n\ttype GoogleClient,\n\ttype GoogleSignIn,\n\tgcloudSignIn,\n\tgoogleClient,\n} from './google.js';\nimport {\n\tensurePlayAccess,\n\tPLAY_CONSOLE_CHECKLIST,\n\tPlayAppMissing,\n\tPlayKeyUnverified,\n\tplayPermissions,\n\tpushPlayStore,\n\treadPlayStore,\n\ttype ServiceAccountJson,\n\tverifyKey,\n} from './play.js';\n\nconst logger = console;\n\n/** No Play developer account to act in. */\nexport class PlayDeveloperMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'Name the Play developer account: --developer <id>. It is the number in ' +\n\t\t\t\t\"Play Console's address: https://play.google.com/console/developers/<id>/….\",\n\t\t);\n\t\tthis.name = 'PlayDeveloperMissing';\n\t}\n}\n\n/** The app's Expo config has no Android package, or no EAS project. */\nexport class ExpoConfigIncomplete extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly missing: 'android.package' | 'extra.eas.projectId',\n\t) {\n\t\tsuper(\n\t\t\tmissing === 'android.package'\n\t\t\t\t? `'${app}' has no android.package in its Expo config: Play knows an app ` +\n\t\t\t\t\t\t\"by its package. Set it in app.config.ts: android: { package: 'com.acme.shop' }.\"\n\t\t\t\t: `'${app}' has no extra.eas.projectId in its Expo config: it is not ` +\n\t\t\t\t\t\t'linked to an EAS project. Run `eas init` in the app.',\n\t\t);\n\t\tthis.name = 'ExpoConfigIncomplete';\n\t}\n}\n\n/** `expo config` could not read the app's config. */\nexport class ExpoConfigFailed extends GkmError {\n\tconstructor(\n\t\treadonly dir: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read the Expo config in ${dir} (${detail}). Check that ` +\n\t\t\t\t'`npx expo config --json` works there.',\n\t\t);\n\t\tthis.name = 'ExpoConfigFailed';\n\t}\n}\n\n/** The workspace deploys no stage. */\nexport class NoDeployedStage extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm.config.ts deploys no stage, so there is nothing to publish to: add one to stages.deployed.',\n\t\t);\n\t\tthis.name = 'NoDeployedStage';\n\t}\n}\n\n/** What the run needs of the app's Expo config. */\nexport interface ExpoAppConfig {\n\tname?: string;\n\tandroid?: { package?: string };\n\textra?: { eas?: { projectId?: string } };\n}\n\nexport interface GooglePlayDeps {\n\tsignIn: GoogleSignIn;\n\teas: EasApi;\n\t/** The app's Expo config, evaluated with the values its build reads. */\n\texpoConfig: (\n\t\tdir: string,\n\t\troot: string,\n\t\tenv: Readonly<Record<string, string>>,\n\t) => Promise<ExpoAppConfig>;\n\t/** Whether a person is here: to sign in, and to create the app. */\n\tinteractive: boolean;\n\t/** Wait between two looks for the app, and two tries of the new key. */\n\tsleep: (ms: number) => Promise<void>;\n}\n\n/** `expo config --json`, run with the app's Expo — its own, or the workspace's. */\nexport function expoConfig(\n\tdir: string,\n\troot: string,\n\tenv: Readonly<Record<string, string>>,\n): Promise<ExpoAppConfig> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('expo', ['config', '--json', '--type', 'public'], {\n\t\t\tcwd: dir,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...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});\n\t\tchild.on('error', (error) =>\n\t\t\treject(new ExpoConfigFailed(dir, error.message)),\n\t\t);\n\t\tchild.on('close', (code) => {\n\t\t\tif (code !== 0) {\n\t\t\t\treject(new ExpoConfigFailed(dir, stderr.trim() || `exit ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresolve(JSON.parse(stdout) as ExpoAppConfig);\n\t\t\t} catch {\n\t\t\t\treject(new ExpoConfigFailed(dir, `no JSON: ${stdout.slice(0, 200)}`));\n\t\t\t}\n\t\t});\n\t});\n}\n\nexport const defaultGooglePlayDeps: GooglePlayDeps = {\n\tsignIn: gcloudSignIn(),\n\teas: easApi(),\n\texpoConfig,\n\tinteractive: canPrompt(),\n\tsleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface GooglePlayOptions {\n\t/** The mobile app; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The Google Cloud project: `<workspace>-play` by default. */\n\tproject?: string;\n\t/** The Play developer account's id. */\n\tdeveloper?: string;\n\t/**\n\t * The stage whose values evaluate the app's Expo config: the protected\n\t * one, else the first deployed.\n\t */\n\tstage?: string;\n\t/** The AWS profile for that stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n}\n\nexport interface GooglePlayResult {\n\tpackageName: string;\n\tproject: string;\n\temail: string;\n\tpermissions: string[];\n\taccess: 'invited' | 'granted' | 'updated' | 'unchanged';\n\t/** The new key's id, in Google and in EAS. */\n\tkey: { google: string; eas: string };\n\t/** The older keys deleted. */\n\tdeleted: { google: string[]; eas: string[] };\n\tlisting:\n\t\t| { listing: boolean; dataSafety: boolean; reason?: string }\n\t\t| undefined;\n}\n\n/**\n * Set up publishing a mobile app to Google Play.\n *\n * @throws {PlayDeveloperMissing}, {ExpoConfigIncomplete}, {PlayAppMissing},\n * {PlayKeyUnverified} — and what each step's API throws\n */\nexport async function deployGooglePlay(\n\toptions: GooglePlayOptions,\n\tdeps: GooglePlayDeps = defaultGooglePlayDeps,\n): Promise<GooglePlayResult> {\n\tif (!options.developer) throw new PlayDeveloperMissing();\n\tconst developer = options.developer;\n\n\t// The stage only evaluates the Expo config: any deployed one's values do.\n\tconst declared = (await loadWorkspaceConfig(options.cwd)).workspace.stages;\n\tconst stageName =\n\t\toptions.stage ?? declared.protected?.[0] ?? declared.deployed[0];\n\tif (!stageName) throw new NoDeployedStage();\n\tconst loaded = await loadStage({\n\t\tstage: stageName,\n\t\t...(options.cwd ? { cwd: options.cwd } : {}),\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { workspace, manifest, stack } = loaded;\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = declaration.id;\n\tconst dir = join(workspace.root, declaration.app.path);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.id === declaration.id)\n\t\t\t?.values ?? {};\n\n\tconst config = await deps.expoConfig(dir, workspace.root, values);\n\tconst packageName = config.android?.package;\n\tif (!packageName) throw new ExpoConfigIncomplete(name, 'android.package');\n\tconst projectId = config.extra?.eas?.projectId;\n\tif (!projectId) throw new ExpoConfigIncomplete(name, 'extra.eas.projectId');\n\n\t// 1. Each stage's track, in eas.json — and the tracks the grant follows.\n\tconst stages = workspace.stages.deployed.map((stage) =>\n\t\tmobileStage(workspace, declaration, stage),\n\t);\n\tconst easJsonPath = join(dir, 'eas.json');\n\tconst before = existsSync(easJsonPath)\n\t\t? JSON.parse(await readFile(easJsonPath, 'utf8'))\n\t\t: {};\n\tconst after = stages.reduce(withStageProfiles, before);\n\tif (JSON.stringify(before) !== JSON.stringify(after)) {\n\t\tawait writeFile(easJsonPath, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(`📝 ${easJsonPath}: each stage's build and submit profiles`);\n\t}\n\tconst store = readPlayStore(dir);\n\tconst permissions = playPermissions({\n\t\ttracks: stages.map((stage) => stage.android.track),\n\t\tlisting: (store?.listings.length ?? 0) > 0,\n\t});\n\n\t// 2. Google: signed in as a Play Console admin, the project and account.\n\tconst project = options.project ?? playId(workspace.name);\n\tassertGoogleId('project', project);\n\tconst account = playId(workspace.name);\n\tconst token = await deps.signIn.token({ interactive: deps.interactive });\n\tconst bare: GoogleClient = googleClient(token);\n\tconst created = await ensureProject(bare, project, `${workspace.name} Play`);\n\tlogger.log(\n\t\t`☁️ Google Cloud project ${project}${created ? ' (created)' : ''}`,\n\t);\n\tconst client = googleClient(token, project);\n\tawait enablePublisherApi(client, project);\n\tconst email = await ensureServiceAccount(\n\t\tclient,\n\t\tproject,\n\t\taccount,\n\t\t`${workspace.name} publishing (gkm)`,\n\t);\n\tlogger.log(`🤖 Service account ${email}`);\n\n\t// 3. Play Console: the app grant, waiting for the app where someone can\n\t// make it.\n\tlet access: GooglePlayResult['access'] | undefined;\n\tfor (let attempt = 0; !access; attempt++) {\n\t\ttry {\n\t\t\taccess = await ensurePlayAccess(client, {\n\t\t\t\tdeveloper,\n\t\t\t\temail,\n\t\t\t\tpackageName,\n\t\t\t\tpermissions,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayAppMissing) || !deps.interactive) throw error;\n\t\t\tif (attempt === 0) {\n\t\t\t\tlogger.log(`\\n⏳ ${error.message}\\n Waiting for it…`);\n\t\t\t}\n\t\t\tawait deps.sleep(15_000);\n\t\t}\n\t}\n\tlogger.log(\n\t\t`🎟️ Play access on ${packageName}: ${access} (${permissions.join(', ')})`,\n\t);\n\n\t// 4. A new key: to EAS, proved, and only then the old ones gone.\n\tconst older = await listKeys(client, project, email);\n\tconst key = await createKey(client, project, email);\n\tconst json = JSON.parse(key.json) as ServiceAccountJson &\n\t\tRecord<string, unknown>;\n\tconst easProject = await deps.eas.project(projectId);\n\tconst credentials = await deps.eas.androidCredentials(\n\t\t{ projectId, fullName: easProject.fullName },\n\t\tpackageName,\n\t);\n\tconst easKey = await deps.eas.createKey(easProject.accountId, json);\n\tawait deps.eas.useForSubmissions(credentials.id, easKey.id);\n\tlogger.log(\n\t\t`🔑 New key ${keyId(key)} assigned to EAS Submit for ${packageName}`,\n\t);\n\n\t// A new grant takes a moment to reach Play.\n\tfor (let attempt = 0; ; attempt++) {\n\t\ttry {\n\t\t\tawait verifyKey(json, packageName);\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayKeyUnverified) || attempt >= 5) throw error;\n\t\t\tawait deps.sleep(10_000);\n\t\t}\n\t}\n\tlogger.log('✅ The key opens an edit on the app');\n\n\tconst easOlder = (await deps.eas.keys(easProject.accountName)).filter(\n\t\t(candidate) =>\n\t\t\tcandidate.clientEmail === email && candidate.id !== easKey.id,\n\t);\n\tfor (const old of easOlder) await deps.eas.deleteKey(old.id);\n\tfor (const old of older) await deleteKey(client, old);\n\tif (older.length + easOlder.length > 0) {\n\t\tlogger.log(\n\t\t\t`🧹 Deleted ${older.length} older key(s) from Google, ${easOlder.length} from EAS`,\n\t\t);\n\t}\n\n\t// 5. The listing, where the workspace keeps one.\n\tconst listing = store\n\t\t? await pushPlayStore(client, packageName, store)\n\t\t: undefined;\n\tif (listing?.listing) logger.log('🛍️ Store listing pushed');\n\tif (store && !listing?.listing && listing?.reason) {\n\t\tlogger.log(\n\t\t\t`⏭️ Store listing not pushed yet (${listing.reason}): run this again after the app's first upload`,\n\t\t);\n\t}\n\tif (listing?.dataSafety) logger.log('🛡️ Data safety pushed');\n\n\t// 6. What only a person can do.\n\tlogger.log('\\nLeft to do in Play Console, by hand:');\n\tfor (const item of PLAY_CONSOLE_CHECKLIST) logger.log(` [ ] ${item}`);\n\n\treturn {\n\t\tpackageName,\n\t\tproject,\n\t\temail,\n\t\tpermissions,\n\t\taccess,\n\t\tkey: { google: keyId(key), eas: easKey.id },\n\t\tdeleted: {\n\t\t\tgoogle: older.map(keyId),\n\t\t\teas: easOlder.map((candidate) => candidate.id),\n\t\t},\n\t\tlisting,\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAaA,MAAa,cAAc;;AAG3B,IAAa,mBAAb,cAAsCA,eAAAA,SAAS;CAC9C,cAAc;EACb,MACC,gKAGD;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,cAAb,cAAiCA,eAAAA,SAAS;CAE/B;CACA;CAFV,YACC,WACA,QACC;EACD,MAAM,eAAe,UAAU,IAAI,QAAQ;EAHlC,KAAA,YAAA;EACA,KAAA,SAAA;EAGT,KAAK,OAAO;CACb;AACD;AASA,MAAM,aAAa;AA0BnB,SAAgB,OACf,QAA4B,QAAQ,IAAI,YACxC,WAAmB,aACV;CACT,MAAM,OAAO,OACZ,WACA,OACA,cACgB;EAChB,IAAI,CAAC,OAAO,MAAM,IAAI,iBAAiB;EACvC,MAAM,WAAW,MAAM,MAAM,UAAU;GACtC,QAAQ;GACR,SAAS;IACR,eAAe,UAAU;IACzB,gBAAgB;GACjB;GACA,MAAM,KAAK,UAAU;IAAE;IAAO;IAAW,eAAe;GAAU,CAAC;EACpE,CAAC;EACD,MAAM,OAAQ,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;EAIpD,IAAI,CAAC,SAAS,MAAM,KAAK,QAAQ,UAAU,CAAC,KAAK,MAChD,MAAM,IAAI,YACT,WACA,KAAK,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,IAAI,KACnD,QAAQ,SAAS,QACnB;EAED,OAAO,KAAK;CACb;CAEA,MAAM,oBAAoB,8CAA8C,WAAW;CAKnF,MAAM,iBAAiB,iBAA8B;EACpD,IAAI,YAAY;EAChB,GAAI,YAAY,wCACb,EAAE,gBAAgB,YAAY,sCAAsC,IACpE,CAAC;CACL;CAEA,OAAO;EACN,MAAM,QAAQ,WAAW;GAexB,MAAM,OAAM,MAdO,KAQlB,gBACA;;IAGA,EAAE,OAAO,UAAU,CACpB,EAAA,CACiB,IAAI;GACrB,OAAO;IACN,WAAW,IAAI,aAAa;IAC5B,aAAa,IAAI,aAAa;IAC9B,UAAU,IAAI;GACf;EACD;EAEA,MAAM,mBAAmB,SAAS,aAAa;GAyB9C,MAAM,YAAW,MAxBG,KAGnB,qDACA;;;;;;;;;;YAUQ,kBAAkB;;;IAI1B;IACC,iBAAiB,QAAQ;IACzB,uBAAuB;IACvB,YAAY;GACb,CACD,EAAA,CACuB,IAAI,WAAW,sBAAsB;GAC5D,IAAI,UAAU,OAAO,cAAc,QAAQ;GAE3C,MAAM,UAAU,MAAM,KAGrB,uCACA;;;;;;;;;;UAUM,kBAAkB;;IAGxB;IACC,4BAA4B,CAAC;IAC7B,OAAO,QAAQ;IACf,uBAAuB;GACxB,CACD;GACA,OAAO,cACN,QAAQ,sBAAsB,2BAC/B;EACD;EAEA,MAAM,UAAU,WAAW,MAAM;GAoBhC,QAAO,MAnBY,KAKlB,yCACA;;;;;;;;UAQM,WAAW;;IAGjB;IAAE,8BAA8B,EAAE,SAAS,KAAK;IAAG;GAAU,CAC9D,EAAA,CACY,wBAAwB;EACrC;EAEA,MAAM,kBAAkB,eAAe,OAAO;GAC7C,MAAM,KACL,oDACA;;;;;;;;;;IAWA;IACC,yBAAyB;IACzB,2BAA2B;GAC5B,CACD;EACD;EAEA,MAAM,KAAK,aAAa;GACvB,MAAM,OAAuB,CAAC;GAC9B,IAAI,QAAuB;GAC3B,GAAG;IA6BF,MAAM,QAAO,MAnBH,KACT,mDACA;;;;;;;;;yBASoB,WAAW;;;;;IAM/B;KAAE;KAAa;KAAO,OAAO;IAAG,CACjC,EAAA,CACkB,QAAQ,OAAO;IACjC,KAAK,KAAK,GAAG,KAAK,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;IAChD,QAAQ,KAAK,SAAS,cAAc,KAAK,SAAS,YAAY;GAC/D,SAAS;GACT,OAAO;EACR;EAEA,MAAM,UAAU,IAAI;GACnB,MAAM,KACL,yCACA;;IAGA,EAAE,2BAA2B,GAAG,CACjC;EACD;CACD;AACD;;;;;;;;;;;;;;;;AC7QA,MAAa,gBAAgB,CAC5B,kDACA,kDACD;;AAGA,IAAa,gBAAb,cAAmCC,eAAAA,SAAS;CAC3C,cAAc;EACb,MACC,6IAED;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0CA,eAAAA,SAAS;CAC7B;CAArB,YAAY,QAAyB;EACpC,MACC,mJAEoB,OAAO,kDACd,cAAc,KAAK,GAAG,EAAE,EACtC;EANoB,KAAA,SAAA;EAOpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,iBAAb,cAAoCA,eAAAA,SAAS;CAElC;CACA;CACA;CACA;CAJV,YACC,QACA,KACA,QACA,QACC;EACD,MAAM,mBAAmB,OAAO,MAAM,OAAO,GAAG,IAAI,IAAI,QAAQ;EALvD,KAAA,SAAA;EACA,KAAA,MAAA;EACA,KAAA,SAAA;EACA,KAAA,SAAA;EAGT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,0BAAb,cAA6CA,eAAAA,SAAS;CAChC;CAArB,YAAY,WAA4B;EACvC,MACC,sBAAsB,UAAU,mFAEjC;EAJoB,KAAA,YAAA;EAKpB,KAAK,OAAO;CACb;AACD;;AAGA,SAAgB,aAAa,MAAyB,QAAQ,KAAa;CAC1E,QAAA,GAAOC,UAAAA,KAAAA,CAAKC,YAAAA,QAAQ,GAAG,GAAG,QAAQ;AACnC;AAQA,SAAS,OACR,MACA,QACA,SACmD;CACnD,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,MAAM,SAAA,GAAQC,mBAAAA,MAAAA,CAAM,UAAU,CAAC,GAAG,IAAI,GAAG;GACxC,OAAO;IACN;IACA,UAAU,YAAY;IACtB,UAAU,YAAY;GACvB;GACA,KAAK;IAAE,GAAG,QAAQ;IAAK,iBAAiB;GAAO;EAChD,CAAC;EACD,IAAI,SAAS;EACb,MAAM,QAAQ,GAAG,SAAS,UAAkB;GAC3C,UAAU,MAAM,SAAS;EAC1B,CAAC;EACD,MAAM,GAAG,UAAU,UAClB,OAAO,MAAM,SAAS,WAAW,IAAI,cAAc,IAAI,KAAK,CAC7D;EACA,MAAM,GAAG,UAAU,SAAS,QAAQ;GAAE;GAAM;EAAO,CAAC,CAAC;CACtD,CAAC;AACF;;AAGA,SAAgB,aAAa,SAAiB,aAAa,GAAiB;CAC3E,OAAO,EACN,MAAM,MAAM,EAAE,eAAe;EAC5B,MAAM,cACL,OACC;GAAC;GAAQ;GAAuB;EAAoB,GACpD,QACA,KACD;EACD,IAAI,UAAU,MAAM,MAAM;EAC1B,IAAI,QAAQ,SAAS,GAAG;GACvB,IAAI,CAAC,aAAa,MAAM,IAAI,qBAAqB,MAAM;GACvD,MAAM,OACL;IACC;IACA;IACA;IACA,YAAY,cAAc,KAAK,GAAG;GACnC,GACA,QACA,IACD;GACA,UAAU,MAAM,MAAM;GACtB,IAAI,QAAQ,SAAS,GAAG,MAAM,IAAI,qBAAqB,MAAM;EAC9D;EACA,OAAO,QAAQ,OAAO,KAAK;CAC5B,EACD;AACD;AAYA,SAAgB,aACf,OACA,cACe;CACf,OAAO,EACN,MAAM,QAAQ,QAAQ,KAAK,MAAM,SAAS;EACzC,MAAM,UAAkC;GACvC,eAAe,UAAU;GACzB,GAAI,eAAe,EAAE,uBAAuB,aAAa,IAAI,CAAC;EAC/D;EACA,IAAI;EACJ,IAAI,SAAS,KAAK;GACjB,QAAQ,kBAAkB,QAAQ,IAAI;GACtC,UAAU,QAAQ,IAAI;EACvB,OAAO,IAAI,SAAS,KAAA,GAAW;GAC9B,QAAQ,kBAAkB;GAC1B,UAAU,KAAK,UAAU,IAAI;EAC9B;EACA,MAAM,WAAW,MAAM,MAAM,KAAK;GACjC;GACA;GACA,GAAI,YAAY,KAAA,IAAY,EAAE,MAAM,QAAQ,IAAI,CAAC;EAClD,CAAC;EACD,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,IAAI,CAAC,SAAS,IAAI;GACjB,IAAI,SAAS;GACb,IAAI;IAEH,SADe,KAAK,MAAM,IACZ,CAAC,CAAC,OAAO,WAAW;GACnC,QAAQ,CAER;GACA,MAAM,IAAI,eAAe,QAAQ,KAAK,SAAS,QAAQ,MAAM;EAC9D;EACA,OAAQ,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;CACpC,EACD;AACD;;AAGA,eAAsB,SACrB,QACA,MACA,WACA,UAAsD,CAAC,GACvC;CAChB,IAAI,UAAU;CACd,KAAK,IAAI,UAAU,GAAG,CAAC,QAAQ,MAAM,WAAW;EAC/C,IAAI,YAAY,QAAQ,YAAY,KACnC,MAAM,IAAI,wBAAwB,QAAQ,QAAQ,WAAW;EAE9D,MAAM,IAAI,SAAS,YAClB,WAAW,SAAS,QAAQ,cAAc,GAAK,CAChD;EACA,UAAU,MAAM,OAAO,QAAQ,OAAO,GAAG,KAAK,GAAG,QAAQ,MAAM;CAChE;CACA,IAAI,QAAQ,OACX,MAAM,IAAI,eACT,OACA,GAAG,KAAK,GAAG,QAAQ,QACnB,KACA,QAAQ,MAAM,WAAW,sBAC1B;AAEF;;;;;;;;;AC3MA,MAAa,yBACZ;AACD,MAAa,gBAAgB;AAC7B,MAAa,MAAM;;AAGnB,MAAa,wBAAwB;;AAGrC,IAAa,kBAAb,cAAqCC,eAAAA,SAAS;CAEnC;CACA;CAFV,YACC,MACA,IACC;EACD,MACC,IAAI,GAAG,0BAA0B,KAAK,4GAEpC,SAAS,YAAY,6BAA6B,GACrD;EAPS,KAAA,OAAA;EACA,KAAA,KAAA;EAOT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,2BAAb,cAA8CA,eAAAA,SAAS;CAE5C;CACA;CAFV,YACC,SACA,QACC;EACD,MACC,8CAA8C,QAAQ,KAAK,OAAO,kFAEnE;EANS,KAAA,UAAA;EACA,KAAA,SAAA;EAMT,KAAK,OAAO;CACb;AACD;AAEA,MAAM,KAAK;;;;;AAMX,SAAgB,OAAO,WAA2B;CAKjD,OAAO,GAJM,UACX,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,iBAAiB,EACd,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,QAAQ,OAAO,EAAE,EAAE;AAChD;;AAGA,SAAgB,eACf,MACA,IACO;CACP,IAAI,CAAC,GAAG,KAAK,EAAE,GAAG,MAAM,IAAI,gBAAgB,MAAM,EAAE;AACrD;;;;;;AAOA,eAAsB,cACrB,QACA,SACA,aACA,UAAmC,CAAC,GACjB;CACnB,eAAe,WAAW,OAAO;CACjC,IAAI;EACH,MAAM,OAAO,QACZ,OACA,GAAG,uBAAuB,YAAY,SACvC;EACA,OAAO;CACR,SAAS,OAAO;EAEf,IACC,EAAE,iBAAiB,mBACnB,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,GAEjC,MAAM;CAER;CACA,IAAI;EACH,MAAM,YAAY,MAAM,OAAO,QAC9B,QACA,GAAG,uBAAuB,YAC1B;GAAE,WAAW;GAAS,aAAa,YAAY,MAAM,GAAG,EAAE;EAAE,CAC7D;EACA,MAAM,SAAS,QAAQ,wBAAwB,WAAW,OAAO;CAClE,SAAS,OAAO;EACf,IAAI,iBAAiB,gBACpB,MAAM,IAAI,yBAAyB,SAAS,MAAM,MAAM;EAEzD,MAAM;CACP;CACA,OAAO;AACR;;AAGA,eAAsB,mBACrB,QACA,SACA,UAAmC,CAAC,GACpB;CAChB,MAAM,YAAY,MAAM,OAAO,QAC9B,QACA,GAAG,cAAc,YAAY,QAAQ,YAAY,sBAAsB,UACvE,CAAC,CACF;CACA,MAAM,SAAS,QAAQ,eAAe,WAAW,OAAO;AACzD;;AAGA,SAAgB,oBAAoB,SAAiB,SAAyB;CAC7E,OAAO,GAAG,QAAQ,GAAG,QAAQ;AAC9B;;AAGA,eAAsB,qBACrB,QACA,SACA,SACA,aACkB;CAClB,eAAe,mBAAmB,OAAO;CACzC,MAAM,QAAQ,oBAAoB,SAAS,OAAO;CAClD,IAAI;EACH,MAAM,OAAO,QACZ,OACA,GAAG,IAAI,YAAY,QAAQ,mBAAmB,OAC/C;EACA,OAAO;CACR,SAAS,OAAO;EACf,IAAI,EAAE,iBAAiB,mBAAmB,MAAM,WAAW,KAAK,MAAM;CACvE;CACA,MAAM,OAAO,QAAQ,QAAQ,GAAG,IAAI,YAAY,QAAQ,mBAAmB;EAC1E,WAAW;EACX,gBAAgB,EAAE,aAAa,YAAY,MAAM,GAAG,GAAG,EAAE;CAC1D,CAAC;CACD,OAAO;AACR;;AAWA,eAAsB,UACrB,QACA,SACA,OACgD;CAChD,MAAM,MAAM,MAAM,OAAO,QACxB,QACA,GAAG,IAAI,YAAY,QAAQ,mBAAmB,MAAM,QACpD,CAAC,CACF;CACA,OAAO;EACN,MAAM,IAAI;EACV,MAAM,OAAO,KAAK,IAAI,gBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM;CAChE;AACD;;AAGA,eAAsB,SACrB,QACA,SACA,OAC+B;CAC/B,MAAM,EAAE,OAAO,CAAC,MAAM,MAAM,OAAO,QAClC,OACA,GAAG,IAAI,YAAY,QAAQ,mBAAmB,MAAM,4BACrD;CACA,OAAO,KAAK,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;AACzC;AAEA,eAAsB,UACrB,QACA,KACgB;CAChB,MAAM,OAAO,QAAQ,UAAU,GAAG,IAAI,GAAG,IAAI,MAAM;AACpD;;AAGA,SAAgB,MAAM,KAA8C;CACnE,OAAO,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI;AAChC;;;;;;;;AC/LA,MAAa,OACZ;AACD,MAAa,cACZ;;AAGD,MAAM,kBAAkB;;AAGxB,IAAa,iBAAb,cAAoCC,eAAAA,SAAS;CAElC;CACA;CAFV,YACC,aACA,WACC;EACD,MACC,4BAA4B,YAAY,mGAEpC,UAAU,wBAAwB,YAAY,sCAEnD;EARS,KAAA,cAAA;EACA,KAAA,YAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuCA,eAAAA,SAAS;CAErC;CACA;CAFV,YACC,OACA,QACC;EACD,MACC,mBAAmB,MAAM,sCAAsC,OAAO,4EAEvE;EANS,KAAA,QAAA;EACA,KAAA,SAAA;EAMT,KAAK,OAAO;CACb;AACD;;;;;;AAOA,SAAgB,gBAAgB,SAGnB;CACZ,OAAO;EACN;EACA,GAAI,QAAQ,OAAO,SAAS,YAAY,IACrC,CAAC,wBAAwB,IACzB,CAAC;EACJ,GAAI,QAAQ,UAAU,CAAC,2BAA2B,IAAI,CAAC;CACxD,CAAC,CAAC,KAAK;AACR;;;;;;;;;AAeA,eAAsB,iBACrB,QACA,SAM2D;CAC3D,MAAM,EAAE,WAAW,OAAO,gBAAgB;CAC1C,MAAM,cAAc,CAAC,GAAG,QAAQ,WAAW,CAAC,CAAC,KAAK;CAClD,MAAM,QAAQ,GAAG,KAAK,cAAc,UAAU;CAC9C,MAAM,WAAW,UAChB,iBAAiB,kBAAkB,gBAAgB,KAAK,MAAM,MAAM,IACjE,IAAI,eAAe,aAAa,SAAS,IACzC;CAIJ,MAAM,EAAE,OAAO,SAAS,CAAC,MAAM,MAAM,OAAO,QAC3C,OACA,GAAG,MAAM,aACV;CACA,MAAM,OAAO,OAAO,MAAM,cAAc,UAAU,UAAU,KAAK;CAEjE,IAAI,CAAC,MAAM;EACV,MAAM,OACJ,QAAQ,QAAQ,OAAO;GACvB;GACA,QAAQ,CAAC;IAAE;IAAa,qBAAqB;GAAY,CAAC;EAC3D,CAAC,CAAC,CACD,OAAO,UAAU;GACjB,MAAM,QAAQ,KAAK;EACpB,CAAC;EACF,OAAO;CACR;CAEA,MAAM,QAAQ,KAAK,QAAQ,MAAM,MAAM,EAAE,gBAAgB,WAAW;CACpE,MAAM,YAAY,GAAG,MAAM,GAAG,mBAAmB,KAAK,EAAE;CACxD,IAAI,CAAC,OAAO;EACX,MAAM,OACJ,QAAQ,QAAQ,WAAW;GAC3B;GACA,qBAAqB;EACtB,CAAC,CAAC,CACD,OAAO,UAAU;GACjB,MAAM,QAAQ,KAAK;EACpB,CAAC;EACF,OAAO;CACR;CAEA,IADY,CAAC,GAAI,MAAM,uBAAuB,CAAC,CAAE,CAAC,CAAC,KAC7C,CAAC,CAAC,KAAK,MAAM,YAAY,KAAK,GAAG,OAAO;CAC9C,MAAM,OAAO,QACZ,SACA,GAAG,UAAU,GAAG,YAAY,kCAC5B,EAAE,qBAAqB,YAAY,CACpC;CACA,OAAO;AACR;;AAUA,eAAsB,SAAS,KAA0C;CACxE,MAAM,WAAW,IAAI,aAAa;CAClC,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CACxC,MAAM,UAAU,UACf,OAAO,KAAK,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,SAAS,WAAW;CACxD,MAAM,WAAW,GAAG,OAAO;EAAE,KAAK;EAAS,KAAK;CAAM,CAAC,EAAE,GAAG,OAAO;EAClE,KAAK,IAAI;EACT,OAAO;EACP,KAAK;EACL,KAAK;EACL,KAAK,MAAM;CACZ,CAAC;CACD,MAAM,aAAA,GAAYC,YAAAA,WAAAA,CAAW,YAAY,CAAC,CACxC,OAAO,QAAQ,CAAC,CAChB,KAAK,IAAI,WAAW,CAAC,CACrB,SAAS,WAAW;CACtB,MAAM,WAAW,MAAM,MAAM,UAAU;EACtC,QAAQ;EACR,SAAS,EAAE,gBAAgB,oCAAoC;EAC/D,MAAM,IAAI,gBAAgB;GACzB,YAAY;GACZ,WAAW,GAAG,SAAS,GAAG;EAC3B,CAAC;CACF,CAAC;CACD,MAAM,OAAQ,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;CAIpD,IAAI,CAAC,SAAS,MAAM,CAAC,KAAK,cACzB,MAAM,IAAI,kBACT,IAAI,cACJ,KAAK,qBAAqB,2BAA2B,SAAS,QAC/D;CAED,OAAO,KAAK;AACb;;;;;;;AAQA,eAAsB,UACrB,KACA,aACgB;CAChB,MAAM,SAAS,aAAa,MAAM,SAAS,GAAG,CAAC;CAC/C,IAAI;EACH,MAAM,OAAO,MAAM,OAAO,QACzB,QACA,GAAG,KAAK,gBAAgB,YAAY,SACpC,CAAC,CACF;EACA,MAAM,OAAO,QACZ,UACA,GAAG,KAAK,gBAAgB,YAAY,SAAS,KAAK,IACnD;CACD,SAAS,OAAO;EACf,IAAI,iBAAiB,gBACpB,MAAM,IAAI,kBAAkB,IAAI,cAAc,MAAM,MAAM;EAE3D,MAAM;CACP;AACD;;AAGA,MAAM,cAAc;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;;;;;;;AA+BA,SAAgB,cAAc,QAAuC;CACpE,MAAM,QAAA,GAAOC,UAAAA,KAAAA,CAAK,QAAQ,SAAS,MAAM;CACzC,IAAI,EAAA,GAACC,QAAAA,WAAAA,CAAW,IAAI,GAAG,OAAO,KAAA;CAC9B,MAAM,QAAQ,UAAA,GACbA,QAAAA,WAAAA,CAAW,IAAI,KAAA,GAAIC,QAAAA,aAAAA,CAAa,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI,KAAA;CAExD,MAAM,WAA0B,CAAC;CACjC,MAAM,eAAA,GAAcF,UAAAA,KAAAA,CAAK,MAAM,UAAU;CACzC,KAAK,MAAM,aAAA,GAAYC,QAAAA,WAAAA,CAAW,WAAW,KAAA,GAC1CE,QAAAA,YAAAA,CAAY,WAAW,CAAC,CAAC,KAAK,IAC9B,CAAC,GAAG;EACN,MAAM,OAAA,GAAMH,UAAAA,KAAAA,CAAK,aAAa,QAAQ;EACtC,IAAI,EAAA,GAACI,QAAAA,SAAAA,CAAS,GAAG,CAAC,CAAC,YAAY,GAAG;EAClC,MAAM,SAAgC,CAAC;EACvC,MAAM,aAAA,GAAYJ,UAAAA,KAAAA,CAAK,KAAK,QAAQ;EACpC,KAAK,MAAM,QAAQ,aAAa;GAC/B,MAAM,SAAS;IAAC;IAAQ;IAAQ;GAAO,CAAC,CACtC,KAAK,SAAA,GAAQA,UAAAA,KAAAA,CAAK,WAAW,GAAG,OAAO,KAAK,CAAC,CAAC,CAC9C,KAAKC,QAAAA,UAAU;GACjB,MAAM,QAAA,GAAOD,UAAAA,KAAAA,CAAK,WAAW,IAAI;GACjC,MAAM,QAAQ,SACX,CAAC,MAAM,KAAA,GACPC,QAAAA,WAAAA,CAAW,IAAI,KAAA,GACdE,QAAAA,YAAAA,CAAY,IAAI,CAAC,CAChB,QAAQ,MAAM,kBAAkB,KAAK,CAAC,CAAC,CAAC,CACxC,KAAK,CAAC,CACN,KAAK,OAAA,GAAMH,UAAAA,KAAAA,CAAK,MAAM,CAAC,CAAC,IACzB,CAAC;GACL,IAAI,MAAM,SAAS,GAAG,OAAO,QAAQ;EACtC;EACA,MAAM,UAAuB;GAAE;GAAU;EAAO;EAChD,MAAM,QAAQ,MAAA,GAAKA,UAAAA,KAAAA,CAAK,KAAK,WAAW,CAAC;EACzC,MAAM,mBAAmB,MAAA,GAAKA,UAAAA,KAAAA,CAAK,KAAK,uBAAuB,CAAC;EAChE,MAAM,kBAAkB,MAAA,GAAKA,UAAAA,KAAAA,CAAK,KAAK,sBAAsB,CAAC;EAC9D,MAAM,QAAQ,MAAA,GAAKA,UAAAA,KAAAA,CAAK,KAAK,WAAW,CAAC;EACzC,IAAI,OAAO,QAAQ,QAAQ;EAC3B,IAAI,kBAAkB,QAAQ,mBAAmB;EACjD,IAAI,iBAAiB,QAAQ,kBAAkB;EAC/C,IAAI,OAAO,QAAQ,QAAQ;EAC3B,SAAS,KAAK,OAAO;CACtB;CAEA,MAAM,aAAa,MAAA,GAAKA,UAAAA,KAAAA,CAAK,MAAM,iBAAiB,CAAC;CACrD,OAAO;EAAE;EAAU,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CAAG;AAC1D;;;;;;;;AASA,eAAsB,cACrB,QACA,aACA,OACsE;CACtE,MAAM,MAAM,GAAG,KAAK,gBAAgB;CACpC,IAAI,UAAU;CACd,IAAI;CACJ,IAAI,MAAM,SAAS,SAAS,GAAG;EAC9B,IAAI;EACJ,IAAI;GACH,OAAO,MAAM,OAAO,QAAwB,QAAQ,GAAG,IAAI,SAAS,CAAC,CAAC;EACvE,SAAS,OAAO;GACf,IAAI,EAAE,iBAAiB,iBAAiB,MAAM;GAC9C,SAAS,MAAM;EAChB;EACA,IAAI,MAAM;GACT,MAAM,OAAO,GAAG,IAAI,SAAS,KAAK,GAAG;GACrC,KAAK,MAAM,SAAS,MAAM,UAAU;IACnC,MAAM,EAAE,QAAQ,GAAG,SAAS;IAC5B,IAAI,KAAK,SAAS,KAAK,oBAAoB,KAAK,iBAC/C,MAAM,OAAO,QAAQ,OAAO,GAAG,KAAK,GAAG,MAAM,YAAY,IAAI;IAE9D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAAG;KACnD,MAAM,OAAO,QAAQ,UAAU,GAAG,KAAK,GAAG,MAAM,SAAS,GAAG,MAAM;KAClE,KAAK,MAAM,QAAQ,SAAS,CAAC,GAC5B,MAAM,OAAO,QACZ,QACA,GAAG,YAAY,gBAAgB,YAAY,SAAS,KAAK,GAAG,YAAY,MAAM,SAAS,GAAG,KAAK,oBAC/F,KAAA,GACA,EACC,KAAK;MACJ,cAAA,GACCK,UAAAA,QAAAA,CAAQ,IAAI,CAAC,CAAC,YAAY,MAAM,SAC7B,cACA;MACJ,OAAA,GAAMH,QAAAA,aAAAA,CAAa,IAAI;KACxB,EACD,CACD;IAEF;GACD;GACA,MAAM,OAAO,QAAQ,QAAQ,GAAG,IAAI,SAAS,KAAK,GAAG,QAAQ;GAC7D,UAAU;EACX;CACD;CAEA,IAAI,aAAa;CACjB,IAAI,MAAM,YAAY;EACrB,MAAM,OAAO,QAAQ,QAAQ,GAAG,IAAI,cAAc,EACjD,cAAc,MAAM,WACrB,CAAC;EACD,aAAa;CACd;CACA,OAAO;EAAE;EAAS;EAAY,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;CAAG;AAC7D;;AAGA,MAAa,yBAAyB;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1TA,MAAM,SAAS;;AAGf,IAAa,uBAAb,cAA0CI,eAAAA,SAAS;CAClD,cAAc;EACb,MACC,mJAED;EACA,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0CA,eAAAA,SAAS;CAExC;CACA;CAFV,YACC,KACA,SACC;EACD,MACC,YAAY,oBACT,IAAI,IAAI,kJAER,IAAI,IAAI,kHAEZ;EATS,KAAA,MAAA;EACA,KAAA,UAAA;EAST,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,mBAAb,cAAsCA,eAAAA,SAAS;CAEpC;CACA;CAFV,YACC,KACA,QACC;EACD,MACC,qCAAqC,IAAI,IAAI,OAAO,sDAErD;EANS,KAAA,MAAA;EACA,KAAA,SAAA;EAMT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,kBAAb,cAAqCA,eAAAA,SAAS;CAC7C,cAAc;EACb,MACC,gGACD;EACA,KAAK,OAAO;CACb;AACD;;AAyBA,SAAgB,WACf,KACA,MACA,KACyB;CACzB,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,MAAM,SAAA,GAAQC,mBAAAA,MAAAA,CAAM,QAAQ;GAAC;GAAU;GAAU;GAAU;EAAQ,GAAG;GACrE,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;GAAM;GAChC,KAAK;IACJ,GAAG,QAAQ;IACX,GAAG;IACH,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,OAAO,GAAG,SAAS,UAAkB;GAC1C,UAAU,MAAM,SAAS;EAC1B,CAAC;EACD,MAAM,OAAO,GAAG,SAAS,UAAkB;GAC1C,UAAU,MAAM,SAAS;EAC1B,CAAC;EACD,MAAM,GAAG,UAAU,UAClB,OAAO,IAAI,iBAAiB,KAAK,MAAM,OAAO,CAAC,CAChD;EACA,MAAM,GAAG,UAAU,SAAS;GAC3B,IAAI,SAAS,GAAG;IACf,OAAO,IAAI,iBAAiB,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,CAAC;IACjE;GACD;GACA,IAAI;IACH,QAAQ,KAAK,MAAM,MAAM,CAAkB;GAC5C,QAAQ;IACP,OAAO,IAAI,iBAAiB,KAAK,YAAY,OAAO,MAAM,GAAG,GAAG,GAAG,CAAC;GACrE;EACD,CAAC;CACF,CAAC;AACF;AAEA,MAAa,wBAAwC;CACpD,QAAQ,aAAa;CACrB,KAAK,OAAO;CACZ;CACA,aAAaC,cAAAA,UAAU;CACvB,QAAQ,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AAChE;;;;;;;AAwCA,eAAsB,iBACrB,SACA,OAAuB,uBACK;CAC5B,IAAI,CAAC,QAAQ,WAAW,MAAM,IAAI,qBAAqB;CACvD,MAAM,YAAY,QAAQ;CAG1B,MAAM,YAAY,MAAMC,eAAAA,oBAAoB,QAAQ,GAAG,EAAA,CAAG,UAAU;CACpE,MAAM,YACL,QAAQ,SAAS,SAAS,YAAY,MAAM,SAAS,SAAS;CAC/D,IAAI,CAAC,WAAW,MAAM,IAAI,gBAAgB;CAC1C,MAAM,SAAS,MAAMC,cAAAA,UAAU;EAC9B,OAAO;EACP,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;CACvD,CAAC;CACD,MAAM,EAAE,WAAW,UAAU,UAAU;CACvC,MAAM,cAAcC,YAAAA,gBAAgB,UAAU,QAAQ,KAAK,OAAO,OAAO;CACzE,MAAM,OAAO,YAAY;CACzB,MAAM,OAAA,GAAML,UAAAA,KAAAA,CAAK,UAAU,MAAM,YAAY,IAAI,IAAI;CACrD,MAAM,SACL,MAAM,WAAW,MAAM,cAAc,UAAU,OAAO,YAAY,EAAE,CAAC,EAClE,UAAU,CAAC;CAEf,MAAM,SAAS,MAAM,KAAK,WAAW,KAAK,UAAU,MAAM,MAAM;CAChE,MAAM,cAAc,OAAO,SAAS;CACpC,IAAI,CAAC,aAAa,MAAM,IAAI,qBAAqB,MAAM,iBAAiB;CACxE,MAAM,YAAY,OAAO,OAAO,KAAK;CACrC,IAAI,CAAC,WAAW,MAAM,IAAI,qBAAqB,MAAM,qBAAqB;CAG1E,MAAM,SAAS,UAAU,OAAO,SAAS,KAAK,UAC7CM,cAAAA,YAAY,WAAW,aAAa,KAAK,CAC1C;CACA,MAAM,eAAA,GAAcN,UAAAA,KAAAA,CAAK,KAAK,UAAU;CACxC,MAAM,UAAA,GAASO,QAAAA,WAAAA,CAAW,WAAW,IAClC,KAAK,MAAM,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,aAAa,MAAM,CAAC,IAC9C,CAAC;CACJ,MAAM,QAAQ,OAAO,OAAOC,YAAAA,mBAAmB,MAAM;CACrD,IAAI,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,KAAK,GAAG;EACrD,OAAA,GAAMC,iBAAAA,UAAAA,CAAU,aAAa,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;EAClE,OAAO,IAAI,MAAM,YAAY,yCAAyC;CACvE;CACA,MAAM,QAAQ,cAAc,GAAG;CAC/B,MAAM,cAAc,gBAAgB;EACnC,QAAQ,OAAO,KAAK,UAAU,MAAM,QAAQ,KAAK;EACjD,UAAU,OAAO,SAAS,UAAU,KAAK;CAC1C,CAAC;CAGD,MAAM,UAAU,QAAQ,WAAW,OAAO,UAAU,IAAI;CACxD,eAAe,WAAW,OAAO;CACjC,MAAM,UAAU,OAAO,UAAU,IAAI;CACrC,MAAM,QAAQ,MAAM,KAAK,OAAO,MAAM,EAAE,aAAa,KAAK,YAAY,CAAC;CAEvE,MAAM,UAAU,MAAM,cADK,aAAa,KACJ,GAAM,SAAS,GAAG,UAAU,KAAK,MAAM;CAC3E,OAAO,IACN,4BAA4B,UAAU,UAAU,eAAe,IAChE;CACA,MAAM,SAAS,aAAa,OAAO,OAAO;CAC1C,MAAM,mBAAmB,QAAQ,OAAO;CACxC,MAAM,QAAQ,MAAM,qBACnB,QACA,SACA,SACA,GAAG,UAAU,KAAK,kBACnB;CACA,OAAO,IAAI,sBAAsB,OAAO;CAIxC,IAAI;CACJ,KAAK,IAAI,UAAU,GAAG,CAAC,QAAQ,WAC9B,IAAI;EACH,SAAS,MAAM,iBAAiB,QAAQ;GACvC;GACA;GACA;GACA;EACD,CAAC;CACF,SAAS,OAAO;EACf,IAAI,EAAE,iBAAiB,mBAAmB,CAAC,KAAK,aAAa,MAAM;EACnE,IAAI,YAAY,GACf,OAAO,IAAI,OAAO,MAAM,QAAQ,qBAAqB;EAEtD,MAAM,KAAK,MAAM,IAAM;CACxB;CAED,OAAO,IACN,uBAAuB,YAAY,IAAI,OAAO,IAAI,YAAY,KAAK,IAAI,EAAE,EAC1E;CAGA,MAAM,QAAQ,MAAM,SAAS,QAAQ,SAAS,KAAK;CACnD,MAAM,MAAM,MAAM,UAAU,QAAQ,SAAS,KAAK;CAClD,MAAM,OAAO,KAAK,MAAM,IAAI,IAAI;CAEhC,MAAM,aAAa,MAAM,KAAK,IAAI,QAAQ,SAAS;CACnD,MAAM,cAAc,MAAM,KAAK,IAAI,mBAClC;EAAE;EAAW,UAAU,WAAW;CAAS,GAC3C,WACD;CACA,MAAM,SAAS,MAAM,KAAK,IAAI,UAAU,WAAW,WAAW,IAAI;CAClE,MAAM,KAAK,IAAI,kBAAkB,YAAY,IAAI,OAAO,EAAE;CAC1D,OAAO,IACN,cAAc,MAAM,GAAG,EAAE,8BAA8B,aACxD;CAGA,KAAK,IAAI,UAAU,IAAK,WACvB,IAAI;EACH,MAAM,UAAU,MAAM,WAAW;EACjC;CACD,SAAS,OAAO;EACf,IAAI,EAAE,iBAAiB,sBAAsB,WAAW,GAAG,MAAM;EACjE,MAAM,KAAK,MAAM,GAAM;CACxB;CAED,OAAO,IAAI,oCAAoC;CAE/C,MAAM,YAAY,MAAM,KAAK,IAAI,KAAK,WAAW,WAAW,EAAA,CAAG,QAC7D,cACA,UAAU,gBAAgB,SAAS,UAAU,OAAO,OAAO,EAC7D;CACA,KAAK,MAAM,OAAO,UAAU,MAAM,KAAK,IAAI,UAAU,IAAI,EAAE;CAC3D,KAAK,MAAM,OAAO,OAAO,MAAM,UAAU,QAAQ,GAAG;CACpD,IAAI,MAAM,SAAS,SAAS,SAAS,GACpC,OAAO,IACN,cAAc,MAAM,OAAO,6BAA6B,SAAS,OAAO,UACzE;CAID,MAAM,UAAU,QACb,MAAM,cAAc,QAAQ,aAAa,KAAK,IAC9C,KAAA;CACH,IAAI,SAAS,SAAS,OAAO,IAAI,2BAA2B;CAC5D,IAAI,SAAS,CAAC,SAAS,WAAW,SAAS,QAC1C,OAAO,IACN,qCAAqC,QAAQ,OAAO,+CACrD;CAED,IAAI,SAAS,YAAY,OAAO,IAAI,yBAAyB;CAG7D,OAAO,IAAI,wCAAwC;CACnD,KAAK,MAAM,QAAQ,wBAAwB,OAAO,IAAI,SAAS,MAAM;CAErE,OAAO;EACN;EACA;EACA;EACA;EACA;EACA,KAAK;GAAE,QAAQ,MAAM,GAAG;GAAG,KAAK,OAAO;EAAG;EAC1C,SAAS;GACR,QAAQ,MAAM,IAAI,KAAK;GACvB,KAAK,SAAS,KAAK,cAAc,UAAU,EAAE;EAC9C;EACA;CACD;AACD"}
1
+ {"version":3,"file":"googlePlay-B6uiHi65.cjs","names":["GkmError","GkmError","join","gkmHome","spawn","GkmError","GkmError","createSign","join","existsSync","readFileSync","readdirSync","statSync","extname","GkmError","spawn","join","delimiter","canPrompt","loadWorkspaceConfig","loadStage","chooseMobileApp","mobileStage","existsSync","readFile","withStageProfiles","writeFile"],"sources":["../src/mobile/googlePlay/easApi.ts","../src/mobile/googlePlay/google.ts","../src/mobile/googlePlay/gcp.ts","../src/mobile/googlePlay/play.ts","../src/mobile/googlePlay/index.ts"],"sourcesContent":["/**\n * EAS, as `gkm deploy:google-play` reaches it: the GraphQL API eas-cli itself\n * calls, with an `EXPO_TOKEN` — to upload a Google service account key and\n * assign it to the app's Android credentials for EAS Submit, which is what\n * `eas credentials` does interactively.\n *\n * The documents are eas-cli's own (24.x), each selecting only what gkm reads:\n * eas-cli's credentials fragment also brings back the app's keystore and its\n * passwords, which gkm has no reason to hold.\n */\n\nimport { GkmError } from '../../errors';\n\nexport const EAS_GRAPHQL = 'https://api.expo.dev/graphql';\n\n/** No `EXPO_TOKEN` to call EAS with. */\nexport class ExpoTokenMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm uploads the key to EAS with an access token, and EXPO_TOKEN is not ' +\n\t\t\t\t'set. Create one at https://expo.dev/settings/access-tokens and export ' +\n\t\t\t\t'it as EXPO_TOKEN.',\n\t\t);\n\t\tthis.name = 'ExpoTokenMissing';\n\t}\n}\n\n/** EAS refused a call. */\nexport class EasApiError extends GkmError {\n\tconstructor(\n\t\treadonly operation: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`EAS refused ${operation}: ${detail}`);\n\t\tthis.name = 'EasApiError';\n\t}\n}\n\n/** A Google service account key, as EAS holds it. */\nexport interface EasGoogleKey {\n\tid: string;\n\tclientEmail: string;\n\tprivateKeyIdentifier: string;\n}\n\nconst KEY_FIELDS = 'id clientEmail privateKeyIdentifier';\n\nexport interface EasApi {\n\t/** The project's owner account. */\n\tproject(projectId: string): Promise<{\n\t\taccountId: string;\n\t\taccountName: string;\n\t\tfullName: string;\n\t}>;\n\t/** The app's Android credentials for a package, created when absent. */\n\tandroidCredentials(\n\t\tproject: { projectId: string; fullName: string },\n\t\tpackageName: string,\n\t): Promise<{ id: string; submissionsKey?: EasGoogleKey }>;\n\t/** Upload a key file's JSON to the account. */\n\tcreateKey(\n\t\taccountId: string,\n\t\tjson: Record<string, unknown>,\n\t): Promise<EasGoogleKey>;\n\t/** Make a key the one EAS Submit uses for the credentials. */\n\tuseForSubmissions(credentialsId: string, keyId: string): Promise<void>;\n\t/** Every key on the account. */\n\tkeys(accountName: string): Promise<EasGoogleKey[]>;\n\tdeleteKey(id: string): Promise<void>;\n}\n\nexport function easApi(\n\ttoken: string | undefined = process.env.EXPO_TOKEN,\n\tendpoint: string = EAS_GRAPHQL,\n): EasApi {\n\tconst call = async <T>(\n\t\toperation: string,\n\t\tquery: string,\n\t\tvariables: Record<string, unknown>,\n\t): Promise<T> => {\n\t\tif (!token) throw new ExpoTokenMissing();\n\t\tconst response = await fetch(endpoint, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t'content-type': 'application/json',\n\t\t\t},\n\t\t\tbody: JSON.stringify({ query, variables, operationName: operation }),\n\t\t});\n\t\tconst body = (await response.json().catch(() => ({}))) as {\n\t\t\tdata?: T;\n\t\t\terrors?: { message: string }[];\n\t\t};\n\t\tif (!response.ok || body.errors?.length || !body.data) {\n\t\t\tthrow new EasApiError(\n\t\t\t\toperation,\n\t\t\t\tbody.errors?.map((error) => error.message).join('; ') ||\n\t\t\t\t\t`HTTP ${response.status}`,\n\t\t\t);\n\t\t}\n\t\treturn body.data;\n\t};\n\n\tconst credentialsFields = `id googleServiceAccountKeyForSubmissions { ${KEY_FIELDS} }`;\n\ttype Credentials = {\n\t\tid: string;\n\t\tgoogleServiceAccountKeyForSubmissions: EasGoogleKey | null;\n\t};\n\tconst asCredentials = (credentials: Credentials) => ({\n\t\tid: credentials.id,\n\t\t...(credentials.googleServiceAccountKeyForSubmissions\n\t\t\t? { submissionsKey: credentials.googleServiceAccountKeyForSubmissions }\n\t\t\t: {}),\n\t});\n\n\treturn {\n\t\tasync project(projectId) {\n\t\t\tconst data = await call<{\n\t\t\t\tapp: {\n\t\t\t\t\tbyId: {\n\t\t\t\t\t\tfullName: string;\n\t\t\t\t\t\townerAccount: { id: string; name: string };\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'AppByIdQuery',\n\t\t\t\t`query AppByIdQuery($appId: String!) {\n app { byId(appId: $appId) { id fullName ownerAccount { id name } } }\n}`,\n\t\t\t\t{ appId: projectId },\n\t\t\t);\n\t\t\tconst app = data.app.byId;\n\t\t\treturn {\n\t\t\t\taccountId: app.ownerAccount.id,\n\t\t\t\taccountName: app.ownerAccount.name,\n\t\t\t\tfullName: app.fullName,\n\t\t\t};\n\t\t},\n\n\t\tasync androidCredentials(project, packageName) {\n\t\t\tconst found = await call<{\n\t\t\t\tapp: { byFullName: { androidAppCredentials: Credentials[] } };\n\t\t\t}>(\n\t\t\t\t'AndroidAppCredentialsByApplicationIdentifierQuery',\n\t\t\t\t`query AndroidAppCredentialsByApplicationIdentifierQuery(\n $projectFullName: String!\n $applicationIdentifier: String\n $legacyOnly: Boolean\n) {\n app {\n byFullName(fullName: $projectFullName) {\n id\n androidAppCredentials(\n filter: { applicationIdentifier: $applicationIdentifier, legacyOnly: $legacyOnly }\n ) { ${credentialsFields} }\n }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tprojectFullName: project.fullName,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t\tlegacyOnly: false,\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst existing = found.app.byFullName.androidAppCredentials[0];\n\t\t\tif (existing) return asCredentials(existing);\n\n\t\t\tconst created = await call<{\n\t\t\t\tandroidAppCredentials: { createAndroidAppCredentials: Credentials };\n\t\t\t}>(\n\t\t\t\t'CreateAndroidAppCredentialsMutation',\n\t\t\t\t`mutation CreateAndroidAppCredentialsMutation(\n $androidAppCredentialsInput: AndroidAppCredentialsInput!\n $appId: ID!\n $applicationIdentifier: String!\n) {\n androidAppCredentials {\n createAndroidAppCredentials(\n androidAppCredentialsInput: $androidAppCredentialsInput\n appId: $appId\n applicationIdentifier: $applicationIdentifier\n ) { ${credentialsFields} }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsInput: {},\n\t\t\t\t\tappId: project.projectId,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn asCredentials(\n\t\t\t\tcreated.androidAppCredentials.createAndroidAppCredentials,\n\t\t\t);\n\t\t},\n\n\t\tasync createKey(accountId, json) {\n\t\t\tconst data = await call<{\n\t\t\t\tgoogleServiceAccountKey: {\n\t\t\t\t\tcreateGoogleServiceAccountKey: EasGoogleKey;\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'CreateGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation CreateGoogleServiceAccountKeyMutation(\n $googleServiceAccountKeyInput: GoogleServiceAccountKeyInput!\n $accountId: ID!\n) {\n googleServiceAccountKey {\n createGoogleServiceAccountKey(\n googleServiceAccountKeyInput: $googleServiceAccountKeyInput\n accountId: $accountId\n ) { ${KEY_FIELDS} }\n }\n}`,\n\t\t\t\t{ googleServiceAccountKeyInput: { jsonKey: json }, accountId },\n\t\t\t);\n\t\t\treturn data.googleServiceAccountKey.createGoogleServiceAccountKey;\n\t\t},\n\n\t\tasync useForSubmissions(credentialsId, keyId) {\n\t\t\tawait call(\n\t\t\t\t'SetGoogleServiceAccountKeyForSubmissionsMutation',\n\t\t\t\t`mutation SetGoogleServiceAccountKeyForSubmissionsMutation(\n $androidAppCredentialsId: ID!\n $googleServiceAccountKeyId: ID!\n) {\n androidAppCredentials {\n setGoogleServiceAccountKeyForSubmissions(\n id: $androidAppCredentialsId\n googleServiceAccountKeyId: $googleServiceAccountKeyId\n ) { id }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsId: credentialsId,\n\t\t\t\t\tgoogleServiceAccountKeyId: keyId,\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync keys(accountName) {\n\t\t\tconst keys: EasGoogleKey[] = [];\n\t\t\tlet after: string | null = null;\n\t\t\tdo {\n\t\t\t\tconst data: {\n\t\t\t\t\taccount: {\n\t\t\t\t\t\tbyName: {\n\t\t\t\t\t\t\tgoogleServiceAccountKeysPaginated: {\n\t\t\t\t\t\t\t\tedges: { node: EasGoogleKey }[];\n\t\t\t\t\t\t\t\tpageInfo: { hasNextPage: boolean; endCursor: string | null };\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t} = await call(\n\t\t\t\t\t'GoogleServiceAccountKeysPaginatedByAccountQuery',\n\t\t\t\t\t`query GoogleServiceAccountKeysPaginatedByAccountQuery(\n $accountName: String!\n $after: String\n $first: Int\n) {\n account {\n byName(accountName: $accountName) {\n id\n googleServiceAccountKeysPaginated(after: $after, first: $first) {\n edges { node { ${KEY_FIELDS} } }\n pageInfo { hasNextPage endCursor }\n }\n }\n }\n}`,\n\t\t\t\t\t{ accountName, after, first: 50 },\n\t\t\t\t);\n\t\t\t\tconst page = data.account.byName.googleServiceAccountKeysPaginated;\n\t\t\t\tkeys.push(...page.edges.map((edge) => edge.node));\n\t\t\t\tafter = page.pageInfo.hasNextPage ? page.pageInfo.endCursor : null;\n\t\t\t} while (after);\n\t\t\treturn keys;\n\t\t},\n\n\t\tasync deleteKey(id) {\n\t\t\tawait call(\n\t\t\t\t'DeleteGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation DeleteGoogleServiceAccountKeyMutation($googleServiceAccountKeyId: ID!) {\n googleServiceAccountKey { deleteGoogleServiceAccountKey(id: $googleServiceAccountKeyId) { id } }\n}`,\n\t\t\t\t{ googleServiceAccountKeyId: id },\n\t\t\t);\n\t\t},\n\t};\n}\n","/**\n * Google, as `gkm deploy:google-play` reaches it: a token for the person\n * running it, and the Cloud APIs that make a project, a service account and\n * its key.\n *\n * The token is gcloud's own application-default credential, with the\n * `cloud-platform` and `androidpublisher` scopes — no OAuth client of gkm's —\n * kept in gkm's own gcloud config (`<gkm home>/gcloud`), so the person's own\n * gcloud setup is never read or changed. Every call carries the project as\n * its quota project (`x-goog-user-project`), which the Play Developer API\n * requires of a user credential.\n */\n\nimport { spawn } from 'node:child_process';\nimport { join } from 'node:path';\nimport { GkmError } from '../../errors';\nimport { gkmHome } from '../../home.js';\n\n/** The scopes the sign-in asks for. */\nexport const GOOGLE_SCOPES = [\n\t'https://www.googleapis.com/auth/cloud-platform',\n\t'https://www.googleapis.com/auth/androidpublisher',\n];\n\n/** `gcloud` is not installed. */\nexport class GcloudMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm signs in to Google with gcloud, and there is no `gcloud` here. ' +\n\t\t\t\t'Install the Google Cloud CLI: https://cloud.google.com/sdk/docs/install.',\n\t\t);\n\t\tthis.name = 'GcloudMissing';\n\t}\n}\n\n/** Not signed in, and no terminal to sign in from. */\nexport class GoogleSignInRequired extends GkmError {\n\tconstructor(readonly config: string) {\n\t\tsuper(\n\t\t\t'Signing in to Google opens a browser, and this run has no terminal. ' +\n\t\t\t\t'Run it once where you can sign in, as a Play Console admin: ' +\n\t\t\t\t`CLOUDSDK_CONFIG=${config} gcloud auth application-default login ` +\n\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}.`,\n\t\t);\n\t\tthis.name = 'GoogleSignInRequired';\n\t}\n}\n\n/** A Google API refused a call. */\nexport class GoogleApiError extends GkmError {\n\tconstructor(\n\t\treadonly method: string,\n\t\treadonly url: string,\n\t\treadonly status: number,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`Google answered ${status} to ${method} ${url}: ${detail}`);\n\t\tthis.name = 'GoogleApiError';\n\t}\n}\n\n/** A long-running Google operation did not finish in time. */\nexport class GoogleOperationTimedOut extends GkmError {\n\tconstructor(readonly operation: string) {\n\t\tsuper(\n\t\t\t`Google's operation ${operation} had not finished after a minute. ` +\n\t\t\t\t'Run the command again: what it finished is kept.',\n\t\t);\n\t\tthis.name = 'GoogleOperationTimedOut';\n\t}\n}\n\n/** Where gkm keeps its gcloud sign-in. */\nexport function gcloudConfig(env: NodeJS.ProcessEnv = process.env): string {\n\treturn join(gkmHome(env), 'gcloud');\n}\n\n/** How a token is had: the real one runs gcloud. */\nexport interface GoogleSignIn {\n\t/** An access token for the signed-in person, signing in if allowed. */\n\ttoken(options: { interactive: boolean }): Promise<string>;\n}\n\nfunction gcloud(\n\targs: readonly string[],\n\tconfig: string,\n\tinherit: boolean,\n): Promise<{ code: number | null; stdout: string }> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('gcloud', [...args], {\n\t\t\tstdio: [\n\t\t\t\t'inherit',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t],\n\t\t\tenv: { ...process.env, CLOUDSDK_CONFIG: config },\n\t\t});\n\t\tlet stdout = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(error.code === 'ENOENT' ? new GcloudMissing() : error),\n\t\t);\n\t\tchild.on('close', (code) => resolve({ code, stdout }));\n\t});\n}\n\n/** gcloud's application-default credential, in gkm's own gcloud config. */\nexport function gcloudSignIn(config: string = gcloudConfig()): GoogleSignIn {\n\treturn {\n\t\tasync token({ interactive }) {\n\t\t\tconst print = () =>\n\t\t\t\tgcloud(\n\t\t\t\t\t['auth', 'application-default', 'print-access-token'],\n\t\t\t\t\tconfig,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\tlet printed = await print();\n\t\t\tif (printed.code !== 0) {\n\t\t\t\tif (!interactive) throw new GoogleSignInRequired(config);\n\t\t\t\tawait gcloud(\n\t\t\t\t\t[\n\t\t\t\t\t\t'auth',\n\t\t\t\t\t\t'application-default',\n\t\t\t\t\t\t'login',\n\t\t\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}`,\n\t\t\t\t\t],\n\t\t\t\t\tconfig,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tprinted = await print();\n\t\t\t\tif (printed.code !== 0) throw new GoogleSignInRequired(config);\n\t\t\t}\n\t\t\treturn printed.stdout.trim();\n\t\t},\n\t};\n}\n\n/** One authenticated Google API caller, for one quota project. */\nexport interface GoogleClient {\n\trequest<T>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody?: unknown,\n\t\toptions?: { raw?: { contentType: string; data: Uint8Array } },\n\t): Promise<T>;\n}\n\nexport function googleClient(\n\ttoken: string,\n\tquotaProject?: string,\n): GoogleClient {\n\treturn {\n\t\tasync request(method, url, body, options) {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t...(quotaProject ? { 'x-goog-user-project': quotaProject } : {}),\n\t\t\t};\n\t\t\tlet payload: string | Uint8Array | undefined;\n\t\t\tif (options?.raw) {\n\t\t\t\theaders['content-type'] = options.raw.contentType;\n\t\t\t\tpayload = options.raw.data;\n\t\t\t} else if (body !== undefined) {\n\t\t\t\theaders['content-type'] = 'application/json';\n\t\t\t\tpayload = JSON.stringify(body);\n\t\t\t}\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\t...(payload !== undefined ? { body: payload } : {}),\n\t\t\t});\n\t\t\tconst text = await response.text();\n\t\t\tif (!response.ok) {\n\t\t\t\tlet detail = text;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(text) as { error?: { message?: string } };\n\t\t\t\t\tdetail = parsed.error?.message ?? text;\n\t\t\t\t} catch {\n\t\t\t\t\t// Not JSON: the text is the detail.\n\t\t\t\t}\n\t\t\t\tthrow new GoogleApiError(method, url, response.status, detail);\n\t\t\t}\n\t\t\treturn (text ? JSON.parse(text) : {}) as never;\n\t\t},\n\t};\n}\n\n/** A long-running operation, polled until it is done. */\nexport async function finished(\n\tclient: GoogleClient,\n\tbase: string,\n\toperation: { name?: string; done?: boolean; error?: { message?: string } },\n\toptions: { intervalMs?: number; attempts?: number } = {},\n): Promise<void> {\n\tlet current = operation;\n\tfor (let attempt = 0; !current.done; attempt++) {\n\t\tif (attempt >= (options.attempts ?? 30)) {\n\t\t\tthrow new GoogleOperationTimedOut(current.name ?? '(unnamed)');\n\t\t}\n\t\tawait new Promise((resolve) =>\n\t\t\tsetTimeout(resolve, options.intervalMs ?? 2_000),\n\t\t);\n\t\tcurrent = await client.request('GET', `${base}/${current.name}`);\n\t}\n\tif (current.error) {\n\t\tthrow new GoogleApiError(\n\t\t\t'GET',\n\t\t\t`${base}/${current.name}`,\n\t\t\t500,\n\t\t\tcurrent.error.message ?? 'the operation failed',\n\t\t);\n\t}\n}\n","/**\n * The Google Cloud side of publishing to Play: a project with the Android\n * Publisher API on, and a service account in it whose JSON key EAS Submit\n * uses. Each step finds what is there before it creates anything, so a\n * second run reuses the project and the account and only makes a new key.\n */\n\nimport { GkmError } from '../../errors';\nimport { finished, GoogleApiError, type GoogleClient } from './google.js';\n\nexport const CLOUD_RESOURCE_MANAGER =\n\t'https://cloudresourcemanager.googleapis.com/v3';\nexport const SERVICE_USAGE = 'https://serviceusage.googleapis.com/v1';\nexport const IAM = 'https://iam.googleapis.com/v1';\n\n/** The API EAS Submit and gkm call Play through. */\nexport const ANDROID_PUBLISHER_API = 'androidpublisher.googleapis.com';\n\n/** A project id or service account id gkm would derive is not a valid one. */\nexport class GoogleIdInvalid extends GkmError {\n\tconstructor(\n\t\treadonly kind: 'project' | 'service account',\n\t\treadonly id: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is not a valid Google ${kind} id: 6 to 30 lowercase letters, ` +\n\t\t\t\t'digits and hyphens, starting with a letter and not ending in a hyphen. ' +\n\t\t\t\t(kind === 'project' ? 'Name one with --project.' : ''),\n\t\t);\n\t\tthis.name = 'GoogleIdInvalid';\n\t}\n}\n\n/** The project could not be created — most often, its id is someone else's. */\nexport class GoogleProjectUnavailable extends GkmError {\n\tconstructor(\n\t\treadonly project: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not create the Google Cloud project '${project}' (${detail}). ` +\n\t\t\t\t'Project ids are global: name another, or one you already have, with --project.',\n\t\t);\n\t\tthis.name = 'GoogleProjectUnavailable';\n\t}\n}\n\nconst ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;\n\n/**\n * An id Google accepts, from a workspace's name: `shop` → `shop-play`.\n * Lowercased, anything else made a hyphen, cut to 30.\n */\nexport function playId(workspace: string): string {\n\tconst base = workspace\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^[^a-z]+|-+$/g, '');\n\treturn `${base.slice(0, 25).replace(/-+$/, '')}-play`;\n}\n\n/** Throws unless `id` is a valid project or service account id. */\nexport function assertGoogleId(\n\tkind: 'project' | 'service account',\n\tid: string,\n): void {\n\tif (!ID.test(id)) throw new GoogleIdInvalid(kind, id);\n}\n\n/**\n * The project, created when it is not there. Answers whether it was made.\n *\n * @throws {GoogleProjectUnavailable}\n */\nexport async function ensureProject(\n\tclient: GoogleClient,\n\tproject: string,\n\tdisplayName: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<boolean> {\n\tassertGoogleId('project', project);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects/${project}`,\n\t\t);\n\t\treturn false;\n\t} catch (error) {\n\t\t// Absent, or not ours to see — either way, try to create it.\n\t\tif (\n\t\t\t!(error instanceof GoogleApiError) ||\n\t\t\t![403, 404].includes(error.status)\n\t\t) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\ttry {\n\t\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t\t'POST',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects`,\n\t\t\t{ projectId: project, displayName: displayName.slice(0, 30) },\n\t\t);\n\t\tawait finished(client, CLOUD_RESOURCE_MANAGER, operation, options);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new GoogleProjectUnavailable(project, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n\treturn true;\n}\n\n/** The Android Publisher API, on in the project. */\nexport async function enablePublisherApi(\n\tclient: GoogleClient,\n\tproject: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<void> {\n\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t'POST',\n\t\t`${SERVICE_USAGE}/projects/${project}/services/${ANDROID_PUBLISHER_API}:enable`,\n\t\t{},\n\t);\n\tawait finished(client, SERVICE_USAGE, operation, options);\n}\n\n/** The service account's email, which is what Play and EAS know it by. */\nexport function serviceAccountEmail(project: string, account: string): string {\n\treturn `${account}@${project}.iam.gserviceaccount.com`;\n}\n\n/** The service account, created when it is not there. Answers its email. */\nexport async function ensureServiceAccount(\n\tclient: GoogleClient,\n\tproject: string,\n\taccount: string,\n\tdisplayName: string,\n): Promise<string> {\n\tassertGoogleId('service account', account);\n\tconst email = serviceAccountEmail(project, account);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${IAM}/projects/${project}/serviceAccounts/${email}`,\n\t\t);\n\t\treturn email;\n\t} catch (error) {\n\t\tif (!(error instanceof GoogleApiError) || error.status !== 404) throw error;\n\t}\n\tawait client.request('POST', `${IAM}/projects/${project}/serviceAccounts`, {\n\t\taccountId: account,\n\t\tserviceAccount: { displayName: displayName.slice(0, 100) },\n\t});\n\treturn email;\n}\n\n/** A service account key: its id, and — only when just made — its JSON. */\nexport interface ServiceAccountKey {\n\t/** `projects/<p>/serviceAccounts/<email>/keys/<id>`. */\n\tname: string;\n\t/** The key file's JSON, held in memory and never written anywhere. */\n\tjson?: string;\n}\n\n/** A new key for the account. Its JSON exists only in what this returns. */\nexport async function createKey(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey & { json: string }> {\n\tconst key = await client.request<{ name: string; privateKeyData: string }>(\n\t\t'POST',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys`,\n\t\t{},\n\t);\n\treturn {\n\t\tname: key.name,\n\t\tjson: Buffer.from(key.privateKeyData, 'base64').toString('utf8'),\n\t};\n}\n\n/** The account's keys that someone made — not Google's own. */\nexport async function listKeys(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey[]> {\n\tconst { keys = [] } = await client.request<{ keys?: { name: string }[] }>(\n\t\t'GET',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys?keyTypes=USER_MANAGED`,\n\t);\n\treturn keys.map(({ name }) => ({ name }));\n}\n\nexport async function deleteKey(\n\tclient: GoogleClient,\n\tkey: ServiceAccountKey,\n): Promise<void> {\n\tawait client.request('DELETE', `${IAM}/${key.name}`);\n}\n\n/** A key's id: the last part of its name. */\nexport function keyId(key: Pick<ServiceAccountKey, 'name'>): string {\n\treturn key.name.split('/').pop()!;\n}\n","/**\n * Play Console, through the Play Developer API (androidpublisher v3): the\n * service account's access to the app, the store listing as code, and a\n * check that the new key can act on the app.\n */\n\nimport { createSign } from 'node:crypto';\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { extname, join } from 'node:path';\nimport type { PlayTrack } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\nimport { GoogleApiError, type GoogleClient, googleClient } from './google.js';\n\nexport const PLAY =\n\t'https://androidpublisher.googleapis.com/androidpublisher/v3';\nexport const PLAY_UPLOAD =\n\t'https://androidpublisher.googleapis.com/upload/androidpublisher/v3';\n\n/** What Play answers about a package no app in the account has. */\nconst PACKAGE_MISSING = /packages are not available/i;\n\n/** The app is not in Play Console yet, and Play has no API that makes one. */\nexport class PlayAppMissing extends GkmError {\n\tconstructor(\n\t\treadonly packageName: string,\n\t\treadonly developer: string,\n\t) {\n\t\tsuper(\n\t\t\t`Play Console has no app '${packageName}', and Play has no API that ` +\n\t\t\t\t`creates one. Create it at https://play.google.com/console/developers/` +\n\t\t\t\t`${developer}/create-new-app, with ${packageName} as its package, then ` +\n\t\t\t\t'run this again.',\n\t\t);\n\t\tthis.name = 'PlayAppMissing';\n\t}\n}\n\n/** The new key could not open an edit on the app. */\nexport class PlayKeyUnverified extends GkmError {\n\tconstructor(\n\t\treadonly email: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`The new key for ${email} could not open an edit on the app (${detail}). ` +\n\t\t\t\t'Its access can take a few minutes to reach Play: run this again shortly.',\n\t\t);\n\t\tthis.name = 'PlayKeyUnverified';\n\t}\n}\n\n/**\n * The app-level permissions the service account needs: releasing to the\n * testing tracks, to production only where a stage submits there, and the\n * store listing where the workspace keeps one.\n */\nexport function playPermissions(options: {\n\ttracks: readonly PlayTrack[];\n\tlisting: boolean;\n}): string[] {\n\treturn [\n\t\t'CAN_MANAGE_TRACK_APKS',\n\t\t...(options.tracks.includes('production')\n\t\t\t? ['CAN_MANAGE_PUBLIC_APKS']\n\t\t\t: []),\n\t\t...(options.listing ? ['CAN_MANAGE_PUBLIC_LISTING'] : []),\n\t].sort();\n}\n\ninterface PlayUser {\n\temail: string;\n\tgrants?: { packageName: string; appLevelPermissions?: string[] }[];\n}\n\n/**\n * The service account in Play Console with exactly `permissions` on the\n * app: invited with the grant in one call — Play refuses a user with no\n * permission, and a grant for a user not yet there — and, already there,\n * its grant made what it should be. Answers what changed.\n *\n * @throws {PlayAppMissing}\n */\nexport async function ensurePlayAccess(\n\tclient: GoogleClient,\n\toptions: {\n\t\tdeveloper: string;\n\t\temail: string;\n\t\tpackageName: string;\n\t\tpermissions: readonly string[];\n\t},\n): Promise<'invited' | 'granted' | 'updated' | 'unchanged'> {\n\tconst { developer, email, packageName } = options;\n\tconst permissions = [...options.permissions].sort();\n\tconst users = `${PLAY}/developers/${developer}/users`;\n\tconst missing = (error: unknown) =>\n\t\terror instanceof GoogleApiError && PACKAGE_MISSING.test(error.detail)\n\t\t\t? new PlayAppMissing(packageName, developer)\n\t\t\t: error;\n\n\t// Listing takes no paging: Play says \"Pagination is not currently\n\t// available\" unless asked for every user at once.\n\tconst { users: listed = [] } = await client.request<{ users?: PlayUser[] }>(\n\t\t'GET',\n\t\t`${users}?pageSize=-1`,\n\t);\n\tconst user = listed.find((candidate) => candidate.email === email);\n\n\tif (!user) {\n\t\tawait client\n\t\t\t.request('POST', users, {\n\t\t\t\temail,\n\t\t\t\tgrants: [{ packageName, appLevelPermissions: permissions }],\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'invited';\n\t}\n\n\tconst grant = user.grants?.find((g) => g.packageName === packageName);\n\tconst grantPath = `${users}/${encodeURIComponent(email)}/grants`;\n\tif (!grant) {\n\t\tawait client\n\t\t\t.request('POST', grantPath, {\n\t\t\t\tpackageName,\n\t\t\t\tappLevelPermissions: permissions,\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'granted';\n\t}\n\tconst had = [...(grant.appLevelPermissions ?? [])].sort();\n\tif (had.join() === permissions.join()) return 'unchanged';\n\tawait client.request(\n\t\t'PATCH',\n\t\t`${grantPath}/${packageName}?updateMask=appLevelPermissions`,\n\t\t{ appLevelPermissions: permissions },\n\t);\n\treturn 'updated';\n}\n\n/** A service account key file, as Google writes it. */\nexport interface ServiceAccountJson {\n\tclient_email: string;\n\tprivate_key: string;\n\ttoken_uri?: string;\n}\n\n/** An access token for the key itself: a signed JWT, exchanged. */\nexport async function keyToken(key: ServiceAccountJson): Promise<string> {\n\tconst tokenUri = key.token_uri ?? 'https://oauth2.googleapis.com/token';\n\tconst now = Math.floor(Date.now() / 1000);\n\tconst encode = (value: object) =>\n\t\tBuffer.from(JSON.stringify(value)).toString('base64url');\n\tconst unsigned = `${encode({ alg: 'RS256', typ: 'JWT' })}.${encode({\n\t\tiss: key.client_email,\n\t\tscope: 'https://www.googleapis.com/auth/androidpublisher',\n\t\taud: tokenUri,\n\t\tiat: now,\n\t\texp: now + 600,\n\t})}`;\n\tconst signature = createSign('RSA-SHA256')\n\t\t.update(unsigned)\n\t\t.sign(key.private_key)\n\t\t.toString('base64url');\n\tconst response = await fetch(tokenUri, {\n\t\tmethod: 'POST',\n\t\theaders: { 'content-type': 'application/x-www-form-urlencoded' },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n\t\t\tassertion: `${unsigned}.${signature}`,\n\t\t}),\n\t});\n\tconst body = (await response.json().catch(() => ({}))) as {\n\t\taccess_token?: string;\n\t\terror_description?: string;\n\t};\n\tif (!response.ok || !body.access_token) {\n\t\tthrow new PlayKeyUnverified(\n\t\t\tkey.client_email,\n\t\t\tbody.error_description ?? `token exchange answered ${response.status}`,\n\t\t);\n\t}\n\treturn body.access_token;\n}\n\n/**\n * Proof the key and its grant work: as the key, open an edit on the app and\n * discard it — what EAS Submit's first call is.\n *\n * @throws {PlayKeyUnverified}\n */\nexport async function verifyKey(\n\tkey: ServiceAccountJson,\n\tpackageName: string,\n): Promise<void> {\n\tconst client = googleClient(await keyToken(key));\n\ttry {\n\t\tconst edit = await client.request<{ id: string }>(\n\t\t\t'POST',\n\t\t\t`${PLAY}/applications/${packageName}/edits`,\n\t\t\t{},\n\t\t);\n\t\tawait client.request(\n\t\t\t'DELETE',\n\t\t\t`${PLAY}/applications/${packageName}/edits/${edit.id}`,\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new PlayKeyUnverified(key.client_email, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** The image kinds Play's listing takes, as fastlane supply names them. */\nconst IMAGE_TYPES = [\n\t'icon',\n\t'featureGraphic',\n\t'promoGraphic',\n\t'tvBanner',\n\t'phoneScreenshots',\n\t'sevenInchScreenshots',\n\t'tenInchScreenshots',\n\t'tvScreenshots',\n\t'wearScreenshots',\n] as const;\n\n/** One locale's listing, read from `store/play/listings/<locale>/`. */\nexport interface PlayListing {\n\tlanguage: string;\n\ttitle?: string;\n\tshortDescription?: string;\n\tfullDescription?: string;\n\tvideo?: string;\n\t/** Each image kind's files, in order. */\n\timages: Partial<Record<(typeof IMAGE_TYPES)[number], string[]>>;\n}\n\n/** The store listing kept beside the app, in fastlane supply's layout. */\nexport interface PlayStore {\n\tlistings: PlayListing[];\n\t/** `store/play/data-safety.csv`: Play Console's data safety export. */\n\tdataSafety?: string;\n}\n\n/**\n * `<app>/store/play`, read: undefined where there is none.\n *\n * ```\n * store/play/\n * data-safety.csv\n * listings/en-US/title.txt, short_description.txt, full_description.txt, video.txt\n * listings/en-US/images/icon.png, featureGraphic.png\n * listings/en-US/images/phoneScreenshots/1.png, 2.png …\n * ```\n */\nexport function readPlayStore(appDir: string): PlayStore | undefined {\n\tconst root = join(appDir, 'store', 'play');\n\tif (!existsSync(root)) return undefined;\n\tconst text = (path: string) =>\n\t\texistsSync(path) ? readFileSync(path, 'utf8').trim() : undefined;\n\n\tconst listings: PlayListing[] = [];\n\tconst listingsDir = join(root, 'listings');\n\tfor (const language of existsSync(listingsDir)\n\t\t? readdirSync(listingsDir).sort()\n\t\t: []) {\n\t\tconst dir = join(listingsDir, language);\n\t\tif (!statSync(dir).isDirectory()) continue;\n\t\tconst images: PlayListing['images'] = {};\n\t\tconst imagesDir = join(dir, 'images');\n\t\tfor (const type of IMAGE_TYPES) {\n\t\t\tconst single = ['.png', '.jpg', '.jpeg']\n\t\t\t\t.map((ext) => join(imagesDir, `${type}${ext}`))\n\t\t\t\t.find(existsSync);\n\t\t\tconst many = join(imagesDir, type);\n\t\t\tconst files = single\n\t\t\t\t? [single]\n\t\t\t\t: existsSync(many)\n\t\t\t\t\t? readdirSync(many)\n\t\t\t\t\t\t\t.filter((f) => /\\.(png|jpe?g)$/i.test(f))\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.map((f) => join(many, f))\n\t\t\t\t\t: [];\n\t\t\tif (files.length > 0) images[type] = files;\n\t\t}\n\t\tconst listing: PlayListing = { language, images };\n\t\tconst title = text(join(dir, 'title.txt'));\n\t\tconst shortDescription = text(join(dir, 'short_description.txt'));\n\t\tconst fullDescription = text(join(dir, 'full_description.txt'));\n\t\tconst video = text(join(dir, 'video.txt'));\n\t\tif (title) listing.title = title;\n\t\tif (shortDescription) listing.shortDescription = shortDescription;\n\t\tif (fullDescription) listing.fullDescription = fullDescription;\n\t\tif (video) listing.video = video;\n\t\tlistings.push(listing);\n\t}\n\n\tconst dataSafety = text(join(root, 'data-safety.csv'));\n\treturn { listings, ...(dataSafety ? { dataSafety } : {}) };\n}\n\n/**\n * Push the listing through one edit, committed: each locale's text, and each\n * image kind replaced by the files kept. Then the data safety form, which\n * Play takes outside an edit. Answers whether the listing was pushed: an\n * app that has never had an upload may refuse an edit, and that is left for\n * the next run, not failed.\n */\nexport async function pushPlayStore(\n\tclient: GoogleClient,\n\tpackageName: string,\n\tstore: PlayStore,\n): Promise<{ listing: boolean; dataSafety: boolean; reason?: string }> {\n\tconst app = `${PLAY}/applications/${packageName}`;\n\tlet listing = false;\n\tlet reason: string | undefined;\n\tif (store.listings.length > 0) {\n\t\tlet edit: { id: string } | undefined;\n\t\ttry {\n\t\t\tedit = await client.request<{ id: string }>('POST', `${app}/edits`, {});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof GoogleApiError)) throw error;\n\t\t\treason = error.detail;\n\t\t}\n\t\tif (edit) {\n\t\t\tconst base = `${app}/edits/${edit.id}/listings`;\n\t\t\tfor (const entry of store.listings) {\n\t\t\t\tconst { images, ...text } = entry;\n\t\t\t\tif (text.title || text.shortDescription || text.fullDescription) {\n\t\t\t\t\tawait client.request('PUT', `${base}/${entry.language}`, text);\n\t\t\t\t}\n\t\t\t\tfor (const [type, files] of Object.entries(images)) {\n\t\t\t\t\tawait client.request('DELETE', `${base}/${entry.language}/${type}`);\n\t\t\t\t\tfor (const file of files ?? []) {\n\t\t\t\t\t\tawait client.request(\n\t\t\t\t\t\t\t'POST',\n\t\t\t\t\t\t\t`${PLAY_UPLOAD}/applications/${packageName}/edits/${edit.id}/listings/${entry.language}/${type}?uploadType=media`,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\traw: {\n\t\t\t\t\t\t\t\t\tcontentType:\n\t\t\t\t\t\t\t\t\t\textname(file).toLowerCase() === '.png'\n\t\t\t\t\t\t\t\t\t\t\t? 'image/png'\n\t\t\t\t\t\t\t\t\t\t\t: 'image/jpeg',\n\t\t\t\t\t\t\t\t\tdata: readFileSync(file),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait client.request('POST', `${app}/edits/${edit.id}:commit`);\n\t\t\tlisting = true;\n\t\t}\n\t}\n\n\tlet dataSafety = false;\n\tif (store.dataSafety) {\n\t\tawait client.request('POST', `${app}/dataSafety`, {\n\t\t\tsafetyLabels: store.dataSafety,\n\t\t});\n\t\tdataSafety = true;\n\t}\n\treturn { listing, dataSafety, ...(reason ? { reason } : {}) };\n}\n\n/** What Play Console only takes by hand, for the person to tick off. */\nexport const PLAY_CONSOLE_CHECKLIST = [\n\t'App content → Privacy policy: the URL of the published policy',\n\t'App content → App access: how a reviewer signs in',\n\t'App content → Ads: whether the app shows ads',\n\t'App content → Content rating: the questionnaire',\n\t'App content → Target audience and content',\n\t'App content → Health apps / Health Connect, where the app reads health data',\n\t'App content → Sensitive permissions, where the app asks for any',\n\t'Release → the first upload of a new app, by hand',\n];\n","/**\n * `gkm deploy:google-play`: what EAS Submit needs to publish a `MobileApp`\n * to Google Play, set up in one run — the counterpart of `gkm deploy:github`\n * for the AWS side.\n *\n * 1. Each deployed stage's track and release status into `eas.json`\n * (`submit.<stage>.android`), from `mobileStage`.\n * 2. Google: the project (`--project`, else `<workspace>-play`, made when\n * absent) with the Android Publisher API on, and the service account\n * `<workspace>-play` in it, found or made.\n * 3. Play Console: the account invited with a grant on the app alone, its\n * permissions following the stages' tracks — production releases only\n * when a stage submits there. An app not in Play Console yet is a step\n * only a person can take; interactively, the run says so and waits.\n * 4. A new JSON key, held in memory: uploaded to EAS, assigned to the app's\n * Android credentials for EAS Submit, and proved by opening and\n * discarding an edit with it. Only then are the account's older keys\n * deleted, from EAS and from Google — a failed run leaves the old key\n * working.\n * 5. The store listing kept beside the app (`store/play/`), pushed.\n * 6. What Play Console only takes by hand, as a checklist.\n *\n * Running it again rotates the key and pushes the listing.\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 { loadWorkspaceConfig } from '../../config.js';\nimport { GkmError } from '../../errors';\nimport { loadStage } from '../../exec/stage.js';\nimport { canPrompt } from '../../prompt.js';\nimport { chooseMobileApp, withStageProfiles } from '../eas.js';\nimport { mobileStage } from '../stage.js';\nimport { type EasApi, easApi } from './easApi.js';\nimport {\n\tassertGoogleId,\n\tcreateKey,\n\tdeleteKey,\n\tenablePublisherApi,\n\tensureProject,\n\tensureServiceAccount,\n\tkeyId,\n\tlistKeys,\n\tplayId,\n} from './gcp.js';\nimport {\n\ttype GoogleClient,\n\ttype GoogleSignIn,\n\tgcloudSignIn,\n\tgoogleClient,\n} from './google.js';\nimport {\n\tensurePlayAccess,\n\tPLAY_CONSOLE_CHECKLIST,\n\tPlayAppMissing,\n\tPlayKeyUnverified,\n\tplayPermissions,\n\tpushPlayStore,\n\treadPlayStore,\n\ttype ServiceAccountJson,\n\tverifyKey,\n} from './play.js';\n\nconst logger = console;\n\n/** No Play developer account to act in. */\nexport class PlayDeveloperMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'Name the Play developer account: --developer <id>. It is the number in ' +\n\t\t\t\t\"Play Console's address: https://play.google.com/console/developers/<id>/….\",\n\t\t);\n\t\tthis.name = 'PlayDeveloperMissing';\n\t}\n}\n\n/** The app's Expo config has no Android package, or no EAS project. */\nexport class ExpoConfigIncomplete extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly missing: 'android.package' | 'extra.eas.projectId',\n\t) {\n\t\tsuper(\n\t\t\tmissing === 'android.package'\n\t\t\t\t? `'${app}' has no android.package in its Expo config: Play knows an app ` +\n\t\t\t\t\t\t\"by its package. Set it in app.config.ts: android: { package: 'com.acme.shop' }.\"\n\t\t\t\t: `'${app}' has no extra.eas.projectId in its Expo config: it is not ` +\n\t\t\t\t\t\t'linked to an EAS project. Run `eas init` in the app.',\n\t\t);\n\t\tthis.name = 'ExpoConfigIncomplete';\n\t}\n}\n\n/** `expo config` could not read the app's config. */\nexport class ExpoConfigFailed extends GkmError {\n\tconstructor(\n\t\treadonly dir: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read the Expo config in ${dir} (${detail}). Check that ` +\n\t\t\t\t'`npx expo config --json` works there.',\n\t\t);\n\t\tthis.name = 'ExpoConfigFailed';\n\t}\n}\n\n/** The workspace deploys no stage. */\nexport class NoDeployedStage extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm.config.ts deploys no stage, so there is nothing to publish to: add one to stages.deployed.',\n\t\t);\n\t\tthis.name = 'NoDeployedStage';\n\t}\n}\n\n/** What the run needs of the app's Expo config. */\nexport interface ExpoAppConfig {\n\tname?: string;\n\tandroid?: { package?: string };\n\textra?: { eas?: { projectId?: string } };\n}\n\nexport interface GooglePlayDeps {\n\tsignIn: GoogleSignIn;\n\teas: EasApi;\n\t/** The app's Expo config, evaluated with the values its build reads. */\n\texpoConfig: (\n\t\tdir: string,\n\t\troot: string,\n\t\tenv: Readonly<Record<string, string>>,\n\t) => Promise<ExpoAppConfig>;\n\t/** Whether a person is here: to sign in, and to create the app. */\n\tinteractive: boolean;\n\t/** Wait between two looks for the app, and two tries of the new key. */\n\tsleep: (ms: number) => Promise<void>;\n}\n\n/** `expo config --json`, run with the app's Expo — its own, or the workspace's. */\nexport function expoConfig(\n\tdir: string,\n\troot: string,\n\tenv: Readonly<Record<string, string>>,\n): Promise<ExpoAppConfig> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('expo', ['config', '--json', '--type', 'public'], {\n\t\t\tcwd: dir,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...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});\n\t\tchild.on('error', (error) =>\n\t\t\treject(new ExpoConfigFailed(dir, error.message)),\n\t\t);\n\t\tchild.on('close', (code) => {\n\t\t\tif (code !== 0) {\n\t\t\t\treject(new ExpoConfigFailed(dir, stderr.trim() || `exit ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresolve(JSON.parse(stdout) as ExpoAppConfig);\n\t\t\t} catch {\n\t\t\t\treject(new ExpoConfigFailed(dir, `no JSON: ${stdout.slice(0, 200)}`));\n\t\t\t}\n\t\t});\n\t});\n}\n\nexport const defaultGooglePlayDeps: GooglePlayDeps = {\n\tsignIn: gcloudSignIn(),\n\teas: easApi(),\n\texpoConfig,\n\tinteractive: canPrompt(),\n\tsleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface GooglePlayOptions {\n\t/** The mobile app; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The Google Cloud project: `<workspace>-play` by default. */\n\tproject?: string;\n\t/** The Play developer account's id. */\n\tdeveloper?: string;\n\t/**\n\t * The stage whose values evaluate the app's Expo config: the protected\n\t * one, else the first deployed.\n\t */\n\tstage?: string;\n\t/** The AWS profile for that stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n}\n\nexport interface GooglePlayResult {\n\tpackageName: string;\n\tproject: string;\n\temail: string;\n\tpermissions: string[];\n\taccess: 'invited' | 'granted' | 'updated' | 'unchanged';\n\t/** The new key's id, in Google and in EAS. */\n\tkey: { google: string; eas: string };\n\t/** The older keys deleted. */\n\tdeleted: { google: string[]; eas: string[] };\n\tlisting:\n\t\t| { listing: boolean; dataSafety: boolean; reason?: string }\n\t\t| undefined;\n}\n\n/**\n * Set up publishing a mobile app to Google Play.\n *\n * @throws {PlayDeveloperMissing}, {ExpoConfigIncomplete}, {PlayAppMissing},\n * {PlayKeyUnverified} — and what each step's API throws\n */\nexport async function deployGooglePlay(\n\toptions: GooglePlayOptions,\n\tdeps: GooglePlayDeps = defaultGooglePlayDeps,\n): Promise<GooglePlayResult> {\n\tif (!options.developer) throw new PlayDeveloperMissing();\n\tconst developer = options.developer;\n\n\t// The stage only evaluates the Expo config: any deployed one's values do.\n\tconst declared = (await loadWorkspaceConfig(options.cwd)).workspace.stages;\n\tconst stageName =\n\t\toptions.stage ?? declared.protected?.[0] ?? declared.deployed[0];\n\tif (!stageName) throw new NoDeployedStage();\n\tconst loaded = await loadStage({\n\t\tstage: stageName,\n\t\t...(options.cwd ? { cwd: options.cwd } : {}),\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { workspace, manifest, stack } = loaded;\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = declaration.id;\n\tconst dir = join(workspace.root, declaration.app.path);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.id === declaration.id)\n\t\t\t?.values ?? {};\n\n\tconst config = await deps.expoConfig(dir, workspace.root, values);\n\tconst packageName = config.android?.package;\n\tif (!packageName) throw new ExpoConfigIncomplete(name, 'android.package');\n\tconst projectId = config.extra?.eas?.projectId;\n\tif (!projectId) throw new ExpoConfigIncomplete(name, 'extra.eas.projectId');\n\n\t// 1. Each stage's track, in eas.json — and the tracks the grant follows.\n\tconst stages = workspace.stages.deployed.map((stage) =>\n\t\tmobileStage(workspace, declaration, stage),\n\t);\n\tconst easJsonPath = join(dir, 'eas.json');\n\tconst before = existsSync(easJsonPath)\n\t\t? JSON.parse(await readFile(easJsonPath, 'utf8'))\n\t\t: {};\n\tconst after = stages.reduce(withStageProfiles, before);\n\tif (JSON.stringify(before) !== JSON.stringify(after)) {\n\t\tawait writeFile(easJsonPath, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(`📝 ${easJsonPath}: each stage's build and submit profiles`);\n\t}\n\tconst store = readPlayStore(dir);\n\tconst permissions = playPermissions({\n\t\ttracks: stages.map((stage) => stage.android.track),\n\t\tlisting: (store?.listings.length ?? 0) > 0,\n\t});\n\n\t// 2. Google: signed in as a Play Console admin, the project and account.\n\tconst project = options.project ?? playId(workspace.name);\n\tassertGoogleId('project', project);\n\tconst account = playId(workspace.name);\n\tconst token = await deps.signIn.token({ interactive: deps.interactive });\n\tconst bare: GoogleClient = googleClient(token);\n\tconst created = await ensureProject(bare, project, `${workspace.name} Play`);\n\tlogger.log(\n\t\t`☁️ Google Cloud project ${project}${created ? ' (created)' : ''}`,\n\t);\n\tconst client = googleClient(token, project);\n\tawait enablePublisherApi(client, project);\n\tconst email = await ensureServiceAccount(\n\t\tclient,\n\t\tproject,\n\t\taccount,\n\t\t`${workspace.name} publishing (gkm)`,\n\t);\n\tlogger.log(`🤖 Service account ${email}`);\n\n\t// 3. Play Console: the app grant, waiting for the app where someone can\n\t// make it.\n\tlet access: GooglePlayResult['access'] | undefined;\n\tfor (let attempt = 0; !access; attempt++) {\n\t\ttry {\n\t\t\taccess = await ensurePlayAccess(client, {\n\t\t\t\tdeveloper,\n\t\t\t\temail,\n\t\t\t\tpackageName,\n\t\t\t\tpermissions,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayAppMissing) || !deps.interactive) throw error;\n\t\t\tif (attempt === 0) {\n\t\t\t\tlogger.log(`\\n⏳ ${error.message}\\n Waiting for it…`);\n\t\t\t}\n\t\t\tawait deps.sleep(15_000);\n\t\t}\n\t}\n\tlogger.log(\n\t\t`🎟️ Play access on ${packageName}: ${access} (${permissions.join(', ')})`,\n\t);\n\n\t// 4. A new key: to EAS, proved, and only then the old ones gone.\n\tconst older = await listKeys(client, project, email);\n\tconst key = await createKey(client, project, email);\n\tconst json = JSON.parse(key.json) as ServiceAccountJson &\n\t\tRecord<string, unknown>;\n\tconst easProject = await deps.eas.project(projectId);\n\tconst credentials = await deps.eas.androidCredentials(\n\t\t{ projectId, fullName: easProject.fullName },\n\t\tpackageName,\n\t);\n\tconst easKey = await deps.eas.createKey(easProject.accountId, json);\n\tawait deps.eas.useForSubmissions(credentials.id, easKey.id);\n\tlogger.log(\n\t\t`🔑 New key ${keyId(key)} assigned to EAS Submit for ${packageName}`,\n\t);\n\n\t// A new grant takes a moment to reach Play.\n\tfor (let attempt = 0; ; attempt++) {\n\t\ttry {\n\t\t\tawait verifyKey(json, packageName);\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayKeyUnverified) || attempt >= 5) throw error;\n\t\t\tawait deps.sleep(10_000);\n\t\t}\n\t}\n\tlogger.log('✅ The key opens an edit on the app');\n\n\tconst easOlder = (await deps.eas.keys(easProject.accountName)).filter(\n\t\t(candidate) =>\n\t\t\tcandidate.clientEmail === email && candidate.id !== easKey.id,\n\t);\n\tfor (const old of easOlder) await deps.eas.deleteKey(old.id);\n\tfor (const old of older) await deleteKey(client, old);\n\tif (older.length + easOlder.length > 0) {\n\t\tlogger.log(\n\t\t\t`🧹 Deleted ${older.length} older key(s) from Google, ${easOlder.length} from EAS`,\n\t\t);\n\t}\n\n\t// 5. The listing, where the workspace keeps one.\n\tconst listing = store\n\t\t? await pushPlayStore(client, packageName, store)\n\t\t: undefined;\n\tif (listing?.listing) logger.log('🛍️ Store listing pushed');\n\tif (store && !listing?.listing && listing?.reason) {\n\t\tlogger.log(\n\t\t\t`⏭️ Store listing not pushed yet (${listing.reason}): run this again after the app's first upload`,\n\t\t);\n\t}\n\tif (listing?.dataSafety) logger.log('🛡️ Data safety pushed');\n\n\t// 6. What only a person can do.\n\tlogger.log('\\nLeft to do in Play Console, by hand:');\n\tfor (const item of PLAY_CONSOLE_CHECKLIST) logger.log(` [ ] ${item}`);\n\n\treturn {\n\t\tpackageName,\n\t\tproject,\n\t\temail,\n\t\tpermissions,\n\t\taccess,\n\t\tkey: { google: keyId(key), eas: easKey.id },\n\t\tdeleted: {\n\t\t\tgoogle: older.map(keyId),\n\t\t\teas: easOlder.map((candidate) => candidate.id),\n\t\t},\n\t\tlisting,\n\t};\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
2
  import { s as gkmHome } from "./run-BJLIerMM.mjs";
3
3
  import { loadWorkspaceConfig } from "./config.mjs";
4
- import { n as mobileStage, s as canPrompt } from "./index.mjs";
5
- import { t as loadStage } from "./stage-DjxR_7C0.mjs";
6
- import { chooseMobileApp, withStageProfiles } from "./eas-6ezdJtry.mjs";
4
+ import { l as canPrompt, n as mobileStage } from "./index.mjs";
5
+ import { t as loadStage } from "./stage-ClKTHQoM.mjs";
6
+ import { chooseMobileApp, withStageProfiles } from "./eas-DEpwISrQ.mjs";
7
7
  import { delimiter, extname, join } from "node:path";
8
8
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
9
9
  import { readFile, writeFile } from "node:fs/promises";
@@ -928,4 +928,4 @@ async function deployGooglePlay(options, deps = defaultGooglePlayDeps) {
928
928
  //#endregion
929
929
  export { deployGooglePlay };
930
930
 
931
- //# sourceMappingURL=googlePlay-DY9vZQ1p.mjs.map
931
+ //# sourceMappingURL=googlePlay-BYZ-J5sC.mjs.map