expo-release 2.0.0 → 2.2.1

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
@@ -17,15 +17,70 @@ decide — git diff + eas fingerprint:generate + eas build:list
17
17
  └─ no matching native build → eas build --auto-submit --no-wait
18
18
  ```
19
19
 
20
+ ## Install and requirements
21
+
22
+ ```sh
23
+ bun add -D expo-release
24
+ bunx expo-release --help
25
+ ```
26
+
27
+ The CLI and GitHub Action require Bun. EAS operations also need an authenticated Expo account and the EAS CLI in the calling project; Expo SDK 55+ OTA updates require an explicit EAS environment. CI verifies Bun 1.4.2. Installing the npm package does not deploy an application.
28
+
29
+ ## Set up with AI
30
+
31
+ Copy the prompt below into Cursor, Claude Code, or another coding agent to wire up expo-release in your existing repo.
32
+
33
+ ```
34
+ Integrate expo-release (https://github.com/HansKristoffer/expo-release) into this codebase for fingerprint-driven Expo EAS releases.
35
+
36
+ Goal: on push to the main deploy branch, when Expo-related paths change, automatically publish an OTA update if a finished native build matches the current fingerprint, or queue a native store build (with auto-submit) when no matching build exists.
37
+
38
+ Do the following in this repo:
39
+
40
+ 1. Inspect the repo layout: find the Expo app directory, monorepo package manager (bun/npm/pnpm), lockfile location, EAS profiles in eas.json, and any app.config.ts variant env vars (e.g. APP_VARIANT, SECRETS_ENV, APS_ENVIRONMENT).
41
+
42
+ 2. Ensure the Expo app is ready for fingerprint-based updates:
43
+ - runtimeVersion: { policy: 'fingerprint' } in app config
44
+ - expo-updates installed and configured
45
+ - channel set on each EAS build profile we deploy
46
+ - submit config in eas.json if we use auto-submit (iOS ascAppId, Android serviceAccountKeyPath)
47
+
48
+ 3. Add .github/workflows/expo-release.yml using the HansKristoffer/expo-release@v2 composite action. Adapt consumer-specific values:
49
+ - paths filter: Expo app dir, shared packages that affect the native bundle, and lockfile if native deps should trigger a release
50
+ - working-directory and impact-paths pointing at the Expo app
51
+ - profile, environment, and channel for each deploy target
52
+ - env vars required by app.config.ts on the action step (APP_VARIANT, etc.)
53
+ - workflow_dispatch inputs for force-native, retry-ota, republish, rollback-embedded
54
+ - fetch-depth: 0 on checkout, Bun/Node setup, dependency install, expo/expo-github-action with pinned eas-version and EXPO_TOKEN secret
55
+
56
+ 4. Document that EXPO_TOKEN must be added as a GitHub secret, and that the first run on a new fingerprint always queues a native build.
57
+
58
+ 5. If we have staging + production (or multiple profiles), use separate jobs or a matrix with profile-specific concurrency groups.
59
+
60
+ 6. Do not change unrelated CI. Keep the diff minimal and match existing workflow conventions in this repo.
61
+
62
+ Reference the upstream README for full input/output docs and recovery operations. Ask me only if a required value (Expo app path, deploy branch, profile names) cannot be inferred from the repo.
63
+ ```
64
+
20
65
  ## Install
21
66
 
22
- Use the composite action. The package is not published to npm.
67
+ ### GitHub Action
23
68
 
24
69
  ```yaml
25
70
  uses: HansKristoffer/expo-release@v2
26
71
  ```
27
72
 
28
- The action runs `bun ${{ github.action_path }}/src/cli.ts`. The caller must already have checked out the repo (`fetch-depth: 0`), installed Bun, set up EAS, and installed workspace dependencies.
73
+ The action installs its own dependencies (`hanzio`, `zod`) in `${{ github.action_path }}`, then runs `bun ${{ github.action_path }}/src/cli.ts`. The caller must already have checked out the repo (`fetch-depth: 0`), installed Bun, set up EAS, and installed workspace dependencies.
74
+
75
+ ### npm (CLI)
76
+
77
+ For local use or custom CI:
78
+
79
+ ```bash
80
+ bun add -D expo-release
81
+ ```
82
+
83
+ Then run `expo-release` (see [CLI](#cli)).
29
84
 
30
85
  Pin `eas-cli` with `expo/expo-github-action`. The CLI invokes `eas` from `PATH` (override with `EAS_BIN` for local tests), so that pin is the version that actually runs.
31
86
 
@@ -176,6 +231,7 @@ A manual `deploy` from `workflow_dispatch` ignores the impact diff. That is inte
176
231
  | `pre-update` | _(empty)_ | Shell command before `eas update` (OTA only) |
177
232
  | `post-update` | _(empty)_ | Shell command after a successful `eas update` (OTA only) |
178
233
  | `update-extra-args` | _(empty)_ | Extra tokens appended to `eas update` |
234
+ | `export-source-maps` | `false` | OTA only: `expo export --source-maps external`, refuse if `dist` has no `.map`, then `eas update --input-dir dist --skip-bundler`. Pair with `post-update` to upload those maps for PostHog error tracking. |
179
235
  | `group` | _(empty)_ | Required when `operation` is `republish` |
180
236
  | `runtime-version` | current fingerprint | Used when `operation` is `rollback-embedded` |
181
237
  | `message` | `{profile} {sha} (fp {hash})` | Override the generated `eas update` message |
@@ -224,7 +280,9 @@ bun "${{ github.action_path }}/src/cli.ts" decide \
224
280
 
225
281
  `pre-update`, `post-update`, and `update-extra-args` run only on the OTA path (`deploy` + `ota`, and `retry-ota`). Skip, native builds, republish, and rollback ignore them. Hook commands inherit process env. A non-zero hook exit fails operate; `post-update` does not run if `eas update` fails.
226
282
 
227
- Production apps that export Hermes maps first set them only on the production operate step:
283
+ `export-source-maps` is the shared Hermes export. Set it only on profiles that should publish a pre-exported `dist` (usually production). Staging stays a default `eas update`.
284
+
285
+ Use it with PostHog error tracking: the same `dist` bundle that goes to `eas update` contains the Hermes `.map` files PostHog needs to symbolicate production crashes. Upload them in `post-update` after a successful OTA (`POSTHOG_CLI_API_KEY`, `POSTHOG_CLI_PROJECT_ID`, and `POSTHOG_CLI_HOST` must reach the action step):
228
286
 
229
287
  ```yaml
230
288
  - uses: HansKristoffer/expo-release@v2
@@ -232,8 +290,7 @@ Production apps that export Hermes maps first set them only on the production op
232
290
  command: release
233
291
  profile: production
234
292
  environment: production
235
- pre-update: bun scripts/release/export-hermes.ts
236
- update-extra-args: --input-dir dist --skip-bundler
293
+ export-source-maps: 'true'
237
294
  post-update: bunx posthog-cli hermes upload --directory dist
238
295
  env:
239
296
  APP_VARIANT: production
@@ -241,7 +298,7 @@ Production apps that export Hermes maps first set them only on the production op
241
298
  APS_ENVIRONMENT: production
242
299
  ```
243
300
 
244
- `export-hermes.ts` is app-owned (typically `expo export --source-maps external` plus a Hermes `.map` check). Staging stays a default `eas update`.
301
+ Override the Expo binary with `EXPO_BIN` (defaults to `bunx expo`). `--dry-run` prints the export command and skips the `.map` check.
245
302
 
246
303
  ### Recovery operations
247
304
 
@@ -255,7 +312,7 @@ Production apps that export Hermes maps first set them only on the production op
255
312
 
256
313
  ### Dry run
257
314
 
258
- `--dry-run` still runs `git diff`, `eas fingerprint:generate`, and `eas build:list` so the decision is real. It only prints `eas update`, `eas build`, recovery commands, and OTA hooks.
315
+ `--dry-run` still runs `git diff`, `eas fingerprint:generate`, and `eas build:list` so the decision is real. It only prints `eas update`, `eas build`, recovery commands, `expo export` (when `export-source-maps` is on), and OTA hooks.
259
316
 
260
317
  ```yaml
261
318
  - uses: HansKristoffer/expo-release@v2
@@ -362,14 +419,215 @@ expo-release operate \
362
419
  [--group <update-group-id>] \
363
420
  [--runtime-version <hash>] \
364
421
  [--message "custom message"] \
365
- [--pre-update "bun scripts/release/export-hermes.ts"] \
366
- [--update-extra-args "--input-dir dist --skip-bundler"] \
422
+ [--export-source-maps true] \
423
+ [--pre-update "bun scripts/release/tag-dist.ts"] \
424
+ [--update-extra-args "--private-key-path key"] \
367
425
  [--post-update "bunx posthog-cli hermes upload --directory dist"]
368
426
  ```
369
427
 
428
+ Run `expo-release <command> --help` for every flag. Each flag also reads `EXPO_RELEASE_<FLAG>` (for example `EXPO_RELEASE_IMPACT_PATHS`) when it is not passed; an empty value counts as unset.
429
+
370
430
  `release` is decide + operate (same as the action default). `force-native`, `republish`, and `rollback-embedded` skip decide. `decide` prints JSON including `deployType` and `nativeBuildPending`. When `GITHUB_OUTPUT` is set, decide writes `deploy_type`, `native_build_pending`, `changed`, `fingerprint`, `build_id`, `build_url`. Operate writes `result`.
371
431
 
372
432
  ```bash
373
- # local
374
- bun src/cli.ts decide --base HEAD^ --head HEAD --profile production
433
+ # local (after bun add -D expo-release)
434
+ expo-release decide --base HEAD^ --head HEAD --profile production
435
+ ```
436
+
437
+ ## App runtime
438
+
439
+ `expo-release/runtime` has the app side of a release: OTA prompts, push notifications and a render-error boundary. Install the package as a dependency (not `-D`) to use it. `react`, `expo-updates`, `expo-notifications` and `expo-device` are optional peers; the CLI never needs them. The helpers render no UI: pass your toast, dialog and error screen.
440
+
441
+ ### OTA updates
442
+
443
+ Checks once per launch, downloads a new update and hands you a `reload` for your "Restart" prompt. It is off in development and wherever expo-updates is disabled (Expo Go).
444
+
445
+ ```tsx
446
+ import { useOTAUpdates } from 'expo-release/runtime'
447
+
448
+ function AppContent() {
449
+ const { toast } = useToast()
450
+ useOTAUpdates((reload) =>
451
+ toast.show({
452
+ id: 'ota-update-available',
453
+ label: 'Update ready',
454
+ description: 'A new version downloaded. Tap Restart to apply.',
455
+ actionLabel: 'Restart',
456
+ onActionPress: reload
457
+ })
458
+ )
459
+ }
375
460
  ```
461
+
462
+ The callback is read through a ref, so an unstable toast function never repeats the check. `checkForOTAUpdate(onUpdateReady)` runs the same check outside React.
463
+
464
+ ### Push notifications
465
+
466
+ Create the service once. It sets the foreground handler and the Android `default` channel, and owns the permission flow and the Expo push token. Your backend call is `registerDevice` (`null` unregisters).
467
+
468
+ ```ts
469
+ import AsyncStorage from '@react-native-async-storage/async-storage'
470
+ import { Alert } from 'react-native'
471
+ import { createPushNotifications } from 'expo-release/runtime'
472
+
473
+ export const push = createPushNotifications({
474
+ storage: AsyncStorage,
475
+ registerDevice: (expoPushToken) =>
476
+ api.user.registerDevice({ platform: Platform.OS, expoPushToken }),
477
+ // Optional soft-ask before the OS dialog.
478
+ confirm: () =>
479
+ new Promise((resolve) =>
480
+ Alert.alert('Turn on alerts?', 'Get notified about new messages.', [
481
+ { text: 'Not now', style: 'cancel', onPress: () => resolve(false) },
482
+ { text: 'Enable', onPress: () => resolve(true) }
483
+ ])
484
+ ),
485
+ onUnavailable: (reason) => {
486
+ if (reason === 'denied') Alert.alert('Notifications off', 'Turn them on in device settings.')
487
+ },
488
+ onError: (message, error) => logger.error(message, error)
489
+ })
490
+ ```
491
+
492
+ | Member | Use |
493
+ |--------|-----|
494
+ | `prompt()` | After sign-in. Asks once per install (soft-ask, then OS dialog), registers the token on a grant. Never throws; concurrent calls share one dialog. |
495
+ | `enable()` / `disable()` | Settings toggle. `enable` requests permission and registers; `disable` calls `registerDevice(null)`. Save the user's preference yourself, before `enable()`: a denied dialog then leaves the preference on with `isEnabled` false, and the token registers on the next mount once permission is granted in device settings. |
496
+ | `syncToken()` | Re-registers the current token when permission is granted. |
497
+ | `listen({ onReceived, onResponse })` | Subscribes and returns the unsubscribe function. `onResponse` also receives the tap that launched the app. |
498
+ | `getPermissionStatus()`, `getPushToken()`, `requestPermissions()`, `hasPrompted()` | Lower-level steps. |
499
+
500
+ `usePushNotifications(push, { enabled })` gives a settings screen `permission`, `isEnabled`, `isLoading` and wrapped `prompt`/`enable`/`disable`. `enabled` is the saved preference; while it is on, the token is re-registered on mount.
501
+
502
+ ### Error boundary
503
+
504
+ ```tsx
505
+ import { ErrorBoundary } from 'expo-release/runtime'
506
+
507
+ <ErrorBoundary
508
+ fallback={({ error, reset }) => <ErrorScreen message={error.message} onRetry={reset} />}
509
+ onError={(error, info) =>
510
+ posthog.captureException(error, { $exception_source: 'error_boundary', component_stack: info.componentStack })
511
+ }
512
+ >
513
+ <App />
514
+ </ErrorBoundary>
515
+ ```
516
+
517
+ Report errors in `onError`: render errors are caught before they become uncaught exceptions, so error-tracking autocapture never sees them.
518
+
519
+ ## Workspace and PR actions
520
+
521
+ Composite actions for Bun monorepos, used as `HansKristoffer/expo-release/.github/actions/<name>@v2`. Pass secrets as inputs.
522
+
523
+ | Action | Does |
524
+ |--------|------|
525
+ | `setup-workspace` | Bun (version from `package.json` `packageManager` by default), optional Node, restore the dependency cache keyed on `bun.lock`, always `bun install --frozen-lockfile`, save on a miss with `save-cache: 'true'`. `cache-paths` and `cache-key-files` are inputs. |
526
+ | `playwright-browsers` | Restores browsers for the installed Playwright version; installs and saves on a miss. |
527
+ | `pr-preview` | Railway preview environment per PR with its own PlanetScale Postgres branch and a Cloudflare-managed host. Creates on open/sync, deletes on close. |
528
+ | `ai-changelog` | Plain-language changelog of a pushed range with Claude; outputs `message` (chat) and `markdown`, optionally POSTs the message to a webhook. |
529
+
530
+ ### Warm the caches on main
531
+
532
+ Caches written by `pull_request` runs are scoped to that PR. Warm them on main so every new PR starts from a hit:
533
+
534
+ ```yaml
535
+ name: Cache Warm
536
+ on:
537
+ push:
538
+ branches: [main]
539
+ workflow_dispatch:
540
+ concurrency:
541
+ group: cache-warm
542
+ cancel-in-progress: true
543
+ permissions:
544
+ contents: read
545
+ jobs:
546
+ warm:
547
+ runs-on: ubuntu-latest
548
+ timeout-minutes: 20
549
+ steps:
550
+ - uses: actions/checkout@v5
551
+ - uses: HansKristoffer/expo-release/.github/actions/setup-workspace@v2
552
+ with:
553
+ save-cache: 'true'
554
+ cache-paths: | # consumer: add generated clients
555
+ node_modules
556
+ packages/*/node_modules
557
+ apps/*/node_modules
558
+ - uses: HansKristoffer/expo-release/.github/actions/playwright-browsers@v2
559
+ with:
560
+ working-directory: apps/platform
561
+ ```
562
+
563
+ Use the same `cache-paths` in every job, since they are part of the cache version.
564
+
565
+ ### PR preview environments
566
+
567
+ The preview duplicates `railway-source-environment` (default `staging`) into `pr-<number>`, points git-connected services at the PR branch, sets `DATABASE_URL` and `DATABASE_URL_DIRECT` to a fresh PlanetScale branch, attaches `pr-<number>.<zone>` with Cloudflare DNS, then sets `BASE_URL`, which triggers the one deployment (run migrations and seeds in your pre-deploy step).
568
+
569
+ ```yaml
570
+ name: PR preview
571
+ on:
572
+ pull_request:
573
+ types: [opened, reopened, synchronize, closed]
574
+ concurrency:
575
+ group: pr-preview-${{ github.event.number }}
576
+ cancel-in-progress: false
577
+ permissions:
578
+ contents: read
579
+ jobs:
580
+ preview:
581
+ # Fork PRs cannot read the secrets.
582
+ if: github.event.pull_request.head.repo.full_name == github.repository
583
+ runs-on: ubuntu-latest
584
+ timeout-minutes: 25
585
+ steps:
586
+ - uses: HansKristoffer/expo-release/.github/actions/pr-preview@v2
587
+ with:
588
+ planetscale-org: ${{ secrets.PLANETSCALE_ORG_NAME }}
589
+ planetscale-database: my-app-staging
590
+ planetscale-service-token-id: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_ID }}
591
+ planetscale-service-token: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
592
+ railway-token: ${{ secrets.RAILWAY_STAGING_TOKEN }}
593
+ railway-project-id: ${{ secrets.RAILWAY_STAGING_PROJECT_ID }}
594
+ railway-service: ${{ secrets.RAILWAY_STAGING_SERVICE }}
595
+ cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
596
+ cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
597
+ cloudflare-zone-name: previews.example.com
598
+ ```
599
+
600
+ The action needs Cloudflare DNS for the preview host; a Railway-generated domain is not supported yet. Requirements: a PlanetScale **Postgres** database with a service token scoped to it (`read_branch`, `create_branch`, `delete_branch`, `create_branch_password`); a Railway account API token; a Cloudflare token with Zone.DNS Edit and the zone's SSL/TLS mode set to Full. Extensions such as `vector` must be enabled on the source branch.
601
+
602
+ ### AI changelog
603
+
604
+ ```yaml
605
+ name: Changelog
606
+ on:
607
+ push:
608
+ branches: [main]
609
+ jobs:
610
+ changelog:
611
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
612
+ runs-on: ubuntu-latest
613
+ timeout-minutes: 15
614
+ permissions:
615
+ contents: read
616
+ pull-requests: read
617
+ steps:
618
+ - uses: actions/checkout@v5
619
+ with:
620
+ fetch-depth: 0
621
+ - uses: HansKristoffer/expo-release/.github/actions/ai-changelog@v2
622
+ with:
623
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
624
+ webhook-url: ${{ secrets.CHANGELOG_WEBHOOK_URL }}
625
+ webhook-token: ${{ secrets.CHANGELOG_WEBHOOK_TOKEN }}
626
+ webhook-payload: '{"groupId": "${{ secrets.CHANGELOG_GROUP_ID }}"}'
627
+ ```
628
+
629
+ Without `webhook-url`, read `steps.<id>.outputs.message` and `outputs.markdown` yourself. `chat-format` sets the message style (WhatsApp by default) and `instructions` adds audience or product context.
630
+
631
+ ## Releasing
632
+
633
+ Squash PRs with conventional titles (`fix:`, `feat:`, or `feat!:`). Release Please keeps the version and changelog in a release PR; merge that PR to publish with release notes and npm provenance. See [release and recovery instructions](docs/releasing.md).
package/action.yml CHANGED
@@ -63,6 +63,10 @@ inputs:
63
63
  description: Extra tokens appended to eas update (e.g. --input-dir dist --skip-bundler)
64
64
  required: false
65
65
  default: ''
66
+ export-source-maps:
67
+ description: On OTA, expo export with external source maps, then publish dist
68
+ required: false
69
+ default: 'false'
66
70
  group:
67
71
  description: Update group ID required by operation=republish
68
72
  required: false
@@ -103,6 +107,12 @@ outputs:
103
107
  runs:
104
108
  using: composite
105
109
  steps:
110
+ # The CLI imports hanzio and zod; the action checkout has no node_modules.
111
+ - name: Install expo-release dependencies
112
+ working-directory: ${{ github.action_path }}
113
+ shell: bash
114
+ run: bun install --production --frozen-lockfile --omit peer
115
+
106
116
  - name: Expo release
107
117
  id: release
108
118
  working-directory: ${{ inputs.working-directory }}
@@ -123,6 +133,7 @@ runs:
123
133
  EXPO_RELEASE_PRE_UPDATE: ${{ inputs.pre-update }}
124
134
  EXPO_RELEASE_POST_UPDATE: ${{ inputs.post-update }}
125
135
  EXPO_RELEASE_UPDATE_EXTRA_ARGS: ${{ inputs.update-extra-args }}
136
+ EXPO_RELEASE_EXPORT_SOURCE_MAPS: ${{ inputs.export-source-maps }}
126
137
  EXPO_RELEASE_GROUP: ${{ inputs.group }}
127
138
  EXPO_RELEASE_RUNTIME_VERSION: ${{ inputs.runtime-version }}
128
139
  EXPO_RELEASE_MESSAGE: ${{ inputs.message }}
package/dist/args.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { CommandContext } from 'hanzio/cli';
2
+ import { z } from 'zod';
1
3
  export declare const DEFAULT_IMPACT_PATHS: readonly ['apps/expo/'];
2
4
  export type ReleasePlatform = 'ios' | 'android';
3
5
  export type CommandName = 'release' | 'decide' | 'operate';
@@ -19,20 +21,58 @@ export type ReleaseOptions = {
19
21
  preUpdate?: string;
20
22
  postUpdate?: string;
21
23
  updateExtraArgs?: string;
24
+ exportSourceMaps: boolean;
22
25
  group?: string;
23
26
  runtimeVersion?: string;
24
27
  message?: string;
25
28
  fingerprint?: string;
26
29
  dryRun: boolean;
27
30
  };
28
- export declare const USAGE = "Usage: expo-release <release|decide|operate> --profile <profile> [--base <sha>] [--head <sha>] [--platform ios|android] [--impact-paths apps/expo/] [--operation deploy|force-native|retry-ota|republish|rollback-embedded] [--deploy-type none|ota|native] [--channel <channel>] [--environment <env>] [--group <id>] [--runtime-version <hash>] [--message <msg>] [--fingerprint <hash>] [--auto-submit true] [--pre-update <cmd>] [--post-update <cmd>] [--update-extra-args <args>] [--dry-run] [--ignore-expo-impact true]";
29
- export declare function optionalString(value: string | undefined): string | undefined;
30
- export declare function parseImpactPaths(value: string | undefined): string[];
31
- export declare function parsePlatform(value: string | undefined): ReleasePlatform;
32
- export declare function parseBoolean(value: string | undefined, fallback: boolean): boolean;
33
- export declare function parseOperation(value: string | undefined): Operation;
34
- export declare function parseDeployType(value: string | undefined): DeployType;
35
- export declare function parseCommandName(value: string | undefined): CommandName;
31
+ export declare const releaseFlags: z.ZodObject<{
32
+ profile: z.ZodString;
33
+ base: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
34
+ head: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
35
+ platform: z.ZodDefault<z.ZodEnum<{
36
+ android: "android";
37
+ ios: "ios";
38
+ }>>;
39
+ impactPaths: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string[], string>>;
40
+ ignoreExpoImpact: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
41
+ false: "false";
42
+ true: "true";
43
+ }>>, z.ZodTransform<boolean, "false" | "true">>;
44
+ operation: z.ZodDefault<z.ZodEnum<{
45
+ deploy: "deploy";
46
+ "force-native": "force-native";
47
+ republish: "republish";
48
+ "retry-ota": "retry-ota";
49
+ "rollback-embedded": "rollback-embedded";
50
+ }>>;
51
+ deployType: z.ZodDefault<z.ZodEnum<{
52
+ native: "native";
53
+ none: "none";
54
+ ota: "ota";
55
+ }>>;
56
+ channel: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
57
+ environment: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
58
+ autoSubmit: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
59
+ false: "false";
60
+ true: "true";
61
+ }>>, z.ZodTransform<boolean, "false" | "true">>;
62
+ preUpdate: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
63
+ postUpdate: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
64
+ updateExtraArgs: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
65
+ exportSourceMaps: z.ZodPipe<z.ZodDefault<z.ZodEnum<{
66
+ false: "false";
67
+ true: "true";
68
+ }>>, z.ZodTransform<boolean, "false" | "true">>;
69
+ group: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
70
+ runtimeVersion: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
71
+ message: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
72
+ fingerprint: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
73
+ dryRun: z.ZodBoolean;
74
+ }, z.core.$strip>;
75
+ export type ReleaseFlags = z.output<typeof releaseFlags>;
76
+ export declare function toReleaseOptions(command: CommandName, { context: flags, env }: CommandContext<ReleaseFlags>): ReleaseOptions;
36
77
  export declare function needsDecide(operation: Operation): boolean;
37
78
  export declare function shouldIgnoreExpoImpact(options: Pick<ReleaseOptions, 'ignoreExpoImpact' | 'operation'>): boolean;
38
- export declare function parseReleaseOptions(args: string[], env?: NodeJS.ProcessEnv): ReleaseOptions;