Compare commits

...
Author SHA1 Message Date
Cedric van Putten 058183f202 v3.1.1 2022-12-19 16:57:33 +01:00
Cedric van Putten 7ee50de78e [eas-cli] Fix update --channel and --branch flag validation (#1579)
* [eas-cli] Add initial test cases for the update command

* [eas-cli] Add runtimeVersion option when mocking exports

* [eas-cli] Fix update flag validation for --branch and --channel

* [eas-cli] Add test case for --non-interactive, --channel, and --message

* update CHANGELOG.md

* [eas-cli] Fix linting issue

* [eas-cli] Reuse the input dir from the command

* [eas-cli] Explicitly mention the allowed flag combinations to avoid ambiguity

* [eas-cli] Move --auto to a distinct location in the error message

* [eas-cli] Fix linting issue
2022-12-19 16:36:18 +01:00
Expo CI ca11aa9e41 update CHANGELOG.md 2022-12-17 00:00:52 +00:00
Brent Vatne 64d985fcda v3.1.0 2022-12-16 15:54:02 -08:00
Jon Samp bd2bb4dcf6 [eas-cli] Add --channel flag to eas update command (#1567)
* Add --channel flag to eas update command

* use existing method

* adds undefined to return type

* move to getBranchNameFromChannelNameAsync.ts

* renames the function to singular "update"
2022-12-15 10:56:30 -06:00
Wojciech Kozyra 17db6c4cf1 [eas-cli] Add build:resign command (#1575) 2022-12-15 14:23:10 +01:00
Jon SampandDominik Sokal 5203096aca [eas-cli] Adds channels to build profiles on eas update:configure (#1570)
* Adds eas.json configuration during eas update configure

* updates next steps message

* renames eas updates to eas update

* self feedback

* update CHANGELOG.md

* Update packages/eas-cli/src/commands/update/configure.ts

Co-authored-by: Dominik Sokal <[email protected]>

* Update packages/eas-cli/src/commands/update/configure.ts

Co-authored-by: Dominik Sokal <[email protected]>

* code style feedback incorp

* use EasJsonAccessor

* Update packages/eas-cli/src/update/configure.ts

Co-authored-by: Dominik Sokal <[email protected]>

* lint

Co-authored-by: Dominik Sokal <[email protected]>
2022-12-12 10:59:41 -06:00
33 changed files with 1763 additions and 199 deletions

No files matched your search

+10
View File
@@ -10,8 +10,18 @@ This is the log of notable changes to EAS CLI and related packages.
### 🐛 Bug fixes
- Fix update `--channel` and `--branch` flag validation. ([#1579](https://github.com/expo/eas-cli/pull/1579) by [@byCedric](https://github.com/byCedric))
### 🧹 Chores
## [3.1.0](https://github.com/expo/eas-cli/releases/tag/v3.1.0) - 2022-12-17
### 🎉 New features
- Adds `--channel` flag to `eas update`. ([#1567](https://github.com/expo/eas-cli/pull/1567) by [@jonsamp](https://github.com/jonsamp))
- Add channel configurations to **eas.json** during `eas update:configure`. ([#1570](https://github.com/expo/eas-cli/pull/1570) by [@jonsamp](https://github.com/jonsamp))
- Add `build:resign` command. ([#1575](https://github.com/expo/eas-cli/pull/1575) by [@wkozyra95](https://github.com/wkozyra95))
## [3.0.0](https://github.com/expo/eas-cli/releases/tag/v3.0.0) - 2022-12-07
### 🛠 Breaking changes
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "3.0.0"
"version": "3.1.1"
}
+82 -103
View File
@@ -70,6 +70,7 @@ eas --help COMMAND
* [`eas build:configure`](#eas-buildconfigure)
* [`eas build:inspect`](#eas-buildinspect)
* [`eas build:list`](#eas-buildlist)
* [`eas build:resign`](#eas-buildresign)
* [`eas build:run`](#eas-buildrun)
* [`eas build:submit`](#eas-buildsubmit)
* [`eas build:version:set`](#eas-buildversionset)
@@ -96,8 +97,6 @@ eas --help COMMAND
* [`eas open`](#eas-open)
* [`eas project:info`](#eas-projectinfo)
* [`eas project:init`](#eas-projectinit)
* [`eas run`](#eas-run)
* [`eas run:run`](#eas-runrun)
* [`eas secret:create`](#eas-secretcreate)
* [`eas secret:delete`](#eas-secretdelete)
* [`eas secret:list`](#eas-secretlist)
@@ -131,7 +130,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -148,7 +147,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -165,7 +164,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -179,7 +178,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -229,7 +228,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -250,7 +249,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -270,7 +269,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -290,7 +289,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -313,7 +312,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -343,7 +342,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -360,7 +359,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -377,7 +376,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -412,7 +411,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.0.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -449,7 +448,33 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
re-sign a build archive
```
USAGE
$ eas build:resign [-p android|ios] [-e <value>] [--wait] [--id <value>] [--offset <value>] [--limit <value>]
[--json --non-interactive]
FLAGS
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
eas.json.
-p, --platform=(android|ios)
--id=<value> ID of the build to re-sign.
--json Enable JSON output, non-JSON messages will be printed to stderr.
--limit=<value> The number of items to fetch each query. Defaults to 50 and is capped at 100.
--non-interactive Run the command in non-interactive mode.
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
--[no-]wait Wait for build(s) to complete.
DESCRIPTION
re-sign a build archive
```
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -473,7 +498,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/run.ts)_
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -520,7 +545,7 @@ DESCRIPTION
Update version of an app.
```
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.0.0/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.1.1/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -539,7 +564,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.0.0/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.1.1/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -556,7 +581,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -577,7 +602,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -599,7 +624,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -619,7 +644,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -642,7 +667,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -661,7 +686,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -678,7 +703,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -692,7 +717,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.0.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -712,7 +737,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -733,7 +758,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -747,7 +772,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -761,7 +786,7 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -849,7 +874,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -866,7 +891,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.0.0/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -883,7 +908,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.0.0/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas open`
@@ -897,7 +922,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/open.ts)_
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -911,7 +936,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -933,55 +958,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas run`
run simulator build
```
USAGE
$ eas run [--latest | --id <value> | --path <value> | --url <value>] [-p android|ios|all] [--offset
<value>] [--limit <value>]
FLAGS
-p, --platform=(android|ios|all)
--id=<value> ID of the simulator build to run
--latest Run the latest simulator build for specified platform
--limit=<value> The number of items to fetch each query. Defaults to 50 and is capped at 100.
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
--path=<value> Path to the simulator build file file
--url=<value> Simulator build archive url
DESCRIPTION
run simulator build
```
_See code: [src/commands/run/index.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/run/index.ts)_
## `eas run:run`
run simulator build
```
USAGE
$ eas run:run [--latest | --id <value> | --path <value> | --url <value>] [-p android|ios|all] [--offset
<value>] [--limit <value>]
FLAGS
-p, --platform=(android|ios|all)
--id=<value> ID of the simulator build to run
--latest Run the latest simulator build for specified platform
--limit=<value> The number of items to fetch each query. Defaults to 50 and is capped at 100.
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
--path=<value> Path to the simulator build file file
--url=<value> Simulator build archive url
DESCRIPTION
run simulator build
```
_See code: [src/commands/run/run.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/run/run.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -1004,7 +981,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.0.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1022,7 +999,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1036,7 +1013,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.0.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1056,7 +1033,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.0.0/packages/eas-cli/src/commands/secret/push.ts)_
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/secret/push.ts)_
## `eas submit`
@@ -1086,7 +1063,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1094,13 +1071,15 @@ publish an update group
```
USAGE
$ eas update [--branch <value>] [--message <value>] [--republish | --input-dir <value> | --skip-bundler]
[--group <value> | | ] [-p android|ios|all] [--auto] [--private-key-path <value>] [--json --non-interactive]
$ eas update [--branch <value>] [--channel <value>] [--message <value>] [--republish | --input-dir <value>
| --skip-bundler] [--group <value> | | ] [-p android|ios|all] [--auto] [--private-key-path <value>] [--json
--non-interactive]
FLAGS
-p, --platform=(android|ios|all) [default: all]
--auto Use the current git branch and commit message for the EAS branch and update message
--branch=<value> Branch to publish the update group on
--channel=<value> Channel that the published update should affect
--group=<value> Update group to republish (deprecated, see republish command)
--input-dir=<value> [default: dist] Location of the bundle
--json Enable JSON output, non-JSON messages will be printed to stderr.
@@ -1116,7 +1095,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1134,7 +1113,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1155,7 +1134,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.0.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1177,7 +1156,7 @@ DESCRIPTION
view the recent updates
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1200,7 +1179,7 @@ DESCRIPTION
rollback to an existing update
```
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:view GROUPID`
@@ -1220,7 +1199,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1241,7 +1220,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1261,7 +1240,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1278,7 +1257,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1300,7 +1279,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1317,7 +1296,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.0.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.1.1/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+552 -56
View File
@@ -2258,22 +2258,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "redirectUri",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "revokeEndpoint",
"description": "",
@@ -2438,22 +2422,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "redirectUri",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "revokeEndpoint",
"description": "",
@@ -2742,22 +2710,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "redirectUri",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "revokeEndpoint",
"description": "",
@@ -5129,6 +5081,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "mode",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildMode",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "projectArchive",
"description": "",
@@ -5435,6 +5399,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "mode",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildMode",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "releaseChannel",
"description": "",
@@ -6623,6 +6599,51 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deploymentNew",
"description": "",
"args": [
{
"name": "channel",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "runtimeVersion",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "OBJECT",
"name": "DeploymentNew",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deployments",
"description": "Deployments associated with this app",
@@ -12012,7 +12033,20 @@
{
"name": "canRetry",
"description": "",
"args": [],
"args": [
{
"name": "newMode",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildMode",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
@@ -13860,6 +13894,29 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "BuildMode",
"description": "",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "BUILD",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RESIGN",
"description": "",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "BuildMutation",
@@ -14713,6 +14770,29 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "BuildResignInput",
"description": "",
"fields": null,
"inputFields": [
{
"name": "applicationArchiveSource",
"description": "",
"type": {
"kind": "INPUT_OBJECT",
"name": "ProjectArchiveSourceInput",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "BuildResourceClass",
@@ -14849,6 +14929,12 @@
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "UNKNOWN",
"description": "",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
@@ -16499,6 +16585,224 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeploymentBuildEdge",
"description": "",
"fields": [
{
"name": "cursor",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Build",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeploymentBuildsConnection",
"description": "",
"fields": [
{
"name": "edges",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeploymentBuildEdge",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeploymentNew",
"description": "Represents a Deployment - a set of Builds with the same Runtime Version and Channel",
"fields": [
{
"name": "builds",
"description": "",
"args": [
{
"name": "after",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "before",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "first",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "last",
"description": "",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeploymentBuildsConnection",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "channel",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "UpdateChannel",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "runtime",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Runtime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "DeploymentOptions",
@@ -17580,13 +17884,9 @@
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
@@ -20750,6 +21050,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "mode",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildMode",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "projectArchive",
"description": "",
@@ -21021,6 +21333,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "mode",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildMode",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "releaseChannel",
"description": "",
@@ -21033,6 +21357,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "resign",
"description": "",
"type": {
"kind": "INPUT_OBJECT",
"name": "BuildResignInput",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "scheme",
"description": "",
@@ -21069,6 +21405,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "type",
"description": "",
"type": {
"kind": "ENUM",
"name": "BuildWorkflow",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updates",
"description": "",
@@ -22479,6 +22827,73 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PageInfo",
"description": "",
"fields": [
{
"name": "endCursor",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasNextPage",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasPreviousPage",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "startCursor",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "PartialManifest",
@@ -22938,6 +23353,12 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NONE",
"description": "",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "S3",
"description": "",
@@ -25130,6 +25551,81 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Runtime",
"description": "",
"fields": [
{
"name": "app",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "App",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "firstBuildCreatedAt",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "version",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "SecondFactorBooleanResult",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "3.0.0",
"version": "3.1.1",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
+2 -1
View File
@@ -15,6 +15,7 @@ import Log from '../log';
import { formatBranch, getBranchDescription } from '../update/utils';
import { printJsonOnlyOutput } from '../utils/json';
import {
SelectPromptEntry,
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
@@ -31,7 +32,7 @@ export async function selectBranchOnAppAsync(
paginatedQueryOptions,
}: {
projectId: string;
displayTextForListItem: (queryItem: UpdateBranchFragment) => string;
displayTextForListItem: (queryItem: UpdateBranchFragment) => SelectPromptEntry;
promptTitle: string;
paginatedQueryOptions: PaginatedQueryOptions;
}
@@ -1,5 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import { CredentialsContext } from '../../credentials/context';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider';
import { getAppFromContextAsync } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { IosCredentials, Target } from '../../credentials/ios/types';
@@ -31,6 +33,27 @@ export async function ensureIosCredentialsAsync(
};
}
export async function ensureIosCredentialsForBuildResignAsync(
credentialsCtx: CredentialsContext,
targets: Target[],
buildProfile: BuildProfile<Platform.IOS>
): Promise<CredentialsResult<IosCredentials>> {
const provider = new IosCredentialsProvider(credentialsCtx, {
app: await getAppFromContextAsync(credentialsCtx),
targets,
distribution: 'internal',
enterpriseProvisioning: buildProfile.enterpriseProvisioning,
});
const { credentialsSource } = buildProfile;
logCredentialsSource(credentialsSource, Platform.IOS);
return {
credentials: await provider.getCredentialsAsync(credentialsSource),
source: credentialsSource,
};
}
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
return !buildCtx.buildProfile.simulator;
}
+1 -1
View File
@@ -26,7 +26,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
};
}
function transformIosSecrets(secrets: {
export function transformIosSecrets(secrets: {
buildCredentials?: Ios.BuildCredentials;
}): IosJobSecretsInput {
const buildCredentials: IosJobSecretsInput['buildCredentials'] = [];
@@ -1,8 +1,10 @@
import { ArchiveSource, Ios, Job, Platform, sanitizeJob } from '@expo/eas-build-job';
import nullthrows from 'nullthrows';
import path from 'path';
import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { IosJobSecretsInput } from '../../graphql/generated';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
@@ -79,6 +81,22 @@ export async function prepareJobAsync(
return sanitizeJob(job);
}
export function prepareCredentialsToResign(credentials: IosCredentials): IosJobSecretsInput {
const buildCredentials: IosJobSecretsInput['buildCredentials'] = [];
for (const targetName of Object.keys(credentials ?? {})) {
buildCredentials.push({
targetName,
provisioningProfileBase64: nullthrows(credentials?.[targetName].provisioningProfile),
distributionCertificate: {
dataBase64: nullthrows(credentials?.[targetName].distributionCertificate.certificateP12),
password: nullthrows(credentials?.[targetName].distributionCertificate.certificatePassword),
},
});
}
return { buildCredentials };
}
function prepareTargetCredentials(targetCredentials: TargetCredentials): Ios.TargetCredentials {
return {
provisioningProfileBase64: targetCredentials.provisioningProfile,
+40 -1
View File
@@ -8,7 +8,10 @@ import Log from '../log';
import { promptAsync } from '../prompts';
import { fromNow } from '../utils/date';
import { printJsonOnlyOutput } from '../utils/json';
import { paginatedQueryWithConfirmPromptAsync } from '../utils/queries';
import {
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
import { formatGraphQLBuild } from './utils/formatBuild';
export const BUILDS_LIMIT = 50;
@@ -55,6 +58,42 @@ export async function listAndRenderBuildsOnAppAsync(
}
}
export async function listAndSelectBuildOnAppAsync(
graphqlClient: ExpoGraphqlClient,
{
projectId,
title,
filter,
paginatedQueryOptions,
}: {
projectId: string;
title: string;
filter?: BuildFilter;
paginatedQueryOptions: PaginatedQueryOptions;
}
): Promise<BuildFragment | void> {
if (paginatedQueryOptions.nonInteractive) {
throw new Error('Unable to select a build in non-interactive mode.');
} else {
return await paginatedQueryWithSelectPromptAsync({
limit: paginatedQueryOptions.limit ?? BUILDS_LIMIT,
offset: paginatedQueryOptions.offset,
queryToPerform: (limit, offset) =>
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId,
limit,
offset,
filter,
}),
promptOptions: {
title,
getIdentifierForQueryItem: build => build.id,
createDisplayTextForSelectionPromptListItem: formatBuildChoiceTitleAndDescription,
},
});
}
}
function formatBuildChoiceValue(value: string | undefined | null): string {
return value ? chalk.bold(value) : chalk.dim('Unknown');
}
+5
View File
@@ -0,0 +1,5 @@
export class ChannelNotFoundError extends Error {
constructor(message?: string) {
super(message ?? 'Channel not found.');
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ export async function selectChannelOnAppAsync(
queryChannelsOnAppAsync(graphqlClient, { appId: projectId, limit, offset }),
promptOptions: {
title: selectionPromptTitle,
createDisplayTextForSelectionPromptListItem: updateChannel => updateChannel.name,
createDisplayTextForSelectionPromptListItem: updateChannel => ({ title: updateChannel.name }),
getIdentifierForQueryItem: updateChannel => updateChannel.id,
},
});
@@ -120,7 +120,7 @@ export default class BranchDelete extends EasCommand {
}
({ name: branchName } = await selectBranchOnAppAsync(graphqlClient, {
projectId,
displayTextForListItem: updateBranch => updateBranch.name,
displayTextForListItem: updateBranch => ({ title: updateBranch.name }),
promptTitle: 'Which branch would you like to delete?',
paginatedQueryOptions,
}));
+3 -1
View File
@@ -57,7 +57,9 @@ export default class BranchView extends EasCommand {
({ name: branchName } = await selectBranchOnAppAsync(graphqlClient, {
projectId,
promptTitle: 'Which branch would you like to view?',
displayTextForListItem: updateBranch => updateBranch.name,
displayTextForListItem: updateBranch => ({
title: updateBranch.name,
}),
// discard limit and offset because this query is not their intended target
paginatedQueryOptions: {
json: paginatedQueryOptions.json,
+1 -1
View File
@@ -245,7 +245,7 @@ export default class Build extends EasCommand {
}
}
async function handleDeprecatedEasJsonAsync(
export async function handleDeprecatedEasJsonAsync(
projectDir: string,
nonInteractive: boolean
): Promise<void> {
@@ -0,0 +1,296 @@
import { Platform } from '@expo/eas-build-job';
import { CredentialsSource, EasJson, EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import { handleDeprecatedEasJsonAsync } from '.';
import { waitForBuildEndAsync } from '../../build/build';
import { ensureIosCredentialsForBuildResignAsync } from '../../build/ios/credentials';
import { prepareCredentialsToResign } from '../../build/ios/prepareJob';
import { listAndSelectBuildOnAppAsync } from '../../build/queries';
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EasPaginatedQueryFlags } from '../../commandUtils/pagination';
import { CredentialsContext } from '../../credentials/context';
import {
BuildFragment,
BuildMode,
BuildStatus,
BuildWorkflow,
DistributionType,
IosJobOverridesInput,
ProjectArchiveSourceType,
StatuspageServiceName,
} from '../../graphql/generated';
import { BuildMutation } from '../../graphql/mutations/BuildMutation';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log from '../../log';
import { requestedPlatformDisplayNames, selectPlatformAsync } from '../../platform';
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
import { resolveTargetsAsync } from '../../project/ios/target';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
interface BuildResignFlags {
json: boolean;
nonInteractive: boolean;
offset?: number;
limit?: number;
platform?: Platform;
profile?: string;
maybeBuildId?: string;
wait: boolean;
}
interface RawBuildResignFlags {
json: boolean;
'non-interactive': boolean;
offset: number | undefined;
limit: number | undefined;
platform: 'android' | 'ios' | undefined;
profile: string | undefined;
wait: boolean;
id: string | undefined;
}
export default class BuildResign extends EasCommand {
static override description = 're-sign a build archive';
static override flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
wait: Flags.boolean({
default: true,
allowNo: true,
description: 'Wait for build(s) to complete.',
}),
id: Flags.string({
description: 'ID of the build to re-sign.',
}),
...EasPaginatedQueryFlags,
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
};
async runAsync(): Promise<void> {
const { flags: rawFlags } = await this.parse(BuildResign);
if (rawFlags.json) {
enableJsonOutput();
}
const flags = this.sanitizeFlags(rawFlags as RawBuildResignFlags);
const { limit, offset, nonInteractive } = flags;
const {
loggedIn: { actor, graphqlClient },
getDynamicProjectConfigAsync,
projectDir,
analytics,
} = await this.getContextAsync(BuildResign, {
nonInteractive: flags.nonInteractive,
});
const maybeBuild = flags.maybeBuildId
? await this.maybeGetBuildAsync(graphqlClient, flags.maybeBuildId)
: undefined;
const platform =
(maybeBuild?.platform.toLowerCase() as Platform | undefined) ??
(await selectPlatformAsync(flags.platform));
if (platform === Platform.ANDROID) {
throw new Error('Re-signing archives is not supported on Android yet.');
}
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasBuild]);
const easJsonAccessor = new EasJsonAccessor(projectDir);
const easJsonCliConfig: EasJson['cli'] =
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
easJsonAccessor,
platform,
flags.profile ?? 'production'
);
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
const build = await this.ensureBuildSelectedAsync(
{ graphqlClient, projectId, platform, nonInteractive, limit, offset },
maybeBuild
);
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
nonInteractive,
projectDir,
user: actor,
graphqlClient,
analytics,
env: buildProfile.env,
easJsonCliConfig,
});
if (buildProfile.credentialsSource !== CredentialsSource.LOCAL && !nonInteractive) {
await credentialsCtx.appStore.ensureAuthenticatedAsync();
}
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{
projectDir,
nonInteractive,
exp,
},
buildProfile
);
const targets = await resolveTargetsAsync({
projectDir,
exp,
xcodeBuildContext,
env: buildProfile.env,
});
const credentialsResult = await ensureIosCredentialsForBuildResignAsync(
credentialsCtx,
targets,
buildProfile
);
assert(build.artifacts?.applicationArchiveUrl, 'Missing application archive.');
const jobOverrides: IosJobOverridesInput = {
mode: BuildMode.Resign,
type: BuildWorkflow.Unknown,
resign: {
applicationArchiveSource: {
type: ProjectArchiveSourceType.Url,
url: build.artifacts?.applicationArchiveUrl,
},
},
secrets: prepareCredentialsToResign(credentialsResult.credentials),
builderEnvironment: { image: 'default' },
};
const newBuild = await BuildMutation.retryIosBuildAsync(graphqlClient, {
buildId: build.id,
jobOverrides,
});
Log.addNewLineIfNone();
printLogsUrls([newBuild]);
Log.newLine();
if (!flags.wait) {
if (flags.json) {
printJsonOnlyOutput(newBuild);
}
return;
}
const buildResult = await waitForBuildEndAsync(graphqlClient, {
buildIds: [newBuild.id],
accountName: account.name,
});
if (!flags.json) {
printBuildResults(buildResult);
} else {
assert(buildResult[0], 'missing build results');
printJsonOnlyOutput(buildResult[0]);
}
}
sanitizeFlags(flags: RawBuildResignFlags): BuildResignFlags {
const nonInteractive = flags['non-interactive'];
if (nonInteractive && !flags.id) {
throw new Error(
`${chalk.bold('--id')} is required when running with ${chalk.bold(
'--non-interactive'
)} flag.`
);
}
return {
json: flags.json,
nonInteractive,
offset: flags.offset,
limit: flags.limit,
platform: flags.platform as Platform | undefined,
profile: flags.profile,
maybeBuildId: flags.id,
wait: flags.wait,
};
}
async ensureBuildSelectedAsync(
{
graphqlClient,
projectId,
platform,
nonInteractive,
limit,
offset,
}: {
graphqlClient: ExpoGraphqlClient;
projectId: string;
platform: Platform;
nonInteractive: boolean;
limit?: number;
offset?: number;
},
maybeBuild?: BuildFragment
): Promise<BuildFragment> {
if (maybeBuild) {
return maybeBuild;
}
const build = await listAndSelectBuildOnAppAsync(graphqlClient, {
projectId,
title: 'Which build would you like to re-sign with new credentials?',
paginatedQueryOptions: {
limit,
offset: offset ?? 0,
nonInteractive,
json: false,
},
filter: {
distribution: DistributionType.Internal,
platform: toAppPlatform(platform),
status: BuildStatus.Finished,
},
});
if (!build) {
throw new Error('There are no builds that can be re-signed on this project.');
}
return build;
}
async maybeGetBuildAsync(
graphqlClient: ExpoGraphqlClient,
maybeBuildId?: string,
maybePlatform?: Platform
): Promise<BuildFragment | undefined> {
if (maybeBuildId) {
const build = await BuildQuery.byIdAsync(graphqlClient, maybeBuildId);
if (build.distribution !== DistributionType.Internal) {
throw new Error('This is not an internal distribution build.');
}
if (build.status !== BuildStatus.Finished) {
throw new Error('Only builds that finished successfully can be re-signed.');
}
if (maybePlatform && build.platform !== toAppPlatform(maybePlatform)) {
throw new Error(
`Build with ID ${maybeBuildId} was not created for platform ${requestedPlatformDisplayNames[maybePlatform]}.`
);
}
return build;
}
return undefined;
}
}
@@ -107,7 +107,7 @@ export default class ChannelEdit extends EasCommand {
: await selectBranchOnAppAsync(graphqlClient, {
projectId,
promptTitle: `Which branch would you like ${existingChannel.name} to point at?`,
displayTextForListItem: updateBranch => updateBranch.name,
displayTextForListItem: updateBranch => ({ title: updateBranch.name }),
paginatedQueryOptions: {
json,
nonInteractive,
@@ -0,0 +1,248 @@
import { AppJSONConfig, PackageJSONConfig, Platform, getConfig } from '@expo/config';
import { Updates } from '@expo/config-plugins';
import { vol } from 'memfs';
import path from 'path';
import { instance, mock } from 'ts-mockito';
import UpdatePublish from '..';
import { ensureBranchExistsAsync } from '../../../branch/queries';
import { DynamicProjectConfigContextField } from '../../../commandUtils/context/DynamicProjectConfigContextField';
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
import { jester } from '../../../credentials/__tests__/fixtures-constants';
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { collectAssetsAsync, uploadAssetsAsync } from '../../../project/publish';
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
const projectRoot = '/test-project';
const commandOptions = { root: projectRoot } as any;
jest.mock('fs');
jest.mock('@expo/config');
jest.mock('@expo/config-plugins');
jest.mock('../../../branch/queries');
jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync');
jest.mock('../../../update/configure');
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
jest.mock('../../../graphql/mutations/PublishMutation');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../graphql/queries/UpdateQuery');
jest.mock('../../../ora', () => ({
ora: () => ({
start: () => ({ succeed: () => {}, fail: () => {} }),
}),
}));
jest.mock('../../../project/publish', () => ({
...jest.requireActual('../../../project/publish'),
buildBundlesAsync: jest.fn(),
collectAssetsAsync: jest.fn(),
resolveInputDirectoryAsync: jest.fn((inputDir = 'dist') => path.join(projectRoot, inputDir)),
uploadAssetsAsync: jest.fn(),
}));
describe(UpdatePublish.name, () => {
afterEach(() => vol.reset());
// Deprecated and split to a new command: update:republish
it('errors with --republish', async () => {
await expect(new UpdatePublish(['--republish'], commandOptions).run()).rejects.toThrow(
'--group and --republish flags are deprecated'
);
});
// Deprecated and split to a new command: update:republish
it('errors with --group', async () => {
await expect(new UpdatePublish(['--group=abc123'], commandOptions).run()).rejects.toThrow(
'--group and --republish flags are deprecated'
);
});
it('errors with both --channel and --branch', async () => {
const flags = ['--channel=channel123', '--branch=branch123'];
mockTestProject();
await expect(new UpdatePublish(flags, commandOptions).run()).rejects.toThrow(
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto'
);
});
it('creates a new update with --non-interactive, --branch, and --message', async () => {
const flags = ['--non-interactive', '--branch=branch123', '--message=abc'];
mockTestProject();
const { platforms, runtimeVersion } = mockTestExport();
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
branchId: 'branch123',
createdBranch: false,
});
jest.mocked(PublishMutation.publishUpdateGroupAsync).mockResolvedValue(
platforms.map(platform => ({
id: 'update123',
group: 'group123',
runtimeVersion,
platform,
manifestPermalink: 'https://example.com/update123/manifest',
}))
);
await new UpdatePublish(flags, commandOptions).run();
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalled();
});
it('creates a new update with --non-interactive, --channel, and --message', async () => {
const flags = ['--non-interactive', '--channel=channel123', '--message=abc'];
const { projectId } = mockTestProject();
const { platforms, runtimeVersion } = mockTestExport();
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
branchId: 'branch123',
createdBranch: false,
});
jest.mocked(PublishMutation.publishUpdateGroupAsync).mockResolvedValue(
platforms.map(platform => ({
id: 'update123',
group: 'group123',
runtimeVersion,
platform,
manifestPermalink: 'https://example.com/update123/manifest',
}))
);
await new UpdatePublish(flags, commandOptions).run();
expect(ensureBranchExistsAsync).toHaveBeenCalledWith(
expect.any(Object), // graphql client
{
appId: projectId,
branchName: 'branchFromChannel',
}
);
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalled();
});
});
/** Create a new in-memory project, copied from src/commands/update/__tests__/republish.test.ts */
function mockTestProject({
configuredProjectId = '1234',
}: {
configuredProjectId?: string;
} = {}): { projectId: string } {
const packageJSON: PackageJSONConfig = {
name: 'testing123',
version: '0.1.0',
description: 'fake description',
main: 'index.js',
};
const appJSON: AppJSONConfig = {
expo: {
name: 'testing 123',
version: '0.1.0',
slug: 'testing-123',
sdkVersion: '33.0.0',
owner: jester.accounts[0].name,
extra: {
eas: {
projectId: configuredProjectId,
},
},
},
};
vol.fromJSON(
{
'package.json': JSON.stringify(packageJSON),
'app.json': JSON.stringify(appJSON),
},
projectRoot
);
const mockManifest = { exp: appJSON.expo };
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
jest
.spyOn(DynamicProjectConfigContextField.prototype, 'getValueAsync')
.mockResolvedValue(async () => ({
exp: mockManifest.exp,
projectDir: projectRoot,
projectId: configuredProjectId,
}));
jest.spyOn(LoggedInContextField.prototype, 'getValueAsync').mockResolvedValue({
actor: jester,
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
graphqlClient,
});
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
return { projectId: configuredProjectId };
}
/** Create a new in-memory export of the project */
function mockTestExport({
exportDir = 'dist',
platforms = ['android', 'ios'],
runtimeVersion = 'exposdk:47.0.0',
}: {
exportDir?: string;
platforms?: Platform[];
runtimeVersion?: string;
} = {}): {
inputDir: string;
platforms: Platform[];
runtimeVersion: string;
} {
/* eslint-disable node/no-sync */
vol.mkdirpSync(path.join(projectRoot, exportDir, 'bundles'));
for (const platform of platforms) {
vol.writeFileSync(
path.join(projectRoot, exportDir, 'bundles', `${platform}-fake.js`),
`console.log("fake bundle for ${platform}");`
);
}
jest.mocked(collectAssetsAsync).mockResolvedValue(
Object.fromEntries(
platforms.map(platform => [
platform,
{
assets: [],
launchAsset: {
contentType: 'application/javascript',
path: path.join(projectRoot, exportDir, 'bundles', `${platform}-fake.js`),
},
},
])
)
);
jest.mocked(uploadAssetsAsync).mockResolvedValue({
// platforms are mocked all containing only the launch asset
assetCount: platforms.length,
uniqueAssetCount: platforms.length,
uniqueUploadedAssetCount: platforms.length,
assetLimitPerUpdateGroup: 9001,
});
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
return { inputDir: exportDir, platforms, runtimeVersion };
}
@@ -48,6 +48,8 @@ jest.mock('../../../ora', () => ({
}));
describe(UpdateRepublish.name, () => {
afterEach(() => vol.reset());
it('errors without --branch or --group', async () => {
await expect(new UpdateRepublish([], commandOptions).run()).rejects.toThrow(
'--branch or --group must be specified'
@@ -1,10 +1,11 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import Log from '../../log';
import { RequestedPlatform } from '../../platform';
import { ensureEASUpdatesIsConfiguredAsync } from '../../update/configure';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
export default class UpdateConfigure extends EasCommand {
static override description = 'configure the project to support EAS Update';
@@ -38,7 +39,7 @@ export default class UpdateConfigure extends EasCommand {
'💡 The following process will configure your project to run EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
);
await ensureEASUpdatesIsConfiguredAsync(graphqlClient, {
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
exp,
projectId,
projectDir,
@@ -46,6 +47,23 @@ export default class UpdateConfigure extends EasCommand {
});
Log.addNewLineIfNone();
Log.log(`🎉 Your app is configured to run EAS Update!`);
Log.log(`🎉 Your app is configured with EAS Update!`);
Log.newLine();
Log.log(`${chalk.bold('Next steps')}:`);
Log.newLine();
Log.log('Update a production build:');
Log.log(`1. Create a new build. Example: ${chalk.bold('eas build --profile production')}.`);
Log.log('2. Make changes in your project.');
Log.log(`3. Publish an update. Example: ${chalk.bold('eas update --channel production')}.`);
Log.log('4. Force close and reopen the app at least twice to view the update.');
Log.newLine();
Log.log('Preview an update:');
Log.log(
`1. Publish an update to a branch. Example: ${chalk.bold('eas update --branch new-feature')}.`
);
Log.log(
'2. In Expo Go or a development build, navigate to Projects > [project name] > Branch > Open.'
);
}
}
+29 -9
View File
@@ -37,7 +37,8 @@ import {
} from '../../project/publish';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import { ensureEASUpdatesIsConfiguredAsync } from '../../update/configure';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
import { getBranchNameFromChannelNameAsync } from '../../update/getBranchNameFromChannelNameAsync';
import { formatUpdateMessage, truncateString as truncateUpdateMessage } from '../../update/utils';
import {
checkManifestBodyAgainstUpdateInfoGroup,
@@ -56,6 +57,7 @@ export const defaultPublishPlatforms: Partial<PublishPlatform>[] = ['android', '
type RawUpdateFlags = {
auto: boolean;
branch?: string;
channel?: string;
message?: string;
platform: string;
'input-dir': string;
@@ -73,6 +75,7 @@ type UpdateFlags = {
auto: boolean;
platform: ExpoCLIExportPlatformFlag;
branchName?: string;
channelName?: string;
updateMessage?: string;
inputDir: string;
skipBundler: boolean;
@@ -104,6 +107,10 @@ export default class UpdatePublish extends EasCommand {
description: 'Branch to publish the update group on',
required: false,
}),
channel: Flags.string({
description: 'Channel that the published update should affect',
required: false,
}),
message: Flags.string({
description: 'A short message describing the update',
required: false,
@@ -158,7 +165,7 @@ export default class UpdatePublish extends EasCommand {
let {
auto: autoFlag,
platform: platformFlag,
branchName,
channelName,
updateMessage,
inputDir,
skipBundler,
@@ -166,6 +173,7 @@ export default class UpdatePublish extends EasCommand {
json: jsonFlag,
nonInteractive,
} = this.sanitizeFlags(rawFlags);
let branchName = this.sanitizeFlags(rawFlags).branchName;
const {
getDynamicProjectConfigAsync,
@@ -192,7 +200,7 @@ export default class UpdatePublish extends EasCommand {
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
await ensureEASUpdatesIsConfiguredAsync(graphqlClient, {
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
exp: expPossiblyWithoutEasUpdateConfigured,
platform: getRequestedPlatform(platformFlag),
projectDir,
@@ -204,20 +212,31 @@ export default class UpdatePublish extends EasCommand {
let realizedPlatforms: PublishPlatform[] = [];
if (channelName && branchName) {
throw new Error(
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto'
);
}
if (channelName) {
branchName = await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelName);
}
if (!branchName) {
if (autoFlag) {
branchName = await getDefaultBranchNameAsync();
} else if (nonInteractive) {
throw new Error('Must supply --branch or use --auto when in non-interactive mode');
throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode');
} else {
try {
const branch = await selectBranchOnAppAsync(graphqlClient, {
projectId,
promptTitle: `Which branch would you like to publish on?`,
displayTextForListItem: updateBranch =>
`${updateBranch.name} ${chalk.grey(
displayTextForListItem: updateBranch => ({
title: `${updateBranch.name} ${chalk.grey(
`- current update: ${formatUpdateMessage(updateBranch.updates[0])}`
)}`,
}),
paginatedQueryOptions,
});
branchName = branch.name;
@@ -489,10 +508,10 @@ export default class UpdatePublish extends EasCommand {
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
const nonInteractive = flags['non-interactive'] ?? false;
const { auto, branch: branchName, message: updateMessage } = flags;
if (nonInteractive && !auto && !(branchName && updateMessage)) {
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
if (nonInteractive && !auto && !(updateMessage && (branchName || channelName))) {
Errors.error(
'--auto or both --branch and --message are required when updating in non-interactive mode',
'--branch and --message, or --channel and --message are required when updating in non-interactive mode unless --auto is specified',
{ exit: 1 }
);
}
@@ -517,6 +536,7 @@ export default class UpdatePublish extends EasCommand {
return {
auto,
branchName,
channelName,
updateMessage,
inputDir: flags['input-dir'],
skipBundler: flags['skip-bundler'],
+3 -1
View File
@@ -70,7 +70,9 @@ export default class UpdateList extends EasCommand {
const selectedBranch = await selectBranchOnAppAsync(graphqlClient, {
projectId,
promptTitle: 'Which branch would you like to view?',
displayTextForListItem: updateBranch => updateBranch.name,
displayTextForListItem: updateBranch => ({
title: updateBranch.name,
}),
paginatedQueryOptions:
// discard limit and offset because this query is not those flag's intended target
{
+6 -3
View File
@@ -44,10 +44,11 @@ export async function selectAppleTeamOnAccountAsync(
}),
promptOptions: {
title: selectionPromptTitle,
createDisplayTextForSelectionPromptListItem: appleTeam =>
appleTeam.appleTeamName
createDisplayTextForSelectionPromptListItem: appleTeam => ({
title: appleTeam.appleTeamName
? `${appleTeam.appleTeamName} (ID: ${appleTeam.appleTeamIdentifier})`
: appleTeam.appleTeamIdentifier,
}),
getIdentifierForQueryItem: appleTeam => appleTeam.id,
},
});
@@ -87,7 +88,9 @@ export async function selectAppleDeviceOnAppleTeamAsync(
}),
promptOptions: {
title: selectionPromptTitle,
createDisplayTextForSelectionPromptListItem: appleDevice => formatDeviceLabel(appleDevice),
createDisplayTextForSelectionPromptListItem: appleDevice => ({
title: formatDeviceLabel(appleDevice),
}),
getIdentifierForQueryItem: appleTeam => appleTeam.id,
},
});
+87 -5
View File
@@ -400,7 +400,6 @@ export type AccountSsoConfiguration = {
id: Scalars['ID'];
issuer?: Maybe<Scalars['String']>;
jwksEndpoint?: Maybe<Scalars['String']>;
redirectUri: Scalars['String'];
revokeEndpoint?: Maybe<Scalars['String']>;
tokenEndpoint?: Maybe<Scalars['String']>;
updatedAt: Scalars['DateTime'];
@@ -415,7 +414,6 @@ export type AccountSsoConfigurationData = {
clientSecret: Scalars['String'];
issuer?: InputMaybe<Scalars['String']>;
jwksEndpoint?: InputMaybe<Scalars['String']>;
redirectUri: Scalars['String'];
revokeEndpoint?: InputMaybe<Scalars['String']>;
tokenEndpoint?: InputMaybe<Scalars['String']>;
userInfoEndpoint?: InputMaybe<Scalars['String']>;
@@ -458,7 +456,6 @@ export type AccountSsoConfigurationPublicData = {
id: Scalars['ID'];
issuer?: Maybe<Scalars['String']>;
jwksEndpoint?: Maybe<Scalars['String']>;
redirectUri: Scalars['String'];
revokeEndpoint?: Maybe<Scalars['String']>;
tokenEndpoint?: Maybe<Scalars['String']>;
userInfoEndpoint?: Maybe<Scalars['String']>;
@@ -787,6 +784,7 @@ export type AndroidJobInput = {
developmentClient?: InputMaybe<Scalars['Boolean']>;
experimental?: InputMaybe<Scalars['JSONObject']>;
gradleCommand?: InputMaybe<Scalars['String']>;
mode?: InputMaybe<BuildMode>;
projectArchive: ProjectArchiveSourceInput;
projectRootDirectory: Scalars['String'];
releaseChannel?: InputMaybe<Scalars['String']>;
@@ -815,6 +813,7 @@ export type AndroidJobOverridesInput = {
developmentClient?: InputMaybe<Scalars['Boolean']>;
experimental?: InputMaybe<Scalars['JSONObject']>;
gradleCommand?: InputMaybe<Scalars['String']>;
mode?: InputMaybe<BuildMode>;
releaseChannel?: InputMaybe<Scalars['String']>;
secrets?: InputMaybe<AndroidJobSecretsInput>;
updates?: InputMaybe<BuildUpdatesInput>;
@@ -922,6 +921,7 @@ export type App = Project & {
/** Classic update release channel names that have at least one build */
buildsReleaseChannels: Array<Scalars['String']>;
deployment?: Maybe<Deployment>;
deploymentNew?: Maybe<DeploymentNew>;
/** Deployments associated with this app */
deployments: Array<Deployment>;
description: Scalars['String'];
@@ -1051,6 +1051,13 @@ export type AppDeploymentArgs = {
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentNewArgs = {
channel: Scalars['String'];
runtimeVersion: Scalars['String'];
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppDeploymentsArgs = {
limit: Scalars['Int'];
@@ -1800,6 +1807,12 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
workerStartedAt?: Maybe<Scalars['DateTime']>;
};
/** Represents an EAS Build */
export type BuildCanRetryArgs = {
newMode?: InputMaybe<BuildMode>;
};
export type BuildArtifact = {
__typename?: 'BuildArtifact';
manifestPlistUrl?: Maybe<Scalars['String']>;
@@ -1960,6 +1973,11 @@ export type BuildMetrics = {
buildWaitTime?: Maybe<Scalars['Int']>;
};
export enum BuildMode {
Build = 'BUILD',
Resign = 'RESIGN'
}
export type BuildMutation = {
__typename?: 'BuildMutation';
/**
@@ -2116,6 +2134,10 @@ export type BuildQueryByIdArgs = {
buildId: Scalars['ID'];
};
export type BuildResignInput = {
applicationArchiveSource?: InputMaybe<ProjectArchiveSourceInput>;
};
export enum BuildResourceClass {
AndroidDefault = 'ANDROID_DEFAULT',
AndroidLarge = 'ANDROID_LARGE',
@@ -2141,7 +2163,8 @@ export type BuildUpdatesInput = {
export enum BuildWorkflow {
Generic = 'GENERIC',
Managed = 'MANAGED'
Managed = 'MANAGED',
Unknown = 'UNKNOWN'
}
export enum CacheControlScope {
@@ -2381,6 +2404,36 @@ export type Deployment = {
runtimeVersion: Scalars['String'];
};
export type DeploymentBuildEdge = {
__typename?: 'DeploymentBuildEdge';
cursor: Scalars['String'];
node: Build;
};
export type DeploymentBuildsConnection = {
__typename?: 'DeploymentBuildsConnection';
edges: Array<DeploymentBuildEdge>;
pageInfo: PageInfo;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentNew = {
__typename?: 'DeploymentNew';
builds: DeploymentBuildsConnection;
channel: UpdateChannel;
id: Scalars['ID'];
runtime: Runtime;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentNewBuildsArgs = {
after?: InputMaybe<Scalars['String']>;
before?: InputMaybe<Scalars['String']>;
first?: InputMaybe<Scalars['Int']>;
last?: InputMaybe<Scalars['Int']>;
};
export type DeploymentOptions = {
/** Max number of associated builds to return */
buildListMaxSize?: InputMaybe<Scalars['Int']>;
@@ -2547,7 +2600,7 @@ export type GitHubAppInstallationAccessibleRepositoriesArgs = {
export type GitHubAppInstallationAccessibleRepository = {
__typename?: 'GitHubAppInstallationAccessibleRepository';
defaultBranch: Scalars['String'];
defaultBranch?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
id: Scalars['Int'];
name: Scalars['String'];
@@ -2982,6 +3035,7 @@ export type IosJobInput = {
/** @deprecated */
distribution?: InputMaybe<DistributionType>;
experimental?: InputMaybe<Scalars['JSONObject']>;
mode?: InputMaybe<BuildMode>;
projectArchive: ProjectArchiveSourceInput;
projectRootDirectory: Scalars['String'];
releaseChannel?: InputMaybe<Scalars['String']>;
@@ -3008,10 +3062,13 @@ export type IosJobOverridesInput = {
/** @deprecated */
distribution?: InputMaybe<DistributionType>;
experimental?: InputMaybe<Scalars['JSONObject']>;
mode?: InputMaybe<BuildMode>;
releaseChannel?: InputMaybe<Scalars['String']>;
resign?: InputMaybe<BuildResignInput>;
scheme?: InputMaybe<Scalars['String']>;
secrets?: InputMaybe<IosJobSecretsInput>;
simulator?: InputMaybe<Scalars['Boolean']>;
type?: InputMaybe<BuildWorkflow>;
updates?: InputMaybe<BuildUpdatesInput>;
username?: InputMaybe<Scalars['String']>;
version?: InputMaybe<IosJobVersionInput>;
@@ -3249,6 +3306,14 @@ export enum Order {
Desc = 'DESC'
}
export type PageInfo = {
__typename?: 'PageInfo';
endCursor?: Maybe<Scalars['String']>;
hasNextPage: Scalars['Boolean'];
hasPreviousPage: Scalars['Boolean'];
startCursor?: Maybe<Scalars['String']>;
};
export type PartialManifest = {
assets: Array<InputMaybe<PartialManifestAsset>>;
extra?: InputMaybe<Scalars['JSONObject']>;
@@ -3298,6 +3363,7 @@ export type ProjectArchiveSourceInput = {
export enum ProjectArchiveSourceType {
Gcs = 'GCS',
None = 'NONE',
S3 = 'S3',
Url = 'URL'
}
@@ -3632,6 +3698,14 @@ export type RootQueryUserByUsernameArgs = {
username: Scalars['String'];
};
export type Runtime = {
__typename?: 'Runtime';
app: App;
firstBuildCreatedAt: Scalars['DateTime'];
id: Scalars['ID'];
version: Scalars['String'];
};
export type SecondFactorBooleanResult = {
__typename?: 'SecondFactorBooleanResult';
success: Scalars['Boolean'];
@@ -5032,6 +5106,14 @@ export type CreateIosBuildMutationVariables = Exact<{
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type RetryIosBuildMutationVariables = Exact<{
buildId: Scalars['ID'];
jobOverrides: IosJobOverridesInput;
}>;
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
input: CreateEnvironmentSecretInput;
accountId: Scalars['String'];
@@ -15,6 +15,9 @@ import {
CreateIosBuildMutationVariables,
EasBuildDeprecationInfo,
IosJobInput,
IosJobOverridesInput,
RetryIosBuildMutation,
RetryIosBuildMutationVariables,
} from '../generated';
import { BuildFragmentNode } from '../types/Build';
@@ -116,4 +119,32 @@ export const BuildMutation = {
);
return nullthrows(data.build?.createIosBuild);
},
async retryIosBuildAsync(
graphqlClient: ExpoGraphqlClient,
input: {
buildId: string;
jobOverrides: IosJobOverridesInput;
}
): Promise<BuildFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<RetryIosBuildMutation, RetryIosBuildMutationVariables>(
gql`
mutation RetryIosBuildMutation($buildId: ID!, $jobOverrides: IosJobOverridesInput!) {
build {
retryIosBuild(buildId: $buildId, jobOverrides: $jobOverrides) {
id
...BuildFragment
}
}
}
${print(BuildFragmentNode)}
`,
input,
{ noRetry: true }
)
.toPromise()
);
return nullthrows(data.build?.retryIosBuild);
},
};
@@ -1,6 +1,7 @@
import { print } from 'graphql';
import gql from 'graphql-tag';
import { ChannelNotFoundError } from '../../channel/errors';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
@@ -59,7 +60,7 @@ export const ChannelQuery = {
const { updateChannelByName } = response.app.byId;
if (!updateChannelByName) {
throw new Error(`Could not find channel with the name ${channelName}`);
throw new ChannelNotFoundError(`Could not find channel with the name ${channelName}`);
}
return updateChannelByName;
+1
View File
@@ -31,6 +31,7 @@ export type RawAsset = {
contentType: string;
path: string;
};
type CollectedAssets = {
[platform in Platform]?: {
launchAsset: RawAsset;
@@ -0,0 +1,155 @@
import { instance, mock } from 'ts-mockito';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { UpdateBranch, UpdateChannel } from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import { getBranchNameFromChannelNameAsync } from '../getBranchNameFromChannelNameAsync';
jest.mock('../../graphql/queries/ChannelQuery');
jest.mock('../../graphql/queries/BranchQuery');
describe(getBranchNameFromChannelNameAsync, () => {
beforeEach(() => {
jest.resetAllMocks();
});
test('returns branch name when channel exists and has one connected branch', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockImplementationOnce(
async () =>
mockUpdateChannel({
channelName: 'test-channel-name',
branchNames: ['test-branch-name'],
}) as any
);
const result = await getBranchNameFromChannelNameAsync(
graphqlClient,
'test-project-id',
'channel-name'
);
expect(result).toBe('test-branch-name');
});
test('errors when no branch is connected to channel', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockImplementationOnce(
async () =>
mockUpdateChannel({
channelName: 'test-channel-name',
}) as any
);
expect(
getBranchNameFromChannelNameAsync(graphqlClient, 'test-project-id', 'test-channel-name')
).rejects.toThrow(
"Channel has no branches associated with it. Run 'eas channel:edit' to map a branch"
);
});
test('errors when more than one branch is connected to channel', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockImplementationOnce(
async () =>
mockUpdateChannel({
channelName: 'test-channel-name',
branchNames: ['test-branch-name', 'test-branch-name-2'],
}) as any
);
expect(
getBranchNameFromChannelNameAsync(graphqlClient, 'test-project-id', 'test-channel-name')
).rejects.toThrow(
"Channel has multiple branches associated with it. Instead, use 'eas update --branch'"
);
});
test('creates channel and branch when channel does not exist, and returns branch name', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockImplementationOnce(
async () =>
mockUpdateChannel({
channelName: 'test-channel-name',
branchNames: ['test-branch-name'],
}) as any
);
const result = await getBranchNameFromChannelNameAsync(
graphqlClient,
'test-project-id',
'test-channel-name'
);
expect(result).toBe('test-branch-name');
});
});
function mockUpdateChannel({
channelName,
branchNames,
}: {
channelName?: string;
branchNames?: string[];
}): UpdateChannel {
return {
id: 'a2a1fa12-9d6a-433a-a432-49c64ef8439f',
name: channelName ?? 'default-channel-name',
createdAt: '2022-12-07T02:24:29.786Z',
appId: '123',
updatedAt: '2022-12-07T02:24:29.786Z',
branchMapping:
'{"data":[{"branchId":"f9dc4dbb-663f-4a19-8cf2-a783b484d2db","branchMappingLogic":"true"}],"version":0}',
updateBranches: branchNames ? mockUpdateBranches(branchNames) : [],
__typename: 'UpdateChannel',
};
}
function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
return branchNames.map(branchName => ({
id: 'f9dc4dbb-663f-4a19-8cf2-a783b484d2db',
name: branchName ?? 'default-branch-name',
appId: '123',
createdAt: '2022-12-07T02:24:29.786Z',
updatedAt: '2022-12-07T02:24:29.786Z',
updates: [],
updateGroups: [
[
{
id: '8503286d-4b18-4b38-a0f7-2b9ae09531a0',
group: 'd715bc20-76b5-477b-a060-3f8184aae87a',
message: 'my message',
createdAt: '2022-12-07T02:24:43.487Z',
runtimeVersion: '1.0.0',
platform: 'ios',
manifestFragment: '...',
activityTimestamp: '2022-12-07T02:24:43.487Z',
branchId: '',
awaitingCodeSigningInfo: false,
isGitWorkingTreeDirty: false,
manifestPermalink: '...',
updatedAt: '2022-12-07T02:24:43.487Z',
gitCommitHash: 'abc5baf77eee821b800af97879d19cc3132b46a7',
actor: {
__typename: 'User' as const,
id: 'f42b73a3-ce6c-4059-a825-e5ddafa022f7',
username: 'jonexpo',
} as any,
branch: {
id: 'f9dc4dbb-663f-4a19-8cf2-a783b484d2db',
name: 'production3',
appId: '123',
createdAt: '2022-12-07T02:24:29.786Z',
updatedAt: '2022-12-07T02:24:29.786Z',
updateGroups: [],
updates: [],
__typename: 'UpdateBranch' as const,
},
codeSigningInfo: null,
__typename: 'Update' as const,
},
],
],
__typename: 'UpdateBranch' as const,
}));
}
+68 -3
View File
@@ -1,7 +1,9 @@
import { modifyConfigAsync } from '@expo/config';
import { ExpoConfig } from '@expo/config-types';
import { Platform, Workflow } from '@expo/eas-build-job';
import { EasJsonAccessor } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import { getEASUpdateURL } from '../api';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
@@ -225,7 +227,7 @@ function warnEASUpdatesManualConfig({
/**
* Make sure that the current `app.json` configuration for EAS Updates is set natively.
*/
async function ensureEASUpdatesIsConfiguredNativelyAsync(
async function ensureEASUpdateIsConfiguredNativelyAsync(
graphqlClient: ExpoGraphqlClient,
{
exp,
@@ -252,6 +254,67 @@ async function ensureEASUpdatesIsConfiguredNativelyAsync(
}
}
/**
* Make sure EAS Build profiles are configured to work with EAS Update by adding channels to build profiles.
*/
async function ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir: string): Promise<void> {
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
if (!(await fs.pathExists(easJsonPath))) {
Log.warn(
`EAS Build is not configured. If you'd like to use EAS Build with EAS Update, run ${chalk.bold(
'eas build:configure'
)}, then re-run ${chalk.bold('eas update:configure')} to configure ${chalk.bold(
'eas.json'
)} with EAS Update.`
);
return;
}
try {
const easJsonAccessor = new EasJsonAccessor(projectDir);
await easJsonAccessor.readRawJsonAsync();
easJsonAccessor.patch(easJsonRawObject => {
const easBuildProfilesWithChannels = Object.keys(easJsonRawObject.build).reduce(
(acc, profileNameKey) => {
const buildProfile = easJsonRawObject.build[profileNameKey];
const isNotAlreadyConfigured = !buildProfile.channel && !buildProfile.releaseChannel;
if (isNotAlreadyConfigured) {
return {
...acc,
[profileNameKey]: {
...buildProfile,
channel: profileNameKey,
},
};
}
return {
...acc,
[profileNameKey]: {
...easJsonRawObject.build[profileNameKey],
},
};
},
{}
);
return {
...easJsonRawObject,
build: easBuildProfilesWithChannels,
};
});
await easJsonAccessor.writeAsync();
Log.withTick(`Configured ${chalk.bold('eas.json')}.`);
} catch (error) {
Log.error(`We were not able to configure ${chalk.bold('eas.json')}. Error: ${error}.`);
}
}
/**
* Make sure EAS Update is fully configured in the current project.
* This goes over a checklist and performs the following checks or changes:
@@ -261,7 +324,7 @@ async function ensureEASUpdatesIsConfiguredNativelyAsync(
* - Sets `updates.url` if not set
* - Ensure latest changes are reflected in the native config, if any
*/
export async function ensureEASUpdatesIsConfiguredAsync(
export async function ensureEASUpdateIsConfiguredAsync(
graphqlClient: ExpoGraphqlClient,
{
exp: expWithoutUpdates,
@@ -300,8 +363,10 @@ export async function ensureEASUpdatesIsConfiguredAsync(
workflows,
});
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
if (projectChanged || !hasExpoUpdates) {
await ensureEASUpdatesIsConfiguredNativelyAsync(graphqlClient, {
await ensureEASUpdateIsConfiguredNativelyAsync(graphqlClient, {
exp: expWithUpdates,
projectDir,
projectId,
@@ -0,0 +1,58 @@
import { ensureBranchExistsAsync } from '../branch/queries';
import { ChannelNotFoundError } from '../channel/errors';
import { createChannelOnAppAsync } from '../channel/queries';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { ChannelQuery } from '../graphql/queries/ChannelQuery';
export async function getBranchNameFromChannelNameAsync(
graphqlClient: ExpoGraphqlClient,
projectId: string,
channelName: string
): Promise<string> {
let branchName;
try {
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName,
});
if (channel.updateBranches.length === 1) {
branchName = channel.updateBranches[0].name;
} else if (channel.updateBranches.length === 0) {
throw new Error(
"Channel has no branches associated with it. Run 'eas channel:edit' to map a branch"
);
} else {
throw new Error(
"Channel has multiple branches associated with it. Instead, use 'eas update --branch'"
);
}
} catch (error) {
if (!(error instanceof ChannelNotFoundError)) {
throw error;
}
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
appId: projectId,
branchName: channelName,
});
const {
updateChannel: { createUpdateChannelForApp: newChannel },
} = await createChannelOnAppAsync(graphqlClient, {
appId: projectId,
channelName,
branchId,
});
if (!newChannel) {
throw new Error(
`Could not create channel with name ${channelName} on project with id ${projectId}`
);
}
branchName = channelName;
}
return branchName;
}
+3 -1
View File
@@ -126,7 +126,9 @@ export async function selectUpdateGroupOnBranchAsync(
}),
promptOptions: {
title: 'Load more update groups?',
createDisplayTextForSelectionPromptListItem: updateGroup => formatUpdateTitle(updateGroup[0]),
createDisplayTextForSelectionPromptListItem: updateGroup => ({
title: formatUpdateTitle(updateGroup[0]),
}),
getIdentifierForQueryItem: updateGroup => updateGroup[0].group,
},
});
@@ -129,7 +129,7 @@ describe(paginatedQueryWithSelectPromptAsync.name, () => {
promptOptions: {
title: '',
getIdentifierForQueryItem: item => item.id,
createDisplayTextForSelectionPromptListItem: item => 'item: ' + item.value,
createDisplayTextForSelectionPromptListItem: item => ({ title: 'item: ' + item.value }),
},
});
@@ -179,7 +179,7 @@ describe(paginatedQueryWithSelectPromptAsync.name, () => {
promptOptions: {
title: '',
getIdentifierForQueryItem: item => item.id,
createDisplayTextForSelectionPromptListItem: item => item.value,
createDisplayTextForSelectionPromptListItem: item => ({ title: item.value }),
},
});
+9 -2
View File
@@ -3,6 +3,11 @@ import uniqBy from './expodash/uniqBy';
const fetchMoreValue = '_fetchMore';
export interface SelectPromptEntry {
title: string;
description?: string;
}
type BasePaginatedQueryArgs<QueryReturnType extends Record<string, any>> = {
limit: number;
offset: number;
@@ -21,7 +26,9 @@ type PaginatedQueryWithSelectPromptArgs<QueryReturnType extends Record<string, a
BasePaginatedQueryArgs<QueryReturnType> & {
promptOptions: {
readonly title: string;
createDisplayTextForSelectionPromptListItem: (queryItem: QueryReturnType) => string;
createDisplayTextForSelectionPromptListItem: (
queryItem: QueryReturnType
) => SelectPromptEntry;
getIdentifierForQueryItem: (queryItem: QueryReturnType) => string;
};
};
@@ -100,7 +107,7 @@ async function paginatedQueryWithSelectPromptInternalAsync<
const selectionPromptListItems = uniqBy(newAccumulator, queryItem =>
promptOptions.getIdentifierForQueryItem(queryItem)
).map(queryItem => ({
title: promptOptions.createDisplayTextForSelectionPromptListItem(queryItem),
...promptOptions.createDisplayTextForSelectionPromptListItem(queryItem),
value: promptOptions.getIdentifierForQueryItem(queryItem),
}));
if (areMorePagesAvailable) {