mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 03:38:08 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd9561cfc3 | ||
|
|
93ce451bc7 | ||
|
|
554e433548 | ||
|
|
12a1cb7011 | ||
|
|
360de90eea | ||
|
|
ea02fcebda | ||
|
|
0b5194b1a5 | ||
|
|
05799aba9c | ||
|
|
2cece65dbf |
No files matched your search
@@ -8,10 +8,20 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- .nvmrc support for setting node version. ([#1954](https://github.com/expo/eas-cli/pull/1954) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
- Provide credentials for custom Android builds. ([#1969](https://github.com/expo/eas-cli/pull/1969) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- More branch mapping utility. ([#1957](https://github.com/expo/eas-cli/pull/1957) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Utility classes to select existing rollouts and channels. ([#1958](https://github.com/expo/eas-cli/pull/1958) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [3.17.1](https://github.com/expo/eas-cli/releases/tag/v3.17.1) - 2023-07-27
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Unify channel graphql query types. ([#1949](https://github.com/expo/eas-cli/pull/1949) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Revert UpdateBranchWithCurrentGroupFragment. ([#1952](https://github.com/expo/eas-cli/pull/1952) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Fetch entire relay compliant dataset. ([#1953](https://github.com/expo/eas-cli/pull/1953) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "3.17.1",
|
||||
"version": "3.18.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+55
-55
@@ -133,7 +133,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -150,7 +150,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -167,7 +167,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -181,7 +181,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -231,7 +231,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -252,7 +252,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -272,7 +272,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -292,7 +292,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -315,7 +315,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -345,7 +345,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -362,7 +362,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -379,7 +379,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -414,7 +414,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -452,7 +452,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -478,7 +478,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -502,7 +502,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -551,7 +551,7 @@ DESCRIPTION
|
||||
get the latest version from EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
@@ -570,7 +570,7 @@ DESCRIPTION
|
||||
update version of an app
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -589,7 +589,7 @@ DESCRIPTION
|
||||
update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -606,7 +606,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -627,7 +627,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -649,7 +649,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -669,7 +669,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
@@ -693,7 +693,7 @@ DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -716,7 +716,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -737,7 +737,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -754,7 +754,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -768,7 +768,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -788,7 +788,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -809,7 +809,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:rename`
|
||||
|
||||
@@ -830,7 +830,7 @@ DESCRIPTION
|
||||
rename a registered device
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -844,7 +844,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -858,7 +858,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -946,7 +946,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -963,7 +963,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -980,7 +980,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -994,7 +994,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1008,7 +1008,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1030,7 +1030,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -1053,7 +1053,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -1071,7 +1071,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1085,7 +1085,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1105,7 +1105,7 @@ DESCRIPTION
|
||||
read environment secrets from env file and store on the server
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1135,7 +1135,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1168,7 +1168,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1186,7 +1186,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1207,7 +1207,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1229,7 +1229,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1253,7 +1253,7 @@ DESCRIPTION
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1273,7 +1273,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1294,7 +1294,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1314,7 +1314,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1332,7 +1332,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1354,7 +1354,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1371,7 +1371,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.17.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.18.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "3.17.1",
|
||||
"version": "3.18.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
@@ -13,8 +13,8 @@
|
||||
"@expo/config": "8.1.2",
|
||||
"@expo/config-plugins": "7.2.4",
|
||||
"@expo/config-types": "49.0.0",
|
||||
"@expo/eas-build-job": "1.0.13",
|
||||
"@expo/eas-json": "3.17.0",
|
||||
"@expo/eas-build-job": "1.0.30",
|
||||
"@expo/eas-json": "3.18.0",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.0.33",
|
||||
@@ -27,7 +27,7 @@
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.0",
|
||||
"@expo/steps": "1.0.28",
|
||||
"@expo/steps": "1.0.31",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "1.23.2",
|
||||
"@oclif/plugin-autocomplete": "1.3.10",
|
||||
|
||||
@@ -124,8 +124,7 @@ export async function prepareAndroidBuildAsync(
|
||||
}
|
||||
|
||||
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
|
||||
const isCustomBuild = ctx.buildProfile.config !== undefined;
|
||||
return !ctx.buildProfile.withoutCredentials && !isCustomBuild;
|
||||
return !ctx.buildProfile.withoutCredentials;
|
||||
}
|
||||
|
||||
async function ensureAndroidCredentialsAsync(
|
||||
|
||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.27';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.31';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
|
||||
@@ -117,6 +117,7 @@ export async function runBuildAndSubmitAsync(
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
Log.log(
|
||||
`Loaded "env" configuration for the "${buildProfiles[0].profileName}" profile: ${
|
||||
@@ -183,6 +184,7 @@ export async function runBuildAndSubmitAsync(
|
||||
platforms,
|
||||
profileName: flags.submitProfile,
|
||||
type: 'submit',
|
||||
projectDir,
|
||||
});
|
||||
for (const startedBuild of startedBuilds) {
|
||||
const submitProfile = nullthrows(
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
UpdateChannelBasicInfoFragment,
|
||||
ViewUpdateChannelsPaginatedOnAppQuery,
|
||||
} from '../../graphql/generated';
|
||||
import { UpdateBranchObject, UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
|
||||
|
||||
export const testUpdateBranch1: UpdateBranchObject = {
|
||||
@@ -60,7 +64,7 @@ export const testUpdateBranch1: UpdateBranchObject = {
|
||||
__typename: 'UpdateBranch',
|
||||
};
|
||||
|
||||
const testUpdateBranch2: UpdateBranchObject = {
|
||||
export const testUpdateBranch2: UpdateBranchObject = {
|
||||
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
|
||||
name: 'production',
|
||||
updateGroups: [
|
||||
@@ -128,3 +132,53 @@ export const testChannelObject: UpdateChannelObject = {
|
||||
updateBranches: [testUpdateBranch1, testUpdateBranch2],
|
||||
__typename: 'UpdateChannel',
|
||||
};
|
||||
|
||||
export const testBasicChannelInfo: UpdateChannelBasicInfoFragment = {
|
||||
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
|
||||
name: 'production',
|
||||
branchMapping:
|
||||
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
|
||||
__typename: 'UpdateChannel',
|
||||
};
|
||||
|
||||
export const testBasicChannelInfo2: UpdateChannelBasicInfoFragment = {
|
||||
id: '9d5c7bf0-d52d-474c-9140-c1bad7f0de9d',
|
||||
name: 'staging',
|
||||
branchMapping:
|
||||
'{"data":[{"branchId":"d7d68e32-d9c9-4a8d-8d1b-21e53100a5e8","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"f9f708c2-0c91-4360-b2a4-0b61834aef4a","branchMappingLogic":"true"}],"version":0}',
|
||||
__typename: 'UpdateChannel',
|
||||
};
|
||||
|
||||
export const paginatedChannels: ViewUpdateChannelsPaginatedOnAppQuery['app']['byId']['channelsPaginated'] =
|
||||
{
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '9d5c7bf0-d52d-474c-9140-c1bad7f0de9d',
|
||||
name: 'staging',
|
||||
branchMapping:
|
||||
'{"data":[{"branchId":"d7d68e32-d9c9-4a8d-8d1b-21e53100a5e8","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"f9f708c2-0c91-4360-b2a4-0b61834aef4a","branchMappingLogic":"true"}],"version":0}',
|
||||
__typename: 'UpdateChannel',
|
||||
},
|
||||
__typename: 'AppChannelEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '95630b3c-5b91-48cb-bd31-a84315a13246',
|
||||
name: 'gh/quinlanj/3/orig',
|
||||
branchMapping:
|
||||
'{"data":[{"branchId":"d7d68e32-d9c9-4a8d-8d1b-21e53100a5e8","branchMappingLogic":"true"}],"version":0}',
|
||||
__typename: 'UpdateChannel',
|
||||
},
|
||||
__typename: 'AppChannelEdge',
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: 'IjlkNWM3YmYwLWQ1MmQtNDc0Yy05MTQwLWMxYmFkN2YwZGU5ZCI',
|
||||
endCursor: 'ImE4Y2M5ZWVmLTFkZjMtNDNmZi05YTZkLTRmYTZjMTFlYmEwYiI',
|
||||
__typename: 'PageInfo',
|
||||
},
|
||||
__typename: 'AppChannelsConnection',
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
import { EASUpdateAction, EASUpdateContext, NonInteractiveError } from '../../eas-update/utils';
|
||||
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { getChannelsDatasetAsync } from '../queries';
|
||||
|
||||
/**
|
||||
* Select a channel for the project.
|
||||
*
|
||||
* @constructor
|
||||
* @param {function} options.filterPredicate - A predicate to filter the channels that are shown to the user. It takes a channelInfo object as a parameter and returns a boolean.
|
||||
* @param {string} options.printedType - The type of channel printed to the user. Defaults to 'channel'.
|
||||
*/
|
||||
export class SelectChannel implements EASUpdateAction<UpdateChannelBasicInfoFragment | null> {
|
||||
constructor(
|
||||
private options: {
|
||||
filterPredicate?: (channelInfo: UpdateChannelBasicInfoFragment) => boolean;
|
||||
printedType?: string;
|
||||
} = {}
|
||||
) {}
|
||||
public async runAsync(ctx: EASUpdateContext): Promise<UpdateChannelBasicInfoFragment | null> {
|
||||
const { nonInteractive, graphqlClient, app } = ctx;
|
||||
const { projectId } = app;
|
||||
const { filterPredicate } = this.options;
|
||||
const printedType = this.options.printedType ?? 'channel';
|
||||
if (nonInteractive) {
|
||||
throw new NonInteractiveError(
|
||||
`${printedType} selection cannot be run in non-interactive mode.`
|
||||
);
|
||||
}
|
||||
|
||||
const channels = await getChannelsDatasetAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
filterPredicate,
|
||||
});
|
||||
|
||||
if (channels.length === 0) {
|
||||
return null;
|
||||
} else if (channels.length === 1) {
|
||||
return channels[0];
|
||||
}
|
||||
|
||||
const { channel: selectedChannel } = await promptAsync({
|
||||
type: 'select',
|
||||
name: 'channel',
|
||||
message: `Select a ${printedType}`,
|
||||
choices: channels.map(channel => ({
|
||||
value: channel,
|
||||
title: channel.name,
|
||||
})),
|
||||
});
|
||||
return selectedChannel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||
import { NonInteractiveError } from '../../../eas-update/utils';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { testBasicChannelInfo, testBasicChannelInfo2 } from '../../__tests__/fixtures';
|
||||
import { getChannelsDatasetAsync } from '../../queries';
|
||||
import { SelectChannel } from '../SelectChannel';
|
||||
|
||||
jest.mock('../../queries');
|
||||
jest.mock('../../../prompts');
|
||||
describe(SelectChannel, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('returns null if project has no channels', async () => {
|
||||
jest.mocked(getChannelsDatasetAsync).mockResolvedValue([]);
|
||||
const ctx = createCtxMock();
|
||||
const selectChannel = new SelectChannel();
|
||||
const channel = await selectChannel.runAsync(ctx);
|
||||
expect(channel).toBeNull();
|
||||
});
|
||||
it('returns the channel without prompting if project only one channel', async () => {
|
||||
jest.mocked(getChannelsDatasetAsync).mockResolvedValue([testBasicChannelInfo]);
|
||||
const ctx = createCtxMock();
|
||||
const selectChannel = new SelectChannel();
|
||||
const channel = await selectChannel.runAsync(ctx);
|
||||
expect(channel).toBe(testBasicChannelInfo);
|
||||
});
|
||||
it('returns the selected channel', async () => {
|
||||
jest
|
||||
.mocked(getChannelsDatasetAsync)
|
||||
.mockResolvedValue([testBasicChannelInfo, testBasicChannelInfo2]);
|
||||
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||
channel: testBasicChannelInfo,
|
||||
}));
|
||||
const ctx = createCtxMock();
|
||||
const selectChannel = new SelectChannel();
|
||||
const channel = await selectChannel.runAsync(ctx);
|
||||
expect(channel).toBe(testBasicChannelInfo);
|
||||
});
|
||||
it('throws an error in Non-Interactive Mode', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
});
|
||||
const selectChannel = new SelectChannel();
|
||||
|
||||
// dont fail if users are running in non-interactive mode
|
||||
await expect(selectChannel.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
|
||||
});
|
||||
});
|
||||
@@ -35,6 +35,26 @@ export type BranchMapping = {
|
||||
}[];
|
||||
};
|
||||
|
||||
export function getAlwaysTrueBranchMapping(branchId: string): BranchMapping {
|
||||
return {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId,
|
||||
branchMappingLogic: 'true',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function getBranchMapping(branchMappingString: string): BranchMapping {
|
||||
try {
|
||||
return JSON.parse(branchMappingString);
|
||||
} catch {
|
||||
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getNodesFromStatement(statement: BranchMappingStatement): BranchMappingNode[] {
|
||||
return statement.slice(1) as BranchMappingNode[];
|
||||
}
|
||||
@@ -73,6 +93,13 @@ export function hashLtOperator(): BranchMappingOperator {
|
||||
return 'hash_lt';
|
||||
}
|
||||
|
||||
export function assertStatement(node: BranchMappingNode): asserts node is BranchMappingStatement {
|
||||
assert(
|
||||
isStatement(node),
|
||||
'Branch mapping node must be a statement. Received: ' + JSON.stringify(node)
|
||||
);
|
||||
}
|
||||
|
||||
export function assertNodeObject(node: BranchMappingNode): asserts node is BranchMappingObject {
|
||||
assert(
|
||||
isNodeObject(node),
|
||||
@@ -83,3 +110,7 @@ export function assertNodeObject(node: BranchMappingNode): asserts node is Branc
|
||||
export function assertNumber(operand: string | number | string[]): asserts operand is number {
|
||||
assert(typeof operand === 'number', 'Expected a number. Received: ' + JSON.stringify(operand));
|
||||
}
|
||||
|
||||
export function assertString(operand: string | number | string[]): asserts operand is string {
|
||||
assert(typeof operand === 'string', 'Expected a string. Received: ' + JSON.stringify(operand));
|
||||
}
|
||||
@@ -68,6 +68,7 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
const results: { [key in Platform]?: string } = {};
|
||||
for (const { profile, platform } of buildProfiles) {
|
||||
|
||||
@@ -80,6 +80,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
for (const profileInfo of buildProfiles) {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class MetadataLint extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -49,6 +49,7 @@ export default class MetadataPull extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class MetadataPush extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -118,6 +118,7 @@ export default class Submit extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms,
|
||||
profileName: flagsWithPlatform.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
const submissions: SubmissionFragment[] = [];
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { testAppJson } from '../../credentials/__tests__/fixtures-constants';
|
||||
import { EASUpdateContext } from '../utils';
|
||||
|
||||
export function createCtxMock(options: { nonInteractive?: boolean } = {}): EASUpdateContext {
|
||||
return {
|
||||
graphqlClient: jest.fn() as any,
|
||||
nonInteractive: options.nonInteractive ?? false,
|
||||
app: { exp: testAppJson, projectId: 'test-project-id' },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
|
||||
export type EASUpdateContext = {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
nonInteractive: boolean;
|
||||
app: { exp: ExpoConfig; projectId: string };
|
||||
};
|
||||
|
||||
export interface EASUpdateAction<T = any> {
|
||||
runAsync(ctx: EASUpdateContext): Promise<T>;
|
||||
}
|
||||
|
||||
export class NonInteractiveError extends Error {}
|
||||
@@ -38,7 +38,7 @@ const AppExtensionsConfigSchema = Joi.array().items(
|
||||
})
|
||||
);
|
||||
|
||||
export async function resolveMangedProjectTargetsAsync({
|
||||
export async function resolveManagedProjectTargetsAsync({
|
||||
exp,
|
||||
projectDir,
|
||||
xcodeBuildContext,
|
||||
@@ -138,7 +138,7 @@ export async function resolveTargetsAsync(opts: ResolveTargetOptions): Promise<T
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
return await resolveBareProjectTargetsAsync(opts);
|
||||
} else if (workflow === Workflow.MANAGED) {
|
||||
return await resolveMangedProjectTargetsAsync(opts);
|
||||
return await resolveManagedProjectTargetsAsync(opts);
|
||||
} else {
|
||||
throw new Error(`Unknown workflow: ${workflow}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
testChannelObject,
|
||||
testUpdateBranch1,
|
||||
testUpdateBranch2,
|
||||
} from '../../channel/__tests__/fixtures';
|
||||
import {
|
||||
BranchMappingOperator,
|
||||
alwaysTrue,
|
||||
andStatement,
|
||||
equalsOperator,
|
||||
hashLtOperator,
|
||||
} from '../../channel/branch-mapping';
|
||||
import {
|
||||
editRolloutBranchMapping,
|
||||
getRollout,
|
||||
getRolloutInfoFromBranchMapping,
|
||||
isConstrainedRollout,
|
||||
isConstrainedRolloutInfo,
|
||||
isLegacyRolloutInfo,
|
||||
isRollout,
|
||||
} from '../branch-mapping';
|
||||
import {
|
||||
rolloutBranchMapping,
|
||||
rolloutBranchMappingLegacy,
|
||||
standardBranchMapping,
|
||||
} from './fixtures';
|
||||
|
||||
describe(isLegacyRolloutInfo, () => {
|
||||
it('classifies rollouts properly', () => {
|
||||
const constrainedRollout = getRolloutInfoFromBranchMapping(rolloutBranchMapping);
|
||||
expect(isLegacyRolloutInfo(constrainedRollout)).toBe(false);
|
||||
const legacyRollout = getRolloutInfoFromBranchMapping(rolloutBranchMappingLegacy);
|
||||
expect(isLegacyRolloutInfo(legacyRollout)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(isConstrainedRollout, () => {
|
||||
it('classifies rollouts properly', () => {
|
||||
const constrainedRollout = getRolloutInfoFromBranchMapping(rolloutBranchMapping);
|
||||
expect(isConstrainedRolloutInfo(constrainedRollout)).toBe(true);
|
||||
const legacyRollout = getRolloutInfoFromBranchMapping(rolloutBranchMappingLegacy);
|
||||
expect(isConstrainedRolloutInfo(legacyRollout)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(getRollout, () => {
|
||||
it('doesnt get the mapping if it isnt a rollout', () => {
|
||||
const notRollout = { ...testChannelObject };
|
||||
notRollout.branchMapping = JSON.stringify(standardBranchMapping);
|
||||
expect(() => getRollout(notRollout)).toThrowError();
|
||||
});
|
||||
it('gets a constrained rollout', () => {
|
||||
const constrainedRollout = { ...testChannelObject };
|
||||
const rolloutBranchMappingWithCorrectBranch = { ...rolloutBranchMapping };
|
||||
rolloutBranchMappingWithCorrectBranch.data[0].branchId = testUpdateBranch1.id;
|
||||
rolloutBranchMappingWithCorrectBranch.data[1].branchId = testUpdateBranch2.id;
|
||||
constrainedRollout.branchMapping = JSON.stringify(rolloutBranchMapping);
|
||||
|
||||
const rollout = getRollout(constrainedRollout);
|
||||
expect(rollout.percentRolledOut).toEqual(10);
|
||||
expect(isConstrainedRolloutInfo(rollout)).toBe(true);
|
||||
expect(rollout.defaultBranch).toEqual(testUpdateBranch2);
|
||||
expect(rollout.rolledOutBranch).toEqual(testUpdateBranch1);
|
||||
expect(rollout.defaultBranchId).toEqual(testUpdateBranch2.id);
|
||||
expect(rollout.rolledOutBranchId).toEqual(testUpdateBranch1.id);
|
||||
});
|
||||
it('gets a legacy rollout', () => {
|
||||
const rollout = getRollout(testChannelObject);
|
||||
expect(rollout.percentRolledOut).toEqual(15);
|
||||
expect(isLegacyRolloutInfo(rollout)).toBe(true);
|
||||
expect(rollout.defaultBranch).toEqual(testUpdateBranch2);
|
||||
expect(rollout.rolledOutBranch).toEqual(testUpdateBranch1);
|
||||
expect(rollout.defaultBranchId).toEqual(testUpdateBranch2.id);
|
||||
expect(rollout.rolledOutBranchId).toEqual(testUpdateBranch1.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe(getRolloutInfoFromBranchMapping, () => {
|
||||
it('doesnt get the mapping if it isnt a rollout', () => {
|
||||
expect(() => getRolloutInfoFromBranchMapping(standardBranchMapping)).toThrowError();
|
||||
});
|
||||
it('gets a constrained rollout', () => {
|
||||
const rollout = getRolloutInfoFromBranchMapping(rolloutBranchMapping);
|
||||
expect(rollout.percentRolledOut).toEqual(10);
|
||||
expect(isConstrainedRolloutInfo(rollout)).toBe(true);
|
||||
expect(rollout.defaultBranchId).toEqual(rolloutBranchMapping.data[1].branchId);
|
||||
expect(rollout.rolledOutBranchId).toEqual(rolloutBranchMapping.data[0].branchId);
|
||||
});
|
||||
it('gets a legacy rollout', () => {
|
||||
const rollout = getRolloutInfoFromBranchMapping(rolloutBranchMappingLegacy);
|
||||
expect(rollout.percentRolledOut).toEqual(10);
|
||||
expect(isLegacyRolloutInfo(rollout)).toBe(true);
|
||||
expect(rollout.defaultBranchId).toEqual(rolloutBranchMappingLegacy.data[1].branchId);
|
||||
expect(rollout.rolledOutBranchId).toEqual(rolloutBranchMappingLegacy.data[0].branchId);
|
||||
});
|
||||
});
|
||||
|
||||
describe(editRolloutBranchMapping, () => {
|
||||
it('doesnt edit the branch mapping if it isnt a rollout', () => {
|
||||
expect(() => editRolloutBranchMapping(standardBranchMapping, 50)).toThrowError();
|
||||
});
|
||||
it('doesnt edit the branch mapping if the input is out of range', () => {
|
||||
expect(() => editRolloutBranchMapping(standardBranchMapping, -1)).toThrowError();
|
||||
expect(() => editRolloutBranchMapping(standardBranchMapping, 101)).toThrowError();
|
||||
expect(() => editRolloutBranchMapping(standardBranchMapping, 0.1)).toThrowError();
|
||||
});
|
||||
it('edits the branch mapping for a constrained rollout', () => {
|
||||
const editedBranchMapping = editRolloutBranchMapping(rolloutBranchMapping, 50);
|
||||
const rollout = getRolloutInfoFromBranchMapping(editedBranchMapping);
|
||||
expect(rollout.percentRolledOut).toEqual(50);
|
||||
expect(isConstrainedRolloutInfo(rollout)).toBe(true);
|
||||
});
|
||||
it('edits the branch mapping for a legacy rollout', () => {
|
||||
const editedBranchMapping = editRolloutBranchMapping(rolloutBranchMappingLegacy, 50);
|
||||
const rollout = getRolloutInfoFromBranchMapping(editedBranchMapping);
|
||||
expect(rollout.percentRolledOut).toEqual(50);
|
||||
expect(isLegacyRolloutInfo(rollout)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(isRollout, () => {
|
||||
it('detects a rollout made by the cli', () => {
|
||||
expect(isRollout(rolloutBranchMappingLegacy)).toBe(true);
|
||||
expect(isRollout(rolloutBranchMapping)).toBe(true);
|
||||
});
|
||||
it('detects custom mappings equivalent to rollouts made by the cli', () => {
|
||||
const customMapping1 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping1)).toBe(true);
|
||||
});
|
||||
it('correctly classifies branchMappings that arent rollouts', () => {
|
||||
expect(isRollout(standardBranchMapping)).toBe(false);
|
||||
|
||||
const customMapping1 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: 'hash_gt' as BranchMappingOperator,
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping1)).toBe(false);
|
||||
|
||||
const customMapping2 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping2)).toBe(false);
|
||||
|
||||
const customMapping3 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping3)).toBe(false);
|
||||
|
||||
const customMapping4 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping4)).toBe(false);
|
||||
|
||||
const customMapping5 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
{
|
||||
operand: '2.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping5)).toBe(false);
|
||||
|
||||
const customMapping6 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping6)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
alwaysTrue,
|
||||
andStatement,
|
||||
equalsOperator,
|
||||
hashLtOperator,
|
||||
} from '../../channel/branch-mapping';
|
||||
|
||||
export const rolloutBranchMapping = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
|
||||
export const rolloutBranchMappingLegacy = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
|
||||
export const standardBranchMapping = {
|
||||
version: 0,
|
||||
data: [{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() }],
|
||||
};
|
||||
@@ -1,200 +0,0 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
BranchMappingOperator,
|
||||
alwaysTrue,
|
||||
andStatement,
|
||||
equalsOperator,
|
||||
hashLtOperator,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { isRollout } from '../utils';
|
||||
|
||||
const rolloutBranchMapping = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
|
||||
const rolloutBranchMappingLegacy = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
|
||||
const standardBranchMapping = {
|
||||
version: 0,
|
||||
data: [{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() }],
|
||||
};
|
||||
|
||||
describe(isRollout, () => {
|
||||
it('detects a rollout made by the cli', () => {
|
||||
expect(isRollout(rolloutBranchMappingLegacy)).toBe(true);
|
||||
expect(isRollout(rolloutBranchMapping)).toBe(true);
|
||||
});
|
||||
it('detects custom mappings equivalent to rollouts made by the cli', () => {
|
||||
const customMapping1 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping1)).toBe(true);
|
||||
});
|
||||
it('correctly classifies branchMappings that arent rollouts', () => {
|
||||
expect(isRollout(standardBranchMapping)).toBe(false);
|
||||
|
||||
const customMapping1 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: 'hash_gt' as BranchMappingOperator,
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping1)).toBe(false);
|
||||
|
||||
const customMapping2 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping2)).toBe(false);
|
||||
|
||||
const customMapping3 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping3)).toBe(false);
|
||||
|
||||
const customMapping4 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'FOOBAR',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping4)).toBe(false);
|
||||
|
||||
const customMapping5 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
{
|
||||
operand: '2.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping5)).toBe(false);
|
||||
|
||||
const customMapping6 = {
|
||||
version: 0,
|
||||
data: [
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isRollout(customMapping6)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import { SelectChannel } from '../../channel/actions/SelectChannel';
|
||||
import { getBranchMapping } from '../../channel/branch-mapping';
|
||||
import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
|
||||
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
|
||||
import { isRollout } from '../branch-mapping';
|
||||
|
||||
/**
|
||||
* Select an existing rollout for the project.
|
||||
*/
|
||||
export class SelectRollout implements EASUpdateAction<UpdateChannelBasicInfoFragment | null> {
|
||||
public async runAsync(ctx: EASUpdateContext): Promise<UpdateChannelBasicInfoFragment | null> {
|
||||
const rolloutFilterPredicate = (channelInfo: UpdateChannelBasicInfoFragment): boolean => {
|
||||
const branchMapping = getBranchMapping(channelInfo.branchMapping);
|
||||
return isRollout(branchMapping);
|
||||
};
|
||||
const selectChannel = new SelectChannel({
|
||||
printedType: 'rollout',
|
||||
filterPredicate: rolloutFilterPredicate,
|
||||
});
|
||||
return await selectChannel.runAsync(ctx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { testBasicChannelInfo, testBasicChannelInfo2 } from '../../../channel/__tests__/fixtures';
|
||||
import { getChannelsDatasetAsync } from '../../../channel/queries';
|
||||
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||
import { NonInteractiveError } from '../../../eas-update/utils';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { standardBranchMapping } from '../../__tests__/fixtures';
|
||||
import { SelectRollout } from '../SelectRollout';
|
||||
|
||||
jest.mock('../../../channel/queries');
|
||||
jest.mock('../../../prompts');
|
||||
describe(SelectRollout, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('returns null if project has no rollouts', async () => {
|
||||
jest.mocked(getChannelsDatasetAsync).mockResolvedValue([]);
|
||||
const ctx = createCtxMock();
|
||||
const selectRollout = new SelectRollout();
|
||||
const rollout = await selectRollout.runAsync(ctx);
|
||||
expect(rollout).toBeNull();
|
||||
});
|
||||
it('returns the rollout without prompting if project only one rollout', async () => {
|
||||
jest.mocked(getChannelsDatasetAsync).mockResolvedValue([testBasicChannelInfo]);
|
||||
const ctx = createCtxMock();
|
||||
const selectRollout = new SelectRollout();
|
||||
const rollout = await selectRollout.runAsync(ctx);
|
||||
expect(rollout).toBe(testBasicChannelInfo);
|
||||
});
|
||||
it('returns the selected rollout', async () => {
|
||||
jest
|
||||
.mocked(getChannelsDatasetAsync)
|
||||
.mockResolvedValue([testBasicChannelInfo, testBasicChannelInfo2]);
|
||||
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||
channel: testBasicChannelInfo,
|
||||
}));
|
||||
const ctx = createCtxMock();
|
||||
const selectRollout = new SelectRollout();
|
||||
const rollout = await selectRollout.runAsync(ctx);
|
||||
expect(rollout).toBe(testBasicChannelInfo);
|
||||
});
|
||||
it('filters the channels for rollouts', async () => {
|
||||
const standardBranchMappingChannel = { ...testBasicChannelInfo };
|
||||
standardBranchMappingChannel.branchMapping = JSON.stringify(standardBranchMapping);
|
||||
const mockDataset = [standardBranchMappingChannel];
|
||||
jest.mocked(getChannelsDatasetAsync).mockImplementation((_gqlClient, args: any) => {
|
||||
const filterPredicate = args.filterPredicate as any;
|
||||
return mockDataset.filter(filterPredicate) as any;
|
||||
});
|
||||
const ctx = createCtxMock();
|
||||
const selectRollout = new SelectRollout();
|
||||
const rollout = await selectRollout.runAsync(ctx);
|
||||
expect(rollout).toBeNull();
|
||||
});
|
||||
it('throws an error in Non-Interactive Mode', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
});
|
||||
const selectRollout = new SelectRollout();
|
||||
|
||||
// dont fail if users are running in non-interactive mode
|
||||
await expect(selectRollout.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,265 @@
|
||||
import assert from 'assert';
|
||||
|
||||
import {
|
||||
BranchMapping,
|
||||
BranchMappingNode,
|
||||
assertNodeObject,
|
||||
assertNumber,
|
||||
assertStatement,
|
||||
assertString,
|
||||
getBranchMapping,
|
||||
getNodesFromStatement,
|
||||
isAlwaysTrue,
|
||||
isAndStatement,
|
||||
isStatement,
|
||||
} from '../channel/branch-mapping';
|
||||
import { getUpdateBranch } from '../channel/utils';
|
||||
import { UpdateChannelBasicInfoFragment } from '../graphql/generated';
|
||||
import { UpdateBranchObject, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||
|
||||
export type Rollout = LegacyRollout | ConstrainedRollout;
|
||||
export type RolloutInfo = LegacyRolloutInfo | ConstrainedRolloutInfo;
|
||||
type ConstrainedRollout = LegacyRollout & {
|
||||
runtimeVersion: string;
|
||||
};
|
||||
|
||||
type LegacyRollout = {
|
||||
rolledOutBranch: UpdateBranchObject;
|
||||
defaultBranch: UpdateBranchObject;
|
||||
} & LegacyRolloutInfo;
|
||||
|
||||
type ConstrainedRolloutInfo = LegacyRolloutInfo & {
|
||||
runtimeVersion: string;
|
||||
};
|
||||
|
||||
type LegacyRolloutInfo = {
|
||||
rolledOutBranchId: string;
|
||||
percentRolledOut: number;
|
||||
defaultBranchId: string;
|
||||
};
|
||||
|
||||
export function isLegacyRolloutInfo(rollout: RolloutInfo): rollout is LegacyRolloutInfo {
|
||||
return !isConstrainedRolloutInfo(rollout);
|
||||
}
|
||||
|
||||
export function isConstrainedRolloutInfo(rollout: RolloutInfo): rollout is ConstrainedRolloutInfo {
|
||||
return 'runtimeVersion' in rollout;
|
||||
}
|
||||
|
||||
export function isConstrainedRollout(rollout: Rollout): rollout is ConstrainedRollout {
|
||||
return isConstrainedRolloutInfo(rollout);
|
||||
}
|
||||
|
||||
export function getRolloutInfo(basicChannelInfo: UpdateChannelBasicInfoFragment): RolloutInfo {
|
||||
const branchMapping = getBranchMapping(basicChannelInfo.branchMapping);
|
||||
assertRollout(branchMapping);
|
||||
return getRolloutInfoFromBranchMapping(branchMapping);
|
||||
}
|
||||
|
||||
export function getRolloutInfoFromBranchMapping(branchMapping: BranchMapping): RolloutInfo {
|
||||
assertRollout(branchMapping);
|
||||
const rolledOutBranchId = branchMapping.data[0].branchId;
|
||||
const defaultBranchId = branchMapping.data[1].branchId;
|
||||
|
||||
if (isRtvConstrainedRollout(branchMapping)) {
|
||||
const statementNode = branchMapping.data[0].branchMappingLogic;
|
||||
assertStatement(statementNode);
|
||||
const nodesFromStatement = getNodesFromStatement(statementNode);
|
||||
|
||||
const runtimeVersionNode = nodesFromStatement.find(isRuntimeVersionNode);
|
||||
assert(runtimeVersionNode, 'Runtime version node must be defined.');
|
||||
assertNodeObject(runtimeVersionNode);
|
||||
const runtimeVersion = runtimeVersionNode.operand;
|
||||
assertString(runtimeVersion);
|
||||
|
||||
const rolloutNode = nodesFromStatement.find(isRolloutNode);
|
||||
assert(rolloutNode, 'Rollout node must be defined.');
|
||||
assertNodeObject(rolloutNode);
|
||||
const operand = rolloutNode.operand;
|
||||
assertNumber(operand);
|
||||
return {
|
||||
rolledOutBranchId,
|
||||
percentRolledOut: operand * 100,
|
||||
runtimeVersion,
|
||||
defaultBranchId,
|
||||
};
|
||||
} else {
|
||||
const rolloutNode = branchMapping.data[0].branchMappingLogic;
|
||||
assertNodeObject(rolloutNode);
|
||||
const operand = rolloutNode.operand;
|
||||
assertNumber(operand);
|
||||
return {
|
||||
rolledOutBranchId,
|
||||
percentRolledOut: operand * 100,
|
||||
defaultBranchId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getRollout(channel: UpdateChannelObject): Rollout {
|
||||
const branchMapping = getBranchMapping(channel.branchMapping);
|
||||
assertRollout(branchMapping);
|
||||
const rolledOutBranchId = branchMapping.data[0].branchId;
|
||||
const rolledOutBranch = getUpdateBranch(channel, rolledOutBranchId);
|
||||
const defaultBranchId = branchMapping.data[1].branchId;
|
||||
const defaultBranch = getUpdateBranch(channel, defaultBranchId);
|
||||
|
||||
const rolloutInfo = getRolloutInfo(channel);
|
||||
return {
|
||||
...rolloutInfo,
|
||||
rolledOutBranch,
|
||||
defaultBranch,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if a branch mapping is a rollout.
|
||||
*
|
||||
* Types of rollout:
|
||||
* 1. Legacy unconstrained rollout:
|
||||
* Maps to a rollout branch via a rollout token
|
||||
* Falls back to a default branch
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
}
|
||||
*
|
||||
* 2. RTV constrained rollout:
|
||||
* Maps to a rollout branch via a rollout token, constrained by runtime version
|
||||
* Falls back to a default branch
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
version: 0,
|
||||
data: [
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
}
|
||||
*/
|
||||
export function isRollout(branchMapping: BranchMapping): boolean {
|
||||
return isUnconstrainedRollout(branchMapping) || isRtvConstrainedRollout(branchMapping);
|
||||
}
|
||||
|
||||
export function editRolloutBranchMapping(
|
||||
branchMapping: BranchMapping,
|
||||
percent: number
|
||||
): BranchMapping {
|
||||
assert(
|
||||
Number.isInteger(percent) && percent >= 0 && percent <= 100,
|
||||
'The rollout percentage must be an integer between 0 and 100 inclusive.'
|
||||
);
|
||||
assertRollout(branchMapping);
|
||||
if (isRtvConstrainedRollout(branchMapping)) {
|
||||
return editRtvConstrainedRollout(branchMapping, percent);
|
||||
} else {
|
||||
return editLegacyRollout(branchMapping, percent);
|
||||
}
|
||||
}
|
||||
|
||||
function editRtvConstrainedRollout(branchMapping: BranchMapping, percent: number): BranchMapping {
|
||||
const newBranchMapping = { ...branchMapping };
|
||||
const statementNode = newBranchMapping.data[0].branchMappingLogic;
|
||||
assertStatement(statementNode);
|
||||
const nodesFromStatement = getNodesFromStatement(statementNode);
|
||||
|
||||
const rolloutNode = nodesFromStatement.find(isRolloutNode);
|
||||
assert(rolloutNode, 'Rollout node must be defined.');
|
||||
assertNodeObject(rolloutNode);
|
||||
rolloutNode.operand = percent / 100;
|
||||
return newBranchMapping;
|
||||
}
|
||||
|
||||
function editLegacyRollout(branchMapping: BranchMapping, percent: number): BranchMapping {
|
||||
const newBranchMapping = { ...branchMapping };
|
||||
const rolloutNode = newBranchMapping.data[0].branchMappingLogic;
|
||||
assertNodeObject(rolloutNode);
|
||||
rolloutNode.operand = percent / 100;
|
||||
return newBranchMapping;
|
||||
}
|
||||
|
||||
function assertRollout(branchMapping: BranchMapping): asserts branchMapping is BranchMapping {
|
||||
assert(
|
||||
isRollout(branchMapping),
|
||||
'Branch mapping node must be a rollout. Received: ' + JSON.stringify(branchMapping)
|
||||
);
|
||||
}
|
||||
|
||||
function isRtvConstrainedRollout(branchMapping: BranchMapping): boolean {
|
||||
if (branchMapping.data.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRtvRolloutNode = isRtvConstrainedRolloutNode(branchMapping.data[0].branchMappingLogic);
|
||||
const defaultsToAlwaysTrueNode = isAlwaysTrue(branchMapping.data[1].branchMappingLogic);
|
||||
|
||||
return hasRtvRolloutNode && defaultsToAlwaysTrueNode;
|
||||
}
|
||||
|
||||
function isRtvConstrainedRolloutNode(node: BranchMappingNode): boolean {
|
||||
if (!isStatement(node) || !isAndStatement(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const statementNodes = getNodesFromStatement(node);
|
||||
if (statementNodes.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRuntimeVersionNode = statementNodes.some(isRuntimeVersionNode);
|
||||
const hasRolloutNode = statementNodes.some(isRolloutNode);
|
||||
return hasRuntimeVersionNode && hasRolloutNode;
|
||||
}
|
||||
|
||||
function isUnconstrainedRollout(branchMapping: BranchMapping): boolean {
|
||||
if (branchMapping.data.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRolloutNode = isRolloutNode(branchMapping.data[0].branchMappingLogic);
|
||||
const defaultsToAlwaysTrueNode = isAlwaysTrue(branchMapping.data[1].branchMappingLogic);
|
||||
return hasRolloutNode && defaultsToAlwaysTrueNode;
|
||||
}
|
||||
|
||||
function isRuntimeVersionNode(node: BranchMappingNode): boolean {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
return false;
|
||||
}
|
||||
return node.clientKey === 'runtimeVersion' && node.branchMappingOperator === '==';
|
||||
}
|
||||
|
||||
function isRolloutNode(node: BranchMappingNode): boolean {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
return false;
|
||||
}
|
||||
return node.clientKey === 'rolloutToken' && node.branchMappingOperator === 'hash_lt';
|
||||
}
|
||||
@@ -1,116 +1,50 @@
|
||||
import {
|
||||
BranchMapping,
|
||||
BranchMappingNode,
|
||||
getNodesFromStatement,
|
||||
isAlwaysTrue,
|
||||
isAndStatement,
|
||||
isStatement,
|
||||
} from '../channel/branch-mapping';
|
||||
import chalk from 'chalk';
|
||||
|
||||
/**
|
||||
* Detect if a branch mapping is a rollout.
|
||||
*
|
||||
* Types of rollout:
|
||||
* 1. Legacy unconstrained rollout:
|
||||
* Maps to a rollout branch via a rollout token
|
||||
* Falls back to a default branch
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
version: 0,
|
||||
data: [
|
||||
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||
import Log from '../log';
|
||||
import formatFields from '../utils/formatFields';
|
||||
import { getRollout, isConstrainedRollout } from './branch-mapping';
|
||||
|
||||
export function printRollout(channel: UpdateChannelObject): void {
|
||||
const rollout = getRollout(channel);
|
||||
const rolledOutPercent = rollout.percentRolledOut;
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Rollout:'));
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Channel', value: channel.name },
|
||||
...(isConstrainedRollout(rollout)
|
||||
? [{ label: 'Runtime Version', value: rollout.runtimeVersion }]
|
||||
: []),
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: {
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
label: 'Branches',
|
||||
value: `${rollout.rolledOutBranch.name} (${rolledOutPercent}%), ${
|
||||
rollout.defaultBranch.name
|
||||
} (${100 - rolledOutPercent}%)`,
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
}
|
||||
*
|
||||
* 2. RTV constrained rollout:
|
||||
* Maps to a rollout branch via a rollout token, constrained by runtime version
|
||||
* Falls back to a default branch
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
version: 0,
|
||||
data: [
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
|
||||
export function printBranch(channel: UpdateChannelObject): void {
|
||||
const rollout = getRollout(channel);
|
||||
const rolledOutPercent = rollout.percentRolledOut;
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Rollout:'));
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Channel', value: channel.name },
|
||||
...(isConstrainedRollout(rollout)
|
||||
? [{ label: 'Runtime Version', value: rollout.runtimeVersion }]
|
||||
: []),
|
||||
{
|
||||
branchId: uuidv4(),
|
||||
branchMappingLogic: andStatement([
|
||||
{
|
||||
operand: '1.0.0',
|
||||
clientKey: 'runtimeVersion',
|
||||
branchMappingOperator: equalsOperator(),
|
||||
},
|
||||
{
|
||||
operand: 10 / 100,
|
||||
clientKey: 'rolloutToken',
|
||||
branchMappingOperator: hashLtOperator(),
|
||||
},
|
||||
]),
|
||||
label: 'Branches',
|
||||
value: `${rollout.rolledOutBranch.name} (${rolledOutPercent}%), ${
|
||||
rollout.defaultBranch.name
|
||||
} (${100 - rolledOutPercent}%)`,
|
||||
},
|
||||
{ branchId: uuidv4(), branchMappingLogic: alwaysTrue() },
|
||||
],
|
||||
}
|
||||
*/
|
||||
export function isRollout(branchMapping: BranchMapping): boolean {
|
||||
return isUnconstrainedRollout(branchMapping) || isRtvConstrainedRollout(branchMapping);
|
||||
}
|
||||
|
||||
function isRtvConstrainedRollout(branchMapping: BranchMapping): boolean {
|
||||
if (branchMapping.data.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRtvRolloutNode = isRtvConstrainedRolloutNode(branchMapping.data[0].branchMappingLogic);
|
||||
const defaultsToAlwaysTrueNode = isAlwaysTrue(branchMapping.data[1].branchMappingLogic);
|
||||
|
||||
return hasRtvRolloutNode && defaultsToAlwaysTrueNode;
|
||||
}
|
||||
|
||||
function isRtvConstrainedRolloutNode(node: BranchMappingNode): boolean {
|
||||
if (!isStatement(node) || !isAndStatement(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const statementNodes = getNodesFromStatement(node);
|
||||
if (statementNodes.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRuntimeVersionNode = statementNodes.some(isRuntimeVersionNode);
|
||||
const hasRolloutNode = statementNodes.some(isRolloutNode);
|
||||
return hasRuntimeVersionNode && hasRolloutNode;
|
||||
}
|
||||
|
||||
function isUnconstrainedRollout(branchMapping: BranchMapping): boolean {
|
||||
if (branchMapping.data.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
const hasRolloutNode = isRolloutNode(branchMapping.data[0].branchMappingLogic);
|
||||
const defaultsToAlwaysTrueNode = isAlwaysTrue(branchMapping.data[1].branchMappingLogic);
|
||||
return hasRolloutNode && defaultsToAlwaysTrueNode;
|
||||
}
|
||||
|
||||
function isRuntimeVersionNode(node: BranchMappingNode): boolean {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
return false;
|
||||
}
|
||||
return node.clientKey === 'runtimeVersion' && node.branchMappingOperator === '==';
|
||||
}
|
||||
|
||||
function isRolloutNode(node: BranchMappingNode): boolean {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (Array.isArray(node)) {
|
||||
return false;
|
||||
}
|
||||
return node.clientKey === 'rolloutToken' && node.branchMappingOperator === 'hash_lt';
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonAccessor, EasJsonUtils, errors } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
|
||||
import Log from '../../log';
|
||||
import { selectAsync } from '../../prompts';
|
||||
@@ -9,6 +12,7 @@ import {
|
||||
maybePrintBuildProfileDeprecationWarningsAsync,
|
||||
} from '../profiles';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../prompts');
|
||||
jest.mock('@expo/eas-json', () => {
|
||||
const actual = jest.requireActual('@expo/eas-json');
|
||||
@@ -32,6 +36,7 @@ const getBuildProfileDeprecationWarningsAsync = jest.spyOn(
|
||||
);
|
||||
const newLineSpy = jest.spyOn(Log, 'newLine');
|
||||
const warnSpy = jest.spyOn(Log, 'warn');
|
||||
const projectDir = '/app';
|
||||
|
||||
describe(getProfilesAsync, () => {
|
||||
afterEach(() => {
|
||||
@@ -51,6 +56,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profileName).toBe('production');
|
||||
@@ -71,6 +77,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
})
|
||||
).rejects.toThrowError(errors.MissingProfileError);
|
||||
});
|
||||
@@ -86,6 +93,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profileName).toBe('custom-profile');
|
||||
@@ -98,6 +106,52 @@ describe(getProfilesAsync, () => {
|
||||
expect(getBuildProfileAsync).toBeCalledWith(easJsonAccessor, Platform.IOS, 'custom-profile');
|
||||
});
|
||||
|
||||
describe('node version', () => {
|
||||
const nodeVersion = '14.17.1';
|
||||
|
||||
it('is read from profile', async () => {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
getBuildProfileAsync.mockImplementation(async () => {
|
||||
return { node: nodeVersion } as BuildProfile<Platform.ANDROID>;
|
||||
});
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profile.node).toBe(nodeVersion);
|
||||
});
|
||||
|
||||
describe('with .nvmrc', () => {
|
||||
beforeEach(async () => {
|
||||
vol.reset();
|
||||
await fs.mkdirp(os.tmpdir());
|
||||
|
||||
vol.fromJSON({ '.nvmrc': nodeVersion }, projectDir);
|
||||
});
|
||||
|
||||
it('is read from .nvmrc', async () => {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
getBuildProfileAsync.mockImplementation(async () => {
|
||||
return {} as BuildProfile<Platform.ANDROID>;
|
||||
});
|
||||
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profile.node).toBe(nodeVersion);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('throws validation error if eas.json is invalid', async () => {
|
||||
getBuildProfileAsync.mockImplementation(() => {
|
||||
throw new errors.InvalidEasJsonError('eas.json is not valid');
|
||||
@@ -109,6 +163,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
})
|
||||
).rejects.toThrowError(/eas.json is not valid/);
|
||||
});
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
ProfileType,
|
||||
SubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import semver from 'semver';
|
||||
|
||||
import Log, { learnMore } from '../log';
|
||||
|
||||
@@ -24,19 +27,23 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
platforms,
|
||||
profileName,
|
||||
type,
|
||||
projectDir,
|
||||
}: {
|
||||
easJsonAccessor: EasJsonAccessor;
|
||||
platforms: Platform[];
|
||||
profileName?: string;
|
||||
projectDir: string;
|
||||
type: T;
|
||||
}): Promise<ProfileData<T>[]> {
|
||||
const results = platforms.map(async function (platform) {
|
||||
const profile = await readProfileAsync({
|
||||
const profile = await readProfileWithOverridesAsync({
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
type,
|
||||
profileName,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
return {
|
||||
profile,
|
||||
profileName: profileName ?? 'production',
|
||||
@@ -47,16 +54,53 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
return await Promise.all(results);
|
||||
}
|
||||
|
||||
async function readProfileAsync<T extends ProfileType>({
|
||||
async function setNodeVersionFromFileAsync(
|
||||
projectDir: string,
|
||||
profile: BuildProfile<Platform>
|
||||
): Promise<void> {
|
||||
if (profile?.node) {
|
||||
return;
|
||||
}
|
||||
const nodeVersion = await getNodeVersionFromFileAsync(projectDir);
|
||||
if (nodeVersion) {
|
||||
Log.log(
|
||||
`The EAS build profile does not specify a Node.js version. Using the version specified in .nvmrc: ${nodeVersion} `
|
||||
);
|
||||
|
||||
profile.node = nodeVersion;
|
||||
}
|
||||
}
|
||||
|
||||
async function getNodeVersionFromFileAsync(projectDir: string): Promise<string | undefined> {
|
||||
const nvmrcPath = path.join(projectDir, '.nvmrc');
|
||||
if (!(await fs.pathExists(nvmrcPath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodeVersion: string;
|
||||
try {
|
||||
nodeVersion = (await fs.readFile(nvmrcPath, 'utf8')).toString().trim();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
if (!semver.valid(nodeVersion)) {
|
||||
throw new Error(`Invalid node version in .nvmrc: ${nodeVersion}`);
|
||||
}
|
||||
return nodeVersion;
|
||||
}
|
||||
|
||||
async function readProfileWithOverridesAsync<T extends ProfileType>({
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
type,
|
||||
profileName,
|
||||
projectDir,
|
||||
}: {
|
||||
easJsonAccessor: EasJsonAccessor;
|
||||
platform: Platform;
|
||||
type: T;
|
||||
profileName?: string;
|
||||
projectDir: string;
|
||||
}): Promise<EasProfile<T>> {
|
||||
if (type === 'build') {
|
||||
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
@@ -66,6 +110,7 @@ async function readProfileAsync<T extends ProfileType>({
|
||||
);
|
||||
|
||||
await maybePrintBuildProfileDeprecationWarningsAsync(easJsonAccessor, platform, profileName);
|
||||
await setNodeVersionFromFileAsync(projectDir, buildProfile);
|
||||
|
||||
return buildProfile as EasProfile<T>;
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "3.17.0",
|
||||
"version": "3.18.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.18.6",
|
||||
"@expo/eas-build-job": "1.0.13",
|
||||
"@expo/eas-build-job": "1.0.30",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "10.1.0",
|
||||
|
||||
@@ -1395,13 +1395,13 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@expo/[email protected].13":
|
||||
version "1.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.13.tgz#1cd3981e86e1d4235f20a96f7ac768793b4f7c62"
|
||||
integrity sha512-Q0i3GposBj/b2OPL0CV00HczawtslorNpoJSBQfB4aNR6i7DBIZZ1jWJtrFP8+zHZqlUi4d21aoblNUrKpgBew==
|
||||
"@expo/[email protected]0":
|
||||
version "1.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.30.tgz#f8c57a673a2cc328f4041346996c480c3d910e92"
|
||||
integrity sha512-KQGn9POn5p3guMNQJ5ykG3wffRlSEpjTCLO87d5lJll9E4u1grpqHJH2chOh5ICxg7tiWcO4iXt5S8tU7Ogpig==
|
||||
dependencies:
|
||||
joi "^17.7.0"
|
||||
semver "^7.3.8"
|
||||
joi "^17.9.2"
|
||||
semver "^7.5.4"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "0.3.22"
|
||||
@@ -1438,10 +1438,10 @@
|
||||
json5 "^2.2.2"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/[email protected].21":
|
||||
version "1.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@expo/logger/-/logger-1.0.21.tgz#9271b52b8e27159cbbe3385b9a01fca3317b37c2"
|
||||
integrity sha512-Vz+LHDGWUfowAwL10nBKqxrMYvO4qdZ8hLKcqUYc5mXBt+jmv7RP+8fcsMIejJEMWmYC2meO5eeCMLKKhEIgMA==
|
||||
"@expo/[email protected].30":
|
||||
version "1.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@expo/logger/-/logger-1.0.30.tgz#f964cc7b73cbb0c05de44c874a9451f5cfd2ec2d"
|
||||
integrity sha512-8wEUDZNLAv+Wi4jvo0kFErQ7nc4ZFfArq5GDeSCoIrRfdKK8baot7YJoJVsmpQ9/JXnTEAgwGDP3vYaR5wzcyg==
|
||||
dependencies:
|
||||
"@types/bunyan" "^1.8.8"
|
||||
bunyan "^1.8.15"
|
||||
@@ -1479,23 +1479,6 @@
|
||||
split "^1.0.1"
|
||||
sudo-prompt "9.1.1"
|
||||
|
||||
"@expo/package-manager@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.0.2.tgz#6c5fd0ee9b3d28c5523b2484c62c6c7b0c8dbf89"
|
||||
integrity sha512-dlUp6o8qs1mi3/+l3y7cY3oMoqQVVzvH18cUTi6+t4ob8XwTpaeP2SwOP+obwZN29dMg9YzZAv4eQz+mshAbQA==
|
||||
dependencies:
|
||||
"@expo/json-file" "^8.2.37"
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
ansi-regex "^5.0.0"
|
||||
chalk "^4.0.0"
|
||||
find-up "^5.0.0"
|
||||
find-yarn-workspace-root "~2.0.0"
|
||||
js-yaml "^3.13.1"
|
||||
micromatch "^4.0.2"
|
||||
npm-package-arg "^7.0.0"
|
||||
split "^1.0.1"
|
||||
sudo-prompt "9.1.1"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.0.8.tgz#40ddde3007d90d4fd58779670f1aabfa82ce2950"
|
||||
@@ -1586,29 +1569,28 @@
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/spawn-async@^1.7.0":
|
||||
"@expo/spawn-async@^1.7.2":
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58"
|
||||
integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/[email protected].28":
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.28.tgz#fa44032e58f216239442ec66edef31cde31a7aed"
|
||||
integrity sha512-HyYzc9b6oftTGLoH73RD2ZK/h0dbC0aSezazcf1yvvhfHJpVQrpI0wZ3DHXEJocVSNGykH+RUHmoORVknkFJFA==
|
||||
"@expo/[email protected].31":
|
||||
version "1.0.31"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.31.tgz#61e0456e54bc63bd714aa40b9be27503c032caf0"
|
||||
integrity sha512-TPuaihUQ9MfVmgG5OzFdMKXcBAgv1dfLZWGpk/k5hWBjPZAoe01UXbw8GCt6F09tS2Z256d2G/kOTJzISLNcjQ==
|
||||
dependencies:
|
||||
"@expo/logger" "1.0.21"
|
||||
"@expo/package-manager" "^1.0.2"
|
||||
"@expo/spawn-async" "^1.7.0"
|
||||
"@expo/logger" "1.0.30"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
arg "^5.0.2"
|
||||
fs-extra "^11.1.1"
|
||||
joi "^17.7.0"
|
||||
joi "^17.9.2"
|
||||
lodash.clonedeep "^4.5.0"
|
||||
lodash.get "^4.4.2"
|
||||
this-file "^2.0.3"
|
||||
uuid "^9.0.0"
|
||||
yaml "^2.2.1"
|
||||
yaml "^2.3.1"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.0"
|
||||
@@ -9392,10 +9374,10 @@ [email protected]:
|
||||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
joi@^17.7.0:
|
||||
version "17.8.3"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.8.3.tgz#d772fe27a87a5cda21aace5cf11eee8671ca7e6f"
|
||||
integrity sha512-q5Fn6Tj/jR8PfrLrx4fpGH4v9qM6o+vDUfD4/3vxxyg34OmKcNqYZ1qn2mpLza96S8tL0p0rIw2gOZX+/cTg9w==
|
||||
joi@^17.9.2:
|
||||
version "17.9.2"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.9.2.tgz#8b2e4724188369f55451aebd1d0b1d9482470690"
|
||||
integrity sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
"@hapi/topo" "^5.0.0"
|
||||
@@ -12135,6 +12117,13 @@ semver@^7.3.7:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.5.4:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
[email protected]:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
|
||||
@@ -13863,10 +13852,10 @@ yaml@^1.10.0, yaml@^1.7.2:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
||||
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
||||
|
||||
yaml@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4"
|
||||
integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==
|
||||
yaml@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
||||
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
||||
|
||||
[email protected], yargs-parser@^20.2.2:
|
||||
version "20.2.4"
|
||||
|
||||
Reference in new issue
Block a user