@shiplane/eas 0.4.0 โ†’ 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,3 +91,12 @@ an `eas.json`.
91
91
 
92
92
  Source: `requirements` in `src/eas-mobile-distribution.adapter.ts`. `pnpm shiplane doctor` runs each
93
93
  check and offers the fix.
94
+
95
+ ## ๐Ÿ“ˆ Downloads
96
+
97
+ <a href="https://npm-stat.com/charts.html?package=@shiplane/eas">
98
+ <picture>
99
+ <source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/chart/npm/@shiplane/eas.svg?days=90&logo=false&mode=dark" />
100
+ <img alt="@shiplane/eas npm downloads per week, last 90 days" src="https://shieldcn.dev/chart/npm/@shiplane/eas.svg?days=90&logo=false&mode=light" />
101
+ </picture>
102
+ </a>
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { compareVersions, defineMobileDistribution, nextPatch, perEnv, progressBar, releaseSource, tagName, z } from "shiplane/plugin";
3
+ import { compareVersions, defineMobileDistribution, isEnvVarName, nextPatch, perEnv, progressBar, releaseSource, tagName, z } from "shiplane/plugin";
4
4
  import { styleText } from "node:util";
5
5
  //#region src/eas/policy.ts
6
6
  /** What this commit needs on one platform, given the builds on its channel. */
@@ -492,6 +492,35 @@ const easClient = (shell, cwd) => {
492
492
  };
493
493
  };
494
494
  //#endregion
495
+ //#region src/eas/env.ts
496
+ const NAME_LINE = /^([A-Za-z_][A-Za-z0-9_]*)=/;
497
+ const firstLine = (text) => text.split("\n").find((line) => line.trim())?.trim();
498
+ /** The names of an EAS environment's variables; only names are kept, never values. */
499
+ const easEnv = ({ shell }) => ({
500
+ listNames: async ({ unit, environment }) => {
501
+ const result = await shell.capture("pnpm", [
502
+ "exec",
503
+ "eas",
504
+ "env:list",
505
+ environment,
506
+ "--format",
507
+ "short"
508
+ ], {
509
+ cwd: unit.dir,
510
+ env: { APP_VARIANT: environment }
511
+ });
512
+ if (result.status !== 0) {
513
+ const reason = firstLine(result.stderr) ?? `exit ${result.status}`;
514
+ throw new Error(`eas env:list ${environment}: ${reason}`);
515
+ }
516
+ return new Set(result.stdout.split("\n").flatMap((line) => {
517
+ const name = NAME_LINE.exec(line)?.[1];
518
+ return name && isEnvVarName(name) ? [name] : [];
519
+ }));
520
+ },
521
+ setCommand: ({ unit, environment, names }) => [`cd ${unit.dir}`, ...names.map((name) => `pnpm exec eas env:create --environment ${environment} --name ${name} --value ... --visibility secret`)].join(" && ")
522
+ });
523
+ //#endregion
495
524
  //#region src/eas-mobile-distribution.adapter.ts
