expo-release 2.0.0
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/LICENSE +21 -0
- package/README.md +375 -0
- package/action.yml +162 -0
- package/dist/args.d.ts +38 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +599 -0
- package/dist/decide.d.ts +35 -0
- package/dist/github-output.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +525 -0
- package/dist/operate.d.ts +31 -0
- package/dist/run.d.ts +13 -0
- package/package.json +63 -0
- package/src/args.ts +318 -0
- package/src/cli.ts +97 -0
- package/src/decide.ts +293 -0
- package/src/github-output.ts +12 -0
- package/src/index.ts +19 -0
- package/src/operate.ts +236 -0
- package/src/run.ts +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kristoffer Hansen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# expo-release
|
|
2
|
+
|
|
3
|
+
Fingerprint-driven Expo releases: compare the current EAS fingerprint to **existing native builds**, then either publish an OTA or queue a native build (optionally auto-submitted to TestFlight or Play).
|
|
4
|
+
|
|
5
|
+
This is not “last OTA on the channel vs current fingerprint.” A new runtime needs a matching native binary before installs can receive OTAs for that fingerprint.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Push / dispatch
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
decide — git diff + eas fingerprint:generate + eas build:list
|
|
12
|
+
│
|
|
13
|
+
├─ no matching impact paths → skip
|
|
14
|
+
├─ finished native build with this fingerprint → eas update
|
|
15
|
+
├─ matching native build in progress → OTA only
|
|
16
|
+
├─ newest matching build errored → fail (use force-native after fixing)
|
|
17
|
+
└─ no matching native build → eas build --auto-submit --no-wait
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Use the composite action. The package is not published to npm.
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
uses: HansKristoffer/expo-release@v2
|
|
26
|
+
```
|
|
27
|
+
|
|
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.
|
|
29
|
+
|
|
30
|
+
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
|
+
|
|
32
|
+
## Setup checklist
|
|
33
|
+
|
|
34
|
+
- [ ] GitHub secret `EXPO_TOKEN` from https://expo.dev/settings/access-tokens
|
|
35
|
+
- [ ] `runtimeVersion: { policy: 'fingerprint' }` and `expo-updates` in the Expo app
|
|
36
|
+
- [ ] `eas.json` channel on each profile you deploy (production, staging, …)
|
|
37
|
+
- [ ] `eas.json` submit config when you use `--auto-submit`:
|
|
38
|
+
- iOS: `submit.<profile>.ios.ascAppId`
|
|
39
|
+
- Android: `submit.<profile>.android.serviceAccountKeyPath`
|
|
40
|
+
- [ ] EAS credentials (`eas credentials` from the Expo app directory)
|
|
41
|
+
- [ ] At least one finished native build **per profile and platform** on the current fingerprint. The first run against a new app always queues a native build.
|
|
42
|
+
- [ ] `--environment` on every OTA for Expo SDK 55+
|
|
43
|
+
- [ ] Add lockfiles (`bun.lock`, `package-lock.json`) to `impact-paths` if a dependency bump should trigger a fingerprint check. The default `apps/expo/` path does not include the workspace lockfile.
|
|
44
|
+
|
|
45
|
+
## GitHub Action
|
|
46
|
+
|
|
47
|
+
Copy this workflow into `.github/workflows/expo-release.yml`. Replace the marked consumer-specific bits.
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
# =============================================================================
|
|
51
|
+
# Expo Release
|
|
52
|
+
# =============================================================================
|
|
53
|
+
# Fingerprint decides OTA vs native store build. Push to main deploys production
|
|
54
|
+
# when Expo paths change. Manual dispatch can force a native build or recover
|
|
55
|
+
# an OTA.
|
|
56
|
+
# =============================================================================
|
|
57
|
+
|
|
58
|
+
name: Expo Release
|
|
59
|
+
|
|
60
|
+
on:
|
|
61
|
+
push:
|
|
62
|
+
branches: [main]
|
|
63
|
+
paths:
|
|
64
|
+
- apps/expo/** # consumer: also list shared packages if needed
|
|
65
|
+
- bun.lock # consumer: include if lockfile changes native deps
|
|
66
|
+
workflow_dispatch:
|
|
67
|
+
inputs:
|
|
68
|
+
operation:
|
|
69
|
+
description: Recovery/deployment operation
|
|
70
|
+
required: true
|
|
71
|
+
default: deploy
|
|
72
|
+
type: choice
|
|
73
|
+
options:
|
|
74
|
+
- deploy
|
|
75
|
+
- force-native
|
|
76
|
+
- retry-ota
|
|
77
|
+
- republish
|
|
78
|
+
- rollback-embedded
|
|
79
|
+
group:
|
|
80
|
+
description: Update group ID (required for republish)
|
|
81
|
+
required: false
|
|
82
|
+
type: string
|
|
83
|
+
runtime-version:
|
|
84
|
+
description: Runtime version (optional for rollback-embedded)
|
|
85
|
+
required: false
|
|
86
|
+
type: string
|
|
87
|
+
|
|
88
|
+
concurrency:
|
|
89
|
+
group: expo-release-production-${{ github.ref }}
|
|
90
|
+
cancel-in-progress: false
|
|
91
|
+
|
|
92
|
+
permissions:
|
|
93
|
+
contents: read
|
|
94
|
+
|
|
95
|
+
jobs:
|
|
96
|
+
release:
|
|
97
|
+
name: production
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
timeout-minutes: 45
|
|
100
|
+
env:
|
|
101
|
+
# consumer: only if app.config.ts requires public URLs at eval time
|
|
102
|
+
EXPO_PUBLIC_API_URL: https://example.com
|
|
103
|
+
EXPO_PUBLIC_PLATFORM_URL: https://example.com
|
|
104
|
+
steps:
|
|
105
|
+
- name: Checkout
|
|
106
|
+
uses: actions/checkout@v7
|
|
107
|
+
with:
|
|
108
|
+
fetch-depth: 0
|
|
109
|
+
|
|
110
|
+
- name: Setup Bun
|
|
111
|
+
uses: oven-sh/setup-bun@v2
|
|
112
|
+
with:
|
|
113
|
+
bun-version-file: .bun-version
|
|
114
|
+
|
|
115
|
+
- name: Setup Node
|
|
116
|
+
uses: actions/setup-node@v7
|
|
117
|
+
with:
|
|
118
|
+
node-version: 22.12.0
|
|
119
|
+
|
|
120
|
+
- name: Restore Bun workspace cache
|
|
121
|
+
uses: actions/cache@v6
|
|
122
|
+
with:
|
|
123
|
+
path: |
|
|
124
|
+
node_modules
|
|
125
|
+
packages/*/node_modules
|
|
126
|
+
apps/*/node_modules
|
|
127
|
+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
|
|
128
|
+
restore-keys: |
|
|
129
|
+
${{ runner.os }}-bun-
|
|
130
|
+
|
|
131
|
+
- name: Setup Expo and EAS
|
|
132
|
+
uses: expo/expo-github-action@v8
|
|
133
|
+
with:
|
|
134
|
+
eas-version: 16.32.0
|
|
135
|
+
token: ${{ secrets.EXPO_TOKEN }}
|
|
136
|
+
|
|
137
|
+
- name: Install dependencies
|
|
138
|
+
run: bun install --frozen-lockfile
|
|
139
|
+
|
|
140
|
+
- name: Expo release
|
|
141
|
+
uses: HansKristoffer/expo-release@v2
|
|
142
|
+
with:
|
|
143
|
+
command: release
|
|
144
|
+
profile: production
|
|
145
|
+
operation: ${{ github.event.inputs.operation || 'deploy' }}
|
|
146
|
+
environment: production
|
|
147
|
+
impact-paths: apps/expo/
|
|
148
|
+
working-directory: apps/expo
|
|
149
|
+
group: ${{ github.event.inputs.group }}
|
|
150
|
+
runtime-version: ${{ github.event.inputs.runtime-version }}
|
|
151
|
+
env:
|
|
152
|
+
# Set variant keys on the action step (or the job). They must reach
|
|
153
|
+
# fingerprint:generate, build:list, eas build, and eas update.
|
|
154
|
+
APP_VARIANT: production
|
|
155
|
+
SECRETS_ENV: production
|
|
156
|
+
APS_ENVIRONMENT: production
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A manual `deploy` from `workflow_dispatch` ignores the impact diff. That is intentional: dispatch after an unrelated commit should still release, not silently skip.
|
|
160
|
+
|
|
161
|
+
### Action inputs
|
|
162
|
+
|
|
163
|
+
| Input | Default | Notes |
|
|
164
|
+
|-------|---------|--------|
|
|
165
|
+
| `command` | `release` | `release` (decide + operate), `decide` (PR checks), or `operate` |
|
|
166
|
+
| `profile` | `production` | EAS build profile; default update channel |
|
|
167
|
+
| `operation` | `deploy` | See recovery operations below |
|
|
168
|
+
| `deploy-type` | `none` | Used only when `command` is `operate` |
|
|
169
|
+
| `base` / `head` | event before / `GITHUB_SHA` | Git range for the impact diff |
|
|
170
|
+
| `impact-paths` | `apps/expo/` | Comma-separated repo-root prefixes |
|
|
171
|
+
| `environment` | _(empty)_ | Passed to `eas update`. Required for Expo SDK 55+ |
|
|
172
|
+
| `channel` | same as `profile` | EAS Update channel |
|
|
173
|
+
| `platform` | `ios` | `ios` or `android`. Passed through to `eas update` and `eas build` |
|
|
174
|
+
| `auto-submit` | `true` | Native builds use `--auto-submit` |
|
|
175
|
+
| `working-directory` | `apps/expo` | Where `eas` runs |
|
|
176
|
+
| `pre-update` | _(empty)_ | Shell command before `eas update` (OTA only) |
|
|
177
|
+
| `post-update` | _(empty)_ | Shell command after a successful `eas update` (OTA only) |
|
|
178
|
+
| `update-extra-args` | _(empty)_ | Extra tokens appended to `eas update` |
|
|
179
|
+
| `group` | _(empty)_ | Required when `operation` is `republish` |
|
|
180
|
+
| `runtime-version` | current fingerprint | Used when `operation` is `rollback-embedded` |
|
|
181
|
+
| `message` | `{profile} {sha} (fp {hash})` | Override the generated `eas update` message |
|
|
182
|
+
| `dry-run` | `false` | Print mutating EAS commands without executing them |
|
|
183
|
+
|
|
184
|
+
### Action outputs
|
|
185
|
+
|
|
186
|
+
| Output | Notes |
|
|
187
|
+
|--------|--------|
|
|
188
|
+
| `deploy_type` | Stable PR-check type: `none`, `ota`, `native` |
|
|
189
|
+
| `native_build_pending` | `true` when a matching native build is still in progress |
|
|
190
|
+
| `fingerprint` | Current EAS fingerprint hash |
|
|
191
|
+
| `build_id` / `build_url` | Matching EAS build, if any |
|
|
192
|
+
| `result` | Operate result (`skipped`, `ota`, `native-build`, `recovery`) |
|
|
193
|
+
|
|
194
|
+
### PR checks (decide only)
|
|
195
|
+
|
|
196
|
+
PR jobs should not operate. Use `command: decide` and comment or gate on `deploy_type`:
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
- uses: HansKristoffer/expo-release@v2
|
|
200
|
+
id: decision
|
|
201
|
+
with:
|
|
202
|
+
command: decide
|
|
203
|
+
profile: pr-preview # or production
|
|
204
|
+
base: ${{ steps.merge-base.outputs.sha }}
|
|
205
|
+
head: ${{ github.sha }}
|
|
206
|
+
env:
|
|
207
|
+
APP_VARIANT: staging
|
|
208
|
+
SECRETS_ENV: staging
|
|
209
|
+
APS_ENVIRONMENT: production
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Then use `${{ steps.decision.outputs.deploy_type }}` (`none` / `ota` / `native`).
|
|
213
|
+
|
|
214
|
+
The CLI writes the same JSON to stdout, including `deployType`:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
bun "${{ github.action_path }}/src/cli.ts" decide \
|
|
218
|
+
--base "$BASE" --head "$HEAD" --profile production
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`Bun.spawn` inherits process env. Set variant keys on the **action step or the job**, never on decide alone. When `app.config.ts` switches slug or bundle identifier on `APP_VARIANT`, every EAS command (`fingerprint:generate`, `build:list`, `eas build`, `eas update`) resolves a different project from that config.
|
|
222
|
+
|
|
223
|
+
### OTA hooks
|
|
224
|
+
|
|
225
|
+
`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
|
+
|
|
227
|
+
Production apps that export Hermes maps first set them only on the production operate step:
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
- uses: HansKristoffer/expo-release@v2
|
|
231
|
+
with:
|
|
232
|
+
command: release
|
|
233
|
+
profile: production
|
|
234
|
+
environment: production
|
|
235
|
+
pre-update: bun scripts/release/export-hermes.ts
|
|
236
|
+
update-extra-args: --input-dir dist --skip-bundler
|
|
237
|
+
post-update: bunx posthog-cli hermes upload --directory dist
|
|
238
|
+
env:
|
|
239
|
+
APP_VARIANT: production
|
|
240
|
+
SECRETS_ENV: production
|
|
241
|
+
APS_ENVIRONMENT: production
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`export-hermes.ts` is app-owned (typically `expo export --source-maps external` plus a Hermes `.map` check). Staging stays a default `eas update`.
|
|
245
|
+
|
|
246
|
+
### Recovery operations
|
|
247
|
+
|
|
248
|
+
| Operation | Effect |
|
|
249
|
+
|-----------|--------|
|
|
250
|
+
| `deploy` | Same as a push: decide, then OTA or native build |
|
|
251
|
+
| `force-native` | Queue a native build (skip decide). Use this after fixing a failed fingerprint build. |
|
|
252
|
+
| `retry-ota` | Publish an OTA even if the diff has no impact paths |
|
|
253
|
+
| `republish` | `eas update:republish --group <id>`. Copy the group ID from the Expo dashboard. |
|
|
254
|
+
| `rollback-embedded` | `eas update:roll-back-to-embedded` with `--runtime-version` (defaults to the current fingerprint) |
|
|
255
|
+
|
|
256
|
+
### Dry run
|
|
257
|
+
|
|
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.
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
- uses: HansKristoffer/expo-release@v2
|
|
262
|
+
with:
|
|
263
|
+
command: release
|
|
264
|
+
profile: production
|
|
265
|
+
environment: production
|
|
266
|
+
dry-run: 'true'
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Multiple apps (staging and production)
|
|
270
|
+
|
|
271
|
+
Use one job per profile, or a matrix. Each profile needs its own finished native build before OTAs work. One `EXPO_TOKEN` covers multiple projects under the same Expo account; separate accounts or orgs need separate secrets.
|
|
272
|
+
|
|
273
|
+
Put the **profile in the concurrency group**. Two profiles deploying from the same ref should not serialize:
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
concurrency:
|
|
277
|
+
group: expo-release-${{ matrix.profile }}-${{ github.ref }}
|
|
278
|
+
cancel-in-progress: false
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Two jobs, one branch each
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
on:
|
|
285
|
+
push:
|
|
286
|
+
branches: [main, develop]
|
|
287
|
+
|
|
288
|
+
jobs:
|
|
289
|
+
production:
|
|
290
|
+
if: github.ref == 'refs/heads/main'
|
|
291
|
+
# ... setup steps ...
|
|
292
|
+
steps:
|
|
293
|
+
- uses: HansKristoffer/expo-release@v2
|
|
294
|
+
with:
|
|
295
|
+
profile: production
|
|
296
|
+
environment: production
|
|
297
|
+
channel: production
|
|
298
|
+
env:
|
|
299
|
+
APP_VARIANT: production
|
|
300
|
+
|
|
301
|
+
staging:
|
|
302
|
+
if: github.ref == 'refs/heads/develop'
|
|
303
|
+
# ... setup steps ...
|
|
304
|
+
steps:
|
|
305
|
+
- uses: HansKristoffer/expo-release@v2
|
|
306
|
+
with:
|
|
307
|
+
profile: staging
|
|
308
|
+
environment: staging
|
|
309
|
+
channel: staging
|
|
310
|
+
env:
|
|
311
|
+
APP_VARIANT: staging
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### One job, matrix
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
strategy:
|
|
318
|
+
fail-fast: false
|
|
319
|
+
matrix:
|
|
320
|
+
include:
|
|
321
|
+
- profile: production
|
|
322
|
+
environment: production
|
|
323
|
+
variant: production
|
|
324
|
+
- profile: staging
|
|
325
|
+
environment: staging
|
|
326
|
+
variant: staging
|
|
327
|
+
|
|
328
|
+
steps:
|
|
329
|
+
- uses: HansKristoffer/expo-release@v2
|
|
330
|
+
with:
|
|
331
|
+
profile: ${{ matrix.profile }}
|
|
332
|
+
environment: ${{ matrix.environment }}
|
|
333
|
+
channel: ${{ matrix.profile }}
|
|
334
|
+
env:
|
|
335
|
+
APP_VARIANT: ${{ matrix.variant }}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`channel` defaults to `profile`. If two profiles should publish to the same channel, set `channel` explicitly and keep `profile` as the EAS build profile.
|
|
339
|
+
|
|
340
|
+
## CLI
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
expo-release release \
|
|
344
|
+
--base <sha> --head <sha> --profile production \
|
|
345
|
+
[--platform ios] \
|
|
346
|
+
[--impact-paths apps/expo/,packages/conductor/] \
|
|
347
|
+
[--environment production] \
|
|
348
|
+
[--dry-run]
|
|
349
|
+
|
|
350
|
+
expo-release decide \
|
|
351
|
+
--base <sha> --head <sha> --profile production \
|
|
352
|
+
[--platform ios] \
|
|
353
|
+
[--impact-paths apps/expo/,packages/conductor/] \
|
|
354
|
+
[--ignore-expo-impact true]
|
|
355
|
+
|
|
356
|
+
expo-release operate \
|
|
357
|
+
--profile production \
|
|
358
|
+
--deploy-type <none|ota|native> \
|
|
359
|
+
--operation <deploy|force-native|retry-ota|republish|rollback-embedded> \
|
|
360
|
+
[--channel production] [--environment production] \
|
|
361
|
+
[--platform ios] [--auto-submit true] \
|
|
362
|
+
[--group <update-group-id>] \
|
|
363
|
+
[--runtime-version <hash>] \
|
|
364
|
+
[--message "custom message"] \
|
|
365
|
+
[--pre-update "bun scripts/release/export-hermes.ts"] \
|
|
366
|
+
[--update-extra-args "--input-dir dist --skip-bundler"] \
|
|
367
|
+
[--post-update "bunx posthog-cli hermes upload --directory dist"]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`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
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# local
|
|
374
|
+
bun src/cli.ts decide --base HEAD^ --head HEAD --profile production
|
|
375
|
+
```
|
package/action.yml
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
name: Expo Release
|
|
2
|
+
description: Fingerprint-driven EAS OTA or native store build
|
|
3
|
+
author: Kristoffer Hansen
|
|
4
|
+
|
|
5
|
+
inputs:
|
|
6
|
+
command:
|
|
7
|
+
description: release (decide + operate), decide only, or operate only
|
|
8
|
+
required: false
|
|
9
|
+
default: release
|
|
10
|
+
profile:
|
|
11
|
+
description: EAS build profile (also used as the default update channel)
|
|
12
|
+
required: false
|
|
13
|
+
default: production
|
|
14
|
+
operation:
|
|
15
|
+
description: deploy, force-native, retry-ota, republish, or rollback-embedded
|
|
16
|
+
required: false
|
|
17
|
+
default: deploy
|
|
18
|
+
deploy-type:
|
|
19
|
+
description: Decision type when command is operate (none, ota, native)
|
|
20
|
+
required: false
|
|
21
|
+
default: none
|
|
22
|
+
base:
|
|
23
|
+
description: Git base SHA for the impact diff. Defaults to github.event.before.
|
|
24
|
+
required: false
|
|
25
|
+
default: ''
|
|
26
|
+
head:
|
|
27
|
+
description: Git head SHA for the impact diff. Defaults to GITHUB_SHA.
|
|
28
|
+
required: false
|
|
29
|
+
default: ''
|
|
30
|
+
impact-paths:
|
|
31
|
+
description: Comma-separated repo-root paths that count as an Expo change
|
|
32
|
+
required: false
|
|
33
|
+
default: apps/expo/
|
|
34
|
+
environment:
|
|
35
|
+
description: EAS environment passed to eas update. Required for Expo SDK 55+.
|
|
36
|
+
required: false
|
|
37
|
+
default: ''
|
|
38
|
+
channel:
|
|
39
|
+
description: EAS Update channel. Defaults to the profile name.
|
|
40
|
+
required: false
|
|
41
|
+
default: ''
|
|
42
|
+
platform:
|
|
43
|
+
description: Native platform (ios or android)
|
|
44
|
+
required: false
|
|
45
|
+
default: ios
|
|
46
|
+
auto-submit:
|
|
47
|
+
description: Pass --auto-submit on native builds
|
|
48
|
+
required: false
|
|
49
|
+
default: 'true'
|
|
50
|
+
working-directory:
|
|
51
|
+
description: Directory to run EAS commands from
|
|
52
|
+
required: false
|
|
53
|
+
default: apps/expo
|
|
54
|
+
pre-update:
|
|
55
|
+
description: Shell command to run before eas update (OTA path only)
|
|
56
|
+
required: false
|
|
57
|
+
default: ''
|
|
58
|
+
post-update:
|
|
59
|
+
description: Shell command to run after a successful eas update (OTA path only)
|
|
60
|
+
required: false
|
|
61
|
+
default: ''
|
|
62
|
+
update-extra-args:
|
|
63
|
+
description: Extra tokens appended to eas update (e.g. --input-dir dist --skip-bundler)
|
|
64
|
+
required: false
|
|
65
|
+
default: ''
|
|
66
|
+
group:
|
|
67
|
+
description: Update group ID required by operation=republish
|
|
68
|
+
required: false
|
|
69
|
+
default: ''
|
|
70
|
+
runtime-version:
|
|
71
|
+
description: Runtime version for rollback-embedded. Defaults to the current fingerprint.
|
|
72
|
+
required: false
|
|
73
|
+
default: ''
|
|
74
|
+
message:
|
|
75
|
+
description: Override the generated eas update message
|
|
76
|
+
required: false
|
|
77
|
+
default: ''
|
|
78
|
+
dry-run:
|
|
79
|
+
description: Print mutating EAS commands without executing them
|
|
80
|
+
required: false
|
|
81
|
+
default: 'false'
|
|
82
|
+
|
|
83
|
+
outputs:
|
|
84
|
+
deploy_type:
|
|
85
|
+
description: Stable PR-check type (none, ota, native)
|
|
86
|
+
value: ${{ steps.release.outputs.deploy_type }}
|
|
87
|
+
native_build_pending:
|
|
88
|
+
description: Whether a matching native build is still in progress
|
|
89
|
+
value: ${{ steps.release.outputs.native_build_pending }}
|
|
90
|
+
fingerprint:
|
|
91
|
+
description: Current EAS fingerprint hash
|
|
92
|
+
value: ${{ steps.release.outputs.fingerprint }}
|
|
93
|
+
build_id:
|
|
94
|
+
description: Matching EAS build id, if any
|
|
95
|
+
value: ${{ steps.release.outputs.build_id }}
|
|
96
|
+
build_url:
|
|
97
|
+
description: Matching EAS build URL, if any
|
|
98
|
+
value: ${{ steps.release.outputs.build_url }}
|
|
99
|
+
result:
|
|
100
|
+
description: Operate result (skipped, ota, native-build, recovery)
|
|
101
|
+
value: ${{ steps.release.outputs.result }}
|
|
102
|
+
|
|
103
|
+
runs:
|
|
104
|
+
using: composite
|
|
105
|
+
steps:
|
|
106
|
+
- name: Expo release
|
|
107
|
+
id: release
|
|
108
|
+
working-directory: ${{ inputs.working-directory }}
|
|
109
|
+
shell: bash
|
|
110
|
+
env:
|
|
111
|
+
ACTION_PATH: ${{ github.action_path }}
|
|
112
|
+
EXPO_RELEASE_COMMAND: ${{ inputs.command }}
|
|
113
|
+
EXPO_RELEASE_PROFILE: ${{ inputs.profile }}
|
|
114
|
+
EXPO_RELEASE_OPERATION: ${{ inputs.operation }}
|
|
115
|
+
EXPO_RELEASE_DEPLOY_TYPE: ${{ inputs.deploy-type }}
|
|
116
|
+
EXPO_RELEASE_BASE: ${{ inputs.base }}
|
|
117
|
+
EXPO_RELEASE_HEAD: ${{ inputs.head }}
|
|
118
|
+
EXPO_RELEASE_IMPACT_PATHS: ${{ inputs.impact-paths }}
|
|
119
|
+
EXPO_RELEASE_ENVIRONMENT: ${{ inputs.environment }}
|
|
120
|
+
EXPO_RELEASE_CHANNEL: ${{ inputs.channel }}
|
|
121
|
+
EXPO_RELEASE_PLATFORM: ${{ inputs.platform }}
|
|
122
|
+
EXPO_RELEASE_AUTO_SUBMIT: ${{ inputs.auto-submit }}
|
|
123
|
+
EXPO_RELEASE_PRE_UPDATE: ${{ inputs.pre-update }}
|
|
124
|
+
EXPO_RELEASE_POST_UPDATE: ${{ inputs.post-update }}
|
|
125
|
+
EXPO_RELEASE_UPDATE_EXTRA_ARGS: ${{ inputs.update-extra-args }}
|
|
126
|
+
EXPO_RELEASE_GROUP: ${{ inputs.group }}
|
|
127
|
+
EXPO_RELEASE_RUNTIME_VERSION: ${{ inputs.runtime-version }}
|
|
128
|
+
EXPO_RELEASE_MESSAGE: ${{ inputs.message }}
|
|
129
|
+
EXPO_RELEASE_DRY_RUN: ${{ inputs.dry-run }}
|
|
130
|
+
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
|
|
131
|
+
run: bun "$ACTION_PATH/src/cli.ts"
|
|
132
|
+
|
|
133
|
+
- name: Publish job summary
|
|
134
|
+
if: always()
|
|
135
|
+
shell: bash
|
|
136
|
+
env:
|
|
137
|
+
COMMAND: ${{ inputs.command }}
|
|
138
|
+
OPERATION: ${{ inputs.operation }}
|
|
139
|
+
PROFILE: ${{ inputs.profile }}
|
|
140
|
+
PLATFORM: ${{ inputs.platform }}
|
|
141
|
+
DEPLOY_TYPE: ${{ steps.release.outputs.deploy_type }}
|
|
142
|
+
RESULT: ${{ steps.release.outputs.result || 'did not run' }}
|
|
143
|
+
FINGERPRINT: ${{ steps.release.outputs.fingerprint }}
|
|
144
|
+
MATCHING_BUILD: ${{ steps.release.outputs.build_url || steps.release.outputs.build_id }}
|
|
145
|
+
NATIVE_PENDING: ${{ steps.release.outputs.native_build_pending }}
|
|
146
|
+
run: |
|
|
147
|
+
{
|
|
148
|
+
echo "## ${PROFILE} ${PLATFORM} ${COMMAND}"
|
|
149
|
+
echo ""
|
|
150
|
+
echo "- Command: \`$COMMAND\`"
|
|
151
|
+
echo "- Operation: \`$OPERATION\`"
|
|
152
|
+
echo "- Result: \`$RESULT\`"
|
|
153
|
+
if [[ -n "$DEPLOY_TYPE" ]]; then
|
|
154
|
+
echo "- Deploy type: \`$DEPLOY_TYPE\`"
|
|
155
|
+
echo "- Native build pending: \`${NATIVE_PENDING:-false}\`"
|
|
156
|
+
echo "- Fingerprint: \`${FINGERPRINT:-none}\`"
|
|
157
|
+
echo "- Matching build: ${MATCHING_BUILD:-none}"
|
|
158
|
+
fi
|
|
159
|
+
if [[ "$COMMAND" == "release" ]]; then
|
|
160
|
+
echo "- New native builds are submitted automatically when auto-submit is enabled (TestFlight on iOS, Play on Android)."
|
|
161
|
+
fi
|
|
162
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare const DEFAULT_IMPACT_PATHS: readonly ['apps/expo/'];
|
|
2
|
+
export type ReleasePlatform = 'ios' | 'android';
|
|
3
|
+
export type CommandName = 'release' | 'decide' | 'operate';
|
|
4
|
+
export type Operation = 'deploy' | 'force-native' | 'retry-ota' | 'republish' | 'rollback-embedded';
|
|
5
|
+
export type DeployType = 'none' | 'ota' | 'native';
|
|
6
|
+
export type ReleaseOptions = {
|
|
7
|
+
command: CommandName;
|
|
8
|
+
profile: string;
|
|
9
|
+
base?: string;
|
|
10
|
+
head?: string;
|
|
11
|
+
platform: ReleasePlatform;
|
|
12
|
+
impactPaths: string[];
|
|
13
|
+
ignoreExpoImpact: boolean;
|
|
14
|
+
operation: Operation;
|
|
15
|
+
deployType: DeployType;
|
|
16
|
+
channel: string;
|
|
17
|
+
environment?: string;
|
|
18
|
+
autoSubmit: boolean;
|
|
19
|
+
preUpdate?: string;
|
|
20
|
+
postUpdate?: string;
|
|
21
|
+
updateExtraArgs?: string;
|
|
22
|
+
group?: string;
|
|
23
|
+
runtimeVersion?: string;
|
|
24
|
+
message?: string;
|
|
25
|
+
fingerprint?: string;
|
|
26
|
+
dryRun: boolean;
|
|
27
|
+
};
|
|
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;
|
|
36
|
+
export declare function needsDecide(operation: Operation): boolean;
|
|
37
|
+
export declare function shouldIgnoreExpoImpact(options: Pick<ReleaseOptions, 'ignoreExpoImpact' | 'operation'>): boolean;
|
|
38
|
+
export declare function parseReleaseOptions(args: string[], env?: NodeJS.ProcessEnv): ReleaseOptions;
|
package/dist/cli.d.ts
ADDED