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 +269 -11
- package/action.yml +11 -0
- package/dist/args.d.ts +49 -9
- package/dist/cli.js +178 -171
- package/dist/index.js +132 -134
- package/dist/operate.d.ts +2 -0
- package/dist/run.d.ts +4 -0
- package/dist/runtime/error-boundary.d.ts +27 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +214 -0
- package/dist/runtime/ota.d.ts +19 -0
- package/dist/runtime/push.d.ts +70 -0
- package/dist/source-maps.d.ts +8 -0
- package/package.json +45 -8
- package/src/args.ts +129 -250
- package/src/cli.ts +78 -41
- package/src/operate.ts +19 -1
- package/src/run.ts +33 -4
- package/src/runtime/error-boundary.ts +51 -0
- package/src/runtime/index.ts +19 -0
- package/src/runtime/ota.ts +54 -0
- package/src/runtime/push.ts +243 -0
- package/src/source-maps.ts +61 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
[--
|
|
366
|
-
[--update
|
|
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
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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;
|