496
525
  const EasOptions = z.object({
497
526
  versionFile: z.string().default("app-version.json").describe("Each environment's binary version"),
@@ -876,6 +905,7 @@ const easMobileDistribution = () => defineMobileDistribution({
876
905
  cwd: unit.dir
877
906
  }
878
907
  }],
908
+ env: easEnv,
879
909
  jobs: (input) => [buildJob(input), updateJob(input)],
880
910
  decide: (context, input) => input.environment === "preview" ? previewDecision(context, input) : productionDecision(context, input),
881
911
  inspect: async (context, input) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/eas/policy.ts","../src/eas/analyse.ts","../src/eas/client.ts","../src/eas-mobile-distribution.adapter.ts"],"sourcesContent":["import type { AppBump, Environment, PlannedChangeset, VersionChoice } from \"shiplane/plugin\";\nimport { compareVersions, nextPatch } from \"shiplane/plugin\";\n\nexport type Platform = \"ios\" | \"android\";\n\nexport interface ShippedBuild {\n id: string;\n appVersion: string | null;\n buildNumber: string | null;\n fingerprint: string | null;\n gitCommit: string | null;\n createdAt: string;\n}\n\nexport interface RunningBuild {\n id: string;\n appVersion: string | null;\n buildNumber: string | null;\n}\n\nexport interface PlatformState {\n platform: Platform;\n /** This commit: its native fingerprint (the runtime version updates target), app version and sha. */\n local: { fingerprint: string; version: string; commit: string };\n /** The newest finished store build on the channel, or null when there is none. */\n shipped: ShippedBuild | null;\n /** The newest store build on the channel still queued or running. */\n running: RunningBuild | null;\n}\n\nexport type BuildReason = \"no-build\" | \"no-fingerprint\" | \"native-changed\" | \"version-changed\";\n\nexport type PlatformVerdict =\n | { platform: Platform; action: \"update\"; shipped: ShippedBuild }\n | { platform: Platform; action: \"submit\"; shipped: ShippedBuild }\n | {\n platform: Platform;\n action: \"pull\";\n why: \"version-behind\";\n shipped: ShippedBuild;\n /** The newest version built on the channel, whose bump this branch misses. */\n newerVersion: string;\n }\n | { platform: Platform; action: \"wait\"; why: \"build-running\"; running: RunningBuild }\n | { platform: Platform; action: \"build\"; why: BuildReason; shipped: ShippedBuild | null };\n\nexport type ReleaseAction = PlatformVerdict[\"action\"];\n\n/** What this commit needs on one platform, given the builds on its channel. */\nexport const platformVerdict = ({\n platform,\n local,\n shipped,\n running,\n}: PlatformState): PlatformVerdict => {\n if (running?.appVersion === local.version) {\n return { platform, action: \"wait\", why: \"build-running\", running };\n }\n // A running build of a newer version means its bump commit exists: this branch is behind it.\n const newestVersion = running?.appVersion ?? shipped?.appVersion;\n if (shipped && newestVersion && compareVersions(local.version, newestVersion) < 0) {\n return {\n platform,\n action: \"pull\",\n why: \"version-behind\",\n shipped,\n newerVersion: newestVersion,\n };\n }\n if (!shipped) return { platform, action: \"build\", why: \"no-build\", shipped };\n\n // The version is part of the fingerprint and names the binary: it decides first.\n if (shipped.appVersion && shipped.appVersion !== local.version) {\n return { platform, action: \"build\", why: \"version-changed\", shipped };\n }\n if (!shipped.fingerprint) return { platform, action: \"build\", why: \"no-fingerprint\", shipped };\n if (shipped.fingerprint !== local.fingerprint) {\n return { platform, action: \"build\", why: \"native-changed\", shipped };\n }\n if (shipped.gitCommit === local.commit) return { platform, action: \"submit\", shipped };\n return { platform, action: \"update\", shipped };\n};\n\nexport interface ReleasePlan {\n action: ReleaseAction;\n commands: string[];\n}\n\n// One update reaches every platform, so the most demanding platform decides; a stale branch\n// is caught up first, since every other verdict is about an older version.\nconst PRECEDENCE: ReleaseAction[] = [\"pull\", \"build\", \"wait\", \"submit\", \"update\"];\n\n/** The next step for the whole release, with the release CLI commands that take it. */\nexport const releasePlan = (\n channel: Environment,\n verdicts: PlatformVerdict[],\n unit: string,\n): ReleasePlan => {\n const action =\n PRECEDENCE.find((candidate) => verdicts.some((verdict) => verdict.action === candidate)) ??\n \"update\";\n const flag = channel === \"preview\" ? \"\" : ` --channel ${channel}`;\n const COMMANDS: Record<ReleaseAction, string[]> = {\n // EAS submits a build when it is done: shipping it is one command.\n build: [`pnpm shiplane ship ${unit}${flag}`],\n // The release that versioned the build is on main: the branch catches up from there.\n pull: [\"git fetch origin\", \"git merge origin/main\", `pnpm shiplane${flag}`],\n wait: [`pnpm shiplane status${flag}`],\n // The build made from this commit exists and EAS submitted it.\n submit: [`pnpm shiplane status${flag}`],\n update: [`pnpm shiplane ship ${unit}${flag}`],\n };\n\n return { action, commands: COMMANDS[action] };\n};\n\n/**\n * A build of a version some binary already carries would give two native layers one version: each\n * binary keeps its own. The release pull request, or the promotion one, moves it first.\n */\nexport const needsNewVersion = (states: PlatformState[]) =>\n states.some(({ local, shipped }) => shipped?.appVersion === local.version);\n\nexport type MobileRelease =\n | { ships: \"build\"; version: string; nativeChangeset: boolean }\n | { ships: \"update\"; version: string }\n | { ships: \"nothing\" }\n | { ships: \"blocked\"; why: \"build-running\" | \"behind\" };\n\n/**\n * What the release does for mobile, from preview's verdict. A new native layer gets a new\n * version, so each binary's version names one native layer.\n */\nexport const mobileRelease = (\n action: ReleaseAction,\n expo: AppBump | undefined,\n currentVersion: string,\n): MobileRelease => {\n if (action === \"wait\") return { ships: \"blocked\", why: \"build-running\" };\n if (action === \"pull\") return { ships: \"blocked\", why: \"behind\" };\n if (action === \"build\") {\n return expo\n ? { ships: \"build\", version: expo.to, nativeChangeset: false }\n : { ships: \"build\", version: nextPatch(currentVersion), nativeChangeset: true };\n }\n return expo ? { ships: \"update\", version: expo.to } : { ships: \"nothing\" };\n};\n\n/** The changeset the release writes itself: a build gets a version even with no expo changeset. */\nexport const releaseChangesets = (\n mobile: MobileRelease,\n changes: string[],\n app: string,\n): PlannedChangeset[] =>\n mobile.ships === \"build\" && mobile.nativeChangeset\n ? [\n {\n id: `native-${mobile.version}`,\n releases: [{ name: app, type: \"patch\" }],\n summary:\n changes.length > 0\n ? `Native code changed: ${changes.map((change) => change.replace(/^[+~-] /, \"\")).join(\", \")}.`\n : \"A new build of the app, with the same native code.\",\n },\n ]\n : [];\n\nconst BUILD_LABEL = \"New build, iOS and Android\";\nconst UPDATE_LABEL = \"Over-the-air update\";\n\n/**\n * What the person may pick once detection spoke, the suggestion first: a build is always possible,\n * an update only on the native layer the installed binaries carry.\n */\nexport const mobileChoice = (\n action: ReleaseAction,\n why: string,\n app: string,\n): VersionChoice | undefined => {\n if (action === \"wait\" || action === \"pull\") return undefined;\n const question = `What should ${app} ship to preview?`;\n if (action === \"build\") {\n return {\n question,\n suggested: \"build\",\n options: [\n { value: \"build\", label: `${BUILD_LABEL} (suggested)`, hint: why },\n {\n value: \"update\",\n label: UPDATE_LABEL,\n hint: \"not safe: installed binaries lack this native layer\",\n disabled: true,\n },\n ],\n };\n }\n return {\n question,\n suggested: \"update\",\n options: [\n { value: \"update\", label: `${UPDATE_LABEL} (suggested)`, hint: why },\n {\n value: \"build\",\n label: BUILD_LABEL,\n hint: \"not needed: a new binary anyway, under its own version\",\n },\n ],\n };\n};\n\nexport type MobileShip =\n \"build\" | \"update\" | \"wait\" | \"shipped\" | \"moved-past\" | \"behind\" | \"nothing\";\n\n/**\n * What mobile still needs for the expo version a release tagged. A binary or an update ships its\n * commit: once the app's code moved past the release, it would carry code no release named.\n */\nexport const mobileShip = (\n action: ReleaseAction,\n state: {\n /** The current expo version has a release tag. */\n tagged: boolean;\n /** Code reaching the app changed between that tag and main. */\n codeSinceTag: boolean;\n /** The last build or update shipped the tagged code. */\n codeShipped: boolean;\n },\n): MobileShip => {\n if (!state.tagged) return \"nothing\";\n if (action === \"wait\") return \"wait\";\n if (action === \"pull\") return \"behind\";\n if (action === \"submit\") return \"shipped\";\n if (action === \"update\" && state.codeShipped) return \"shipped\";\n if (state.codeSinceTag) return \"moved-past\";\n return action;\n};\n\nexport type ProductionMobile =\n | \"promote\"\n | \"finish-rollout\"\n | \"build\"\n | \"update\"\n | \"wait\"\n | \"shipped\"\n | \"moved-past\"\n | \"behind\"\n | \"nothing\";\n\n/**\n * What production still needs to run the expo version preview validated. A new native layer needs\n * production's version moved first (the promotion PR).\n */\nexport const productionMobile = (\n action: ReleaseAction,\n state: {\n /** The current expo version has a release tag. */\n tagged: boolean;\n /** Main differs from that tag by version files at most. */\n sinceTagOnlyVersionFiles: boolean;\n expoVersion: string;\n /** apps/expo/app-version.json production: the binary version production builds carry. */\n productionVersion: string;\n /** The last production build or update shipped the tagged code. */\n codeShipped: boolean;\n /** The version of the last finished production build. */\n builtVersion: string | null;\n /** The last update, made from the tagged code, reaches only part of production. */\n partialRollout: boolean;\n },\n): ProductionMobile => {\n if (!state.tagged) return \"nothing\";\n if (action === \"wait\") return \"wait\";\n if (action === \"pull\") return \"behind\";\n if (action !== \"build\" && state.codeShipped) {\n return state.partialRollout ? \"finish-rollout\" : \"shipped\";\n }\n if (!state.sinceTagOnlyVersionFiles) return \"moved-past\";\n if (action === \"build\") {\n return state.productionVersion === state.expoVersion ? \"build\" : \"promote\";\n }\n return \"update\";\n};\n\nconst MOBILE_SHIPS: Partial<Record<MobileRelease[\"ships\"], string>> = {\n build: \"Build iOS and Android, submitted when done\",\n update: \"Over-the-air update\",\n};\n\n/** What happens to each unit once the release pull request is merged, the mobile app's from EAS. */\nexport const onMerge = (mobile: MobileRelease, mobileApp: string) => (bump: AppBump) => {\n if (bump.app === mobileApp) return MOBILE_SHIPS[mobile.ships] ?? \"Nothing ships\";\n const reason = bump.movedWith.length > 0 ? ` (moved with ${bump.movedWith.join(\", \")})` : \"\";\n return `Deploy preview${reason}`;\n};\n\n/** The shares of people a production rollout widens through, in order. */\nexport const ROLLOUT_STEPS = [10, 25, 50, 100] as const;\n\nconst counted = (total: number, one: string, many: string) =>\n `${total} ${total === 1 ? one : many}`;\n\n/** How long an update has been out, as a person says it: hours under two days, then days. */\nexport const timeOut = (hours: number) =>\n hours < 48\n ? counted(Math.max(1, Math.floor(hours)), \"hour\", \"hours\")\n : counted(Math.floor(hours / 24), \"day\", \"days\");\n\n/**\n * The next move for an update reaching part of production: hold while it crashed, nobody ran it or\n * it is under a day old; otherwise the next step up.\n */\nexport const rolloutAdvice = ({\n percentage,\n launches,\n crashes,\n hoursOut,\n}: {\n percentage: number;\n launches: number;\n crashes: number;\n hoursOut: number;\n}): { suggested: number | \"stay\"; why: string } => {\n if (crashes > 0) {\n return {\n suggested: \"stay\",\n why: `${counted(crashes, \"crash\", \"crashes\")} since it went out: look before widening, or roll back`,\n };\n }\n if (launches === 0) {\n return { suggested: \"stay\", why: \"nobody has launched it yet: wait for launches\" };\n }\n if (hoursOut < 24)\n return { suggested: \"stay\", why: `out for ${timeOut(hoursOut)}: give it a day` };\n return {\n suggested: ROLLOUT_STEPS.find((step) => step > percentage) ?? 100,\n why: `healthy for ${timeOut(hoursOut)}: widen it`,\n };\n};\n","import { styleText } from \"node:util\";\nimport type { Environment, GitPosition, Health, SourceControlPort } from \"shiplane/plugin\";\nimport { progressBar } from \"shiplane/plugin\";\n\nimport type { easClient } from \"./client\";\nimport type {\n Platform,\n PlatformState,\n PlatformVerdict,\n ReleaseAction,\n ReleasePlan,\n} from \"./policy\";\nimport { platformVerdict, releasePlan, timeOut } from \"./policy\";\n\nexport const PLATFORMS: Platform[] = [\"ios\", \"android\"];\n\nexport const PLATFORM_NAME: Record<Platform, string> = { ios: \"iOS\", android: \"Android\" };\n\nexport interface Analysis {\n channel: Environment;\n version: string;\n commit: string;\n git: GitPosition;\n checked: { state: PlatformState; verdict: PlatformVerdict; changes: string[] }[];\n plan: ReleasePlan;\n}\n\n/** Each platform's last store build on the channel against this commit's native fingerprint. */\nexport const analyse = async (\n client: ReturnType<typeof easClient>,\n source: SourceControlPort,\n environment: Environment,\n version: string,\n unit: string,\n): Promise<Analysis> => {\n const position = await source.position();\n const checked = await Promise.all(\n PLATFORMS.map(async (platform) => {\n const [fingerprint, builds] = await Promise.all([\n client.localFingerprint(platform, environment),\n client.channelBuilds(platform, environment),\n ]);\n const state: PlatformState = {\n platform,\n local: { fingerprint, version, commit: position.head },\n ...builds,\n };\n const verdict = platformVerdict(state);\n const changes =\n verdict.action === \"build\" && verdict.why === \"native-changed\" && verdict.shipped\n ? await client.nativeChanges(verdict.shipped.id, environment)\n : [];\n return { state, verdict, changes };\n }),\n );\n return {\n channel: environment,\n version,\n commit: position.head,\n git: position,\n checked,\n plan: releasePlan(\n environment,\n checked.map(({ verdict }) => verdict),\n unit,\n ),\n };\n};\n\nconst LOOK: Record<ReleaseAction, { mark: string; style: \"green\" | \"yellow\" | \"red\" }> = {\n update: { mark: \"โœ“\", style: \"green\" },\n submit: { mark: \"โ†‘\", style: \"green\" },\n wait: { mark: \"โ€ฆ\", style: \"yellow\" },\n pull: { mark: \"โ†“\", style: \"yellow\" },\n build: { mark: \"โœ—\", style: \"red\" },\n};\n\n/** Why a platform needs what it needs, as a person reads it. */\nexport const why = (verdict: PlatformVerdict): string => {\n switch (verdict.action) {\n case \"update\":\n return \"same native layer and version as the last build\";\n case \"submit\":\n return \"the last build was made from this commit\";\n case \"wait\":\n return `build ${verdict.running.buildNumber ?? \"?\"} for this version is still running`;\n case \"pull\":\n return `this branch misses the ${verdict.newerVersion} release of a build on this channel`;\n case \"build\":\n // Builds that errored leave no binary: the last finished one is what devices run.\n return {\n \"no-build\": \"no store build on this channel yet\",\n \"no-fingerprint\": \"the last build has no fingerprint, nothing proves an update fits\",\n \"native-changed\": \"native code changed since the last build\",\n \"version-changed\": `no binary carries this version yet: the last finished build is ${verdict.shipped?.appVersion ?? \"older\"}`,\n }[verdict.why];\n }\n};\n\n/** Why the deciding platform needs what the plan says, or nothing when none decided. */\nexport const planWhy = ({ checked, plan }: Analysis) => {\n const shown = checked.find(({ verdict }) => verdict.action === plan.action);\n return shown ? why(shown.verdict) : \"\";\n};\n\nconst MAX_CHANGES = 6;\n\n/** The verdict line a summary shows for the platform that decides, with what changed under it. */\nexport const verdictLines = ({ checked, plan }: Analysis): string[] => {\n const shown = checked.find(({ verdict }) => verdict.action === plan.action);\n if (!shown) return [];\n const look = LOOK[shown.verdict.action];\n return [\n `${styleText(look.style, `${look.mark} ${shown.verdict.action}`)} ${styleText(\"dim\", why(shown.verdict))}`,\n ...shown.changes.slice(0, MAX_CHANGES).map((change) => styleText(\"dim\", ` ${change}`)),\n ];\n};\n\n/** The analysis as `status --json` prints it, the same shape the old release check printed. */\nexport const analysisJson = ({ channel, version, commit, plan, checked }: Analysis) => ({\n channel,\n version,\n commit,\n ...plan,\n platforms: checked.map(({ verdict, changes }) => ({ ...verdict, changes })),\n});\n\nconst count = (total: number, one: string, many: string) => `${total} ${total === 1 ? one : many}`;\n\n/** An update's health from its launches: unwell once it crashed, or while nobody has run it. */\nexport const updateHealth = (\n launches: { platform: Platform; installs: number; failedInstalls: number }[],\n): Health => {\n const summary = launches\n .map(\n ({ platform, installs, failedInstalls }) =>\n `${PLATFORM_NAME[platform]} ${count(installs, \"launch\", \"launches\")}, ${count(failedInstalls, \"crash\", \"crashes\")}`,\n )\n .join(\" ยท \");\n const crashes = launches.reduce((sum, { failedInstalls }) => sum + failedInstalls, 0);\n if (crashes > 0) {\n return { ok: false, summary, why: `${count(crashes, \"crash\", \"crashes\")} on preview` };\n }\n if (launches.every(({ installs }) => installs === 0)) {\n return { ok: false, summary, why: \"nobody has launched it on preview yet\" };\n }\n return { ok: true, summary };\n};\n\n/** Where a rollout stands, as the lines above the question: whose, how far, how long, how it does. */\nexport const rolloutLines = ({\n tag,\n environment,\n percentage,\n hoursOut,\n health,\n}: {\n tag: string;\n environment: Environment;\n percentage: number;\n hoursOut: number;\n health: Health;\n}) => [\n `${styleText(\"bold\", tag)} ${styleText(\"dim\", `on ${environment}`)}`,\n `${styleText(health.ok ? \"green\" : \"yellow\", progressBar(percentage, 100, 20))} ${percentage}% of people ยท out for ${timeOut(hoursOut)}`,\n styleText(\"dim\", health.summary),\n];\n","import type { Environment, Shell } from \"shiplane/plugin\";\nimport { z } from \"shiplane/plugin\";\n\nimport type { Platform, RunningBuild, ShippedBuild } from \"./policy\";\n\n// eas-cli prints notices around its JSON: keep the outermost JSON value only.\nconst parseEasJson = (output: string): unknown => {\n const start = output.search(/[[{]/);\n const end = Math.max(output.lastIndexOf(\"]\"), output.lastIndexOf(\"}\"));\n return JSON.parse(output.slice(start, end + 1));\n};\n\nconst SourceSchema = z.object({\n type: z.string(),\n filePath: z.string().optional(),\n id: z.string().optional(),\n hash: z.string().nullish(),\n});\nconst FingerprintSchema = z.object({ hash: z.string(), sources: z.array(SourceSchema) });\nconst EasBuildSchema = z.object({\n id: z.string(),\n status: z.string(),\n buildProfile: z.string().nullish(),\n isForIosSimulator: z.boolean().nullish(),\n appVersion: z.string().nullish(),\n appBuildVersion: z.string().nullish(),\n gitCommitHash: z.string().nullish(),\n createdAt: z.string(),\n fingerprint: z.object({ hash: z.string() }).nullish(),\n});\nconst UpdateListSchema = z.object({\n currentPage: z.array(\n // EAS lists rolloutPercentage only while a rollout holds the group back from part of the channel.\n z.object({ group: z.string(), rolloutPercentage: z.number().optional() }),\n ),\n});\nconst UpdateViewSchema = z.array(\n z.object({ gitCommitHash: z.string().nullish(), createdAt: z.string().nullish() }),\n);\nconst UpdateInsightsSchema = z.object({\n platforms: z.array(\n z.object({\n platform: z.enum([\"ios\", \"android\"]),\n totals: z.object({ installs: z.number(), failedInstalls: z.number() }),\n }),\n ),\n});\n\nconst RUNNING = new Set([\"NEW\", \"IN_QUEUE\", \"IN_PROGRESS\"]);\n\n// eas-cli rejects --non-interactive on these commands as a nonexistent flag.\nconst WITHOUT_NON_INTERACTIVE_FLAG = new Set([\"update:view\"]);\n\n// The first stderr line is often eas-cli's upgrade notice: report its own error line instead.\nconst failureReason = (stderr: string): string => {\n const lines = stderr\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n return lines.find((line) => line.startsWith(\"Error:\")) ?? lines.at(-1) ?? \"\";\n};\n\n/** The eas CLI, run in the app's folder with its environment as APP_VARIANT. */\nexport const easClient = (shell: Shell, cwd: string) => {\n const eas = async (args: string[], environment: Environment): Promise<unknown> => {\n const command = args[0] ?? \"\";\n const flags = WITHOUT_NON_INTERACTIVE_FLAG.has(command)\n ? [\"--json\"]\n : [\"--json\", \"--non-interactive\"];\n const { status, stdout, stderr } = await shell.capture(\n \"pnpm\",\n [\"exec\", \"eas\", ...args, ...flags],\n {\n cwd,\n env: { APP_VARIANT: environment },\n },\n );\n if (status !== 0) {\n throw new Error(`eas ${command} exited with ${status}: ${failureReason(stderr)}`);\n }\n return parseEasJson(stdout);\n };\n\n return {\n localFingerprint: async (platform: Platform, environment: Environment) =>\n FingerprintSchema.parse(\n await eas([\"fingerprint:generate\", \"--platform\", platform], environment),\n ).hash,\n\n // The simulator profile shares the preview channel: only the channel's own store profile counts.\n channelBuilds: async (platform: Platform, environment: Environment) => {\n const builds = z\n .array(EasBuildSchema)\n .parse(await eas([\"build:list\", \"--platform\", platform, \"--limit\", \"30\"], environment))\n .filter((build) => build.buildProfile === environment && !build.isForIosSimulator);\n const finished = builds.find((build) => build.status === \"FINISHED\");\n const running = builds.find((build) => RUNNING.has(build.status));\n return {\n shipped: finished\n ? ({\n id: finished.id,\n appVersion: finished.appVersion ?? null,\n buildNumber: finished.appBuildVersion ?? null,\n fingerprint: finished.fingerprint?.hash ?? null,\n gitCommit: finished.gitCommitHash ?? null,\n createdAt: finished.createdAt,\n } satisfies ShippedBuild)\n : null,\n running: running\n ? ({\n id: running.id,\n appVersion: running.appVersion ?? null,\n buildNumber: running.appBuildVersion ?? null,\n } satisfies RunningBuild)\n : null,\n };\n },\n\n // What differs between the last build's native layer and this commit's, for a \"native changed\".\n nativeChanges: async (buildId: string, environment: Environment): Promise<string[]> => {\n const { fingerprint1, fingerprint2 } = z\n .object({ fingerprint1: FingerprintSchema, fingerprint2: FingerprintSchema })\n .parse(await eas([\"fingerprint:compare\", \"--build-id\", buildId], environment));\n type Source = z.infer<typeof SourceSchema>;\n const key = (source: Source) => `${source.type}:${source.filePath ?? source.id ?? \"\"}`;\n const label = (source: Source) =>\n (source.filePath ?? source.id ?? source.type).replace(/^(\\.\\.\\/)+/, \"\");\n const before = new Map(fingerprint1.sources.map((source) => [key(source), source]));\n const after = new Map(fingerprint2.sources.map((source) => [key(source), source]));\n return [\n ...[...after].flatMap(([id, source]) => {\n const old = before.get(id);\n if (!old) return [`+ ${label(source)}`];\n return old.hash === source.hash ? [] : [`~ ${label(source)}`];\n }),\n ...[...before].flatMap(([id, source]) => (after.has(id) ? [] : [`- ${label(source)}`])),\n ];\n },\n\n /** The newest update group on the channel, with the share of people it reaches. */\n lastUpdate: async (\n environment: Environment,\n ): Promise<{ group: string; rolloutPercentage: number } | null> => {\n const { currentPage } = UpdateListSchema.parse(\n await eas([\"update:list\", \"--branch\", environment, \"--limit\", \"1\"], environment),\n );\n const [last] = currentPage;\n return last ? { group: last.group, rolloutPercentage: last.rolloutPercentage ?? 100 } : null;\n },\n\n /** Each platform's launches of an update group over the last 7 days, and the failed ones. */\n updateLaunches: async (group: string, environment: Environment) =>\n UpdateInsightsSchema.parse(await eas([\"update:insights\", group], environment)).platforms.map(\n ({ platform, totals }) => ({ platform, ...totals }),\n ),\n\n /** The commit an update group was made from, and when it was published. */\n updateOrigin: async (group: string, environment: Environment) => {\n const [first] = UpdateViewSchema.parse(await eas([\"update:view\", group], environment));\n return { commit: first?.gitCommitHash ?? null, createdAt: first?.createdAt ?? null };\n },\n };\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type {\n DecideContext,\n Decision,\n DeliveryAdapter,\n Environment,\n Job,\n JobInput,\n MobileDistributionPort,\n Refusal,\n ReleaseUnit,\n Resolved,\n} from \"shiplane/plugin\";\nimport { defineMobileDistribution, perEnv, releaseSource, tagName, z } from \"shiplane/plugin\";\n\nimport type { MobileRelease } from \"./eas/policy\";\nimport {\n analyse,\n analysisJson,\n planWhy,\n rolloutLines,\n updateHealth,\n verdictLines,\n} from \"./eas/analyse\";\nimport { easClient } from \"./eas/client\";\nimport {\n mobileChoice,\n mobileRelease,\n mobileShip,\n needsNewVersion,\n productionMobile,\n releaseChangesets,\n ROLLOUT_STEPS,\n rolloutAdvice,\n} from \"./eas/policy\";\n\nconst EasOptions = z.object({\n versionFile: z.string().default(\"app-version.json\").describe(\"Each environment's binary version\"),\n update: perEnv(z.string()).describe(\n \"The package script that publishes an update, per environment\",\n ),\n});\n\ntype Options = Resolved<z.output<typeof EasOptions>>;\n\nconst readJson = (file: string): Record<string, unknown> =>\n z.record(z.string(), z.unknown()).parse(JSON.parse(fs.readFileSync(file, \"utf8\")));\n\nconst writeJson = (file: string, value: unknown) =>\n fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\\n`);\n\nconst VersionsSchema = z.record(z.string(), z.object({ version: z.string() }).loose());\n\n/** The binary version an environment carries. */\nconst versionFileOf = (root: string, unit: ReleaseUnit, options: Options) =>\n path.join(root, unit.dir, options.versionFile);\n\nconst binaryVersion = (\n root: string,\n unit: ReleaseUnit,\n options: Options,\n environment: Environment,\n) =>\n VersionsSchema.parse(readJson(versionFileOf(root, unit, options)))[environment]?.version ??\n unit.version;\n\nconst setBinaryVersion = (\n root: string,\n unit: ReleaseUnit,\n options: Options,\n environment: Environment,\n version: string,\n) => {\n const file = versionFileOf(root, unit, options);\n const versions = VersionsSchema.parse(readJson(file));\n writeJson(file, { ...versions, [environment]: { ...versions[environment], version } });\n return path.relative(root, file);\n};\n\n// A build runs on EAS from this machine; a preview update ships what its tag named, production main.\nconst updateRef = ({ tag, environment }: JobInput<Options>) =>\n environment === \"preview\" ? tag : \"main\";\n\nconst buildJob = (input: JobInput<Options>): Job => {\n const { unit, environment } = input;\n return {\n label: `build iOS and Android on EAS ${environment}, submitted when done`,\n local: [\n {\n command: \"pnpm\",\n args: [\n \"exec\",\n \"eas\",\n \"build\",\n \"--profile\",\n environment,\n \"--platform\",\n \"all\",\n \"--auto-submit\",\n \"--no-wait\",\n ],\n cwd: unit.dir,\n env: { EXPO_ATLAS: \"false\", APP_VARIANT: environment },\n },\n ],\n };\n};\n\nconst updateJob = (input: JobInput<Options>): Job => {\n const { unit, environment, options } = input;\n return {\n label: `publish the ${environment} update`,\n local: [\n {\n command: \"pnpm\",\n args: [\"run\", options.update],\n cwd: unit.dir,\n env: { APP_VARIANT: environment },\n },\n ],\n ci: { ref: updateRef(input) },\n };\n};\n\n/** One eas command on a rollout; no workflow moves a rollout, so it runs here. */\nconst rolloutJob = (\n { unit, environment }: JobInput<Options>,\n label: string,\n args: string[],\n): Job => ({\n label,\n local: [\n {\n command: \"pnpm\",\n args: [\"exec\", \"eas\", ...args, \"--non-interactive\"],\n cwd: unit.dir,\n env: { APP_VARIANT: environment },\n },\n ],\n});\n\n/** Where an update reaching part of the channel stands, and each way it can go from there. */\nconst rolloutChoice = async (\n context: DecideContext,\n client: ReturnType<typeof easClient>,\n input: JobInput<Options>,\n update: { group: string; percentage: number; createdAt: string | null },\n): Promise<Decision> => {\n const { tag, environment } = input;\n const { group, percentage } = update;\n const launches = await client.updateLaunches(group, environment);\n const hoursOut = update.createdAt\n ? (context.now().getTime() - new Date(update.createdAt).getTime()) / 3_600_000\n : 0;\n const advice = rolloutAdvice({\n percentage,\n launches: launches.reduce((sum, { installs }) => sum + installs, 0),\n crashes: launches.reduce((sum, { failedInstalls }) => sum + failedInstalls, 0),\n hoursOut,\n });\n const suggested = String(advice.suggested);\n const widen = (to: number) =>\n rolloutJob(input, `widen the ${tag} rollout on ${environment}: ${percentage}% โ†’ ${to}%`, [\n \"update:edit\",\n group,\n \"--rollout-percentage\",\n String(to),\n ]);\n const options = [\n ...ROLLOUT_STEPS.filter((step) => step > percentage).map((step) => ({\n value: String(step),\n label: step === 100 ? \"Everyone\" : `${step}% of people`,\n hint: step === 100 ? \"finishes the rollout\" : undefined,\n jobs: [widen(step)],\n })),\n {\n value: \"stay\",\n label: `Stay at ${percentage}%`,\n hint: \"nothing changes, check again later\",\n jobs: [],\n },\n {\n value: \"roll-back\",\n label: \"Roll back\",\n hint: \"people on it go back to the update before\",\n jobs: [\n rolloutJob(input, `roll back the ${tag} rollout on ${environment}`, [\n \"update:revert-update-rollout\",\n \"--group\",\n group,\n ]),\n ],\n },\n ].map((option) =>\n option.value === suggested\n ? { ...option, label: `${option.label} (suggested)`, hint: advice.why }\n : option,\n );\n return {\n action: \"choose\",\n lines: rolloutLines({\n tag,\n environment,\n percentage,\n hoursOut,\n health: updateHealth(launches),\n }),\n question: `How far should ${tag} go on ${environment}?`,\n suggested,\n options,\n held: `${tag} stays at ${percentage}% of people`,\n };\n};\n\nconst refuse = (refusal: Refusal): Decision => ({ action: \"fail\", ...refusal });\n\n/** The checks a new binary passes before EAS builds it: from main, a new version, a clean tree. */\nconst buildRefusal = async (\n context: DecideContext,\n analysis: Awaited<ReturnType<typeof analyse>>,\n input: JobInput<Options>,\n): Promise<Refusal | null> => {\n const source = releaseSource(\"build\", input.environment, analysis.git);\n if (!source.allowed) {\n return source.why === \"not-main\"\n ? {\n title: `Not from ${analysis.git.branch}`,\n detail: \"a binary ships what it is built from, so builds start from main.\",\n commands: [\"git switch main\", \"git pull\", \"shiplane ship\"],\n }\n : {\n title: \"main is behind origin\",\n detail: \"pull it first, a release ships what is merged.\",\n commands: [\"git pull\"],\n };\n }\n if (needsNewVersion(analysis.checked.map(({ state }) => state))) {\n return {\n title: `${analysis.version} already has a ${input.environment} binary`,\n detail: \"a new native layer needs a new version, or updates would reach both binaries.\",\n commands: [\"shiplane\"],\n };\n }\n if (!(await context.sourceControl.isClean())) {\n return {\n title: \"Uncommitted changes\",\n detail: \"commit or stash them first, a release ships the commit.\",\n commands: [],\n };\n }\n return null;\n};\n\nconst previewDecision = async (\n context: DecideContext,\n input: JobInput<Options>,\n): Promise<Decision> => {\n const { unit, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"preview\",\n binaryVersion(context.root, unit, options, \"preview\"),\n unit.name,\n );\n const version = unit.version;\n const tag = await context.sourceControl.tagCommit(tagName(unit.name, version, context.tagFormat));\n const codeSince = (commit: string) => (tag ? context.changedCode(unit, tag, commit) : []);\n const sinceTag = async (commit: string | null) =>\n tag !== null && commit !== null && (await codeSince(commit)).length === 0;\n const builtFrom = analysis.checked.map(({ state }) => state.shipped?.gitCommit ?? null);\n const lastBuildCommit = builtFrom.every((commit) => commit === builtFrom[0])\n ? (builtFrom[0] ?? null)\n : null;\n const lastUpdate =\n tag && analysis.plan.action === \"update\" ? await client.lastUpdate(\"preview\") : null;\n const lastUpdateCommit = lastUpdate\n ? (await client.updateOrigin(lastUpdate.group, \"preview\")).commit\n : null;\n const ship = mobileShip(analysis.plan.action, {\n tagged: tag !== null,\n codeSinceTag: tag !== null && !(await sinceTag(analysis.git.head)),\n codeShipped:\n analysis.plan.action === \"update\" &&\n ((await sinceTag(lastBuildCommit)) || (await sinceTag(lastUpdateCommit))),\n });\n // Main moved past the tag: GitHub Actions still ships the tag, this machine only its checkout.\n const movedPast = ship === \"moved-past\";\n const kind = analysis.plan.action === \"build\" ? \"build\" : \"update\";\n // On CI a preview update still ships its tag; this machine would ship code no release named.\n const named = movedPast ? await codeSince(analysis.git.head) : [];\n const notLocal = movedPast\n ? {\n notLocal: `main moved past it (${named.slice(0, 3).join(\", \")}${named.length > 3 ? \", ...\" : \"\"} changed since)`,\n }\n : {};\n switch (movedPast && kind === \"update\" ? kind : ship) {\n case \"build\": {\n const refusal = await buildRefusal(context, analysis, input);\n return refusal\n ? refuse(refusal)\n : { action: \"ship\", kind: \"build\", jobs: [buildJob(input)], ...notLocal };\n }\n case \"update\":\n return { action: \"ship\", kind: \"update\", jobs: [updateJob(input)], ...notLocal };\n case \"wait\":\n return {\n action: \"none\",\n status: `${unit.name} ${version} is building on EAS, submitted when done`,\n };\n case \"shipped\":\n return { action: \"none\", status: `${unit.name} ${version} is on preview` };\n case \"nothing\":\n return {\n action: \"none\",\n status: `No ${unit.name} version to ship: add a changeset, then release.`,\n };\n case \"behind\":\n return refuse({\n title: \"main is behind\",\n detail: \"the last preview build carries a newer version.\",\n commands: [\"git pull\"],\n });\n case \"moved-past\": {\n const changed = await codeSince(analysis.git.head);\n const named = changed.slice(0, 3).join(\", \") + (changed.length > 3 ? \", ...\" : \"\");\n return refuse({\n title: `main moved past ${tagName(unit.name, version, context.tagFormat)}`,\n detail: `a binary or an update ships its commit, and ${named} changed since, code no release named.`,\n commands: [\"pnpm changeset\", \"shiplane\"],\n });\n }\n }\n};\n\nconst productionDecision = async (\n context: DecideContext,\n input: JobInput<Options>,\n): Promise<Decision> => {\n const { unit, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const productionVersion = binaryVersion(context.root, unit, options, \"production\");\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"production\",\n productionVersion,\n unit.name,\n );\n const version = unit.version;\n const tag = await context.sourceControl.tagCommit(tagName(unit.name, version, context.tagFormat));\n const codeSince = (commit: string) => (tag ? context.changedCode(unit, tag, commit) : []);\n const sinceTag = async (commit: string | null) =>\n tag !== null && commit !== null && (await codeSince(commit)).length === 0;\n const action = analysis.plan.action;\n const lastUpdate = tag && action === \"update\" ? await client.lastUpdate(\"production\") : null;\n const origin = lastUpdate ? await client.updateOrigin(lastUpdate.group, \"production\") : null;\n const updateCommit = origin?.commit ?? null;\n const shippedCommits = [\n ...analysis.checked.map(({ state }) => state.shipped?.gitCommit ?? null),\n updateCommit,\n ];\n const builtVersion = analysis.checked[0]?.state.shipped?.appVersion ?? null;\n const shippedAny = (await Promise.all(shippedCommits.map(sinceTag))).some(Boolean);\n const rollout = lastUpdate?.rolloutPercentage ?? 100;\n const decision = productionMobile(action, {\n tagged: tag !== null,\n sinceTagOnlyVersionFiles: await sinceTag(analysis.git.head),\n expoVersion: version,\n productionVersion,\n codeShipped: action === \"submit\" || shippedAny,\n builtVersion,\n partialRollout: rollout < 100 && (await sinceTag(updateCommit)),\n });\n switch (decision) {\n case \"finish-rollout\":\n return lastUpdate\n ? rolloutChoice(context, client, input, {\n group: lastUpdate.group,\n percentage: rollout,\n createdAt: origin?.createdAt ?? null,\n })\n : { action: \"none\", status: `${unit.name} ${version} is live` };\n case \"promote\":\n return {\n action: \"pull-request\",\n question: `Open the PR that moves production to ${unit.name} ${version}?`,\n branch: `chore/promote-${unit.name}-${version}`,\n title: `chore(release): production builds ${unit.name} ${version}`,\n body: [\n \"## ๐ŸŽฏ What & why\",\n \"\",\n `Production builds ${unit.name} ${version}, the version preview validated: \\`${path.posix.join(unit.dir, options.versionFile)}\\` production moves to it, the runtime version production updates target. Merge it, then \\`release --channel production\\` builds.`,\n \"\",\n \"## โœ… Testing\",\n \"\",\n \"Version file only. No UI change.\",\n ].join(\"\\n\"),\n changesets: [],\n write: () =>\n Promise.resolve([setBinaryVersion(context.root, unit, options, \"production\", version)]),\n };\n case \"update\":\n return { action: \"ship\", kind: \"update\", jobs: [updateJob(input)] };\n case \"build\": {\n const refusal = await buildRefusal(context, analysis, input);\n return refusal ? refuse(refusal) : { action: \"ship\", kind: \"build\", jobs: [buildJob(input)] };\n }\n case \"wait\":\n return {\n action: \"none\",\n status: `${unit.name} ${productionVersion} is building on EAS, submitted to the stores when done`,\n };\n case \"shipped\":\n return {\n action: \"none\",\n status: `${unit.name} ${builtVersion ?? version} is live`,\n };\n case \"nothing\":\n return {\n action: \"none\",\n status: `No ${unit.name} release to promote yet: release to preview first.`,\n };\n case \"behind\":\n return refuse({\n title: \"main is behind\",\n detail: \"the last production build carries a newer version.\",\n commands: [\"git pull\"],\n });\n case \"moved-past\": {\n const changed = await codeSince(analysis.git.head);\n const named = changed.slice(0, 3).join(\", \") + (changed.length > 3 ? \", ...\" : \"\");\n return refuse({\n title: `main moved past ${tagName(unit.name, version, context.tagFormat)}`,\n detail: `production ships what preview validated, and ${named} changed since, code preview never ran: release it to preview first.`,\n commands: [\"pnpm changeset\", \"shiplane\"],\n });\n }\n }\n};\n\nconst ON_MERGE: Partial<Record<MobileRelease[\"ships\"], string>> = {\n build: \"Build iOS and Android, submitted when done\",\n update: \"Over-the-air update\",\n};\n\n/**\n * A mobile app on EAS. The native fingerprint decides between a new binary and an over-the-air\n * update; production ships what preview validated, moving its version by PR first when it must.\n */\nexport type EasMobileDistributionAdapter = DeliveryAdapter<MobileDistributionPort>;\n\nexport const easMobileDistribution = (): EasMobileDistributionAdapter =>\n defineMobileDistribution({\n name: \"eas\",\n settings: EasOptions,\n decidedBy: { platform: \"EAS\", choice: \"build or update\", check: \"shiplane status --json\" },\n typicalMinutes: 8,\n // The binary versions move after a tag; the code does not.\n versionFiles: (unit) => [path.posix.join(unit.dir, EasOptions.parse(unit.options).versionFile)],\n describe: ({ environment }) => `EAS ${environment}, iOS and Android`,\n detect: (folder) => {\n if (!folder.has(\"eas.json\")) return null;\n const scripts = z\n .object({ scripts: z.record(z.string(), z.string()).default({}) })\n .safeParse(JSON.parse(folder.read(\"package.json\") ?? \"{}\"));\n const names = scripts.success ? Object.keys(scripts.data.scripts) : [];\n const pick = (environment: string) =>\n [`eas:update:${environment}`, `eas:update:${environment}:gated`].find((name) =>\n names.includes(name),\n ) ?? names.find((name) => name.includes(\"update\") && name.includes(environment));\n const [preview, production] = [pick(\"preview\"), pick(\"production\")];\n return {\n certainty: \"found\",\n reason: \"eas.json\",\n options: preview && production ? { update: { preview, production } } : {},\n };\n },\n questions: (_folder, found) =>\n found.update === undefined\n ? [\n {\n key: \"update\",\n message: \"Package script that publishes an update?\",\n initial: \"eas:update\",\n },\n ]\n : [],\n requirements: (unit) => [\n {\n label: \"eas\",\n check: { command: \"pnpm\", args: [\"exec\", \"eas\", \"whoami\"], cwd: unit.dir },\n fix: { command: \"pnpm\", args: [\"exec\", \"eas\", \"login\"], cwd: unit.dir },\n },\n ],\n jobs: (input) => [buildJob(input), updateJob(input)],\n decide: (context, input) =>\n input.environment === \"preview\"\n ? previewDecision(context, input)\n : productionDecision(context, input),\n inspect: async (context, input) => {\n const { unit, environment, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const [analysis, lastUpdate] = await Promise.all([\n analyse(\n client,\n context.sourceControl,\n environment,\n binaryVersion(context.root, unit, options, environment),\n unit.name,\n ),\n client.lastUpdate(environment),\n ]);\n const rollout = lastUpdate?.rolloutPercentage ?? 100;\n return {\n ...analysisJson(analysis),\n ...(rollout < 100 ? { rolloutPercentage: rollout } : {}),\n };\n },\n // Only an update has launch data: a binary preview got for this release reports nothing here.\n health: async (context, { unit, tag }) => {\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const [last, tagged] = await Promise.all([\n client.lastUpdate(\"preview\"),\n context.sourceControl.tagCommit(tag),\n ]);\n if (!last || !tagged) return null;\n const { commit } = await client.updateOrigin(last.group, \"preview\");\n if (!commit || (await context.changedCode(unit, tagged, commit)).length > 0) return null;\n return updateHealth(await client.updateLaunches(last.group, \"preview\"));\n },\n beforeVersion: async (context, input) => {\n const { unit, options, bump } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"preview\",\n binaryVersion(context.root, unit, options, \"preview\"),\n unit.name,\n );\n const forced = input.chosen === \"build\" && analysis.plan.action !== \"build\";\n const mobile = mobileRelease(forced ? \"build\" : analysis.plan.action, bump, unit.version);\n const summary = forced\n ? [\"โ†‘ build asked for, an update would do\", ...verdictLines(analysis)]\n : verdictLines(analysis);\n if (mobile.ships === \"blocked\") {\n return {\n blocked: {\n title: \"No release\",\n detail:\n mobile.why === \"build-running\"\n ? \"a preview build is still running: release once it's done, the native layer it carries decides the version.\"\n : \"main is behind the last preview build's version.\",\n commands: [\"shiplane\"],\n },\n added: [],\n writes: [],\n summary,\n onMerge: \"Nothing ships\",\n };\n }\n const changes = [...new Set(analysis.checked.flatMap(({ changes: lines }) => lines))];\n const version =\n mobile.ships === \"build\" || mobile.ships === \"update\" ? mobile.version : unit.version;\n return {\n choice: mobileChoice(analysis.plan.action, planWhy(analysis), unit.name),\n added: releaseChangesets(mobile, changes, unit.packageName),\n writes:\n mobile.ships === \"build\"\n ? [\n {\n label: `preview app version โ†’ ${version}`,\n run: async () => {\n setBinaryVersion(context.root, unit, options, \"preview\", version);\n await Promise.resolve();\n },\n },\n ]\n : [],\n summary: mobile.ships === \"nothing\" ? [\"โ€“ nothing changed\"] : summary,\n onMerge: ON_MERGE[mobile.ships] ?? \"Nothing ships\",\n ...(mobile.ships === \"build\"\n ? {\n step: `Build iOS and Android ${version} on EAS, submitted to TestFlight and Play internal when done`,\n }\n : mobile.ships === \"update\"\n ? { step: `Publish ${version} over the air to preview` }\n : {}),\n };\n },\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/eas/policy.ts","../src/eas/analyse.ts","../src/eas/client.ts","../src/eas/env.ts","../src/eas-mobile-distribution.adapter.ts"],"sourcesContent":["import type { AppBump, Environment, PlannedChangeset, VersionChoice } from \"shiplane/plugin\";\nimport { compareVersions, nextPatch } from \"shiplane/plugin\";\n\nexport type Platform = \"ios\" | \"android\";\n\nexport interface ShippedBuild {\n id: string;\n appVersion: string | null;\n buildNumber: string | null;\n fingerprint: string | null;\n gitCommit: string | null;\n createdAt: string;\n}\n\nexport interface RunningBuild {\n id: string;\n appVersion: string | null;\n buildNumber: string | null;\n}\n\nexport interface PlatformState {\n platform: Platform;\n /** This commit: its native fingerprint (the runtime version updates target), app version and sha. */\n local: { fingerprint: string; version: string; commit: string };\n /** The newest finished store build on the channel, or null when there is none. */\n shipped: ShippedBuild | null;\n /** The newest store build on the channel still queued or running. */\n running: RunningBuild | null;\n}\n\nexport type BuildReason = \"no-build\" | \"no-fingerprint\" | \"native-changed\" | \"version-changed\";\n\nexport type PlatformVerdict =\n | { platform: Platform; action: \"update\"; shipped: ShippedBuild }\n | { platform: Platform; action: \"submit\"; shipped: ShippedBuild }\n | {\n platform: Platform;\n action: \"pull\";\n why: \"version-behind\";\n shipped: ShippedBuild;\n /** The newest version built on the channel, whose bump this branch misses. */\n newerVersion: string;\n }\n | { platform: Platform; action: \"wait\"; why: \"build-running\"; running: RunningBuild }\n | { platform: Platform; action: \"build\"; why: BuildReason; shipped: ShippedBuild | null };\n\nexport type ReleaseAction = PlatformVerdict[\"action\"];\n\n/** What this commit needs on one platform, given the builds on its channel. */\nexport const platformVerdict = ({\n platform,\n local,\n shipped,\n running,\n}: PlatformState): PlatformVerdict => {\n if (running?.appVersion === local.version) {\n return { platform, action: \"wait\", why: \"build-running\", running };\n }\n // A running build of a newer version means its bump commit exists: this branch is behind it.\n const newestVersion = running?.appVersion ?? shipped?.appVersion;\n if (shipped && newestVersion && compareVersions(local.version, newestVersion) < 0) {\n return {\n platform,\n action: \"pull\",\n why: \"version-behind\",\n shipped,\n newerVersion: newestVersion,\n };\n }\n if (!shipped) return { platform, action: \"build\", why: \"no-build\", shipped };\n\n // The version is part of the fingerprint and names the binary: it decides first.\n if (shipped.appVersion && shipped.appVersion !== local.version) {\n return { platform, action: \"build\", why: \"version-changed\", shipped };\n }\n if (!shipped.fingerprint) return { platform, action: \"build\", why: \"no-fingerprint\", shipped };\n if (shipped.fingerprint !== local.fingerprint) {\n return { platform, action: \"build\", why: \"native-changed\", shipped };\n }\n if (shipped.gitCommit === local.commit) return { platform, action: \"submit\", shipped };\n return { platform, action: \"update\", shipped };\n};\n\nexport interface ReleasePlan {\n action: ReleaseAction;\n commands: string[];\n}\n\n// One update reaches every platform, so the most demanding platform decides; a stale branch\n// is caught up first, since every other verdict is about an older version.\nconst PRECEDENCE: ReleaseAction[] = [\"pull\", \"build\", \"wait\", \"submit\", \"update\"];\n\n/** The next step for the whole release, with the release CLI commands that take it. */\nexport const releasePlan = (\n channel: Environment,\n verdicts: PlatformVerdict[],\n unit: string,\n): ReleasePlan => {\n const action =\n PRECEDENCE.find((candidate) => verdicts.some((verdict) => verdict.action === candidate)) ??\n \"update\";\n const flag = channel === \"preview\" ? \"\" : ` --channel ${channel}`;\n const COMMANDS: Record<ReleaseAction, string[]> = {\n // EAS submits a build when it is done: shipping it is one command.\n build: [`pnpm shiplane ship ${unit}${flag}`],\n // The release that versioned the build is on main: the branch catches up from there.\n pull: [\"git fetch origin\", \"git merge origin/main\", `pnpm shiplane${flag}`],\n wait: [`pnpm shiplane status${flag}`],\n // The build made from this commit exists and EAS submitted it.\n submit: [`pnpm shiplane status${flag}`],\n update: [`pnpm shiplane ship ${unit}${flag}`],\n };\n\n return { action, commands: COMMANDS[action] };\n};\n\n/**\n * A build of a version some binary already carries would give two native layers one version: each\n * binary keeps its own. The release pull request, or the promotion one, moves it first.\n */\nexport const needsNewVersion = (states: PlatformState[]) =>\n states.some(({ local, shipped }) => shipped?.appVersion === local.version);\n\nexport type MobileRelease =\n | { ships: \"build\"; version: string; nativeChangeset: boolean }\n | { ships: \"update\"; version: string }\n | { ships: \"nothing\" }\n | { ships: \"blocked\"; why: \"build-running\" | \"behind\" };\n\n/**\n * What the release does for mobile, from preview's verdict. A new native layer gets a new\n * version, so each binary's version names one native layer.\n */\nexport const mobileRelease = (\n action: ReleaseAction,\n expo: AppBump | undefined,\n currentVersion: string,\n): MobileRelease => {\n if (action === \"wait\") return { ships: \"blocked\", why: \"build-running\" };\n if (action === \"pull\") return { ships: \"blocked\", why: \"behind\" };\n if (action === \"build\") {\n return expo\n ? { ships: \"build\", version: expo.to, nativeChangeset: false }\n : { ships: \"build\", version: nextPatch(currentVersion), nativeChangeset: true };\n }\n return expo ? { ships: \"update\", version: expo.to } : { ships: \"nothing\" };\n};\n\n/** The changeset the release writes itself: a build gets a version even with no expo changeset. */\nexport const releaseChangesets = (\n mobile: MobileRelease,\n changes: string[],\n app: string,\n): PlannedChangeset[] =>\n mobile.ships === \"build\" && mobile.nativeChangeset\n ? [\n {\n id: `native-${mobile.version}`,\n releases: [{ name: app, type: \"patch\" }],\n summary:\n changes.length > 0\n ? `Native code changed: ${changes.map((change) => change.replace(/^[+~-] /, \"\")).join(\", \")}.`\n : \"A new build of the app, with the same native code.\",\n },\n ]\n : [];\n\nconst BUILD_LABEL = \"New build, iOS and Android\";\nconst UPDATE_LABEL = \"Over-the-air update\";\n\n/**\n * What the person may pick once detection spoke, the suggestion first: a build is always possible,\n * an update only on the native layer the installed binaries carry.\n */\nexport const mobileChoice = (\n action: ReleaseAction,\n why: string,\n app: string,\n): VersionChoice | undefined => {\n if (action === \"wait\" || action === \"pull\") return undefined;\n const question = `What should ${app} ship to preview?`;\n if (action === \"build\") {\n return {\n question,\n suggested: \"build\",\n options: [\n { value: \"build\", label: `${BUILD_LABEL} (suggested)`, hint: why },\n {\n value: \"update\",\n label: UPDATE_LABEL,\n hint: \"not safe: installed binaries lack this native layer\",\n disabled: true,\n },\n ],\n };\n }\n return {\n question,\n suggested: \"update\",\n options: [\n { value: \"update\", label: `${UPDATE_LABEL} (suggested)`, hint: why },\n {\n value: \"build\",\n label: BUILD_LABEL,\n hint: \"not needed: a new binary anyway, under its own version\",\n },\n ],\n };\n};\n\nexport type MobileShip =\n \"build\" | \"update\" | \"wait\" | \"shipped\" | \"moved-past\" | \"behind\" | \"nothing\";\n\n/**\n * What mobile still needs for the expo version a release tagged. A binary or an update ships its\n * commit: once the app's code moved past the release, it would carry code no release named.\n */\nexport const mobileShip = (\n action: ReleaseAction,\n state: {\n /** The current expo version has a release tag. */\n tagged: boolean;\n /** Code reaching the app changed between that tag and main. */\n codeSinceTag: boolean;\n /** The last build or update shipped the tagged code. */\n codeShipped: boolean;\n },\n): MobileShip => {\n if (!state.tagged) return \"nothing\";\n if (action === \"wait\") return \"wait\";\n if (action === \"pull\") return \"behind\";\n if (action === \"submit\") return \"shipped\";\n if (action === \"update\" && state.codeShipped) return \"shipped\";\n if (state.codeSinceTag) return \"moved-past\";\n return action;\n};\n\nexport type ProductionMobile =\n | \"promote\"\n | \"finish-rollout\"\n | \"build\"\n | \"update\"\n | \"wait\"\n | \"shipped\"\n | \"moved-past\"\n | \"behind\"\n | \"nothing\";\n\n/**\n * What production still needs to run the expo version preview validated. A new native layer needs\n * production's version moved first (the promotion PR).\n */\nexport const productionMobile = (\n action: ReleaseAction,\n state: {\n /** The current expo version has a release tag. */\n tagged: boolean;\n /** Main differs from that tag by version files at most. */\n sinceTagOnlyVersionFiles: boolean;\n expoVersion: string;\n /** apps/expo/app-version.json production: the binary version production builds carry. */\n productionVersion: string;\n /** The last production build or update shipped the tagged code. */\n codeShipped: boolean;\n /** The version of the last finished production build. */\n builtVersion: string | null;\n /** The last update, made from the tagged code, reaches only part of production. */\n partialRollout: boolean;\n },\n): ProductionMobile => {\n if (!state.tagged) return \"nothing\";\n if (action === \"wait\") return \"wait\";\n if (action === \"pull\") return \"behind\";\n if (action !== \"build\" && state.codeShipped) {\n return state.partialRollout ? \"finish-rollout\" : \"shipped\";\n }\n if (!state.sinceTagOnlyVersionFiles) return \"moved-past\";\n if (action === \"build\") {\n return state.productionVersion === state.expoVersion ? \"build\" : \"promote\";\n }\n return \"update\";\n};\n\nconst MOBILE_SHIPS: Partial<Record<MobileRelease[\"ships\"], string>> = {\n build: \"Build iOS and Android, submitted when done\",\n update: \"Over-the-air update\",\n};\n\n/** What happens to each unit once the release pull request is merged, the mobile app's from EAS. */\nexport const onMerge = (mobile: MobileRelease, mobileApp: string) => (bump: AppBump) => {\n if (bump.app === mobileApp) return MOBILE_SHIPS[mobile.ships] ?? \"Nothing ships\";\n const reason = bump.movedWith.length > 0 ? ` (moved with ${bump.movedWith.join(\", \")})` : \"\";\n return `Deploy preview${reason}`;\n};\n\n/** The shares of people a production rollout widens through, in order. */\nexport const ROLLOUT_STEPS = [10, 25, 50, 100] as const;\n\nconst counted = (total: number, one: string, many: string) =>\n `${total} ${total === 1 ? one : many}`;\n\n/** How long an update has been out, as a person says it: hours under two days, then days. */\nexport const timeOut = (hours: number) =>\n hours < 48\n ? counted(Math.max(1, Math.floor(hours)), \"hour\", \"hours\")\n : counted(Math.floor(hours / 24), \"day\", \"days\");\n\n/**\n * The next move for an update reaching part of production: hold while it crashed, nobody ran it or\n * it is under a day old; otherwise the next step up.\n */\nexport const rolloutAdvice = ({\n percentage,\n launches,\n crashes,\n hoursOut,\n}: {\n percentage: number;\n launches: number;\n crashes: number;\n hoursOut: number;\n}): { suggested: number | \"stay\"; why: string } => {\n if (crashes > 0) {\n return {\n suggested: \"stay\",\n why: `${counted(crashes, \"crash\", \"crashes\")} since it went out: look before widening, or roll back`,\n };\n }\n if (launches === 0) {\n return { suggested: \"stay\", why: \"nobody has launched it yet: wait for launches\" };\n }\n if (hoursOut < 24)\n return { suggested: \"stay\", why: `out for ${timeOut(hoursOut)}: give it a day` };\n return {\n suggested: ROLLOUT_STEPS.find((step) => step > percentage) ?? 100,\n why: `healthy for ${timeOut(hoursOut)}: widen it`,\n };\n};\n","import { styleText } from \"node:util\";\nimport type { Environment, GitPosition, Health, SourceControlPort } from \"shiplane/plugin\";\nimport { progressBar } from \"shiplane/plugin\";\n\nimport type { easClient } from \"./client\";\nimport type {\n Platform,\n PlatformState,\n PlatformVerdict,\n ReleaseAction,\n ReleasePlan,\n} from \"./policy\";\nimport { platformVerdict, releasePlan, timeOut } from \"./policy\";\n\nexport const PLATFORMS: Platform[] = [\"ios\", \"android\"];\n\nexport const PLATFORM_NAME: Record<Platform, string> = { ios: \"iOS\", android: \"Android\" };\n\nexport interface Analysis {\n channel: Environment;\n version: string;\n commit: string;\n git: GitPosition;\n checked: { state: PlatformState; verdict: PlatformVerdict; changes: string[] }[];\n plan: ReleasePlan;\n}\n\n/** Each platform's last store build on the channel against this commit's native fingerprint. */\nexport const analyse = async (\n client: ReturnType<typeof easClient>,\n source: SourceControlPort,\n environment: Environment,\n version: string,\n unit: string,\n): Promise<Analysis> => {\n const position = await source.position();\n const checked = await Promise.all(\n PLATFORMS.map(async (platform) => {\n const [fingerprint, builds] = await Promise.all([\n client.localFingerprint(platform, environment),\n client.channelBuilds(platform, environment),\n ]);\n const state: PlatformState = {\n platform,\n local: { fingerprint, version, commit: position.head },\n ...builds,\n };\n const verdict = platformVerdict(state);\n const changes =\n verdict.action === \"build\" && verdict.why === \"native-changed\" && verdict.shipped\n ? await client.nativeChanges(verdict.shipped.id, environment)\n : [];\n return { state, verdict, changes };\n }),\n );\n return {\n channel: environment,\n version,\n commit: position.head,\n git: position,\n checked,\n plan: releasePlan(\n environment,\n checked.map(({ verdict }) => verdict),\n unit,\n ),\n };\n};\n\nconst LOOK: Record<ReleaseAction, { mark: string; style: \"green\" | \"yellow\" | \"red\" }> = {\n update: { mark: \"โœ“\", style: \"green\" },\n submit: { mark: \"โ†‘\", style: \"green\" },\n wait: { mark: \"โ€ฆ\", style: \"yellow\" },\n pull: { mark: \"โ†“\", style: \"yellow\" },\n build: { mark: \"โœ—\", style: \"red\" },\n};\n\n/** Why a platform needs what it needs, as a person reads it. */\nexport const why = (verdict: PlatformVerdict): string => {\n switch (verdict.action) {\n case \"update\":\n return \"same native layer and version as the last build\";\n case \"submit\":\n return \"the last build was made from this commit\";\n case \"wait\":\n return `build ${verdict.running.buildNumber ?? \"?\"} for this version is still running`;\n case \"pull\":\n return `this branch misses the ${verdict.newerVersion} release of a build on this channel`;\n case \"build\":\n // Builds that errored leave no binary: the last finished one is what devices run.\n return {\n \"no-build\": \"no store build on this channel yet\",\n \"no-fingerprint\": \"the last build has no fingerprint, nothing proves an update fits\",\n \"native-changed\": \"native code changed since the last build\",\n \"version-changed\": `no binary carries this version yet: the last finished build is ${verdict.shipped?.appVersion ?? \"older\"}`,\n }[verdict.why];\n }\n};\n\n/** Why the deciding platform needs what the plan says, or nothing when none decided. */\nexport const planWhy = ({ checked, plan }: Analysis) => {\n const shown = checked.find(({ verdict }) => verdict.action === plan.action);\n return shown ? why(shown.verdict) : \"\";\n};\n\nconst MAX_CHANGES = 6;\n\n/** The verdict line a summary shows for the platform that decides, with what changed under it. */\nexport const verdictLines = ({ checked, plan }: Analysis): string[] => {\n const shown = checked.find(({ verdict }) => verdict.action === plan.action);\n if (!shown) return [];\n const look = LOOK[shown.verdict.action];\n return [\n `${styleText(look.style, `${look.mark} ${shown.verdict.action}`)} ${styleText(\"dim\", why(shown.verdict))}`,\n ...shown.changes.slice(0, MAX_CHANGES).map((change) => styleText(\"dim\", ` ${change}`)),\n ];\n};\n\n/** The analysis as `status --json` prints it, the same shape the old release check printed. */\nexport const analysisJson = ({ channel, version, commit, plan, checked }: Analysis) => ({\n channel,\n version,\n commit,\n ...plan,\n platforms: checked.map(({ verdict, changes }) => ({ ...verdict, changes })),\n});\n\nconst count = (total: number, one: string, many: string) => `${total} ${total === 1 ? one : many}`;\n\n/** An update's health from its launches: unwell once it crashed, or while nobody has run it. */\nexport const updateHealth = (\n launches: { platform: Platform; installs: number; failedInstalls: number }[],\n): Health => {\n const summary = launches\n .map(\n ({ platform, installs, failedInstalls }) =>\n `${PLATFORM_NAME[platform]} ${count(installs, \"launch\", \"launches\")}, ${count(failedInstalls, \"crash\", \"crashes\")}`,\n )\n .join(\" ยท \");\n const crashes = launches.reduce((sum, { failedInstalls }) => sum + failedInstalls, 0);\n if (crashes > 0) {\n return { ok: false, summary, why: `${count(crashes, \"crash\", \"crashes\")} on preview` };\n }\n if (launches.every(({ installs }) => installs === 0)) {\n return { ok: false, summary, why: \"nobody has launched it on preview yet\" };\n }\n return { ok: true, summary };\n};\n\n/** Where a rollout stands, as the lines above the question: whose, how far, how long, how it does. */\nexport const rolloutLines = ({\n tag,\n environment,\n percentage,\n hoursOut,\n health,\n}: {\n tag: string;\n environment: Environment;\n percentage: number;\n hoursOut: number;\n health: Health;\n}) => [\n `${styleText(\"bold\", tag)} ${styleText(\"dim\", `on ${environment}`)}`,\n `${styleText(health.ok ? \"green\" : \"yellow\", progressBar(percentage, 100, 20))} ${percentage}% of people ยท out for ${timeOut(hoursOut)}`,\n styleText(\"dim\", health.summary),\n];\n","import type { Environment, Shell } from \"shiplane/plugin\";\nimport { z } from \"shiplane/plugin\";\n\nimport type { Platform, RunningBuild, ShippedBuild } from \"./policy\";\n\n// eas-cli prints notices around its JSON: keep the outermost JSON value only.\nconst parseEasJson = (output: string): unknown => {\n const start = output.search(/[[{]/);\n const end = Math.max(output.lastIndexOf(\"]\"), output.lastIndexOf(\"}\"));\n return JSON.parse(output.slice(start, end + 1));\n};\n\nconst SourceSchema = z.object({\n type: z.string(),\n filePath: z.string().optional(),\n id: z.string().optional(),\n hash: z.string().nullish(),\n});\nconst FingerprintSchema = z.object({ hash: z.string(), sources: z.array(SourceSchema) });\nconst EasBuildSchema = z.object({\n id: z.string(),\n status: z.string(),\n buildProfile: z.string().nullish(),\n isForIosSimulator: z.boolean().nullish(),\n appVersion: z.string().nullish(),\n appBuildVersion: z.string().nullish(),\n gitCommitHash: z.string().nullish(),\n createdAt: z.string(),\n fingerprint: z.object({ hash: z.string() }).nullish(),\n});\nconst UpdateListSchema = z.object({\n currentPage: z.array(\n // EAS lists rolloutPercentage only while a rollout holds the group back from part of the channel.\n z.object({ group: z.string(), rolloutPercentage: z.number().optional() }),\n ),\n});\nconst UpdateViewSchema = z.array(\n z.object({ gitCommitHash: z.string().nullish(), createdAt: z.string().nullish() }),\n);\nconst UpdateInsightsSchema = z.object({\n platforms: z.array(\n z.object({\n platform: z.enum([\"ios\", \"android\"]),\n totals: z.object({ installs: z.number(), failedInstalls: z.number() }),\n }),\n ),\n});\n\nconst RUNNING = new Set([\"NEW\", \"IN_QUEUE\", \"IN_PROGRESS\"]);\n\n// eas-cli rejects --non-interactive on these commands as a nonexistent flag.\nconst WITHOUT_NON_INTERACTIVE_FLAG = new Set([\"update:view\"]);\n\n// The first stderr line is often eas-cli's upgrade notice: report its own error line instead.\nconst failureReason = (stderr: string): string => {\n const lines = stderr\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean);\n return lines.find((line) => line.startsWith(\"Error:\")) ?? lines.at(-1) ?? \"\";\n};\n\n/** The eas CLI, run in the app's folder with its environment as APP_VARIANT. */\nexport const easClient = (shell: Shell, cwd: string) => {\n const eas = async (args: string[], environment: Environment): Promise<unknown> => {\n const command = args[0] ?? \"\";\n const flags = WITHOUT_NON_INTERACTIVE_FLAG.has(command)\n ? [\"--json\"]\n : [\"--json\", \"--non-interactive\"];\n const { status, stdout, stderr } = await shell.capture(\n \"pnpm\",\n [\"exec\", \"eas\", ...args, ...flags],\n {\n cwd,\n env: { APP_VARIANT: environment },\n },\n );\n if (status !== 0) {\n throw new Error(`eas ${command} exited with ${status}: ${failureReason(stderr)}`);\n }\n return parseEasJson(stdout);\n };\n\n return {\n localFingerprint: async (platform: Platform, environment: Environment) =>\n FingerprintSchema.parse(\n await eas([\"fingerprint:generate\", \"--platform\", platform], environment),\n ).hash,\n\n // The simulator profile shares the preview channel: only the channel's own store profile counts.\n channelBuilds: async (platform: Platform, environment: Environment) => {\n const builds = z\n .array(EasBuildSchema)\n .parse(await eas([\"build:list\", \"--platform\", platform, \"--limit\", \"30\"], environment))\n .filter((build) => build.buildProfile === environment && !build.isForIosSimulator);\n const finished = builds.find((build) => build.status === \"FINISHED\");\n const running = builds.find((build) => RUNNING.has(build.status));\n return {\n shipped: finished\n ? ({\n id: finished.id,\n appVersion: finished.appVersion ?? null,\n buildNumber: finished.appBuildVersion ?? null,\n fingerprint: finished.fingerprint?.hash ?? null,\n gitCommit: finished.gitCommitHash ?? null,\n createdAt: finished.createdAt,\n } satisfies ShippedBuild)\n : null,\n running: running\n ? ({\n id: running.id,\n appVersion: running.appVersion ?? null,\n buildNumber: running.appBuildVersion ?? null,\n } satisfies RunningBuild)\n : null,\n };\n },\n\n // What differs between the last build's native layer and this commit's, for a \"native changed\".\n nativeChanges: async (buildId: string, environment: Environment): Promise<string[]> => {\n const { fingerprint1, fingerprint2 } = z\n .object({ fingerprint1: FingerprintSchema, fingerprint2: FingerprintSchema })\n .parse(await eas([\"fingerprint:compare\", \"--build-id\", buildId], environment));\n type Source = z.infer<typeof SourceSchema>;\n const key = (source: Source) => `${source.type}:${source.filePath ?? source.id ?? \"\"}`;\n const label = (source: Source) =>\n (source.filePath ?? source.id ?? source.type).replace(/^(\\.\\.\\/)+/, \"\");\n const before = new Map(fingerprint1.sources.map((source) => [key(source), source]));\n const after = new Map(fingerprint2.sources.map((source) => [key(source), source]));\n return [\n ...[...after].flatMap(([id, source]) => {\n const old = before.get(id);\n if (!old) return [`+ ${label(source)}`];\n return old.hash === source.hash ? [] : [`~ ${label(source)}`];\n }),\n ...[...before].flatMap(([id, source]) => (after.has(id) ? [] : [`- ${label(source)}`])),\n ];\n },\n\n /** The newest update group on the channel, with the share of people it reaches. */\n lastUpdate: async (\n environment: Environment,\n ): Promise<{ group: string; rolloutPercentage: number } | null> => {\n const { currentPage } = UpdateListSchema.parse(\n await eas([\"update:list\", \"--branch\", environment, \"--limit\", \"1\"], environment),\n );\n const [last] = currentPage;\n return last ? { group: last.group, rolloutPercentage: last.rolloutPercentage ?? 100 } : null;\n },\n\n /** Each platform's launches of an update group over the last 7 days, and the failed ones. */\n updateLaunches: async (group: string, environment: Environment) =>\n UpdateInsightsSchema.parse(await eas([\"update:insights\", group], environment)).platforms.map(\n ({ platform, totals }) => ({ platform, ...totals }),\n ),\n\n /** The commit an update group was made from, and when it was published. */\n updateOrigin: async (group: string, environment: Environment) => {\n const [first] = UpdateViewSchema.parse(await eas([\"update:view\", group], environment));\n return { commit: first?.gitCommitHash ?? null, createdAt: first?.createdAt ?? null };\n },\n };\n};\n","import type { AdapterContext, PlatformEnvPort } from \"shiplane/plugin\";\nimport { isEnvVarName } from \"shiplane/plugin\";\n\nconst NAME_LINE = /^([A-Za-z_][A-Za-z0-9_]*)=/;\n\nconst firstLine = (text: string) =>\n text\n .split(\"\\n\")\n .find((line) => line.trim())\n ?.trim();\n\n/** The names of an EAS environment's variables; only names are kept, never values. */\nexport const easEnv = ({ shell }: AdapterContext): PlatformEnvPort => ({\n listNames: async ({ unit, environment }) => {\n const result = await shell.capture(\n \"pnpm\",\n [\"exec\", \"eas\", \"env:list\", environment, \"--format\", \"short\"],\n // eas reads app.config, which may branch on APP_VARIANT, from the app's folder.\n { cwd: unit.dir, env: { APP_VARIANT: environment } },\n );\n if (result.status !== 0) {\n const reason = firstLine(result.stderr) ?? `exit ${result.status}`;\n throw new Error(`eas env:list ${environment}: ${reason}`);\n }\n return new Set(\n result.stdout.split(\"\\n\").flatMap((line) => {\n const name = NAME_LINE.exec(line)?.[1];\n return name && isEnvVarName(name) ? [name] : [];\n }),\n );\n },\n setCommand: ({ unit, environment, names }) =>\n [\n `cd ${unit.dir}`,\n ...names.map(\n (name) =>\n `pnpm exec eas env:create --environment ${environment} --name ${name} --value ... --visibility secret`,\n ),\n ].join(\" && \"),\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type {\n DecideContext,\n Decision,\n DeliveryAdapter,\n Environment,\n Job,\n JobInput,\n MobileDistributionPort,\n Refusal,\n ReleaseUnit,\n Resolved,\n} from \"shiplane/plugin\";\nimport { defineMobileDistribution, perEnv, releaseSource, tagName, z } from \"shiplane/plugin\";\n\nimport type { MobileRelease } from \"./eas/policy\";\nimport {\n analyse,\n analysisJson,\n planWhy,\n rolloutLines,\n updateHealth,\n verdictLines,\n} from \"./eas/analyse\";\nimport { easClient } from \"./eas/client\";\nimport { easEnv } from \"./eas/env\";\nimport {\n mobileChoice,\n mobileRelease,\n mobileShip,\n needsNewVersion,\n productionMobile,\n releaseChangesets,\n ROLLOUT_STEPS,\n rolloutAdvice,\n} from \"./eas/policy\";\n\nconst EasOptions = z.object({\n versionFile: z.string().default(\"app-version.json\").describe(\"Each environment's binary version\"),\n update: perEnv(z.string()).describe(\n \"The package script that publishes an update, per environment\",\n ),\n});\n\ntype Options = Resolved<z.output<typeof EasOptions>>;\n\nconst readJson = (file: string): Record<string, unknown> =>\n z.record(z.string(), z.unknown()).parse(JSON.parse(fs.readFileSync(file, \"utf8\")));\n\nconst writeJson = (file: string, value: unknown) =>\n fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\\n`);\n\nconst VersionsSchema = z.record(z.string(), z.object({ version: z.string() }).loose());\n\n/** The binary version an environment carries. */\nconst versionFileOf = (root: string, unit: ReleaseUnit, options: Options) =>\n path.join(root, unit.dir, options.versionFile);\n\nconst binaryVersion = (\n root: string,\n unit: ReleaseUnit,\n options: Options,\n environment: Environment,\n) =>\n VersionsSchema.parse(readJson(versionFileOf(root, unit, options)))[environment]?.version ??\n unit.version;\n\nconst setBinaryVersion = (\n root: string,\n unit: ReleaseUnit,\n options: Options,\n environment: Environment,\n version: string,\n) => {\n const file = versionFileOf(root, unit, options);\n const versions = VersionsSchema.parse(readJson(file));\n writeJson(file, { ...versions, [environment]: { ...versions[environment], version } });\n return path.relative(root, file);\n};\n\n// A build runs on EAS from this machine; a preview update ships what its tag named, production main.\nconst updateRef = ({ tag, environment }: JobInput<Options>) =>\n environment === \"preview\" ? tag : \"main\";\n\nconst buildJob = (input: JobInput<Options>): Job => {\n const { unit, environment } = input;\n return {\n label: `build iOS and Android on EAS ${environment}, submitted when done`,\n local: [\n {\n command: \"pnpm\",\n args: [\n \"exec\",\n \"eas\",\n \"build\",\n \"--profile\",\n environment,\n \"--platform\",\n \"all\",\n \"--auto-submit\",\n \"--no-wait\",\n ],\n cwd: unit.dir,\n env: { EXPO_ATLAS: \"false\", APP_VARIANT: environment },\n },\n ],\n };\n};\n\nconst updateJob = (input: JobInput<Options>): Job => {\n const { unit, environment, options } = input;\n return {\n label: `publish the ${environment} update`,\n local: [\n {\n command: \"pnpm\",\n args: [\"run\", options.update],\n cwd: unit.dir,\n env: { APP_VARIANT: environment },\n },\n ],\n ci: { ref: updateRef(input) },\n };\n};\n\n/** One eas command on a rollout; no workflow moves a rollout, so it runs here. */\nconst rolloutJob = (\n { unit, environment }: JobInput<Options>,\n label: string,\n args: string[],\n): Job => ({\n label,\n local: [\n {\n command: \"pnpm\",\n args: [\"exec\", \"eas\", ...args, \"--non-interactive\"],\n cwd: unit.dir,\n env: { APP_VARIANT: environment },\n },\n ],\n});\n\n/** Where an update reaching part of the channel stands, and each way it can go from there. */\nconst rolloutChoice = async (\n context: DecideContext,\n client: ReturnType<typeof easClient>,\n input: JobInput<Options>,\n update: { group: string; percentage: number; createdAt: string | null },\n): Promise<Decision> => {\n const { tag, environment } = input;\n const { group, percentage } = update;\n const launches = await client.updateLaunches(group, environment);\n const hoursOut = update.createdAt\n ? (context.now().getTime() - new Date(update.createdAt).getTime()) / 3_600_000\n : 0;\n const advice = rolloutAdvice({\n percentage,\n launches: launches.reduce((sum, { installs }) => sum + installs, 0),\n crashes: launches.reduce((sum, { failedInstalls }) => sum + failedInstalls, 0),\n hoursOut,\n });\n const suggested = String(advice.suggested);\n const widen = (to: number) =>\n rolloutJob(input, `widen the ${tag} rollout on ${environment}: ${percentage}% โ†’ ${to}%`, [\n \"update:edit\",\n group,\n \"--rollout-percentage\",\n String(to),\n ]);\n const options = [\n ...ROLLOUT_STEPS.filter((step) => step > percentage).map((step) => ({\n value: String(step),\n label: step === 100 ? \"Everyone\" : `${step}% of people`,\n hint: step === 100 ? \"finishes the rollout\" : undefined,\n jobs: [widen(step)],\n })),\n {\n value: \"stay\",\n label: `Stay at ${percentage}%`,\n hint: \"nothing changes, check again later\",\n jobs: [],\n },\n {\n value: \"roll-back\",\n label: \"Roll back\",\n hint: \"people on it go back to the update before\",\n jobs: [\n rolloutJob(input, `roll back the ${tag} rollout on ${environment}`, [\n \"update:revert-update-rollout\",\n \"--group\",\n group,\n ]),\n ],\n },\n ].map((option) =>\n option.value === suggested\n ? { ...option, label: `${option.label} (suggested)`, hint: advice.why }\n : option,\n );\n return {\n action: \"choose\",\n lines: rolloutLines({\n tag,\n environment,\n percentage,\n hoursOut,\n health: updateHealth(launches),\n }),\n question: `How far should ${tag} go on ${environment}?`,\n suggested,\n options,\n held: `${tag} stays at ${percentage}% of people`,\n };\n};\n\nconst refuse = (refusal: Refusal): Decision => ({ action: \"fail\", ...refusal });\n\n/** The checks a new binary passes before EAS builds it: from main, a new version, a clean tree. */\nconst buildRefusal = async (\n context: DecideContext,\n analysis: Awaited<ReturnType<typeof analyse>>,\n input: JobInput<Options>,\n): Promise<Refusal | null> => {\n const source = releaseSource(\"build\", input.environment, analysis.git);\n if (!source.allowed) {\n return source.why === \"not-main\"\n ? {\n title: `Not from ${analysis.git.branch}`,\n detail: \"a binary ships what it is built from, so builds start from main.\",\n commands: [\"git switch main\", \"git pull\", \"shiplane ship\"],\n }\n : {\n title: \"main is behind origin\",\n detail: \"pull it first, a release ships what is merged.\",\n commands: [\"git pull\"],\n };\n }\n if (needsNewVersion(analysis.checked.map(({ state }) => state))) {\n return {\n title: `${analysis.version} already has a ${input.environment} binary`,\n detail: \"a new native layer needs a new version, or updates would reach both binaries.\",\n commands: [\"shiplane\"],\n };\n }\n if (!(await context.sourceControl.isClean())) {\n return {\n title: \"Uncommitted changes\",\n detail: \"commit or stash them first, a release ships the commit.\",\n commands: [],\n };\n }\n return null;\n};\n\nconst previewDecision = async (\n context: DecideContext,\n input: JobInput<Options>,\n): Promise<Decision> => {\n const { unit, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"preview\",\n binaryVersion(context.root, unit, options, \"preview\"),\n unit.name,\n );\n const version = unit.version;\n const tag = await context.sourceControl.tagCommit(tagName(unit.name, version, context.tagFormat));\n const codeSince = (commit: string) => (tag ? context.changedCode(unit, tag, commit) : []);\n const sinceTag = async (commit: string | null) =>\n tag !== null && commit !== null && (await codeSince(commit)).length === 0;\n const builtFrom = analysis.checked.map(({ state }) => state.shipped?.gitCommit ?? null);\n const lastBuildCommit = builtFrom.every((commit) => commit === builtFrom[0])\n ? (builtFrom[0] ?? null)\n : null;\n const lastUpdate =\n tag && analysis.plan.action === \"update\" ? await client.lastUpdate(\"preview\") : null;\n const lastUpdateCommit = lastUpdate\n ? (await client.updateOrigin(lastUpdate.group, \"preview\")).commit\n : null;\n const ship = mobileShip(analysis.plan.action, {\n tagged: tag !== null,\n codeSinceTag: tag !== null && !(await sinceTag(analysis.git.head)),\n codeShipped:\n analysis.plan.action === \"update\" &&\n ((await sinceTag(lastBuildCommit)) || (await sinceTag(lastUpdateCommit))),\n });\n // Main moved past the tag: GitHub Actions still ships the tag, this machine only its checkout.\n const movedPast = ship === \"moved-past\";\n const kind = analysis.plan.action === \"build\" ? \"build\" : \"update\";\n // On CI a preview update still ships its tag; this machine would ship code no release named.\n const named = movedPast ? await codeSince(analysis.git.head) : [];\n const notLocal = movedPast\n ? {\n notLocal: `main moved past it (${named.slice(0, 3).join(\", \")}${named.length > 3 ? \", ...\" : \"\"} changed since)`,\n }\n : {};\n switch (movedPast && kind === \"update\" ? kind : ship) {\n case \"build\": {\n const refusal = await buildRefusal(context, analysis, input);\n return refusal\n ? refuse(refusal)\n : { action: \"ship\", kind: \"build\", jobs: [buildJob(input)], ...notLocal };\n }\n case \"update\":\n return { action: \"ship\", kind: \"update\", jobs: [updateJob(input)], ...notLocal };\n case \"wait\":\n return {\n action: \"none\",\n status: `${unit.name} ${version} is building on EAS, submitted when done`,\n };\n case \"shipped\":\n return { action: \"none\", status: `${unit.name} ${version} is on preview` };\n case \"nothing\":\n return {\n action: \"none\",\n status: `No ${unit.name} version to ship: add a changeset, then release.`,\n };\n case \"behind\":\n return refuse({\n title: \"main is behind\",\n detail: \"the last preview build carries a newer version.\",\n commands: [\"git pull\"],\n });\n case \"moved-past\": {\n const changed = await codeSince(analysis.git.head);\n const named = changed.slice(0, 3).join(\", \") + (changed.length > 3 ? \", ...\" : \"\");\n return refuse({\n title: `main moved past ${tagName(unit.name, version, context.tagFormat)}`,\n detail: `a binary or an update ships its commit, and ${named} changed since, code no release named.`,\n commands: [\"pnpm changeset\", \"shiplane\"],\n });\n }\n }\n};\n\nconst productionDecision = async (\n context: DecideContext,\n input: JobInput<Options>,\n): Promise<Decision> => {\n const { unit, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const productionVersion = binaryVersion(context.root, unit, options, \"production\");\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"production\",\n productionVersion,\n unit.name,\n );\n const version = unit.version;\n const tag = await context.sourceControl.tagCommit(tagName(unit.name, version, context.tagFormat));\n const codeSince = (commit: string) => (tag ? context.changedCode(unit, tag, commit) : []);\n const sinceTag = async (commit: string | null) =>\n tag !== null && commit !== null && (await codeSince(commit)).length === 0;\n const action = analysis.plan.action;\n const lastUpdate = tag && action === \"update\" ? await client.lastUpdate(\"production\") : null;\n const origin = lastUpdate ? await client.updateOrigin(lastUpdate.group, \"production\") : null;\n const updateCommit = origin?.commit ?? null;\n const shippedCommits = [\n ...analysis.checked.map(({ state }) => state.shipped?.gitCommit ?? null),\n updateCommit,\n ];\n const builtVersion = analysis.checked[0]?.state.shipped?.appVersion ?? null;\n const shippedAny = (await Promise.all(shippedCommits.map(sinceTag))).some(Boolean);\n const rollout = lastUpdate?.rolloutPercentage ?? 100;\n const decision = productionMobile(action, {\n tagged: tag !== null,\n sinceTagOnlyVersionFiles: await sinceTag(analysis.git.head),\n expoVersion: version,\n productionVersion,\n codeShipped: action === \"submit\" || shippedAny,\n builtVersion,\n partialRollout: rollout < 100 && (await sinceTag(updateCommit)),\n });\n switch (decision) {\n case \"finish-rollout\":\n return lastUpdate\n ? rolloutChoice(context, client, input, {\n group: lastUpdate.group,\n percentage: rollout,\n createdAt: origin?.createdAt ?? null,\n })\n : { action: \"none\", status: `${unit.name} ${version} is live` };\n case \"promote\":\n return {\n action: \"pull-request\",\n question: `Open the PR that moves production to ${unit.name} ${version}?`,\n branch: `chore/promote-${unit.name}-${version}`,\n title: `chore(release): production builds ${unit.name} ${version}`,\n body: [\n \"## ๐ŸŽฏ What & why\",\n \"\",\n `Production builds ${unit.name} ${version}, the version preview validated: \\`${path.posix.join(unit.dir, options.versionFile)}\\` production moves to it, the runtime version production updates target. Merge it, then \\`release --channel production\\` builds.`,\n \"\",\n \"## โœ… Testing\",\n \"\",\n \"Version file only. No UI change.\",\n ].join(\"\\n\"),\n changesets: [],\n write: () =>\n Promise.resolve([setBinaryVersion(context.root, unit, options, \"production\", version)]),\n };\n case \"update\":\n return { action: \"ship\", kind: \"update\", jobs: [updateJob(input)] };\n case \"build\": {\n const refusal = await buildRefusal(context, analysis, input);\n return refusal ? refuse(refusal) : { action: \"ship\", kind: \"build\", jobs: [buildJob(input)] };\n }\n case \"wait\":\n return {\n action: \"none\",\n status: `${unit.name} ${productionVersion} is building on EAS, submitted to the stores when done`,\n };\n case \"shipped\":\n return {\n action: \"none\",\n status: `${unit.name} ${builtVersion ?? version} is live`,\n };\n case \"nothing\":\n return {\n action: \"none\",\n status: `No ${unit.name} release to promote yet: release to preview first.`,\n };\n case \"behind\":\n return refuse({\n title: \"main is behind\",\n detail: \"the last production build carries a newer version.\",\n commands: [\"git pull\"],\n });\n case \"moved-past\": {\n const changed = await codeSince(analysis.git.head);\n const named = changed.slice(0, 3).join(\", \") + (changed.length > 3 ? \", ...\" : \"\");\n return refuse({\n title: `main moved past ${tagName(unit.name, version, context.tagFormat)}`,\n detail: `production ships what preview validated, and ${named} changed since, code preview never ran: release it to preview first.`,\n commands: [\"pnpm changeset\", \"shiplane\"],\n });\n }\n }\n};\n\nconst ON_MERGE: Partial<Record<MobileRelease[\"ships\"], string>> = {\n build: \"Build iOS and Android, submitted when done\",\n update: \"Over-the-air update\",\n};\n\n/**\n * A mobile app on EAS. The native fingerprint decides between a new binary and an over-the-air\n * update; production ships what preview validated, moving its version by PR first when it must.\n */\nexport type EasMobileDistributionAdapter = DeliveryAdapter<MobileDistributionPort>;\n\nexport const easMobileDistribution = (): EasMobileDistributionAdapter =>\n defineMobileDistribution({\n name: \"eas\",\n settings: EasOptions,\n decidedBy: { platform: \"EAS\", choice: \"build or update\", check: \"shiplane status --json\" },\n typicalMinutes: 8,\n // The binary versions move after a tag; the code does not.\n versionFiles: (unit) => [path.posix.join(unit.dir, EasOptions.parse(unit.options).versionFile)],\n describe: ({ environment }) => `EAS ${environment}, iOS and Android`,\n detect: (folder) => {\n if (!folder.has(\"eas.json\")) return null;\n const scripts = z\n .object({ scripts: z.record(z.string(), z.string()).default({}) })\n .safeParse(JSON.parse(folder.read(\"package.json\") ?? \"{}\"));\n const names = scripts.success ? Object.keys(scripts.data.scripts) : [];\n const pick = (environment: string) =>\n [`eas:update:${environment}`, `eas:update:${environment}:gated`].find((name) =>\n names.includes(name),\n ) ?? names.find((name) => name.includes(\"update\") && name.includes(environment));\n const [preview, production] = [pick(\"preview\"), pick(\"production\")];\n return {\n certainty: \"found\",\n reason: \"eas.json\",\n options: preview && production ? { update: { preview, production } } : {},\n };\n },\n questions: (_folder, found) =>\n found.update === undefined\n ? [\n {\n key: \"update\",\n message: \"Package script that publishes an update?\",\n initial: \"eas:update\",\n },\n ]\n : [],\n requirements: (unit) => [\n {\n label: \"eas\",\n check: { command: \"pnpm\", args: [\"exec\", \"eas\", \"whoami\"], cwd: unit.dir },\n fix: { command: \"pnpm\", args: [\"exec\", \"eas\", \"login\"], cwd: unit.dir },\n },\n ],\n env: easEnv,\n jobs: (input) => [buildJob(input), updateJob(input)],\n decide: (context, input) =>\n input.environment === \"preview\"\n ? previewDecision(context, input)\n : productionDecision(context, input),\n inspect: async (context, input) => {\n const { unit, environment, options } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const [analysis, lastUpdate] = await Promise.all([\n analyse(\n client,\n context.sourceControl,\n environment,\n binaryVersion(context.root, unit, options, environment),\n unit.name,\n ),\n client.lastUpdate(environment),\n ]);\n const rollout = lastUpdate?.rolloutPercentage ?? 100;\n return {\n ...analysisJson(analysis),\n ...(rollout < 100 ? { rolloutPercentage: rollout } : {}),\n };\n },\n // Only an update has launch data: a binary preview got for this release reports nothing here.\n health: async (context, { unit, tag }) => {\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const [last, tagged] = await Promise.all([\n client.lastUpdate(\"preview\"),\n context.sourceControl.tagCommit(tag),\n ]);\n if (!last || !tagged) return null;\n const { commit } = await client.updateOrigin(last.group, \"preview\");\n if (!commit || (await context.changedCode(unit, tagged, commit)).length > 0) return null;\n return updateHealth(await client.updateLaunches(last.group, \"preview\"));\n },\n beforeVersion: async (context, input) => {\n const { unit, options, bump } = input;\n const client = easClient(context.shell, path.join(context.root, unit.dir));\n const analysis = await analyse(\n client,\n context.sourceControl,\n \"preview\",\n binaryVersion(context.root, unit, options, \"preview\"),\n unit.name,\n );\n const forced = input.chosen === \"build\" && analysis.plan.action !== \"build\";\n const mobile = mobileRelease(forced ? \"build\" : analysis.plan.action, bump, unit.version);\n const summary = forced\n ? [\"โ†‘ build asked for, an update would do\", ...verdictLines(analysis)]\n : verdictLines(analysis);\n if (mobile.ships === \"blocked\") {\n return {\n blocked: {\n title: \"No release\",\n detail:\n mobile.why === \"build-running\"\n ? \"a preview build is still running: release once it's done, the native layer it carries decides the version.\"\n : \"main is behind the last preview build's version.\",\n commands: [\"shiplane\"],\n },\n added: [],\n writes: [],\n summary,\n onMerge: \"Nothing ships\",\n };\n }\n const changes = [...new Set(analysis.checked.flatMap(({ changes: lines }) => lines))];\n const version =\n mobile.ships === \"build\" || mobile.ships === \"update\" ? mobile.version : unit.version;\n return {\n choice: mobileChoice(analysis.plan.action, planWhy(analysis), unit.name),\n added: releaseChangesets(mobile, changes, unit.packageName),\n writes:\n mobile.ships === \"build\"\n ? [\n {\n label: `preview app version โ†’ ${version}`,\n run: async () => {\n setBinaryVersion(context.root, unit, options, \"preview\", version);\n await Promise.resolve();\n },\n },\n ]\n : [],\n summary: mobile.ships === \"nothing\" ? [\"โ€“ nothing changed\"] : summary,\n onMerge: ON_MERGE[mobile.ships] ?? \"Nothing ships\",\n ...(mobile.ships === \"build\"\n ? {\n step: `Build iOS and Android ${version} on EAS, submitted to TestFlight and Play internal when done`,\n }\n : mobile.ships === \"update\"\n ? { step: `Publish ${version} over the air to preview` }\n : {}),\n };\n },\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shiplane/eas",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Mobile distribution for shiplane: an Expo app on EAS, a store build or an over-the-air update.",
5
5
  "homepage": "https://github.com/arnaud-zg/packages/tree/main/packages/shiplane-eas#readme",
6
6
  "bugs": {
@@ -32,11 +32,11 @@
32
32
  "prettier": "^3.8.3",
33
33
  "typescript": "6.0.3",
34
34
  "vitest": "^4.1.11",
35
- "@shiplane/fly": "0.4.0",
36
- "shiplane": "0.4.0"
35
+ "@shiplane/fly": "0.4.2",
36
+ "shiplane": "0.4.2"
37
37
  },
38
38
  "peerDependencies": {
39
- "shiplane": "0.4.0"
39
+ "shiplane": "0.4.2"
40
40
  },
41
41
  "shiplane": {
42
42
  "adapters": [