Compare commits

...
11 Commits
Author SHA1 Message Date
Expo CI 5ba16dbc67 v16.21.0 2025-10-13 15:31:06 +00:00
Phil Pluckthun 896b456dd3 [eas-cli] Pass _expo/.routes.json for SSG exports as routes.json to Hosting deployments (#3211)
* Derive `routes.json` payload from static export `_expo/.routes.json`

* Add CHANGELOG entry
2025-10-10 15:22:32 +01:00
Kadi Kraman f2c0202f06 Add tests for the env commands (#3217) 2025-10-09 15:18:43 +01:00
Expo CI 497bac1cbf update CHANGELOG.md 2025-10-08 05:36:29 +00:00
Expo CI 5b9734b60b v16.20.4 2025-10-08 05:25:57 +00:00
Kadi Kraman 3ac70d8977 Switch to lowercase in exec and pull (#3216) 2025-10-08 06:23:06 +01:00
Douglas Lowder 32adc788d0 Support workflow:run from a Git ref (#3203)
# Why

Add a `--ref` flag so that `eas workflow:run` can initiate a workflow run from an existing ref in the project's GitHub repository.

- Avoids the need to upload the entire project from a local machine
- Ensures that the workflow starts from a known reference point in the project's source control, with no unexpected local changes

# How

- If the ref flag is set, the run command will ensure that the ref is valid, and that the workflow can be used for that ref (using the new mutation for this from https://github.com/expo/universe/pull/22132)
- If the ref flag is set, the run command starts the run from that ref (using https://github.com/expo/universe/pull/20161)
- If the user chooses ref = HEAD, `git rev-parse` is called to convert that to a commit
- The flow is designed to work the same for running from GitHub ref as running from local, so inputs and waiting for results are supported 

# Test Plan

- Tested in an existing project with workflows with and without inputs
- CI should pass
2025-10-07 15:06:24 -07:00
Expo CI e465ed57cb update CHANGELOG.md 2025-10-07 20:04:20 +00:00
Expo CI df819fc232 v16.20.3 2025-10-07 18:42:28 +00:00
Kadi Kraman 539e201972 Lower instead of uppercase environment (#3213) 2025-10-07 19:39:06 +01:00
Expo CI c14b3e179b update CHANGELOG.md 2025-10-07 15:23:38 +00:00
27 changed files with 1173 additions and 193 deletions

No files matched your search

+24
View File
@@ -8,9 +8,33 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
- Support `_expo/.routes.json` metadata file exported by `@expo/cli` for headers configuration on static deployments ([#3211](https://github.com/expo/eas-cli/pull/3211) by [@kitten](https://github.com/kitten))
### 🐛 Bug fixes
### 🧹 Chores
- Add thorough tests for the env commands ([#3217](https://github.com/expo/eas-cli/pull/3217) by [@kadikraman](https://github.com/kadikraman))
## [16.20.4](https://github.com/expo/eas-cli/releases/tag/v16.20.4) - 2025-10-08
### 🎉 New features
- Allow workflow:run to run a workflow from a GitHub ref. ([#3203](https://github.com/expo/eas-cli/pull/3203) by [@douglowder](https://github.com/douglowder))
### 🐛 Bug fixes
- Fix --environment flag in eas exec and pull ([#3216](https://github.com/expo/eas-cli/pull/3216) by [@kadikraman](https://github.com/kadikraman))
## [16.20.3](https://github.com/expo/eas-cli/releases/tag/v16.20.3) - 2025-10-07
### 🐛 Bug fixes
- Fix --environment flag in eas env commands ([#3213](https://github.com/expo/eas-cli/pull/3213) by [@kadikraman](https://github.com/kadikraman))
## [16.20.2](https://github.com/expo/eas-cli/releases/tag/v16.20.2) - 2025-10-07
### 🧹 Chores
- Create an `EnvironmentVariableEnvironment` enum and use it instead of the graphQL Enum ([#3208](https://github.com/expo/eas-cli/pull/3208) by [@kadikraman](https://github.com/kadikraman))
## [16.20.1](https://github.com/expo/eas-cli/releases/tag/v16.20.1) - 2025-10-03
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "16.20.2",
"version": "16.21.0",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+96 -88
View File
@@ -173,7 +173,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/account/login.ts)_
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -190,7 +190,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -207,7 +207,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/account/view.ts)_
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -221,7 +221,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -273,7 +273,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -294,7 +294,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -314,7 +314,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -334,7 +334,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -357,7 +357,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -398,7 +398,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/index.ts)_
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -417,7 +417,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -434,7 +434,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -453,7 +453,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/delete.ts)_
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:dev`
@@ -473,7 +473,7 @@ DESCRIPTION
run dev client simulator/emulator build with matching fingerprint or create a new one
```
_See code: [packages/eas-cli/src/commands/build/dev.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/dev.ts)_
_See code: [packages/eas-cli/src/commands/build/dev.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/dev.ts)_
## `eas build:download`
@@ -494,7 +494,7 @@ DESCRIPTION
download simulator/emulator builds for a given fingerprint hash
```
_See code: [packages/eas-cli/src/commands/build/download.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/download.ts)_
_See code: [packages/eas-cli/src/commands/build/download.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/download.ts)_
## `eas build:inspect`
@@ -529,7 +529,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -581,7 +581,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/list.ts)_
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -610,7 +610,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/resign.ts)_
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -636,7 +636,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/run.ts)_
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -690,7 +690,7 @@ DESCRIPTION
get the latest version from EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/version/get.ts)_
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -709,7 +709,7 @@ DESCRIPTION
update version of an app
```
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/version/set.ts)_
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -728,7 +728,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -745,7 +745,7 @@ DESCRIPTION
view a build for your project
```
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/build/view.ts)_
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -766,7 +766,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:delete [NAME]`
@@ -787,7 +787,7 @@ DESCRIPTION
Delete a channel
```
_See code: [packages/eas-cli/src/commands/channel/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/delete.ts)_
_See code: [packages/eas-cli/src/commands/channel/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/delete.ts)_
## `eas channel:edit [NAME]`
@@ -809,7 +809,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -829,7 +829,7 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:pause [NAME]`
@@ -851,7 +851,7 @@ DESCRIPTION
pause a channel to stop it from sending updates
```
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/pause.ts)_
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/pause.ts)_
## `eas channel:resume [NAME]`
@@ -873,7 +873,7 @@ DESCRIPTION
resume a channel to start sending updates
```
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/resume.ts)_
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/resume.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -906,7 +906,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally.
```
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/rollout.ts)_
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -929,7 +929,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -950,7 +950,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -967,7 +967,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/credentials/index.ts)_
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -985,7 +985,7 @@ DESCRIPTION
Set up credentials for building your project.
```
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas deploy [options]`
@@ -1013,7 +1013,7 @@ ALIASES
$ eas worker:deploy
```
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/deploy/index.ts)_
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/deploy/index.ts)_
## `eas deploy:alias`
@@ -1038,7 +1038,7 @@ ALIASES
$ eas deploy:promote
```
_See code: [packages/eas-cli/src/commands/deploy/alias/index.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/deploy/alias/index.ts)_
_See code: [packages/eas-cli/src/commands/deploy/alias/index.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/deploy/alias/index.ts)_
## `eas deploy:alias:delete [ALIAS_NAME]`
@@ -1059,7 +1059,7 @@ ALIASES
$ eas worker:alias:delete
```
_See code: [packages/eas-cli/src/commands/deploy/alias/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/deploy/alias/delete.ts)_
_See code: [packages/eas-cli/src/commands/deploy/alias/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/deploy/alias/delete.ts)_
## `eas deploy:delete [DEPLOYMENT_ID]`
@@ -1080,7 +1080,7 @@ ALIASES
$ eas worker:delete
```
_See code: [packages/eas-cli/src/commands/deploy/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/deploy/delete.ts)_
_See code: [packages/eas-cli/src/commands/deploy/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/deploy/delete.ts)_
## `eas deploy:promote`
@@ -1117,7 +1117,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/device/create.ts)_
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -1137,7 +1137,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -1158,7 +1158,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/device/list.ts)_
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -1179,7 +1179,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/device/rename.ts)_
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -1193,7 +1193,7 @@ DESCRIPTION
view a device for your project
```
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/device/view.ts)_
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -1207,7 +1207,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas env:create [ENVIRONMENT]`
@@ -1236,7 +1236,7 @@ DESCRIPTION
create an environment variable for the current project or account
```
_See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/create.ts)_
_See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/create.ts)_
## `eas env:delete [ENVIRONMENT]`
@@ -1260,7 +1260,7 @@ DESCRIPTION
delete an environment variable for the current project or account
```
_See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/delete.ts)_
_See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/delete.ts)_
## `eas env:exec ENVIRONMENT BASH_COMMAND`
@@ -1281,7 +1281,7 @@ DESCRIPTION
execute a command with environment variables from the selected environment
```
_See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/exec.ts)_
_See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/exec.ts)_
## `eas env:get [ENVIRONMENT]`
@@ -1306,7 +1306,7 @@ DESCRIPTION
view an environment variable for the current project or account
```
_See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/get.ts)_
_See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/get.ts)_
## `eas env:list [ENVIRONMENT]`
@@ -1331,7 +1331,7 @@ DESCRIPTION
list environment variables for the current project or account
```
_See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/list.ts)_
_See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/list.ts)_
## `eas env:pull [ENVIRONMENT]`
@@ -1354,7 +1354,7 @@ DESCRIPTION
pull environment variables for the selected environment to .env file
```
_See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/pull.ts)_
_See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/pull.ts)_
## `eas env:push [ENVIRONMENT]`
@@ -1375,7 +1375,7 @@ DESCRIPTION
push environment variables from .env file to the selected environment
```
_See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/push.ts)_
_See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/push.ts)_
## `eas env:update [ENVIRONMENT]`
@@ -1406,7 +1406,7 @@ DESCRIPTION
update an environment variable on the current project or account
```
_See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/env/update.ts)_
_See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/env/update.ts)_
## `eas fingerprint:compare [HASH1] [HASH2]`
@@ -1451,7 +1451,7 @@ EXAMPLES
$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> # Compare fingerprint from update against provided fingerprint
```
_See code: [packages/eas-cli/src/commands/fingerprint/compare.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/fingerprint/compare.ts)_
_See code: [packages/eas-cli/src/commands/fingerprint/compare.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/fingerprint/compare.ts)_
## `eas fingerprint:generate`
@@ -1482,7 +1482,7 @@ EXAMPLES
$ eas fingerprint:generate --json --non-interactive --platform android # Output fingerprint json to stdout
```
_See code: [packages/eas-cli/src/commands/fingerprint/generate.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/fingerprint/generate.ts)_
_See code: [packages/eas-cli/src/commands/fingerprint/generate.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/fingerprint/generate.ts)_
## `eas help [COMMAND]`
@@ -1590,7 +1590,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1607,7 +1607,7 @@ DESCRIPTION
generate the local store configuration from the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1624,7 +1624,7 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1654,7 +1654,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1668,7 +1668,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/project/info.ts)_
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1691,7 +1691,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/project/init.ts)_
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1709,7 +1709,7 @@ ALIASES
$ eas onboarding
```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/project/onboarding.ts)_
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas submit`
@@ -1744,7 +1744,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1786,7 +1786,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/index.ts)_
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1806,7 +1806,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1827,7 +1827,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:edit [GROUPID]`
@@ -1851,7 +1851,7 @@ DESCRIPTION
edit all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/edit.ts)_
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/edit.ts)_
## `eas update:list`
@@ -1873,7 +1873,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/list.ts)_
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1909,7 +1909,7 @@ DESCRIPTION
roll back to an existing update
```
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:revert-update-rollout`
@@ -1936,7 +1936,7 @@ DESCRIPTION
revert a rollout update for a project
```
_See code: [packages/eas-cli/src/commands/update/revert-update-rollout.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/revert-update-rollout.ts)_
_See code: [packages/eas-cli/src/commands/update/revert-update-rollout.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/revert-update-rollout.ts)_
## `eas update:roll-back-to-embedded`
@@ -1964,7 +1964,7 @@ DESCRIPTION
roll back to the embedded update
```
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -1985,7 +1985,7 @@ DESCRIPTION
instead execute "eas update:republish" or "eas update:roll-back-to-embedded".
```
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/rollback.ts)_
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -2005,7 +2005,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/update/view.ts)_
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas upload`
@@ -2026,7 +2026,7 @@ DESCRIPTION
upload a local build and generate a sharable link
```
_See code: [packages/eas-cli/src/commands/upload.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/upload.ts)_
_See code: [packages/eas-cli/src/commands/upload.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/upload.ts)_
## `eas webhook:create`
@@ -2047,7 +2047,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -2067,7 +2067,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -2085,7 +2085,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -2107,7 +2107,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -2124,7 +2124,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
@@ -2244,7 +2244,7 @@ DESCRIPTION
to cancel.
```
_See code: [packages/eas-cli/src/commands/workflow/cancel.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/cancel.ts)_
_See code: [packages/eas-cli/src/commands/workflow/cancel.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/cancel.ts)_
## `eas workflow:create [NAME]`
@@ -2264,7 +2264,7 @@ DESCRIPTION
create a new workflow configuration YAML file
```
_See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/create.ts)_
_See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/create.ts)_
## `eas workflow:logs [ID]`
@@ -2288,15 +2288,15 @@ DESCRIPTION
If no ID is passed in, you will be prompted to select from recent workflow runs for the current project.
```
_See code: [packages/eas-cli/src/commands/workflow/logs.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/logs.ts)_
_See code: [packages/eas-cli/src/commands/workflow/logs.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/logs.ts)_
## `eas workflow:run [FILE]`
run an EAS workflow
run an EAS workflow. The entire local project directory will be packaged and uploaded to EAS servers for the workflow run, unless the --ref flag is used.
```
USAGE
$ eas workflow:run [FILE] [--non-interactive] [--wait] [-F <value>] [--json]
$ eas workflow:run [FILE] [--non-interactive] [--wait] [-F <value>] [--ref <value>] [--json]
ARGUMENTS
FILE Path to the workflow file to run
@@ -2305,22 +2305,30 @@ FLAGS
-F, --input=<value>... Set workflow inputs
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
--ref=<value> Git reference to run the workflow on
--[no-]wait Wait for workflow run to complete
DESCRIPTION
run an EAS workflow
run an EAS workflow. The entire local project directory will be packaged and uploaded to EAS servers for the workflow
run, unless the --ref flag is used.
FLAG DESCRIPTIONS
-F, --input=<value>... Set workflow inputs
Add a parameter in key=value format. Use multiple instances of this flag to set multiple inputs.
--ref=<value> Git reference to run the workflow on
The git reference must exist in the project's git repository, and the workflow file must exist at that reference.
When this flag is used, the local project is not uploaded; instead, the workflow is run from the exact state of the
project at the chosen reference.
--[no-]wait Wait for workflow run to complete
Exit codes: 0 = success, 11 = failure, 12 = canceled, 13 = wait aborted.
```
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/run.ts)_
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/run.ts)_
## `eas workflow:runs`
@@ -2345,7 +2353,7 @@ DESCRIPTION
list recent workflow runs for this project, with their IDs, statuses, and timestamps
```
_See code: [packages/eas-cli/src/commands/workflow/runs.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/runs.ts)_
_See code: [packages/eas-cli/src/commands/workflow/runs.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/runs.ts)_
## `eas workflow:validate PATH`
@@ -2365,7 +2373,7 @@ DESCRIPTION
validate a workflow configuration yaml file
```
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/validate.ts)_
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/validate.ts)_
## `eas workflow:view [ID]`
@@ -2387,5 +2395,5 @@ DESCRIPTION
workflow runs for the current project.
```
_See code: [packages/eas-cli/src/commands/workflow/view.ts](https://github.com/expo/eas-cli/blob/v16.20.2/packages/eas-cli/src/commands/workflow/view.ts)_
_See code: [packages/eas-cli/src/commands/workflow/view.ts](https://github.com/expo/eas-cli/blob/v16.21.0/packages/eas-cli/src/commands/workflow/view.ts)_
<!-- commandsstop -->
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "16.20.2",
"version": "16.21.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
@@ -17,7 +17,7 @@ const BuildProfileEnvironmentToEnvironment: Record<Environment, EnvironmentVaria
export function isEnvironment(env: string): env is EnvironmentVariableEnvironment {
return Object.values(EnvironmentVariableEnvironment).includes(
env as EnvironmentVariableEnvironment
env.toLowerCase() as EnvironmentVariableEnvironment
);
}
@@ -138,6 +138,7 @@ export default class WorkerDeploy extends EasCommand {
logExportedProjectInfo(projectDist);
async function* emitWorkerTarballAsync(params: {
routesConfig: WorkerAssets.RoutesConfigEntry | null;
assetMap: WorkerAssets.AssetMap;
manifest: WorkerAssets.Manifest;
}): AsyncGenerator<WorkerAssets.FileEntry> {
@@ -148,6 +149,8 @@ export default class WorkerDeploy extends EasCommand {
for await (const workerFile of workerFiles) {
yield [`server/${workerFile.normalizedPath}`, workerFile.data];
}
} else if (projectDist.type === 'static' && params.routesConfig) {
yield ['routes.json', JSON.stringify(params.routesConfig)];
}
}
@@ -284,12 +287,13 @@ export default class WorkerDeploy extends EasCommand {
manifestResult.conflictingVariableNames.join(' ')
);
}
assetFiles = await WorkerAssets.collectAssetsAsync(
projectDist.type === 'server' ? projectDist.clientPath : projectDist.path,
{ maxFileSize: MAX_UPLOAD_SIZE }
);
const assetPath = projectDist.type === 'server' ? projectDist.clientPath : projectDist.path;
assetFiles = await WorkerAssets.collectAssetsAsync(assetPath, {
maxFileSize: MAX_UPLOAD_SIZE,
});
tarPath = await WorkerAssets.packFilesIterableAsync(
emitWorkerTarballAsync({
routesConfig: await WorkerAssets.getRoutesConfigAsync(assetPath),
assetMap: WorkerAssets.assetsToAssetsMap(assetFiles),
manifest: manifestResult.manifest,
})
@@ -369,4 +369,42 @@ describe(EnvCreate, () => {
);
});
});
it('accepts development environment when using positional argument', async () => {
const command = new EnvCreate(
[
'development',
'--name',
'TEST_VAR',
'--value',
'test-value',
'--visibility',
'plaintext',
'--scope',
'project',
'--non-interactive',
],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
});
await command.runAsync();
expect(EnvironmentVariableMutation.createForAppAsync).toHaveBeenCalledWith(
graphqlClient,
{
name: 'TEST_VAR',
value: 'test-value',
environments: [EnvironmentVariableEnvironment.Development],
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
},
testProjectId
);
});
});
@@ -1,7 +1,11 @@
import { Config } from '@oclif/core';
import { EnvironmentVariableEnvironment } from '../../../build/utils/environment';
import { EnvironmentVariableScope } from '../../../graphql/generated';
import {
EnvironmentSecretType,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
import { EnvironmentVariableMutation } from '../../../graphql/mutations/EnvironmentVariableMutation';
import { EnvironmentVariablesQuery } from '../../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../../log';
@@ -119,4 +123,40 @@ describe(EnvDelete, () => {
expect(EnvironmentVariableMutation.deleteAsync).not.toHaveBeenCalled();
});
it('accepts development environment when using positional argument', async () => {
const mockVariable = {
id: 'var1',
name: 'TEST_VAR_1',
value: 'value1',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
};
jest.mocked(EnvironmentVariablesQuery.byAppIdAsync).mockResolvedValueOnce([mockVariable]);
jest.mocked(EnvironmentVariableMutation.deleteAsync).mockResolvedValueOnce({ id: 'var1' });
jest.spyOn(Log, 'log').mockImplementation(() => {});
const command = new EnvDelete(
['development', '--variable-name', 'TEST_VAR_1', '--scope', 'project', '--non-interactive'],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
environment: EnvironmentVariableEnvironment.Development,
});
});
});
@@ -0,0 +1,79 @@
import { Config } from '@oclif/core';
import { EnvironmentVariableEnvironment } from '../../../build/utils/environment';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { testProjectId } from '../../../credentials/__tests__/fixtures-constants';
import {
EnvironmentSecretType,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
import { EnvironmentVariablesQuery } from '../../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../../log';
import EnvExec from '../exec';
jest.mock('../../../graphql/queries/EnvironmentVariablesQuery');
jest.mock('../../../log');
describe(EnvExec, () => {
const graphqlClient = {} as any as ExpoGraphqlClient;
const mockConfig = {} as unknown as Config;
const mockEnvironmentVariables = [
{
id: 'var1',
name: 'EXPO_PUBLIC_API_URL',
value: 'https://api.example.com',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
},
];
beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(Log, 'log').mockImplementation(() => {});
// Mock GraphQL query
jest
.mocked(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync)
.mockResolvedValue(mockEnvironmentVariables);
});
it('accepts development environment when using positional argument', async () => {
const command = new EnvExec(['development', 'echo $EXPO_PUBLIC_API_URL'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync).toHaveBeenCalledWith(
graphqlClient,
{
appId: testProjectId,
environment: EnvironmentVariableEnvironment.Development,
}
);
});
it('rejects invalid environment when using positional argument', async () => {
const command = new EnvExec(['invalid', 'echo test'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
});
await expect(command.runAsync()).rejects.toThrow(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
);
});
});
@@ -111,4 +111,34 @@ describe(EnvGet, () => {
expect(promptVariableEnvironmentAsync).toHaveBeenCalled();
expect(promptVariableNameAsync).toHaveBeenCalled();
});
it('accepts development environment when using positional argument', async () => {
jest
.mocked(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync)
.mockResolvedValueOnce(mockVariables);
jest.spyOn(Log, 'log').mockImplementation(() => {});
const command = new EnvGet(
['development', '--variable-name', 'TEST_VAR_1', '--scope', 'project'],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync).toHaveBeenCalledWith(
graphqlClient,
{
appId: testProjectId,
environment: EnvironmentVariableEnvironment.Development,
filterNames: ['TEST_VAR_1'],
includeFileContent: true,
}
);
});
});
@@ -95,6 +95,26 @@ describe(EnvList, () => {
expect(Log.log).toHaveBeenCalledWith(expect.stringContaining('TEST_VAR_2'));
});
it('accepts development environment when using positional argument', async () => {
jest.mocked(EnvironmentVariablesQuery.byAppIdAsync).mockResolvedValueOnce(mockVariables);
const command = new EnvList(['development'], mockConfig); // Using positional argument
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdAsync).toHaveBeenCalledWith(graphqlClient, {
appId: testProjectId,
environment: EnvironmentVariableEnvironment.Development,
includeFileContent: false,
});
});
it('lists project environment variables including sensitive values', async () => {
jest
.mocked(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync)
@@ -0,0 +1,381 @@
import { Config } from '@oclif/core';
import * as fs from 'fs-extra';
import path from 'path';
import { EnvironmentVariableEnvironment } from '../../../build/utils/environment';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { testProjectId } from '../../../credentials/__tests__/fixtures-constants';
import {
EnvironmentSecretType,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
import {
EnvironmentVariableWithFileContent,
EnvironmentVariablesQuery,
} from '../../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../../log';
import { confirmAsync } from '../../../prompts';
import EnvPull from '../pull';
jest.mock('../../../graphql/queries/EnvironmentVariablesQuery');
jest.mock('../../../prompts');
jest.mock('fs-extra');
jest.mock('../../../log');
describe(EnvPull, () => {
const graphqlClient = {} as any as ExpoGraphqlClient;
const mockConfig = {} as unknown as Config;
const testProjectDir = '/test/project';
const testTargetPath = '.env.local';
const mockEnvironmentVariables: EnvironmentVariableWithFileContent[] = [
{
id: 'var1',
name: 'EXPO_PUBLIC_API_URL',
value: 'https://api.example.com',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
},
{
id: 'var2',
name: 'DATABASE_URL',
value: 'postgres://localhost:5432/mydb',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Sensitive,
type: EnvironmentSecretType.String,
},
{
id: 'var3',
name: 'SECRET_KEY',
value: 'super-secret-key',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Secret,
type: EnvironmentSecretType.String,
},
{
id: 'var4',
name: 'CONFIG_FILE',
value: 'base64-encoded-file-content',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.FileBase64,
valueWithFileContent: Buffer.from('{"key": "value"}').toString('base64'),
},
];
beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(Log, 'log').mockImplementation(() => {});
jest.spyOn(Log, 'addNewLineIfNone').mockImplementation(() => {});
// Mock fs-extra methods
jest.mocked(fs.exists).mockImplementation(() => Promise.resolve(false));
jest.mocked(fs.writeFile).mockImplementation(() => Promise.resolve());
jest.mocked(fs.mkdir).mockImplementation(() => Promise.resolve());
jest.mocked(fs.readFile).mockImplementation(() => Promise.resolve(''));
// Mock GraphQL query
jest
.mocked(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync)
.mockResolvedValue(mockEnvironmentVariables);
});
describe('environment validation', () => {
it('accepts development environment (case insensitive)', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync).toHaveBeenCalledWith(
graphqlClient,
{
appId: testProjectId,
environment: 'development',
includeFileContent: true,
}
);
});
it('rejects invalid environment', async () => {
const command = new EnvPull(['invalid'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await expect(command.runAsync()).rejects.toThrow(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
);
});
});
describe('file operations', () => {
it('writes environment variables to .env.local file', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(fs.writeFile).toHaveBeenCalledWith(
testTargetPath,
expect.stringContaining('# Environment: development')
);
});
it('writes correct environment variable content', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
// Find the call that writes to .env.local (not the file variable)
const writeFileCalls = jest.mocked(fs.writeFile).mock.calls;
const envFileCall = writeFileCalls.find(call => call[0] === testTargetPath);
expect(envFileCall).toBeDefined();
const fileContent = envFileCall![1] as string;
// Check that the file contains the expected content
expect(fileContent).toContain('# Environment: development');
expect(fileContent).toContain('EXPO_PUBLIC_API_URL=https://api.example.com');
expect(fileContent).toContain('DATABASE_URL=postgres://localhost:5432/mydb');
expect(fileContent).toContain('# SECRET_KEY=***** (secret)');
});
it('handles file variables correctly', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
// Should create .eas/.env directory for file variables
expect(fs.mkdir).toHaveBeenCalledWith(path.join(testProjectDir, '.eas', '.env'), {
recursive: true,
});
// Should write the file content (base64 encoded)
expect(fs.writeFile).toHaveBeenCalledWith(
path.join(testProjectDir, '.eas', '.env', 'CONFIG_FILE'),
Buffer.from('{"key": "value"}').toString('base64'),
'base64'
);
// Should include file path in env file
const writeFileCalls = jest.mocked(fs.writeFile).mock.calls;
const envFileCall = writeFileCalls.find(call => call[0] === testTargetPath);
expect(envFileCall).toBeDefined();
const fileContent = envFileCall![1] as string;
expect(fileContent).toContain('CONFIG_FILE=');
expect(fileContent).toContain(path.join(testProjectDir, '.eas', '.env', 'CONFIG_FILE'));
});
it('prompts for overwrite when file exists in interactive mode', async () => {
jest.mocked(fs.exists).mockImplementation(() => Promise.resolve(true));
jest.mocked(confirmAsync).mockResolvedValue(true);
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(confirmAsync).toHaveBeenCalledWith({
message: `File ${testTargetPath} already exists. Do you want to overwrite it?`,
});
});
it('aborts when user declines overwrite', async () => {
jest.mocked(fs.exists).mockImplementation(() => Promise.resolve(true));
jest.mocked(confirmAsync).mockResolvedValue(false);
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await expect(command.runAsync()).rejects.toThrow(`File ${testTargetPath} already exists.`);
});
});
describe('non-interactive mode', () => {
it('works in non-interactive mode', async () => {
const command = new EnvPull(['development', '--non-interactive'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdWithSensitiveAsync).toHaveBeenCalledWith(
graphqlClient,
{
appId: testProjectId,
environment: 'development',
includeFileContent: true,
}
);
});
it('requires environment in non-interactive mode', async () => {
const command = new EnvPull(['--non-interactive'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await expect(command.runAsync()).rejects.toThrow(
'The `--environment` flag must be set when running in `--non-interactive` mode.'
);
});
});
describe('custom target path', () => {
it('writes to custom path when specified', async () => {
const customPath = '.env.custom';
const command = new EnvPull(['development', '--path', customPath], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(fs.writeFile).toHaveBeenCalledWith(
customPath,
expect.stringContaining('# Environment: development')
);
});
});
describe('logging', () => {
it('logs success message with correct environment', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(Log.log).toHaveBeenCalledWith(
`Pulled plain text and sensitive environment variables from "development" environment to ${testTargetPath}.`
);
});
it('logs secret variables that were skipped', async () => {
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(Log.log).toHaveBeenCalledWith(
expect.stringContaining('The following variables have the secret visibility')
);
expect(Log.log).toHaveBeenCalledWith(expect.stringContaining('SECRET_KEY'));
});
});
describe('existing .env.local file handling', () => {
it('preserves existing secret values when they exist', async () => {
const existingEnvContent = 'SECRET_KEY=existing-secret-value\nOTHER_VAR=other-value';
jest.mocked(fs.exists).mockImplementation(() => Promise.resolve(true));
jest.mocked(fs.readFile).mockImplementation(() => Promise.resolve(existingEnvContent));
jest.mocked(confirmAsync).mockResolvedValue(true);
const command = new EnvPull(['development'], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
// Find the call that writes to .env.local (not the file variable)
const writeFileCalls = jest.mocked(fs.writeFile).mock.calls;
const envFileCall = writeFileCalls.find(call => call[0] === testTargetPath);
expect(envFileCall).toBeDefined();
const fileContent = envFileCall![1] as string;
// Should use existing secret value instead of placeholder
expect(fileContent).toContain('SECRET_KEY=existing-secret-value');
expect(fileContent).not.toContain('# SECRET_KEY=***** (secret)');
// Should log that it reused the local value
expect(Log.log).toHaveBeenCalledWith(
expect.stringContaining('Reused local values for following secrets: SECRET_KEY')
);
});
});
});
@@ -0,0 +1,90 @@
import { Config } from '@oclif/core';
import * as fs from 'fs-extra';
import { EnvironmentVariableEnvironment } from '../../../build/utils/environment';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { testProjectId } from '../../../credentials/__tests__/fixtures-constants';
import {
EnvironmentSecretType,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
import { EnvironmentVariableMutation } from '../../../graphql/mutations/EnvironmentVariableMutation';
import { EnvironmentVariablesQuery } from '../../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../../log';
import EnvPush from '../push';
jest.mock('../../../graphql/mutations/EnvironmentVariableMutation');
jest.mock('../../../graphql/queries/EnvironmentVariablesQuery');
jest.mock('fs-extra');
jest.mock('../../../log');
describe(EnvPush, () => {
const graphqlClient = {} as any as ExpoGraphqlClient;
const mockConfig = {} as unknown as Config;
const testProjectDir = '/test/project';
const testEnvPath = '.env.test';
const mockEnvContent = `EXPO_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgres://localhost:5432/mydb
SECRET_KEY=super-secret-key`;
beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(Log, 'log').mockImplementation(() => {});
// Mock fs-extra methods
jest.mocked(fs.exists).mockImplementation(() => Promise.resolve(true));
jest.mocked(fs.readFile).mockImplementation(() => Promise.resolve(mockEnvContent));
// Mock GraphQL queries and mutations
jest.mocked(EnvironmentVariablesQuery.byAppIdAsync).mockResolvedValue([]); // No existing variables
jest.mocked(EnvironmentVariableMutation.createForAppAsync).mockResolvedValue({
id: 'var1',
name: 'EXPO_PUBLIC_API_URL',
value: 'https://api.example.com',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
});
});
it('accepts development environment when using positional argument', async () => {
jest.mocked(EnvironmentVariablesQuery.byAppIdAsync).mockResolvedValueOnce([]);
const command = new EnvPush(['development', '--path', testEnvPath], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdAsync).toHaveBeenCalledWith(graphqlClient, {
appId: testProjectId,
environment: EnvironmentVariableEnvironment.Development,
filterNames: ['EXPO_PUBLIC_API_URL', 'DATABASE_URL', 'SECRET_KEY'],
});
});
it('rejects invalid environment when using positional argument', async () => {
const command = new EnvPush(['invalid', '--path', testEnvPath], mockConfig);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId: testProjectId,
projectDir: testProjectDir,
});
await expect(command.runAsync()).rejects.toThrow(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
);
});
});
@@ -4,6 +4,7 @@ import chalk from 'chalk';
import { getMockAppFragment } from '../../../__tests__/commands/utils';
import { EnvironmentVariableEnvironment } from '../../../build/utils/environment';
import {
EnvironmentSecretType,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
@@ -162,4 +163,50 @@ describe(EnvUpdate, () => {
'Variable with name NON_EXISTENT_VARIABLE does not exist on project @testuser/testpp.'
);
});
it('accepts development environment when using positional argument', async () => {
const mockVariable = {
id: 'var1',
name: 'TEST_VAR_1',
value: 'value1',
environments: [EnvironmentVariableEnvironment.Development],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
scope: EnvironmentVariableScope.Project,
visibility: EnvironmentVariableVisibility.Public,
type: EnvironmentSecretType.String,
};
jest.mocked(EnvironmentVariablesQuery.byAppIdAsync).mockResolvedValueOnce([mockVariable]);
jest.mocked(EnvironmentVariableMutation.updateAsync).mockResolvedValueOnce(mockVariable);
jest.spyOn(Log, 'log').mockImplementation(() => {});
const command = new EnvUpdate(
[
'development',
'--variable-name',
'TEST_VAR_1',
'--value',
'new-value',
'--scope',
'project',
'--non-interactive',
],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
projectId,
});
await command.runAsync();
expect(EnvironmentVariablesQuery.byAppIdAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
environment: EnvironmentVariableEnvironment.Development,
filterNames: ['TEST_VAR_1'],
});
});
});
+2 -2
View File
@@ -323,14 +323,14 @@ export default class EnvCreate extends EasCommand {
value = environmentFilePath ? await fs.readFile(environmentFilePath, 'base64') : value;
if (environment && !isEnvironment(environment.toUpperCase())) {
if (environment && !isEnvironment(environment.toLowerCase())) {
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
}
let newEnvironments = environments
? environments
: environment
? [environment.toUpperCase() as EnvironmentVariableEnvironment]
? [environment.toLowerCase() as EnvironmentVariableEnvironment]
: undefined;
if (!newEnvironments) {
+1 -2
View File
@@ -166,8 +166,7 @@ export default class EnvDelete extends EasCommand {
: EnvironmentVariableScope.Project;
if (environment) {
environment = environment.toUpperCase();
environment = environment.toLowerCase();
if (!isEnvironment(environment)) {
throw new Error(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
+1 -1
View File
@@ -101,7 +101,7 @@ export default class EnvExec extends EasCommand {
);
}
environment = environment?.toUpperCase();
environment = environment?.toLowerCase();
if (!isEnvironment(environment)) {
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
+1 -1
View File
@@ -156,7 +156,7 @@ export default class EnvGet extends EasCommand {
? EnvironmentVariableScope.Shared
: EnvironmentVariableScope.Project;
if (environment) {
environment = environment.toUpperCase();
environment = environment.toLowerCase();
if (!isEnvironment(environment)) {
throw new Error(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
+2 -2
View File
@@ -180,14 +180,14 @@ export default class EnvList extends EasCommand {
flags: RawListFlags,
{ environment }: { environment?: string }
): ListFlags {
if (environment && !isEnvironment(environment.toUpperCase())) {
if (environment && !isEnvironment(environment.toLowerCase())) {
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
}
const environments = flags.environment
? flags.environment
: environment
? [environment.toUpperCase() as EnvironmentVariableEnvironment]
? [environment.toLowerCase() as EnvironmentVariableEnvironment]
: undefined;
return {
+1 -1
View File
@@ -49,7 +49,7 @@ export default class EnvPull extends EasCommand {
flags: { environment: flagEnvironment, path: targetPath, 'non-interactive': nonInteractive },
} = await this.parse(EnvPull);
let environment = flagEnvironment?.toUpperCase() ?? argEnvironment?.toUpperCase();
let environment = flagEnvironment?.toLowerCase() ?? argEnvironment?.toLowerCase();
if (!environment) {
environment = await promptVariableEnvironmentAsync({ nonInteractive });
+2 -2
View File
@@ -199,13 +199,13 @@ export default class EnvPush extends EasCommand {
flags: { path: string; environment: EnvironmentVariableEnvironment[] | undefined },
{ environment }: Record<string, string>
): { environment?: EnvironmentVariableEnvironment[]; path: string } {
if (environment && !isEnvironment(environment.toUpperCase())) {
if (environment && !isEnvironment(environment.toLowerCase())) {
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
}
const environments =
flags.environment ??
(environment ? [environment.toUpperCase() as EnvironmentVariableEnvironment] : undefined);
(environment ? [environment.toLowerCase() as EnvironmentVariableEnvironment] : undefined);
return {
...flags,
+1 -1
View File
@@ -226,7 +226,7 @@ export default class EnvUpdate extends EasCommand {
: EnvironmentVariableScope.Project;
if (environment) {
environment = environment.toUpperCase();
environment = environment.toLowerCase();
if (!isEnvironment(environment)) {
throw new Error(
"Invalid environment. Use one of 'production', 'preview', or 'development'."
+151 -85
View File
@@ -40,6 +40,7 @@
* ```
*/
import spawnAsync from '@expo/spawn-async';
import { Flags } from '@oclif/core';
import { CombinedError } from '@urql/core';
import chalk from 'chalk';
@@ -55,6 +56,7 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
import { EASNonInteractiveFlag, EasJsonOnlyFlag } from '../../commandUtils/flags';
import {
WorkflowProjectSourceType,
WorkflowRevision,
WorkflowRunByIdQuery,
WorkflowRunStatus,
} from '../../graphql/generated';
@@ -78,7 +80,8 @@ const EXIT_CODES = {
};
export default class WorkflowRun extends EasCommand {
static override description = 'run an EAS workflow';
static override description =
'run an EAS workflow. The entire local project directory will be packaged and uploaded to EAS servers for the workflow run, unless the --ref flag is used.';
static override args = [{ name: 'file', description: 'Path to the workflow file to run' }];
@@ -98,6 +101,11 @@ export default class WorkflowRun extends EasCommand {
'Add a parameter in key=value format. Use multiple instances of this flag to set multiple inputs.',
summary: 'Set workflow inputs',
}),
ref: Flags.string({
description:
"The git reference must exist in the project's git repository, and the workflow file must exist at that reference. When this flag is used, the local project is not uploaded; instead, the workflow is run from the exact state of the project at the chosen reference.",
summary: 'Git reference to run the workflow on',
}),
...EasJsonOnlyFlag,
};
@@ -125,26 +133,70 @@ export default class WorkflowRun extends EasCommand {
withServerSideEnvironment: null,
});
let yamlConfig: string;
try {
const workflowFileContents = await WorkflowFile.readWorkflowFileContentsAsync({
projectDir,
filePath: args.file,
});
Log.log(`Using workflow file from ${workflowFileContents.filePath}`);
yamlConfig = workflowFileContents.yamlConfig;
} catch (err) {
Log.error('Failed to read workflow file.');
throw err;
}
const {
projectId,
exp: { slug: projectName },
} = await getDynamicPrivateProjectConfigAsync();
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
let yamlConfig: string;
let workflowRunId: string;
let workflowRevisionId: string | undefined;
let gitRef: string | undefined;
if (flags.ref) {
// Run from git ref
const fileName = path.basename(args.file);
// Find the real commit, make sure the ref is valid
gitRef = (
await spawnAsync('git', ['rev-parse', flags.ref], {
cwd: projectDir,
})
).output[0].trim();
if (!gitRef) {
throw new Error('Failed to resolve git reference');
}
Log.log(`Using workflow file ${fileName} at ${gitRef}`);
let revisionResult: WorkflowRevision | undefined;
try {
revisionResult = await WorkflowRevisionMutation.getOrCreateWorkflowRevisionFromGitRefAsync(
graphqlClient,
{
appId: projectId,
fileName,
gitRef,
}
);
} catch (err) {
throw new Error(
`Failed to find or create workflow revision for ${fileName} at ${flags.ref}: ${err}`
);
}
Log.debug(`Workflow revision: ${JSON.stringify(revisionResult, null, 2)}`);
if (!revisionResult) {
throw new Error(
`Failed to find or create workflow revision for ${fileName} at ${flags.ref}`
);
}
yamlConfig = revisionResult.yamlConfig;
workflowRevisionId = revisionResult.id;
} else {
// Run from local file
try {
const workflowFileContents = await WorkflowFile.readWorkflowFileContentsAsync({
projectDir,
filePath: args.file,
});
Log.log(`Using workflow file from ${workflowFileContents.filePath}`);
yamlConfig = workflowFileContents.yamlConfig;
} catch (err) {
Log.error('Failed to read workflow file.');
throw err;
}
}
// Validate workflow YAML
try {
await WorkflowRevisionMutation.validateWorkflowYamlConfigAsync(graphqlClient, {
appId: projectId,
@@ -206,81 +258,95 @@ export default class WorkflowRun extends EasCommand {
path.relative(await vcsClient.getRootPathAsync(), projectDir) || '.'
);
try {
({ projectArchiveBucketKey } = await uploadAccountScopedProjectSourceAsync({
graphqlClient,
vcsClient,
accountId: account.id,
}));
if (await fileExistsAsync(easJsonPath)) {
({ fileBucketKey: easJsonBucketKey } = await uploadAccountScopedFileAsync({
graphqlClient,
accountId: account.id,
filePath: easJsonPath,
maxSizeBytes: 1024 * 1024,
}));
} else {
Log.warn(
`⚠ No ${chalk.bold('eas.json')} found in the project directory. Running ${chalk.bold(
'type: build'
)} jobs will not work. Run ${chalk.bold(
'eas build:configure'
)} to configure your project for builds.`
);
}
if (await fileExistsAsync(packageJsonPath)) {
({ fileBucketKey: packageJsonBucketKey } = await uploadAccountScopedFileAsync({
graphqlClient,
accountId: account.id,
filePath: packageJsonPath,
maxSizeBytes: 1024 * 1024,
}));
} else {
Log.warn(
`⚠ No ${chalk.bold(
'package.json'
)} found in the project directory. It is used to automatically infer best job configuration for your project. You may want to define ${chalk.bold(
'image'
)} property in your workflow to specify the image to use.`
);
}
} catch (err) {
Log.error('Failed to upload project sources.');
throw err;
}
let workflowRunId: string;
try {
({ id: workflowRunId } = await WorkflowRunMutation.createWorkflowRunAsync(graphqlClient, {
appId: projectId,
workflowRevisionInput: {
fileName: path.basename(args.file),
yamlConfig,
},
workflowRunInput: {
if (gitRef) {
// Run from git ref
let runResult: { id: string };
try {
runResult = await WorkflowRunMutation.createWorkflowRunFromGitRefAsync(graphqlClient, {
workflowRevisionId: workflowRevisionId ?? '',
gitRef,
inputs,
projectSource: {
type: WorkflowProjectSourceType.Gcs,
projectArchiveBucketKey,
easJsonBucketKey,
packageJsonBucketKey,
projectRootDirectory,
});
} catch (err) {
throw new Error(`Failed to create workflow run: ${err}`);
}
workflowRunId = runResult.id;
} else {
// Run from local file
try {
({ projectArchiveBucketKey } = await uploadAccountScopedProjectSourceAsync({
graphqlClient,
vcsClient,
accountId: account.id,
}));
if (await fileExistsAsync(easJsonPath)) {
({ fileBucketKey: easJsonBucketKey } = await uploadAccountScopedFileAsync({
graphqlClient,
accountId: account.id,
filePath: easJsonPath,
maxSizeBytes: 1024 * 1024,
}));
} else {
Log.warn(
`⚠ No ${chalk.bold('eas.json')} found in the project directory. Running ${chalk.bold(
'type: build'
)} jobs will not work. Run ${chalk.bold(
'eas build:configure'
)} to configure your project for builds.`
);
}
if (await fileExistsAsync(packageJsonPath)) {
({ fileBucketKey: packageJsonBucketKey } = await uploadAccountScopedFileAsync({
graphqlClient,
accountId: account.id,
filePath: packageJsonPath,
maxSizeBytes: 1024 * 1024,
}));
} else {
Log.warn(
`⚠ No ${chalk.bold(
'package.json'
)} found in the project directory. It is used to automatically infer best job configuration for your project. You may want to define ${chalk.bold(
'image'
)} property in your workflow to specify the image to use.`
);
}
} catch (err) {
Log.error('Failed to upload project sources.');
throw err;
}
try {
({ id: workflowRunId } = await WorkflowRunMutation.createWorkflowRunAsync(graphqlClient, {
appId: projectId,
workflowRevisionInput: {
fileName: path.basename(args.file),
yamlConfig,
},
},
}));
workflowRunInput: {
inputs,
projectSource: {
type: WorkflowProjectSourceType.Gcs,
projectArchiveBucketKey,
easJsonBucketKey,
packageJsonBucketKey,
projectRootDirectory,
},
},
}));
} catch (err) {
Log.error('Failed to start the workflow with the API.');
Log.newLine();
Log.log(`See logs: ${link(getWorkflowRunUrl(account.name, projectName, workflowRunId))}`);
} catch (err) {
Log.error('Failed to start the workflow with the API.');
throw err;
throw err;
}
}
Log.newLine();
Log.log(`See logs: ${link(getWorkflowRunUrl(account.name, projectName, workflowRunId))}`);
if (!flags.wait) {
if (flags.json) {
printJsonOnlyOutput({
+18
View File
@@ -9859,6 +9859,15 @@ export type DeleteWebhookMutationVariables = Exact<{
export type DeleteWebhookMutation = { __typename?: 'RootMutation', webhook: { __typename?: 'WebhookMutation', deleteWebhook: { __typename?: 'DeleteWebhookResult', id: string } } };
export type GetOrCreateWorkflowRevisionFromGitRefMutationVariables = Exact<{
appId: Scalars['ID']['input'];
fileName: Scalars['String']['input'];
gitRef: Scalars['String']['input'];
}>;
export type GetOrCreateWorkflowRevisionFromGitRefMutation = { __typename?: 'RootMutation', workflowRevision: { __typename?: 'WorkflowRevisionMutation', getOrCreateWorkflowRevisionFromGitRef: { __typename?: 'WorkflowRevision', id: string, yamlConfig: string, blobSha: string, commitSha?: string | null, createdAt: any, workflow: { __typename?: 'Workflow', id: string } } } };
export type ValidateWorkflowYamlConfigMutationVariables = Exact<{
appId: Scalars['ID']['input'];
yamlConfig: Scalars['String']['input'];
@@ -9876,6 +9885,15 @@ export type CreateWorkflowRunMutationVariables = Exact<{
export type CreateWorkflowRunMutation = { __typename?: 'RootMutation', workflowRun: { __typename?: 'WorkflowRunMutation', createWorkflowRun: { __typename?: 'WorkflowRun', id: string } } };
export type CreateWorkflowRunFromGitRefMutationVariables = Exact<{
workflowRevisionId: Scalars['ID']['input'];
gitRef: Scalars['String']['input'];
inputs?: InputMaybe<Scalars['JSONObject']['input']>;
}>;
export type CreateWorkflowRunFromGitRefMutation = { __typename?: 'RootMutation', workflowRun: { __typename?: 'WorkflowRunMutation', createWorkflowRunFromGitRef: { __typename?: 'WorkflowRun', id: string } } };
export type CancelWorkflowRunMutationVariables = Exact<{
workflowRunId: Scalars['ID']['input'];
}>;
@@ -4,8 +4,11 @@ import { z } from 'zod';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
GetOrCreateWorkflowRevisionFromGitRefMutation,
GetOrCreateWorkflowRevisionFromGitRefMutationVariables,
ValidateWorkflowYamlConfigMutation,
ValidateWorkflowYamlConfigMutationVariables,
WorkflowRevision,
} from '../generated';
export namespace WorkflowRevisionMutation {
@@ -17,6 +20,61 @@ export namespace WorkflowRevisionMutation {
}),
});
export async function getOrCreateWorkflowRevisionFromGitRefAsync(
graphqlClient: ExpoGraphqlClient,
{
appId,
fileName,
gitRef,
}: {
appId: string;
fileName: string;
gitRef: string;
}
): Promise<WorkflowRevision | undefined> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<
GetOrCreateWorkflowRevisionFromGitRefMutation,
GetOrCreateWorkflowRevisionFromGitRefMutationVariables
>(
gql`
mutation GetOrCreateWorkflowRevisionFromGitRef(
$appId: ID!
$fileName: String!
$gitRef: String!
) {
workflowRevision {
getOrCreateWorkflowRevisionFromGitRef(
appId: $appId
fileName: $fileName
gitRef: $gitRef
) {
id
yamlConfig
blobSha
commitSha
createdAt
workflow {
id
}
}
}
}
`,
{
appId,
fileName,
gitRef,
}
)
.toPromise()
);
return (
(data.workflowRevision?.getOrCreateWorkflowRevisionFromGitRef as WorkflowRevision) ??
undefined
);
}
export async function validateWorkflowYamlConfigAsync(
graphqlClient: ExpoGraphqlClient,
{
@@ -5,6 +5,8 @@ import { withErrorHandlingAsync } from '../client';
import {
CancelWorkflowRunMutation,
CancelWorkflowRunMutationVariables,
CreateWorkflowRunFromGitRefMutation,
CreateWorkflowRunFromGitRefMutationVariables,
CreateWorkflowRunMutation,
CreateWorkflowRunMutationVariables,
WorkflowRevisionInput,
@@ -54,6 +56,53 @@ export namespace WorkflowRunMutation {
);
return { id: data.workflowRun.createWorkflowRun.id };
}
export async function createWorkflowRunFromGitRefAsync(
graphqlClient: ExpoGraphqlClient,
{
workflowRevisionId,
gitRef,
inputs,
}: {
workflowRevisionId: string;
gitRef: string;
inputs?: Record<string, any>;
}
): Promise<{ id: string }> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<
CreateWorkflowRunFromGitRefMutation,
CreateWorkflowRunFromGitRefMutationVariables
>(
gql`
mutation CreateWorkflowRunFromGitRef(
$workflowRevisionId: ID!
$gitRef: String!
$inputs: JSONObject
) {
workflowRun {
createWorkflowRunFromGitRef(
workflowRevisionId: $workflowRevisionId
gitRef: $gitRef
inputs: $inputs
) {
id
}
}
}
`,
{
workflowRevisionId,
gitRef,
inputs,
}
)
.toPromise()
);
return { id: data.workflowRun.createWorkflowRunFromGitRef.id };
}
export async function cancelWorkflowRunAsync(
graphqlClient: ExpoGraphqlClient,
{
+29
View File
@@ -12,6 +12,8 @@ import { EnvironmentVariableEnvironment } from '../build/utils/environment';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EnvironmentVariablesQuery } from '../graphql/queries/EnvironmentVariablesQuery';
const EXPO_ROUTES_PATHS = new Set(['_expo/routes.json', '_expo/.routes.json', '.expo-routes.json']);
/** Returns whether a file or folder is ignored */
function isIgnoredName(name: string): boolean {
switch (name) {
@@ -114,6 +116,8 @@ export async function collectAssetsAsync(
throw new Error(
`Upload of "${file.normalizedPath}" aborted: File size is greater than the upload limit (>500MB)`
);
} else if (EXPO_ROUTES_PATHS.has(file.normalizedPath)) {
continue;
}
const sha512$ = computeSha512HashAsync(file.path);
const contentType$ = determineMimeTypeAsync(file.path);
@@ -129,6 +133,31 @@ export async function collectAssetsAsync(
return assets;
}
export interface RoutesConfigEntry {
headers?: Record<string, unknown>;
redirects?: Record<string, unknown>[];
}
export async function getRoutesConfigAsync(
assetPath: string | undefined
): Promise<RoutesConfigEntry | null> {
if (assetPath) {
for (const candidatePath of EXPO_ROUTES_PATHS) {
const targetPath = path.resolve(assetPath, candidatePath);
let json: unknown;
try {
json = JSON.parse(await fs.promises.readFile(targetPath, 'utf8'));
} catch {
continue;
}
if (typeof json === 'object' && json) {
return json as RoutesConfigEntry;
}
}
}
return null;
}
/** Mapping of normalized file paths to a SHA512 hash */
export type AssetMap = Record<
string,