mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9201af566d | ||
|
|
cf4eb7a4e4 | ||
|
|
3ca02ab740 | ||
|
|
c05ef9ccca | ||
|
|
dccd249cb0 | ||
|
|
9e3840a987 | ||
|
|
a32e98a72a | ||
|
|
3496c30a2c | ||
|
|
38e4b65a8a | ||
|
|
5e5273e6a4 | ||
|
|
e1be834d62 | ||
|
|
55e5ab465a | ||
|
|
5be66f3963 |
No files matched your search
@@ -10,8 +10,22 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Bump `eas-cli-local-build-plugin` to 0.0.144 to not require `yarn` for local builds. ([c05ef](https://github.com/expo/eas-cli/commit/c05ef9cccaa819d8e5fb84858cb09cadaae90134) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove EAS Submit errors from codebase. The errors have been moved to the server side. ([#1443](https://github.com/expo/eas-cli/pull/1443) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [2.4.0](https://github.com/expo/eas-cli/releases/tag/v2.4.0) - 2022-10-14
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Handle more common errors for EAS Submit. ([#1440](https://github.com/expo/eas-cli/pull/1440) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Move graphql and session into command context. ([#1435](https://github.com/expo/eas-cli/pull/1435), [#1436](https://github.com/expo/eas-cli/pull/1436) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
## [2.3.0](https://github.com/expo/eas-cli/releases/tag/v2.3.0) - 2022-10-10
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
## Releasing a new version
|
||||
|
||||
1. Run `yarn release` in the repository root folder.
|
||||
1. Run `yarn release` in the repository root folder. The next version is chosen automatically based on the changelog entries. If you want to use different version, pass the version string as a positional argument to the command, e.g. `yarn release 1.2.3`.
|
||||
2. That's it! GitHub Actions is going to take care of the rest. Watch the #eas-cli Slack channel for a successful release notification.
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "2.3.0"
|
||||
"version": "2.4.1"
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
"watch": "yarn start",
|
||||
"eas": "packages/eas-cli/bin/run",
|
||||
"lint": "eslint .",
|
||||
"release": "./scripts/bin/run update-local-plugin && lerna version --exact",
|
||||
"release": "./scripts/bin/run release",
|
||||
"test": "jest",
|
||||
"clean": "lerna run clean && rm -rf node_modules coverage"
|
||||
},
|
||||
|
||||
+47
-47
@@ -125,7 +125,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -142,7 +142,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -159,7 +159,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -173,7 +173,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -223,7 +223,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -244,7 +244,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -264,7 +264,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -284,7 +284,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -307,7 +307,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -337,7 +337,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -354,7 +354,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -371,7 +371,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -406,7 +406,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/v2.3.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -443,7 +443,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -490,7 +490,7 @@ DESCRIPTION
|
||||
Update version of an app.
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v2.3.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/v2.4.1/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -509,7 +509,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/v2.3.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/v2.4.1/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -526,7 +526,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -547,7 +547,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -569,7 +569,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -589,7 +589,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -612,7 +612,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -631,7 +631,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -648,7 +648,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -662,7 +662,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/v2.3.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -682,7 +682,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -703,7 +703,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -717,7 +717,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -731,7 +731,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -818,7 +818,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/v2.3.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -835,7 +835,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/v2.3.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -849,7 +849,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -863,7 +863,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -885,7 +885,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -908,7 +908,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/v2.3.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -926,7 +926,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -940,7 +940,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -970,7 +970,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1000,7 +1000,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1017,7 +1017,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1038,7 +1038,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1060,7 +1060,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1080,7 +1080,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1101,7 +1101,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1121,7 +1121,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1138,7 +1138,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1160,7 +1160,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1177,7 +1177,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v2.3.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v2.4.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "2.3.0",
|
||||
"version": "2.4.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { JSONValue } from '@expo/json-file';
|
||||
|
||||
export class ApiV2Error extends Error {
|
||||
override readonly name = 'ApiV2Error';
|
||||
readonly expoApiV2ErrorCode: string;
|
||||
readonly expoApiV2ErrorDetails?: JSONValue;
|
||||
readonly expoApiV2ErrorServerStack?: string;
|
||||
readonly expoApiV2ErrorMetadata?: object;
|
||||
|
||||
constructor(response: {
|
||||
message: string;
|
||||
code: string;
|
||||
stack?: string;
|
||||
details?: JSONValue;
|
||||
metadata?: object;
|
||||
}) {
|
||||
super(response.message);
|
||||
this.expoApiV2ErrorCode = response.code;
|
||||
this.expoApiV2ErrorDetails = response.details;
|
||||
this.expoApiV2ErrorServerStack = response.stack;
|
||||
this.expoApiV2ErrorMetadata = response.metadata;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import assert from 'assert';
|
||||
import nock from 'nock';
|
||||
|
||||
import { ApiV2Error, api, getExpoApiBaseUrl } from '../api';
|
||||
import { ApiV2Error } from '../ApiV2Error';
|
||||
import { ApiV2Client, getExpoApiBaseUrl } from '../api';
|
||||
|
||||
describe('apiClient', () => {
|
||||
describe(ApiV2Client, () => {
|
||||
it('converts Expo APIv2 error to ApiV2Error', async () => {
|
||||
nock(getExpoApiBaseUrl())
|
||||
.post('/v2/test')
|
||||
@@ -21,7 +22,9 @@ describe('apiClient', () => {
|
||||
|
||||
let error: Error | null = null;
|
||||
try {
|
||||
await api.postAsync('test', { body: {} });
|
||||
await new ApiV2Client({ accessToken: null, sessionSecret: null }).postAsync('test', {
|
||||
body: {},
|
||||
});
|
||||
} catch (e: any) {
|
||||
error = e;
|
||||
}
|
||||
@@ -41,7 +44,9 @@ describe('apiClient', () => {
|
||||
|
||||
let error: Error | null = null;
|
||||
try {
|
||||
await api.postAsync('test', { body: {} });
|
||||
await new ApiV2Client({ accessToken: null, sessionSecret: null }).postAsync('test', {
|
||||
body: {},
|
||||
});
|
||||
} catch (e: any) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
+59
-74
@@ -1,93 +1,78 @@
|
||||
import { JSONValue } from '@expo/json-file';
|
||||
|
||||
import { ApiV2Error } from './ApiV2Error';
|
||||
import fetch, { RequestError, RequestInit } from './fetch';
|
||||
import { getAccessToken, getSessionSecret } from './user/sessionStorage';
|
||||
|
||||
export class ApiV2Error extends Error {
|
||||
override readonly name = 'ApiV2Error';
|
||||
readonly expoApiV2ErrorCode: string;
|
||||
readonly expoApiV2ErrorDetails?: JSONValue;
|
||||
readonly expoApiV2ErrorServerStack?: string;
|
||||
readonly expoApiV2ErrorMetadata?: object;
|
||||
|
||||
constructor(response: {
|
||||
message: string;
|
||||
code: string;
|
||||
stack?: string;
|
||||
details?: JSONValue;
|
||||
metadata?: object;
|
||||
}) {
|
||||
super(response.message);
|
||||
this.expoApiV2ErrorCode = response.code;
|
||||
this.expoApiV2ErrorDetails = response.details;
|
||||
this.expoApiV2ErrorServerStack = response.stack;
|
||||
this.expoApiV2ErrorMetadata = response.metadata;
|
||||
}
|
||||
}
|
||||
|
||||
interface RequestOptions {
|
||||
body: JSONValue;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
async putAsync(path: string, options: RequestOptions): Promise<any> {
|
||||
return await requestAsync(path, { method: 'PUT', body: JSON.stringify(options.body) });
|
||||
},
|
||||
export class ApiV2Client {
|
||||
constructor(
|
||||
private readonly authInfo: {
|
||||
accessToken: string | null;
|
||||
sessionSecret: string | null;
|
||||
}
|
||||
) {}
|
||||
|
||||
async postAsync(path: string, options: RequestOptions): Promise<any> {
|
||||
return await requestAsync(path, { method: 'POST', body: JSON.stringify(options.body) });
|
||||
},
|
||||
|
||||
async deleteAsync(path: string): Promise<any> {
|
||||
return await requestAsync(path, { method: 'DELETE' });
|
||||
},
|
||||
|
||||
async getAsync(path: string): Promise<any> {
|
||||
return await requestAsync(path, { method: 'GET' });
|
||||
},
|
||||
};
|
||||
|
||||
async function requestAsync(path: string, options: RequestInit): Promise<any> {
|
||||
try {
|
||||
const response = await fetch(`${getExpoApiBaseUrl()}/v2/${path}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...getAuthHeaders(),
|
||||
},
|
||||
});
|
||||
return await response.json();
|
||||
} catch (err) {
|
||||
await handleApiErrorAsync(err);
|
||||
public async putAsync(path: string, options: RequestOptions): Promise<any> {
|
||||
return await this.requestAsync(path, { method: 'PUT', body: JSON.stringify(options.body) });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApiErrorAsync(err: any): Promise<void> {
|
||||
if (err instanceof RequestError) {
|
||||
let result: { [key: string]: any };
|
||||
public async postAsync(path: string, options: RequestOptions): Promise<any> {
|
||||
return await this.requestAsync(path, { method: 'POST', body: JSON.stringify(options.body) });
|
||||
}
|
||||
|
||||
public async deleteAsync(path: string): Promise<any> {
|
||||
return await this.requestAsync(path, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
public async getAsync(path: string): Promise<any> {
|
||||
return await this.requestAsync(path, { method: 'GET' });
|
||||
}
|
||||
|
||||
private getAuthHeaders(): Record<string, string> {
|
||||
const token = this.authInfo.accessToken;
|
||||
if (token) {
|
||||
return { authorization: `Bearer ${token}` };
|
||||
}
|
||||
const sessionSecret = this.authInfo.sessionSecret;
|
||||
if (sessionSecret) {
|
||||
return { 'expo-session': sessionSecret };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private async requestAsync(path: string, options: RequestInit): Promise<any> {
|
||||
try {
|
||||
result = (await err.response.json()) as { [key: string]: any };
|
||||
} catch {
|
||||
throw new Error(`Malformed api response: ${await err.response.text()}`);
|
||||
const response = await fetch(`${getExpoApiBaseUrl()}/v2/${path}`, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.getAuthHeaders(),
|
||||
},
|
||||
});
|
||||
return await response.json();
|
||||
} catch (err) {
|
||||
await ApiV2Client.handleApiErrorAsync(err);
|
||||
}
|
||||
if (result.errors?.length) {
|
||||
throw new ApiV2Error(result.errors[0]);
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function getAuthHeaders(): Record<string, string> {
|
||||
const token = getAccessToken();
|
||||
if (token) {
|
||||
return { authorization: `Bearer ${token}` };
|
||||
private static async handleApiErrorAsync(err: any): Promise<void> {
|
||||
if (err instanceof RequestError) {
|
||||
let result: { [key: string]: any };
|
||||
try {
|
||||
result = (await err.response.json()) as { [key: string]: any };
|
||||
} catch {
|
||||
throw new Error(`Malformed api response: ${await err.response.text()}`);
|
||||
}
|
||||
if (result.errors?.length) {
|
||||
throw new ApiV2Error(result.errors[0]);
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const sessionSecret = getSessionSecret();
|
||||
if (sessionSecret) {
|
||||
return { 'expo-session': sessionSecret };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export function getExpoApiBaseUrl(): string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import CliTable from 'cli-table3';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { UpdateBranchFragment } from '../graphql/generated';
|
||||
import { BranchQuery } from '../graphql/queries/BranchQuery';
|
||||
@@ -14,17 +15,20 @@ import {
|
||||
|
||||
export const BRANCHES_LIMIT = 50;
|
||||
|
||||
export async function selectBranchOnAppAsync({
|
||||
projectId,
|
||||
promptTitle,
|
||||
displayTextForListItem,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
displayTextForListItem: (queryItem: UpdateBranchFragment) => string;
|
||||
promptTitle: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<UpdateBranchFragment> {
|
||||
export async function selectBranchOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId,
|
||||
promptTitle,
|
||||
displayTextForListItem,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
displayTextForListItem: (queryItem: UpdateBranchFragment) => string;
|
||||
promptTitle: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<UpdateBranchFragment> {
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
throw new Error('Unable to select a branch in non-interactive mode.');
|
||||
}
|
||||
@@ -32,7 +36,8 @@ export async function selectBranchOnAppAsync({
|
||||
const selectedBranch = await paginatedQueryWithSelectPromptAsync({
|
||||
limit: paginatedQueryOptions.limit ?? BRANCHES_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) => queryBranchesOnProjectAsync(limit, offset, projectId),
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryBranchesOnProjectAsync(graphqlClient, limit, offset, projectId),
|
||||
promptOptions: {
|
||||
title: promptTitle,
|
||||
getIdentifierForQueryItem: updateBranchFragment => updateBranchFragment.id,
|
||||
@@ -45,15 +50,19 @@ export async function selectBranchOnAppAsync({
|
||||
return selectedBranch;
|
||||
}
|
||||
|
||||
export async function listAndRenderBranchesOnAppAsync({
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<void> {
|
||||
export async function listAndRenderBranchesOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<void> {
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
const branches = await queryBranchesOnProjectAsync(
|
||||
graphqlClient,
|
||||
paginatedQueryOptions.limit ?? BRANCHES_LIMIT,
|
||||
paginatedQueryOptions.offset,
|
||||
projectId
|
||||
@@ -63,7 +72,8 @@ export async function listAndRenderBranchesOnAppAsync({
|
||||
await paginatedQueryWithConfirmPromptAsync({
|
||||
limit: paginatedQueryOptions.limit ?? BRANCHES_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) => queryBranchesOnProjectAsync(limit, offset, projectId),
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryBranchesOnProjectAsync(graphqlClient, limit, offset, projectId),
|
||||
promptOptions: {
|
||||
title: 'Load more branches?',
|
||||
renderListItems: branches => renderPageOfBranches(branches, paginatedQueryOptions),
|
||||
@@ -73,11 +83,12 @@ export async function listAndRenderBranchesOnAppAsync({
|
||||
}
|
||||
|
||||
async function queryBranchesOnProjectAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
limit: number,
|
||||
offset: number,
|
||||
projectId: string
|
||||
): Promise<UpdateBranchFragment[]> {
|
||||
return await BranchQuery.listBranchesOnAppAsync({
|
||||
return await BranchQuery.listBranchesOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
limit,
|
||||
offset,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
|
||||
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
@@ -43,14 +45,18 @@ describe(selectBuildToCancelAsync.name, () => {
|
||||
});
|
||||
|
||||
it('does not return build id when confirmation is rejected', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
jest.mocked(confirmAsync).mockResolvedValueOnce(false);
|
||||
expect(selectBuildToCancelAsync(projectId, 'blah')).resolves.toEqual(null);
|
||||
expect(selectBuildToCancelAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(null);
|
||||
});
|
||||
|
||||
it('returns build id when confirmation is confirmed', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
jest.mocked(selectAsync).mockResolvedValueOnce(selectedBuildId);
|
||||
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
|
||||
expect(selectBuildToCancelAsync(projectId, 'blah')).resolves.toEqual(selectedBuildId);
|
||||
expect(selectBuildToCancelAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(
|
||||
selectedBuildId
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
|
||||
const versionCodeOverride =
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? await resolveRemoteVersionCodeAsync({
|
||||
? await resolveRemoteVersionCodeAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
projectId: ctx.projectId,
|
||||
exp: ctx.exp,
|
||||
@@ -84,7 +84,7 @@ export async function prepareAndroidBuildAsync(
|
||||
return await ensureAndroidCredentialsAsync(ctx);
|
||||
},
|
||||
syncProjectConfigurationAsync: async () => {
|
||||
await syncProjectConfigurationAsync({
|
||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
localAutoIncrement:
|
||||
@@ -108,7 +108,7 @@ export async function prepareAndroidBuildAsync(
|
||||
): Promise<BuildResult> => {
|
||||
const graphqlMetadata = transformMetadata(metadata);
|
||||
const graphqlJob = transformJob(job);
|
||||
return await BuildMutation.createAndroidBuildAsync({
|
||||
return await BuildMutation.createAndroidBuildAsync(ctx.graphqlClient, {
|
||||
appId,
|
||||
job: graphqlJob,
|
||||
metadata: graphqlMetadata,
|
||||
|
||||
@@ -6,30 +6,34 @@ import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import Log from '../../log';
|
||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||
projectId: string;
|
||||
}): Promise<void> {
|
||||
export async function syncProjectConfigurationAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
exp,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||
projectId: string;
|
||||
}
|
||||
): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
await syncUpdatesConfigurationAsync(projectDir, exp, projectId);
|
||||
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
@@ -174,20 +175,24 @@ async function writeBuildGradleAsync({
|
||||
* Returns buildNumber that will be used for the next build. If current build profile
|
||||
* has an 'autoIncrement' option set, it increments the version on server.
|
||||
*/
|
||||
export async function resolveRemoteVersionCodeAsync({
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationId,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationId: string;
|
||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||
}): Promise<string> {
|
||||
export async function resolveRemoteVersionCodeAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationId,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationId: string;
|
||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||
}
|
||||
): Promise<string> {
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
AppPlatform.Android,
|
||||
applicationId
|
||||
@@ -217,7 +222,7 @@ export async function resolveRemoteVersionCodeAsync({
|
||||
`Initializing versionCode with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
await AppVersionMutation.createAppVersionAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Android,
|
||||
applicationIdentifier: applicationId,
|
||||
@@ -239,7 +244,7 @@ export async function resolveRemoteVersionCodeAsync({
|
||||
)}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
await AppVersionMutation.createAppVersionAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Android,
|
||||
applicationIdentifier: applicationId,
|
||||
|
||||
@@ -8,6 +8,7 @@ import nullthrows from 'nullthrows';
|
||||
import { withAnalyticsAsync } from '../analytics/common';
|
||||
import { BuildEvent } from '../analytics/events';
|
||||
import { getExpoWebsiteBaseUrl } from '../api';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
@@ -213,6 +214,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
projectTarballPath = projectTarball.path;
|
||||
|
||||
const { bucketKey } = await uploadFileAtPathToS3Async(
|
||||
ctx.graphqlClient,
|
||||
UploadSessionType.EasBuildProjectSources,
|
||||
projectTarball.path,
|
||||
createProgressTracker({
|
||||
@@ -275,6 +277,7 @@ async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJ
|
||||
type MaybeBuildFragment = BuildFragment | null;
|
||||
|
||||
export async function waitForBuildEndAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
|
||||
{ intervalSec = 10 } = {}
|
||||
): Promise<MaybeBuildFragment[]> {
|
||||
@@ -289,7 +292,7 @@ export async function waitForBuildEndAsync(
|
||||
spinner = ora('Waiting for builds to complete. You can press Ctrl+C to exit.').start();
|
||||
}
|
||||
while (true) {
|
||||
const builds = await getBuildsSafelyAsync(buildIds);
|
||||
const builds = await getBuildsSafelyAsync(graphqlClient, buildIds);
|
||||
const { refetch } =
|
||||
builds.length === 1
|
||||
? await handleSingleBuildProgressAsync({ build: builds[0], accountName }, { spinner })
|
||||
@@ -301,10 +304,13 @@ export async function waitForBuildEndAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function getBuildsSafelyAsync(buildIds: string[]): Promise<MaybeBuildFragment[]> {
|
||||
async function getBuildsSafelyAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
buildIds: string[]
|
||||
): Promise<MaybeBuildFragment[]> {
|
||||
const promises = buildIds.map(async buildId => {
|
||||
try {
|
||||
return await BuildQuery.byIdAsync(buildId, { useCache: false });
|
||||
return await BuildQuery.byIdAsync(graphqlClient, buildId, { useCache: false });
|
||||
} catch (err) {
|
||||
Log.debug('Failed to fetch the build status', err);
|
||||
return null;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||
|
||||
import { TrackingContext } from '../analytics/common';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import { Target } from '../credentials/ios/types';
|
||||
import { BuildResourceClass } from '../graphql/generated';
|
||||
@@ -47,6 +48,7 @@ export interface BuildContext<T extends Platform> {
|
||||
message?: string;
|
||||
trackingCtx: TrackingContext;
|
||||
user: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
workflow: Workflow;
|
||||
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
||||
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { TrackingContext } from '../analytics/common';
|
||||
import { Analytics, BuildEvent } from '../analytics/events';
|
||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import { BuildResourceClass } from '../graphql/generated';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||
@@ -31,6 +32,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
resourceClass,
|
||||
message,
|
||||
actor,
|
||||
graphqlClient,
|
||||
getDynamicProjectConfigAsync,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
@@ -45,12 +47,13 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
resourceClass: BuildResourceClass;
|
||||
message?: string;
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
getDynamicProjectConfigAsync: DynamicConfigContextFn;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
|
||||
|
||||
const projectName = exp.slug;
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
const accountId = account.id;
|
||||
const runFromCI = getenv.boolish('CI', false);
|
||||
@@ -60,6 +63,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
nonInteractive,
|
||||
projectDir,
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
env: buildProfile.env,
|
||||
easJsonCliConfig,
|
||||
});
|
||||
@@ -99,6 +103,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
projectName,
|
||||
trackingCtx,
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
workflow,
|
||||
message,
|
||||
runFromCI,
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function createIosContextAsync(
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
const buildNumberOverride =
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? await resolveRemoteBuildNumberAsync({
|
||||
? await resolveRemoteBuildNumberAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
projectId: ctx.projectId,
|
||||
exp: ctx.exp,
|
||||
@@ -72,7 +72,7 @@ export async function prepareIosBuildAsync(
|
||||
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
|
||||
},
|
||||
syncProjectConfigurationAsync: async () => {
|
||||
await syncProjectConfigurationAsync({
|
||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
targets: ctx.ios.targets,
|
||||
@@ -100,7 +100,7 @@ export async function prepareIosBuildAsync(
|
||||
): Promise<BuildResult> => {
|
||||
const graphqlMetadata = transformMetadata(metadata);
|
||||
const graphqlJob = transformJob(job);
|
||||
return await BuildMutation.createIosBuildAsync({
|
||||
return await BuildMutation.createIosBuildAsync(ctx.graphqlClient, {
|
||||
appId,
|
||||
job: graphqlJob,
|
||||
metadata: graphqlMetadata,
|
||||
|
||||
@@ -2,31 +2,35 @@ import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
targets: Target[];
|
||||
localAutoIncrement?: IosVersionAutoIncrement;
|
||||
projectId: string;
|
||||
}): Promise<void> {
|
||||
export async function syncProjectConfigurationAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
targets: Target[];
|
||||
localAutoIncrement?: IosVersionAutoIncrement;
|
||||
projectId: string;
|
||||
}
|
||||
): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
await syncUpdatesConfigurationAsync(projectDir, exp, projectId);
|
||||
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import chalk from 'chalk';
|
||||
import path from 'path';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
@@ -255,20 +256,24 @@ export function evaluateTemplateString(
|
||||
* Returns buildNumber that will be used for the next build. If current build profile
|
||||
* has an 'autoIncrement' option set, it increments the version on server.
|
||||
*/
|
||||
export async function resolveRemoteBuildNumberAsync({
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationTarget,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationTarget: Target;
|
||||
buildProfile: BuildProfile<Platform.IOS>;
|
||||
}): Promise<string> {
|
||||
export async function resolveRemoteBuildNumberAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationTarget,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationTarget: Target;
|
||||
buildProfile: BuildProfile<Platform.IOS>;
|
||||
}
|
||||
): Promise<string> {
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
AppPlatform.Ios,
|
||||
applicationTarget.bundleIdentifier
|
||||
@@ -307,7 +312,7 @@ export async function resolveRemoteBuildNumberAsync({
|
||||
`Initializing buildNumber with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
await AppVersionMutation.createAppVersionAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Ios,
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
@@ -329,7 +334,7 @@ export async function resolveRemoteBuildNumberAsync({
|
||||
)}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
await AppVersionMutation.createAppVersionAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Ios,
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
|
||||
@@ -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 = '0.0.110';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.114';
|
||||
|
||||
export interface LocalBuildOptions {
|
||||
enable: boolean;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { BuildFilter, BuildFragment } from '../graphql/generated';
|
||||
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||
@@ -10,19 +11,22 @@ import { formatGraphQLBuild } from './utils/formatBuild';
|
||||
|
||||
export const BUILDS_LIMIT = 50;
|
||||
|
||||
export async function listAndRenderBuildsOnAppAsync({
|
||||
projectId,
|
||||
projectDisplayName,
|
||||
filter,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
projectDisplayName: string;
|
||||
filter?: BuildFilter;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<void> {
|
||||
export async function listAndRenderBuildsOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId,
|
||||
projectDisplayName,
|
||||
filter,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
projectDisplayName: string;
|
||||
filter?: BuildFilter;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<void> {
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
const builds = await BuildQuery.viewBuildsOnAppAsync({
|
||||
const builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
limit: paginatedQueryOptions.limit ?? BUILDS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
@@ -34,7 +38,7 @@ export async function listAndRenderBuildsOnAppAsync({
|
||||
limit: paginatedQueryOptions.limit ?? BUILDS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) =>
|
||||
BuildQuery.viewBuildsOnAppAsync({
|
||||
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
limit,
|
||||
offset,
|
||||
|
||||
@@ -11,6 +11,7 @@ import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
@@ -114,6 +115,7 @@ function resolveResourceClass(
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
flags: BuildFlags,
|
||||
actor: Actor,
|
||||
@@ -166,6 +168,7 @@ export async function runBuildAndSubmitAsync(
|
||||
),
|
||||
easJsonCliConfig,
|
||||
actor,
|
||||
graphqlClient,
|
||||
getDynamicProjectConfigAsync,
|
||||
});
|
||||
if (maybeBuild) {
|
||||
@@ -205,7 +208,10 @@ export async function runBuildAndSubmitAsync(
|
||||
submitProfile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(startedBuild.build.id);
|
||||
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(
|
||||
graphqlClient,
|
||||
startedBuild.build.id
|
||||
);
|
||||
submissions.push(submission);
|
||||
}
|
||||
|
||||
@@ -222,7 +228,7 @@ export async function runBuildAndSubmitAsync(
|
||||
}
|
||||
|
||||
const { accountName } = Object.values(buildCtxByPlatform)[0];
|
||||
const builds = await waitForBuildEndAsync({
|
||||
const builds = await waitForBuildEndAsync(graphqlClient, {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
accountName,
|
||||
});
|
||||
@@ -239,13 +245,16 @@ export async function runBuildAndSubmitAsync(
|
||||
}
|
||||
exitWithNonZeroCodeIfSomeBuildsFailed(builds);
|
||||
} else {
|
||||
const completedSubmissions = await waitForSubmissionsToCompleteAsync(submissions);
|
||||
const completedSubmissions = await waitForSubmissionsToCompleteAsync(
|
||||
graphqlClient,
|
||||
submissions
|
||||
);
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(
|
||||
await Promise.all(
|
||||
builds
|
||||
.filter((i): i is BuildWithSubmissionsFragment => !!i)
|
||||
.map(build => BuildQuery.withSubmissionsByIdAsync(build.id))
|
||||
.map(build => BuildQuery.withSubmissionsByIdAsync(graphqlClient, build.id))
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -261,6 +270,7 @@ async function prepareAndStartBuildAsync({
|
||||
resourceClass,
|
||||
easJsonCliConfig,
|
||||
actor,
|
||||
graphqlClient,
|
||||
getDynamicProjectConfigAsync,
|
||||
}: {
|
||||
projectDir: string;
|
||||
@@ -270,6 +280,7 @@ async function prepareAndStartBuildAsync({
|
||||
resourceClass: BuildResourceClass;
|
||||
easJsonCliConfig: EasJson['cli'];
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
getDynamicProjectConfigAsync: DynamicConfigContextFn;
|
||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||
const buildCtx = await createBuildContextAsync({
|
||||
@@ -285,6 +296,7 @@ async function prepareAndStartBuildAsync({
|
||||
easJsonCliConfig,
|
||||
message: flags.message,
|
||||
actor,
|
||||
graphqlClient,
|
||||
getDynamicProjectConfigAsync,
|
||||
});
|
||||
|
||||
@@ -365,6 +377,7 @@ async function prepareAndStartSubmissionAsync({
|
||||
credentialsCtx: buildCtx.credentialsCtx,
|
||||
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
|
||||
actor: buildCtx.user,
|
||||
graphqlClient: buildCtx.graphqlClient,
|
||||
projectId: buildCtx.projectId,
|
||||
exp: buildCtx.exp,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import {
|
||||
ViewBranchesOnUpdateChannelQueryVariables,
|
||||
@@ -18,15 +19,18 @@ import { logChannelDetails } from './utils';
|
||||
|
||||
export const CHANNELS_LIMIT = 25;
|
||||
|
||||
export async function selectChannelOnAppAsync({
|
||||
projectId,
|
||||
selectionPromptTitle,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
selectionPromptTitle: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<UpdateChannelObject> {
|
||||
export async function selectChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId,
|
||||
selectionPromptTitle,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
selectionPromptTitle: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<UpdateChannelObject> {
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
throw new Error('Unable to select a channel in non-interactive mode.');
|
||||
}
|
||||
@@ -34,7 +38,8 @@ export async function selectChannelOnAppAsync({
|
||||
const updateChannel = await paginatedQueryWithSelectPromptAsync({
|
||||
limit: paginatedQueryOptions.limit ?? CHANNELS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) => queryChannelsOnAppAsync({ appId: projectId, limit, offset }),
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryChannelsOnAppAsync(graphqlClient, { appId: projectId, limit, offset }),
|
||||
promptOptions: {
|
||||
title: selectionPromptTitle,
|
||||
createDisplayTextForSelectionPromptListItem: updateChannel => updateChannel.name,
|
||||
@@ -48,15 +53,18 @@ export async function selectChannelOnAppAsync({
|
||||
return updateChannel;
|
||||
}
|
||||
|
||||
export async function listAndRenderChannelsOnAppAsync({
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<void> {
|
||||
export async function listAndRenderChannelsOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<void> {
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
const channels = await queryChannelsOnAppAsync({
|
||||
const channels = await queryChannelsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
limit: paginatedQueryOptions.limit ?? CHANNELS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
@@ -67,7 +75,7 @@ export async function listAndRenderChannelsOnAppAsync({
|
||||
limit: paginatedQueryOptions.limit ?? CHANNELS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryChannelsOnAppAsync({ limit, offset, appId: projectId }),
|
||||
queryChannelsOnAppAsync(graphqlClient, { limit, offset, appId: projectId }),
|
||||
promptOptions: {
|
||||
title: 'Load more channels?',
|
||||
renderListItems: channels => renderPageOfChannels(channels, paginatedQueryOptions),
|
||||
@@ -76,20 +84,23 @@ export async function listAndRenderChannelsOnAppAsync({
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAndRenderBranchesAndUpdatesOnChannelAsync({
|
||||
projectId: appId,
|
||||
channelName,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
channelName: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<void> {
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync({ appId, channelName });
|
||||
export async function listAndRenderBranchesAndUpdatesOnChannelAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectId: appId,
|
||||
channelName,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
projectId: string;
|
||||
channelName: string;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}
|
||||
): Promise<void> {
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, { appId, channelName });
|
||||
renderChannelHeaderContent({ channelName: channel.name, channelId: channel.id });
|
||||
|
||||
if (paginatedQueryOptions.nonInteractive) {
|
||||
const branches = await queryBranchesAndUpdateGroupsOnChannelAsync({
|
||||
const branches = await queryBranchesAndUpdateGroupsOnChannelAsync(graphqlClient, {
|
||||
appId,
|
||||
channelName,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
@@ -101,7 +112,7 @@ export async function listAndRenderBranchesAndUpdatesOnChannelAsync({
|
||||
limit: paginatedQueryOptions.limit ?? CHANNELS_LIMIT,
|
||||
offset: paginatedQueryOptions.offset,
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryBranchesAndUpdateGroupsOnChannelAsync({
|
||||
queryBranchesAndUpdateGroupsOnChannelAsync(graphqlClient, {
|
||||
channelName,
|
||||
appId,
|
||||
offset,
|
||||
@@ -116,12 +127,11 @@ export async function listAndRenderBranchesAndUpdatesOnChannelAsync({
|
||||
}
|
||||
}
|
||||
|
||||
async function queryChannelsOnAppAsync({
|
||||
appId,
|
||||
offset,
|
||||
limit,
|
||||
}: ViewUpdateChannelsOnAppQueryVariables): Promise<UpdateChannelObject[]> {
|
||||
return await ChannelQuery.viewUpdateChannelsOnAppAsync({
|
||||
async function queryChannelsOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, offset, limit }: ViewUpdateChannelsOnAppQueryVariables
|
||||
): Promise<UpdateChannelObject[]> {
|
||||
return await ChannelQuery.viewUpdateChannelsOnAppAsync(graphqlClient, {
|
||||
appId,
|
||||
offset,
|
||||
limit,
|
||||
@@ -129,9 +139,10 @@ async function queryChannelsOnAppAsync({
|
||||
}
|
||||
|
||||
async function queryBranchesAndUpdateGroupsOnChannelAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
args: ViewBranchesOnUpdateChannelQueryVariables
|
||||
): Promise<UpdateBranchOnChannelObject[]> {
|
||||
return await BranchQuery.listBranchesOnChannelAsync(args);
|
||||
return await BranchQuery.listBranchesOnChannelAsync(graphqlClient, args);
|
||||
}
|
||||
|
||||
function renderPageOfChannels(
|
||||
|
||||
@@ -6,13 +6,15 @@ import {
|
||||
initAsync as initAnalyticsAsync,
|
||||
logEvent,
|
||||
} from '../analytics/rudderstackClient';
|
||||
import { getUserAsync } from '../user/User';
|
||||
import ActorContextField from './context/ActorContextField';
|
||||
import SessionManager from '../user/SessionManager';
|
||||
import ContextField from './context/ContextField';
|
||||
import { DynamicProjectConfigContextField } from './context/DynamicProjectConfigContextField';
|
||||
import LoggedInContextField from './context/LoggedInContextField';
|
||||
import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
|
||||
import { OptionalProjectConfigContextField } from './context/OptionalProjectConfigContextField';
|
||||
import ProjectConfigContextField from './context/ProjectConfigContextField';
|
||||
import ProjectDirContextField from './context/ProjectDirContextField';
|
||||
import SessionManagementContextField from './context/SessionManagementContextField';
|
||||
|
||||
type ContextInput<
|
||||
T extends {
|
||||
@@ -33,10 +35,24 @@ type ContextOutput<
|
||||
export default abstract class EasCommand extends Command {
|
||||
protected static readonly ContextOptions = {
|
||||
/**
|
||||
* Require this command to be run when logged-in. Returns the logged-in actor in the context.
|
||||
* Require this command to be run when logged-in. Returns the logged-in actor and a logged-in
|
||||
* graphql client in the context.
|
||||
*/
|
||||
LoggedIn: {
|
||||
actor: new ActorContextField(),
|
||||
loggedIn: new LoggedInContextField(),
|
||||
},
|
||||
/**
|
||||
* Do not require this command to be run when logged-in, but if it is get the logged-in actor and a
|
||||
* maybe-logged-in graphql client.
|
||||
*/
|
||||
MaybeLoggedIn: {
|
||||
maybeLoggedIn: new MaybeLoggedInContextField(),
|
||||
},
|
||||
/**
|
||||
* Specify this context requirement if the command needs to mutate the user session.
|
||||
*/
|
||||
SessionManagment: {
|
||||
sessionManager: new SessionManagementContextField(),
|
||||
},
|
||||
/**
|
||||
* Require the project to be identified and registered on server if this command is being
|
||||
@@ -92,13 +108,25 @@ export default abstract class EasCommand extends Command {
|
||||
|
||||
const contextValuePairs = await Promise.all(
|
||||
Object.keys(contextDefinition).map(async contextKey => {
|
||||
return [contextKey, await contextDefinition[contextKey].getValueAsync({ nonInteractive })];
|
||||
return [
|
||||
contextKey,
|
||||
await contextDefinition[contextKey].getValueAsync({
|
||||
nonInteractive,
|
||||
sessionManager: this.sessionManager,
|
||||
}),
|
||||
];
|
||||
})
|
||||
);
|
||||
|
||||
return Object.fromEntries(contextValuePairs);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user session manager. Responsible for coordinating all user session related state.
|
||||
* If needed in a subclass, SessionManager ContextOption.
|
||||
*/
|
||||
private readonly sessionManager = new SessionManager();
|
||||
|
||||
protected abstract runAsync(): Promise<any>;
|
||||
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
@@ -106,7 +134,8 @@ export default abstract class EasCommand extends Command {
|
||||
await initAnalyticsAsync();
|
||||
|
||||
// this is needed for logEvent call below as it identifies the user in the analytics system
|
||||
await getUserAsync();
|
||||
await this.sessionManager.getUserAsync();
|
||||
|
||||
logEvent(AnalyticsEvent.ACTION, {
|
||||
// id is assigned by oclif in constructor based on the filepath:
|
||||
// commands/submit === submit, commands/build/list === build:list
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient';
|
||||
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
|
||||
import { getUserAsync } from '../../user/User';
|
||||
import SessionManager from '../../user/SessionManager';
|
||||
import EasCommand from '../EasCommand';
|
||||
|
||||
jest.mock('../../user/User');
|
||||
jest.mock('../../user/SessionManager');
|
||||
jest.mock('../../analytics/rudderstackClient', () => {
|
||||
const { AnalyticsEvent } = jest.requireActual('../../analytics/rudderstackClient');
|
||||
return {
|
||||
@@ -26,10 +26,6 @@ afterAll(() => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest
|
||||
.mocked(getUserAsync)
|
||||
.mockReset()
|
||||
.mockImplementation(async () => mockJester);
|
||||
jest.mocked(initAsync).mockReset();
|
||||
jest.mocked(flushAsync).mockReset();
|
||||
jest.mocked(logEvent).mockReset();
|
||||
@@ -57,7 +53,8 @@ describe(EasCommand.name, () => {
|
||||
const TestEasCommand = createTestEasCommand();
|
||||
await TestEasCommand.run();
|
||||
|
||||
expect(jest.mocked(getUserAsync)).toBeCalledTimes(1);
|
||||
const sessionManagerSpy = jest.spyOn(SessionManager.prototype, 'getUserAsync');
|
||||
expect(sessionManagerSpy).toBeCalledTimes(1);
|
||||
}, 15_000);
|
||||
|
||||
it('initializes analytics', async () => {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Actor } from '../../user/User';
|
||||
import ContextField, { ContextOptions } from './ContextField';
|
||||
import { ensureLoggedInAsync } from './contextUtils/ensureLoggedInAsync';
|
||||
|
||||
export default class ActorContextField extends ContextField<Actor> {
|
||||
async getValueAsync({ nonInteractive }: ContextOptions): Promise<Actor> {
|
||||
return await ensureLoggedInAsync({ nonInteractive });
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import SessionManager from '../../user/SessionManager';
|
||||
|
||||
export interface ContextOptions {
|
||||
sessionManager: SessionManager;
|
||||
nonInteractive: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,14 @@ export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
|
||||
}>;
|
||||
|
||||
export class DynamicProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
|
||||
async getValueAsync({ nonInteractive }: ContextOptions): Promise<DynamicConfigContextFn> {
|
||||
async getValueAsync({
|
||||
nonInteractive,
|
||||
sessionManager,
|
||||
}: ContextOptions): Promise<DynamicConfigContextFn> {
|
||||
return async (options?: ExpoConfigOptions) => {
|
||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||
const expBefore = getExpoConfig(projectDir, options);
|
||||
const projectId = await getProjectIdAsync(expBefore, {
|
||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||
nonInteractive,
|
||||
env: options?.env,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Actor } from '../../user/User';
|
||||
import ContextField, { ContextOptions } from './ContextField';
|
||||
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
||||
|
||||
export default class LoggedInContextField extends ContextField<{
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
}> {
|
||||
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
}> {
|
||||
const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||
return { actor, graphqlClient };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Actor } from '../../user/User';
|
||||
import ContextField, { ContextOptions } from './ContextField';
|
||||
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
|
||||
|
||||
export default class MaybeLoggedInContextField extends ContextField<{
|
||||
actor: Actor | null;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
}> {
|
||||
async getValueAsync({ sessionManager }: ContextOptions): Promise<{
|
||||
actor: Actor | null;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
}> {
|
||||
const authenticationInfo = {
|
||||
accessToken: sessionManager.getAccessToken(),
|
||||
sessionSecret: sessionManager.getSessionSecret(),
|
||||
};
|
||||
|
||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||
const actor = await sessionManager.getUserAsync();
|
||||
|
||||
return { actor: actor ?? null, graphqlClient };
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export class OptionalProjectConfigContextField extends ContextField<
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
async getValueAsync({ nonInteractive }: ContextOptions): Promise<
|
||||
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<
|
||||
| {
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
@@ -36,7 +36,7 @@ export class OptionalProjectConfigContextField extends ContextField<
|
||||
}
|
||||
|
||||
const expBefore = getExpoConfig(projectDir);
|
||||
const projectId = await getProjectIdAsync(expBefore, {
|
||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||
nonInteractive,
|
||||
});
|
||||
const exp = getExpoConfig(projectDir);
|
||||
|
||||
@@ -10,14 +10,14 @@ export default class ProjectConfigContextField extends ContextField<{
|
||||
exp: ExpoConfig;
|
||||
projectDir: string;
|
||||
}> {
|
||||
async getValueAsync({ nonInteractive }: ContextOptions): Promise<{
|
||||
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
projectDir: string;
|
||||
}> {
|
||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||
const expBefore = getExpoConfig(projectDir);
|
||||
const projectId = await getProjectIdAsync(expBefore, {
|
||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||
nonInteractive,
|
||||
});
|
||||
const exp = getExpoConfig(projectDir);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import SessionManager from '../../user/SessionManager';
|
||||
import ContextField, { ContextOptions } from './ContextField';
|
||||
|
||||
export default class SessionManagementContextField extends ContextField<SessionManager> {
|
||||
async getValueAsync({ sessionManager }: ContextOptions): Promise<SessionManager> {
|
||||
return sessionManager;
|
||||
}
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
import { ApiV2Error } from '../../../../api';
|
||||
import { promptAsync } from '../../../../prompts';
|
||||
import { loginAsync } from '../../../../user/User';
|
||||
import {
|
||||
UserSecondFactorDeviceMethod,
|
||||
retryUsernamePasswordAuthWithOTPAsync,
|
||||
} from '../../../../user/otp';
|
||||
import { showLoginPromptAsync } from '../../contextUtils/ensureLoggedInAsync';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('../../../../api', () => ({
|
||||
ApiV2Error: jest.requireActual('../../../../api').ApiV2Error,
|
||||
getExpoApiBaseUrl: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../user/otp');
|
||||
jest.mock('../../../../user/User', () => ({
|
||||
loginAsync: jest.fn(),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.mocked(promptAsync).mockReset();
|
||||
jest.mocked(promptAsync).mockImplementation(async () => {
|
||||
throw new Error('Should not be called');
|
||||
});
|
||||
|
||||
jest.mocked(loginAsync).mockReset();
|
||||
});
|
||||
|
||||
describe(showLoginPromptAsync, () => {
|
||||
it('prompts for OTP when 2FA is enabled', async () => {
|
||||
jest
|
||||
.mocked(promptAsync)
|
||||
.mockImplementationOnce(async () => ({ username: 'hello', password: 'world' }))
|
||||
.mockImplementationOnce(async () => ({ otp: '123456' }))
|
||||
.mockImplementation(async () => {
|
||||
throw new Error("shouldn't happen");
|
||||
});
|
||||
jest
|
||||
.mocked(loginAsync)
|
||||
.mockImplementationOnce(async () => {
|
||||
throw new ApiV2Error({
|
||||
message: 'An OTP is required',
|
||||
code: 'ONE_TIME_PASSWORD_REQUIRED',
|
||||
metadata: {
|
||||
secondFactorDevices: [
|
||||
{
|
||||
id: 'p0',
|
||||
is_primary: true,
|
||||
method: UserSecondFactorDeviceMethod.SMS,
|
||||
sms_phone_number: 'testphone',
|
||||
},
|
||||
],
|
||||
smsAutomaticallySent: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
.mockImplementation(async () => {});
|
||||
|
||||
await showLoginPromptAsync();
|
||||
|
||||
expect(retryUsernamePasswordAuthWithOTPAsync).toHaveBeenCalledWith('hello', 'world', {
|
||||
secondFactorDevices: [
|
||||
{
|
||||
id: 'p0',
|
||||
is_primary: true,
|
||||
method: UserSecondFactorDeviceMethod.SMS,
|
||||
sms_phone_number: 'testphone',
|
||||
},
|
||||
],
|
||||
smsAutomaticallySent: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
+32
-21
@@ -1,11 +1,12 @@
|
||||
import { getConfig, modifyConfigAsync } from '@expo/config';
|
||||
import { vol } from 'memfs';
|
||||
import { anything, instance, mock, when } from 'ts-mockito';
|
||||
|
||||
import { Role } from '../../../../graphql/generated';
|
||||
import { AppQuery } from '../../../../graphql/queries/AppQuery';
|
||||
import { learnMore } from '../../../../log';
|
||||
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||
import { ensureLoggedInAsync } from '../../contextUtils/ensureLoggedInAsync';
|
||||
import SessionManager from '../../../../user/SessionManager';
|
||||
import { findProjectRootAsync } from '../findProjectDirAndVerifyProjectSetupAsync';
|
||||
import { getProjectIdAsync } from '../getProjectIdAsync';
|
||||
|
||||
@@ -13,7 +14,6 @@ jest.mock('@expo/config');
|
||||
jest.mock('fs');
|
||||
|
||||
jest.mock('../../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../contextUtils/ensureLoggedInAsync');
|
||||
jest.mock('../../contextUtils/findProjectDirAndVerifyProjectSetupAsync');
|
||||
jest.mock('../../../../user/User');
|
||||
jest.mock('../../../../ora', () => ({
|
||||
@@ -24,30 +24,37 @@ jest.mock('../../../../ora', () => ({
|
||||
jest.mock('../../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync');
|
||||
|
||||
describe(getProjectIdAsync, () => {
|
||||
let sessionManager: SessionManager;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.mocked(ensureLoggedInAsync).mockResolvedValue({
|
||||
__typename: 'User',
|
||||
id: 'user_id',
|
||||
username: 'notnotbrent',
|
||||
primaryAccount: {
|
||||
id: 'account_id_1',
|
||||
name: 'notnotbrent',
|
||||
users: [{ role: Role.Owner, actor: { id: 'user_id' } }],
|
||||
},
|
||||
accounts: [
|
||||
{
|
||||
const sessionManagerMock = mock<SessionManager>();
|
||||
when(sessionManagerMock.ensureLoggedInAsync(anything())).thenResolve({
|
||||
actor: {
|
||||
__typename: 'User',
|
||||
id: 'user_id',
|
||||
username: 'notnotbrent',
|
||||
primaryAccount: {
|
||||
id: 'account_id_1',
|
||||
name: 'notnotbrent',
|
||||
users: [{ role: Role.Owner, actor: { id: 'user_id' } }],
|
||||
},
|
||||
{
|
||||
id: 'account_id_2',
|
||||
name: 'dominik',
|
||||
users: [{ role: Role.ViewOnly, actor: { id: 'user_id' } }],
|
||||
},
|
||||
],
|
||||
isExpoAdmin: false,
|
||||
accounts: [
|
||||
{
|
||||
id: 'account_id_1',
|
||||
name: 'notnotbrent',
|
||||
users: [{ role: Role.Owner, actor: { id: 'user_id' } }],
|
||||
},
|
||||
{
|
||||
id: 'account_id_2',
|
||||
name: 'dominik',
|
||||
users: [{ role: Role.ViewOnly, actor: { id: 'user_id' } }],
|
||||
},
|
||||
],
|
||||
isExpoAdmin: false,
|
||||
},
|
||||
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
|
||||
});
|
||||
sessionManager = instance(sessionManagerMock);
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
@@ -74,6 +81,7 @@ describe(getProjectIdAsync, () => {
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
@@ -93,6 +101,7 @@ describe(getProjectIdAsync, () => {
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'test', owner: 'wat', extra: { eas: { projectId: '1234' } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
@@ -116,6 +125,7 @@ describe(getProjectIdAsync, () => {
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'wat', extra: { eas: { projectId: '1234' } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
@@ -137,6 +147,7 @@ describe(getProjectIdAsync, () => {
|
||||
.mockImplementation(async () => '2345');
|
||||
|
||||
const projectId = await getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'test' },
|
||||
{
|
||||
nonInteractive: false,
|
||||
@@ -162,7 +173,7 @@ describe(getProjectIdAsync, () => {
|
||||
.mockImplementation(async () => '4567');
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync({ name: 'test', slug: 'test' }, { nonInteractive: false })
|
||||
getProjectIdAsync(sessionManager, { name: 'test', slug: 'test' }, { nonInteractive: false })
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
Client,
|
||||
OperationContext,
|
||||
OperationResult,
|
||||
PromisifiedSource,
|
||||
TypedDocumentNode,
|
||||
cacheExchange,
|
||||
createClient as createUrqlClient,
|
||||
dedupExchange,
|
||||
fetchExchange,
|
||||
} from '@urql/core';
|
||||
import { retryExchange } from '@urql/exchange-retry';
|
||||
import { DocumentNode } from 'graphql';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import { getExpoApiBaseUrl } from '../../../api';
|
||||
import { httpsProxyAgent } from '../../../fetch';
|
||||
|
||||
export interface ExpoGraphqlClient extends Client {
|
||||
query<Data = any, Variables extends object = object>(
|
||||
query: DocumentNode | TypedDocumentNode<Data, Variables> | string,
|
||||
variables: Variables | undefined,
|
||||
context: Partial<OperationContext> & { additionalTypenames: string[] }
|
||||
): PromisifiedSource<OperationResult<Data, Variables>>;
|
||||
}
|
||||
|
||||
export function createGraphqlClient(authInfo: {
|
||||
accessToken: string | null;
|
||||
sessionSecret: string | null;
|
||||
}): ExpoGraphqlClient {
|
||||
return createUrqlClient({
|
||||
url: getExpoApiBaseUrl() + '/graphql',
|
||||
exchanges: [
|
||||
dedupExchange,
|
||||
cacheExchange,
|
||||
retryExchange({
|
||||
maxDelayMs: 4000,
|
||||
retryIf: (err, operation) => {
|
||||
return !!(
|
||||
err &&
|
||||
!operation.context.noRetry &&
|
||||
(err.networkError || err.graphQLErrors.some(e => e?.extensions?.isTransient))
|
||||
);
|
||||
},
|
||||
}),
|
||||
fetchExchange,
|
||||
],
|
||||
// @ts-expect-error Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
|
||||
fetch,
|
||||
fetchOptions: (): RequestInit => {
|
||||
const headers: Record<string, string> = {};
|
||||
if (authInfo.accessToken) {
|
||||
headers.authorization = `Bearer ${authInfo.accessToken}`;
|
||||
} else if (authInfo.sessionSecret) {
|
||||
headers['expo-session'] = authInfo.sessionSecret;
|
||||
}
|
||||
return {
|
||||
...(httpsProxyAgent ? { agent: httpsProxyAgent } : {}),
|
||||
headers,
|
||||
};
|
||||
},
|
||||
}) as ExpoGraphqlClient;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Errors } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ApiV2Error } from '../../../api';
|
||||
import Log, { learnMore } from '../../../log';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { Actor, getUserAsync, loginAsync } from '../../../user/User';
|
||||
import { retryUsernamePasswordAuthWithOTPAsync } from '../../../user/otp';
|
||||
|
||||
/**
|
||||
* Ensure that there is a logged-in actor. Show a login prompt if not.
|
||||
*
|
||||
* @param nonInteractive whether the log-in prompt if logged-out should be interactive
|
||||
* @returns logged-in Actor
|
||||
*
|
||||
* @deprecated Should not be used outside of context functions.
|
||||
*/
|
||||
export async function ensureLoggedInAsync({
|
||||
nonInteractive,
|
||||
}: {
|
||||
nonInteractive: boolean;
|
||||
}): Promise<Actor> {
|
||||
let user: Actor | undefined;
|
||||
try {
|
||||
user = await getUserAsync();
|
||||
} catch {}
|
||||
|
||||
if (!user) {
|
||||
Log.warn('An Expo user account is required to proceed.');
|
||||
await showLoginPromptAsync({ nonInteractive, printNewLine: true });
|
||||
user = await getUserAsync();
|
||||
}
|
||||
|
||||
return nullthrows(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to log in.
|
||||
*
|
||||
* @deprecated Should not be used outside of context functions, except in the AccountLogin command.
|
||||
*/
|
||||
export async function showLoginPromptAsync({
|
||||
nonInteractive = false,
|
||||
printNewLine = false,
|
||||
} = {}): Promise<void> {
|
||||
if (nonInteractive) {
|
||||
Errors.error(
|
||||
`Either log in with ${chalk.bold('eas login')} or set the ${chalk.bold(
|
||||
'EXPO_TOKEN'
|
||||
)} environment variable if you're using EAS CLI on CI (${learnMore(
|
||||
'https://docs.expo.dev/accounts/programmatic-access/',
|
||||
{ dim: false }
|
||||
)})`
|
||||
);
|
||||
}
|
||||
if (printNewLine) {
|
||||
Log.newLine();
|
||||
}
|
||||
|
||||
Log.log('Log in to EAS');
|
||||
|
||||
const { username, password } = await promptAsync([
|
||||
{
|
||||
type: 'text',
|
||||
name: 'username',
|
||||
message: 'Email or username',
|
||||
},
|
||||
{
|
||||
type: 'password',
|
||||
name: 'password',
|
||||
message: 'Password',
|
||||
},
|
||||
]);
|
||||
try {
|
||||
await loginAsync({
|
||||
username,
|
||||
password,
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof ApiV2Error && e.expoApiV2ErrorCode === 'ONE_TIME_PASSWORD_REQUIRED') {
|
||||
await retryUsernamePasswordAuthWithOTPAsync(
|
||||
username,
|
||||
password,
|
||||
e.expoApiV2ErrorMetadata as any
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@ import { ora } from '../../../ora';
|
||||
import { getExpoConfig } from '../../../project/expoConfig';
|
||||
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||
import { toAppPrivacy } from '../../../project/projectUtils';
|
||||
import SessionManager from '../../../user/SessionManager';
|
||||
import { Actor } from '../../../user/User';
|
||||
import { ensureLoggedInAsync } from './ensureLoggedInAsync';
|
||||
import { createGraphqlClient } from './createGraphqlClient';
|
||||
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
|
||||
|
||||
/**
|
||||
@@ -70,13 +71,22 @@ export async function saveProjectIdToAppConfigAsync(
|
||||
* @deprecated Should not be used outside of context functions.
|
||||
*/
|
||||
export async function getProjectIdAsync(
|
||||
sessionManager: SessionManager,
|
||||
exp: ExpoConfig,
|
||||
options: { env?: Env; nonInteractive: boolean }
|
||||
): Promise<string> {
|
||||
// all codepaths in this function require a logged-in user with access to the owning account
|
||||
// since they either query the app via graphql or create it, which includes getting info about
|
||||
// the owner
|
||||
const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({
|
||||
nonInteractive: options.nonInteractive,
|
||||
});
|
||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||
|
||||
const localProjectId = exp.extra?.eas?.projectId;
|
||||
if (localProjectId) {
|
||||
// check that the local project ID matches account and slug
|
||||
const appForProjectId = await AppQuery.byIdAsync(localProjectId);
|
||||
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, localProjectId);
|
||||
if (exp.owner && exp.owner !== appForProjectId.ownerAccount.name) {
|
||||
throw new Error(
|
||||
`Project config: Project identified by "extra.eas.projectId" (${
|
||||
@@ -121,10 +131,8 @@ export async function getProjectIdAsync(
|
||||
}
|
||||
};
|
||||
|
||||
const actor = await ensureLoggedInAsync({
|
||||
nonInteractive: options.nonInteractive,
|
||||
});
|
||||
const projectId = await fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
accountName: getAccountNameForEASProjectSync(exp, actor),
|
||||
projectName: exp.slug,
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { showLoginPromptAsync } from '../../commandUtils/context/contextUtils/ensureLoggedInAsync';
|
||||
import Log from '../../log';
|
||||
|
||||
export default class AccountLogin extends EasCommand {
|
||||
static override description = 'log in with your Expo account';
|
||||
static override aliases = ['login'];
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.SessionManagment,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
await showLoginPromptAsync();
|
||||
const { sessionManager } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
|
||||
await sessionManager.showLoginPromptAsync();
|
||||
Log.log('Logged in');
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import Log from '../../log';
|
||||
import { logoutAsync } from '../../user/User';
|
||||
|
||||
export default class AccountLogout extends EasCommand {
|
||||
static override description = 'log out';
|
||||
static override aliases = ['logout'];
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.SessionManagment,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
await logoutAsync();
|
||||
const { sessionManager } = await this.getContextAsync(AccountLogout, { nonInteractive: false });
|
||||
await sessionManager.logoutAsync();
|
||||
Log.log('Logged out');
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,22 @@ import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import Log from '../../log';
|
||||
import { getActorDisplayName, getUserAsync } from '../../user/User';
|
||||
import { getActorDisplayName } from '../../user/User';
|
||||
|
||||
export default class AccountView extends EasCommand {
|
||||
static override description = 'show the username you are logged in as';
|
||||
static override aliases = ['whoami'];
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.MaybeLoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const user = await getUserAsync();
|
||||
if (user) {
|
||||
Log.log(chalk.green(getActorDisplayName(user)));
|
||||
const {
|
||||
maybeLoggedIn: { actor },
|
||||
} = await this.getContextAsync(AccountView, { nonInteractive: true });
|
||||
if (actor) {
|
||||
Log.log(chalk.green(getActorDisplayName(actor)));
|
||||
} else {
|
||||
Log.warn('Not logged in');
|
||||
process.exit(1);
|
||||
|
||||
@@ -3,8 +3,9 @@ import gql from 'graphql-tag';
|
||||
|
||||
import { getDefaultBranchNameAsync } from '../../branch/utils';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
CreateUpdateBranchForAppMutation,
|
||||
CreateUpdateBranchForAppMutationVariables,
|
||||
@@ -15,10 +16,10 @@ import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
export async function createUpdateBranchOnAppAsync({
|
||||
appId,
|
||||
name,
|
||||
}: CreateUpdateBranchForAppMutationVariables): Promise<Pick<UpdateBranch, 'id' | 'name'>> {
|
||||
export async function createUpdateBranchOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, name }: CreateUpdateBranchForAppMutationVariables
|
||||
): Promise<Pick<UpdateBranch, 'id' | 'name'>> {
|
||||
const result = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateUpdateBranchForAppMutation, CreateUpdateBranchForAppMutationVariables>(
|
||||
@@ -63,6 +64,7 @@ export default class BranchCreate extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -72,6 +74,7 @@ export default class BranchCreate extends EasCommand {
|
||||
} = await this.parse(BranchCreate);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BranchCreate, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -80,7 +83,7 @@ export default class BranchCreate extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
if (!name) {
|
||||
const validationMessage = 'Branch name may not be empty.';
|
||||
@@ -96,7 +99,7 @@ export default class BranchCreate extends EasCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
const newBranch = await createUpdateBranchOnAppAsync({ appId: projectId, name });
|
||||
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, { appId: projectId, name });
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(newBranch);
|
||||
|
||||
@@ -3,9 +3,10 @@ import gql from 'graphql-tag';
|
||||
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
DeleteUpdateBranchMutation,
|
||||
DeleteUpdateBranchMutationVariables,
|
||||
@@ -18,10 +19,10 @@ import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { toggleConfirmAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
async function getBranchInfoAsync({
|
||||
appId,
|
||||
name,
|
||||
}: GetBranchInfoQueryVariables): Promise<GetBranchInfoQuery> {
|
||||
async function getBranchInfoAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, name }: GetBranchInfoQueryVariables
|
||||
): Promise<GetBranchInfoQuery> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<GetBranchInfoQuery, GetBranchInfoQueryVariables>(
|
||||
@@ -49,9 +50,10 @@ async function getBranchInfoAsync({
|
||||
return data;
|
||||
}
|
||||
|
||||
async function deleteBranchOnAppAsync({
|
||||
branchId,
|
||||
}: DeleteUpdateBranchMutationVariables): Promise<DeleteUpdateBranchResult> {
|
||||
async function deleteBranchOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ branchId }: DeleteUpdateBranchMutationVariables
|
||||
): Promise<DeleteUpdateBranchResult> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateBranchMutation, DeleteUpdateBranchMutationVariables>(
|
||||
@@ -78,6 +80,7 @@ export default class BranchDelete extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
static override args = [
|
||||
@@ -106,15 +109,16 @@ export default class BranchDelete extends EasCommand {
|
||||
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BranchDelete, { nonInteractive });
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
if (!branchName) {
|
||||
const validationMessage = 'branch name may not be empty.';
|
||||
if (nonInteractive) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
({ name: branchName } = await selectBranchOnAppAsync({
|
||||
({ name: branchName } = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
displayTextForListItem: updateBranch => updateBranch.name,
|
||||
promptTitle: 'Which branch would you like to delete?',
|
||||
@@ -122,7 +126,7 @@ export default class BranchDelete extends EasCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
const data = await getBranchInfoAsync({ appId: projectId, name: branchName });
|
||||
const data = await getBranchInfoAsync(graphqlClient, { appId: projectId, name: branchName });
|
||||
const branchId = data.app?.byId.updateBranchByName?.id;
|
||||
if (!branchId) {
|
||||
throw new Error(`Could not find branch ${branchName} on ${projectDisplayName}`);
|
||||
@@ -142,7 +146,7 @@ export default class BranchDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
const deletionResult = await deleteBranchOnAppAsync({
|
||||
const deletionResult = await deleteBranchOnAppAsync(graphqlClient, {
|
||||
branchId,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@ export default class BranchList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BranchList);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BranchList, {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
});
|
||||
@@ -29,6 +31,6 @@ export default class BranchList extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
await listAndRenderBranchesOnAppAsync({ projectId, paginatedQueryOptions });
|
||||
await listAndRenderBranchesOnAppAsync(graphqlClient, { projectId, paginatedQueryOptions });
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@ import chalk from 'chalk';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
EditUpdateBranchInput,
|
||||
EditUpdateBranchMutation,
|
||||
@@ -16,11 +17,10 @@ import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
async function renameUpdateBranchOnAppAsync({
|
||||
appId,
|
||||
name,
|
||||
newName,
|
||||
}: EditUpdateBranchInput): Promise<Pick<UpdateBranch, 'id' | 'name'>> {
|
||||
async function renameUpdateBranchOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, name, newName }: EditUpdateBranchInput
|
||||
): Promise<Pick<UpdateBranch, 'id' | 'name'>> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<EditUpdateBranchMutation, EditUpdateBranchMutationVariables>(
|
||||
@@ -64,6 +64,7 @@ export default class BranchRename extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -72,6 +73,7 @@ export default class BranchRename extends EasCommand {
|
||||
} = await this.parse(BranchRename);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BranchRename, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -79,7 +81,7 @@ export default class BranchRename extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
if (!currentName) {
|
||||
const validationMessage = 'current name may not be empty.';
|
||||
@@ -107,7 +109,7 @@ export default class BranchRename extends EasCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
const editedBranch = await renameUpdateBranchOnAppAsync({
|
||||
const editedBranch = await renameUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: currentName!,
|
||||
newName: newName!,
|
||||
|
||||
@@ -28,6 +28,7 @@ export default class BranchView extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -38,6 +39,7 @@ export default class BranchView extends EasCommand {
|
||||
const { 'non-interactive': nonInteractive } = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BranchView, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -52,7 +54,7 @@ export default class BranchView extends EasCommand {
|
||||
throw new Error('Branch name may not be empty.');
|
||||
}
|
||||
|
||||
({ name: branchName } = await selectBranchOnAppAsync({
|
||||
({ name: branchName } = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: 'Which branch would you like to view?',
|
||||
displayTextForListItem: updateBranch => updateBranch.name,
|
||||
@@ -65,7 +67,7 @@ export default class BranchView extends EasCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
await listAndRenderUpdateGroupsOnBranchAsync({
|
||||
await listAndRenderUpdateGroupsOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName,
|
||||
paginatedQueryOptions,
|
||||
|
||||
@@ -2,8 +2,9 @@ import chalk from 'chalk';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
Build,
|
||||
BuildStatus,
|
||||
@@ -17,7 +18,10 @@ import { appPlatformEmojis } from '../../platform';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { confirmAsync, selectAsync } from '../../prompts';
|
||||
|
||||
async function cancelBuildAsync(buildId: string): Promise<Pick<Build, 'id' | 'status'>> {
|
||||
async function cancelBuildAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
buildId: string
|
||||
): Promise<Pick<Build, 'id' | 'status'>> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CancelBuildMutation, CancelBuildMutationVariables>(
|
||||
@@ -56,6 +60,7 @@ function formatUnfinishedBuild(
|
||||
}
|
||||
|
||||
export async function selectBuildToCancelAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
projectDisplayName: string
|
||||
): Promise<string | null> {
|
||||
@@ -64,19 +69,19 @@ export async function selectBuildToCancelAsync(
|
||||
let builds;
|
||||
try {
|
||||
const [newBuilds, inQueueBuilds, inProgressBuilds] = await Promise.all([
|
||||
BuildQuery.viewBuildsOnAppAsync({
|
||||
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
filter: { status: BuildStatus.New },
|
||||
}),
|
||||
BuildQuery.viewBuildsOnAppAsync({
|
||||
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
filter: { status: BuildStatus.InQueue },
|
||||
}),
|
||||
BuildQuery.viewBuildsOnAppAsync({
|
||||
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
@@ -111,9 +116,12 @@ export async function selectBuildToCancelAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureBuildExistsAsync(buildId: string): Promise<void> {
|
||||
async function ensureBuildExistsAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
buildId: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
await BuildQuery.byIdAsync(buildId);
|
||||
await BuildQuery.byIdAsync(graphqlClient, buildId);
|
||||
} catch {
|
||||
throw new Error(`Couldn't find a build matching the id ${buildId}`);
|
||||
}
|
||||
@@ -130,6 +138,7 @@ export default class BuildCancel extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -139,14 +148,15 @@ export default class BuildCancel extends EasCommand {
|
||||
} = await this.parse(BuildCancel);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BuildCancel, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
const displayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
if (buildIdFromArg) {
|
||||
await ensureBuildExistsAsync(buildIdFromArg);
|
||||
await ensureBuildExistsAsync(graphqlClient, buildIdFromArg);
|
||||
}
|
||||
|
||||
let buildId: string | null = buildIdFromArg;
|
||||
@@ -155,7 +165,7 @@ export default class BuildCancel extends EasCommand {
|
||||
throw new Error('BUILD_ID must not be empty in non-interactive mode');
|
||||
}
|
||||
|
||||
buildId = await selectBuildToCancelAsync(projectId, displayName);
|
||||
buildId = await selectBuildToCancelAsync(graphqlClient, projectId, displayName);
|
||||
if (!buildId) {
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +173,7 @@ export default class BuildCancel extends EasCommand {
|
||||
|
||||
const spinner = ora().start('Canceling the build…');
|
||||
try {
|
||||
const { status } = await cancelBuildAsync(buildId);
|
||||
const { status } = await cancelBuildAsync(graphqlClient, buildId);
|
||||
if (status === BuildStatus.Canceled) {
|
||||
spinner.succeed('Build canceled');
|
||||
} else {
|
||||
|
||||
@@ -27,12 +27,14 @@ export default class BuildConfigure extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildConfigure);
|
||||
const {
|
||||
projectConfig: { exp, projectId, projectDir },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BuildConfigure, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
@@ -65,14 +67,14 @@ export default class BuildConfigure extends EasCommand {
|
||||
if ([RequestedPlatform.Android, RequestedPlatform.All].includes(platform)) {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await syncAndroidUpdatesConfigurationAsync(projectDir, exp, projectId);
|
||||
await syncAndroidUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if ([RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)) {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await syncIosUpdatesConfigurationAsync(projectDir, exp, projectId);
|
||||
await syncIosUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,11 @@ export default class Build extends EasCommand {
|
||||
}
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
|
||||
const { actor, getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(Build, {
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(Build, {
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
|
||||
@@ -118,6 +122,7 @@ export default class Build extends EasCommand {
|
||||
|
||||
if (!flags.localBuildOptions.enable) {
|
||||
await maybeWarnAboutEasOutagesAsync(
|
||||
graphqlClient,
|
||||
flags.autoSubmit
|
||||
? [StatuspageServiceName.EasBuild, StatuspageServiceName.EasSubmit]
|
||||
: [StatuspageServiceName.EasBuild]
|
||||
@@ -127,6 +132,7 @@ export default class Build extends EasCommand {
|
||||
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
flagsWithPlatform,
|
||||
actor,
|
||||
|
||||
@@ -69,12 +69,13 @@ export default class BuildInspect extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildInspect);
|
||||
const { actor, getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(
|
||||
BuildInspect,
|
||||
{
|
||||
nonInteractive: false,
|
||||
}
|
||||
);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(BuildInspect, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const outputDirectory = path.resolve(process.cwd(), flags.output);
|
||||
const tmpWorkingdir = path.join(getTmpDirectory(), uuidv4());
|
||||
@@ -95,6 +96,7 @@ export default class BuildInspect extends EasCommand {
|
||||
} else {
|
||||
try {
|
||||
await runBuildAndSubmitAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
{
|
||||
nonInteractive: false,
|
||||
|
||||
@@ -57,6 +57,7 @@ export default class BuildList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -71,6 +72,7 @@ export default class BuildList extends EasCommand {
|
||||
} = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BuildList, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -81,9 +83,9 @@ export default class BuildList extends EasCommand {
|
||||
const platform = toAppPlatform(requestedPlatform);
|
||||
const graphqlBuildStatus = toGraphQLBuildStatus(buildStatus);
|
||||
const graphqlBuildDistribution = toGraphQLBuildDistribution(buildDistribution);
|
||||
const displayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
await listAndRenderBuildsOnAppAsync({
|
||||
await listAndRenderBuildsOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
projectDisplayName: displayName,
|
||||
filter: {
|
||||
|
||||
@@ -45,12 +45,13 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSetView);
|
||||
const { actor, getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(
|
||||
BuildVersionSetView,
|
||||
{
|
||||
nonInteractive: false,
|
||||
}
|
||||
);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(BuildVersionSetView, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const platform = await selectPlatformAsync(flags.platform);
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
@@ -62,7 +63,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
);
|
||||
|
||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: profile.env });
|
||||
const displayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||
|
||||
@@ -74,6 +75,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
actor
|
||||
);
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
toAppPlatform(platform),
|
||||
applicationIdentifier
|
||||
@@ -104,7 +106,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
? value => isValidVersionCode(value) || `Invalid value: ${VERSION_CODE_REQUIREMENTS}.`
|
||||
: value => isValidBuildNumber(value) || `Invalid value: ${BUILD_NUMBER_REQUIREMENTS}.`,
|
||||
});
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
await AppVersionMutation.createAppVersionAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
platform: toAppPlatform(platform),
|
||||
applicationIdentifier,
|
||||
|
||||
@@ -62,12 +62,13 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSyncView);
|
||||
const { actor, getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(
|
||||
BuildVersionSyncView,
|
||||
{
|
||||
nonInteractive: true,
|
||||
}
|
||||
);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(BuildVersionSyncView, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
@@ -96,6 +97,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
actor
|
||||
);
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
toAppPlatform(profileInfo.platform),
|
||||
applicationIdentifier
|
||||
|
||||
@@ -19,6 +19,7 @@ export default class BuildView extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -28,6 +29,7 @@ export default class BuildView extends EasCommand {
|
||||
} = await this.parse(BuildView);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BuildView, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
@@ -35,7 +37,7 @@ export default class BuildView extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const displayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const spinner = ora().start('Fetching the build…');
|
||||
|
||||
@@ -43,9 +45,9 @@ export default class BuildView extends EasCommand {
|
||||
let build: BuildFragment;
|
||||
|
||||
if (buildId) {
|
||||
build = await BuildQuery.byIdAsync(buildId);
|
||||
build = await BuildQuery.byIdAsync(graphqlClient, buildId);
|
||||
} else {
|
||||
const builds = await BuildQuery.viewBuildsOnAppAsync({
|
||||
const builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
offset: 0,
|
||||
limit: 1,
|
||||
|
||||
@@ -3,8 +3,9 @@ import gql from 'graphql-tag';
|
||||
|
||||
import { BranchNotFoundError } from '../../branch/utils';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
CreateUpdateChannelOnAppMutation,
|
||||
CreateUpdateChannelOnAppMutationVariables,
|
||||
@@ -17,15 +18,18 @@ import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { createUpdateBranchOnAppAsync } from '../branch/create';
|
||||
|
||||
export async function createUpdateChannelOnAppAsync({
|
||||
appId,
|
||||
channelName,
|
||||
branchId,
|
||||
}: {
|
||||
appId: string;
|
||||
channelName: string;
|
||||
branchId: string;
|
||||
}): Promise<CreateUpdateChannelOnAppMutation> {
|
||||
export async function createUpdateChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
channelName,
|
||||
branchId,
|
||||
}: {
|
||||
appId: string;
|
||||
channelName: string;
|
||||
branchId: string;
|
||||
}
|
||||
): Promise<CreateUpdateChannelOnAppMutation> {
|
||||
// Point the new channel at a branch with its same name.
|
||||
const branchMapping = JSON.stringify({
|
||||
data: [{ branchId, branchMappingLogic: 'true' }],
|
||||
@@ -72,6 +76,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -81,6 +86,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
} = await this.parse(ChannelCreate);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelCreate, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -105,7 +111,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
let branchMessage: string;
|
||||
|
||||
try {
|
||||
const branch = await BranchQuery.getBranchByNameAsync({
|
||||
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: channelName,
|
||||
});
|
||||
@@ -113,7 +119,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
branchMessage = `We found a branch with the same name`;
|
||||
} catch (error) {
|
||||
if (error instanceof BranchNotFoundError) {
|
||||
const newBranch = await createUpdateBranchOnAppAsync({
|
||||
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: channelName,
|
||||
});
|
||||
@@ -126,7 +132,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
|
||||
const {
|
||||
updateChannel: { createUpdateChannelForApp: newChannel },
|
||||
} = await createUpdateChannelOnAppAsync({
|
||||
} = await createUpdateChannelOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName,
|
||||
branchId,
|
||||
@@ -144,7 +150,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(
|
||||
`Created a new channel on project ${chalk.bold(
|
||||
await getDisplayNameForProjectIdAsync(projectId)
|
||||
await getDisplayNameForProjectIdAsync(graphqlClient, projectId)
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
|
||||
@@ -2,8 +2,9 @@ import gql from 'graphql-tag';
|
||||
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
DeleteUpdateChannelMutation,
|
||||
DeleteUpdateChannelMutationVariables,
|
||||
@@ -31,6 +32,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -40,6 +42,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
} = await this.parse(ChannelDelete);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelDelete, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -49,7 +52,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
|
||||
let channelId, channelName;
|
||||
if (nameArg) {
|
||||
const { id, name } = await ChannelQuery.viewUpdateChannelAsync({
|
||||
const { id, name } = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: nameArg,
|
||||
});
|
||||
@@ -59,7 +62,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Channel name must be set when running in non-interactive mode');
|
||||
}
|
||||
const { id, name } = await selectChannelOnAppAsync({
|
||||
const { id, name } = await selectChannelOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
selectionPromptTitle: 'Select a channel to delete',
|
||||
paginatedQueryOptions: {
|
||||
@@ -85,7 +88,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
const deletionResult = await deleteChannelOnAppAsync({
|
||||
const deletionResult = await deleteChannelOnAppAsync(graphqlClient, {
|
||||
channelId,
|
||||
});
|
||||
|
||||
@@ -97,9 +100,10 @@ export default class ChannelDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteChannelOnAppAsync({
|
||||
channelId,
|
||||
}: DeleteUpdateChannelMutationVariables): Promise<DeleteUpdateChannelResult> {
|
||||
async function deleteChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId }: DeleteUpdateChannelMutationVariables
|
||||
): Promise<DeleteUpdateChannelResult> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateChannelMutation, DeleteUpdateChannelMutationVariables>(
|
||||
|
||||
@@ -5,8 +5,9 @@ import gql from 'graphql-tag';
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
UpdateChannelBranchMappingMutation,
|
||||
UpdateChannelBranchMappingMutationVariables,
|
||||
@@ -16,12 +17,10 @@ import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
export async function updateChannelBranchMappingAsync({
|
||||
channelId,
|
||||
branchMapping,
|
||||
}: UpdateChannelBranchMappingMutationVariables): Promise<
|
||||
UpdateChannelBranchMappingMutation['updateChannel']['editUpdateChannel']
|
||||
> {
|
||||
export async function updateChannelBranchMappingAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId, branchMapping }: UpdateChannelBranchMappingMutationVariables
|
||||
): Promise<UpdateChannelBranchMappingMutation['updateChannel']['editUpdateChannel']> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<UpdateChannelBranchMappingMutation, UpdateChannelBranchMappingMutationVariables>(
|
||||
@@ -67,6 +66,7 @@ export default class ChannelEdit extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -76,6 +76,7 @@ export default class ChannelEdit extends EasCommand {
|
||||
} = await this.parse(ChannelEdit);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelEdit, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -84,8 +85,11 @@ export default class ChannelEdit extends EasCommand {
|
||||
}
|
||||
|
||||
const existingChannel = args.name
|
||||
? await ChannelQuery.viewUpdateChannelAsync({ appId: projectId, channelName: args.name })
|
||||
: await selectChannelOnAppAsync({
|
||||
? await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: args.name,
|
||||
})
|
||||
: await selectChannelOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
selectionPromptTitle: 'Select a channel to edit',
|
||||
paginatedQueryOptions: { json, nonInteractive, offset: 0 },
|
||||
@@ -96,11 +100,11 @@ export default class ChannelEdit extends EasCommand {
|
||||
}
|
||||
|
||||
const branch = branchFlag
|
||||
? await BranchQuery.getBranchByNameAsync({
|
||||
? await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: branchFlag,
|
||||
})
|
||||
: await selectBranchOnAppAsync({
|
||||
: await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: `Which branch would you like ${existingChannel.name} to point at?`,
|
||||
displayTextForListItem: updateBranch => updateBranch.name,
|
||||
@@ -111,7 +115,7 @@ export default class ChannelEdit extends EasCommand {
|
||||
},
|
||||
});
|
||||
|
||||
const channel = await updateChannelBranchMappingAsync({
|
||||
const channel = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: existingChannel.id,
|
||||
// todo: move branch mapping logic to utility
|
||||
branchMapping: JSON.stringify({
|
||||
|
||||
@@ -19,6 +19,7 @@ export default class ChannelList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -27,6 +28,7 @@ export default class ChannelList extends EasCommand {
|
||||
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelList, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -34,7 +36,7 @@ export default class ChannelList extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
await listAndRenderChannelsOnAppAsync({
|
||||
await listAndRenderChannelsOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
||||
|
||||
import { BranchMapping, getBranchMapping } from '../../channel/utils';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { UpdateBranch } from '../../graphql/generated';
|
||||
import { BranchQuery } from '../../graphql/queries/BranchQuery';
|
||||
@@ -54,25 +55,28 @@ function getRolloutInfo(channel: UpdateChannelByNameObject): {
|
||||
return { newBranch, oldBranch, currentPercent };
|
||||
}
|
||||
|
||||
async function startRolloutAsync({
|
||||
channelName,
|
||||
branchName,
|
||||
percent,
|
||||
projectId,
|
||||
displayName,
|
||||
currentBranchMapping,
|
||||
channel,
|
||||
nonInteractive,
|
||||
}: {
|
||||
channelName?: string;
|
||||
branchName: string;
|
||||
percent?: number;
|
||||
projectId: string;
|
||||
displayName: string;
|
||||
currentBranchMapping: BranchMapping;
|
||||
channel: UpdateChannelByNameObject;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<{
|
||||
async function startRolloutAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
channelName,
|
||||
branchName,
|
||||
percent,
|
||||
projectId,
|
||||
displayName,
|
||||
currentBranchMapping,
|
||||
channel,
|
||||
nonInteractive,
|
||||
}: {
|
||||
channelName?: string;
|
||||
branchName: string;
|
||||
percent?: number;
|
||||
projectId: string;
|
||||
displayName: string;
|
||||
currentBranchMapping: BranchMapping;
|
||||
channel: UpdateChannelByNameObject;
|
||||
nonInteractive: boolean;
|
||||
}
|
||||
): Promise<{
|
||||
newChannelInfo: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -80,7 +84,7 @@ async function startRolloutAsync({
|
||||
};
|
||||
logMessage: string;
|
||||
}> {
|
||||
const branch = await BranchQuery.getBranchByNameAsync({
|
||||
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: branchName,
|
||||
});
|
||||
@@ -116,7 +120,7 @@ async function startRolloutAsync({
|
||||
currentBranchMapping.data[0],
|
||||
],
|
||||
};
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync({
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(newBranchMapping),
|
||||
});
|
||||
@@ -139,19 +143,22 @@ async function startRolloutAsync({
|
||||
return { newChannelInfo, logMessage };
|
||||
}
|
||||
|
||||
async function editRolloutAsync({
|
||||
channelName,
|
||||
percent,
|
||||
nonInteractive,
|
||||
currentBranchMapping,
|
||||
channel,
|
||||
}: {
|
||||
channelName?: string;
|
||||
percent?: number;
|
||||
nonInteractive: boolean;
|
||||
currentBranchMapping: BranchMapping;
|
||||
channel: UpdateChannelByNameObject;
|
||||
}): Promise<{
|
||||
async function editRolloutAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
channelName,
|
||||
percent,
|
||||
nonInteractive,
|
||||
currentBranchMapping,
|
||||
channel,
|
||||
}: {
|
||||
channelName?: string;
|
||||
percent?: number;
|
||||
nonInteractive: boolean;
|
||||
currentBranchMapping: BranchMapping;
|
||||
channel: UpdateChannelByNameObject;
|
||||
}
|
||||
): Promise<{
|
||||
newChannelInfo: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -178,7 +185,7 @@ async function editRolloutAsync({
|
||||
const newBranchMapping = { ...currentBranchMapping };
|
||||
newBranchMapping.data[0].branchMappingLogic.operand = percent / 100;
|
||||
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync({
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(newBranchMapping),
|
||||
});
|
||||
@@ -194,19 +201,22 @@ async function editRolloutAsync({
|
||||
return { newChannelInfo, logMessage };
|
||||
}
|
||||
|
||||
async function endRolloutAsync({
|
||||
channelName,
|
||||
branchName,
|
||||
nonInteractive,
|
||||
projectId,
|
||||
channel,
|
||||
}: {
|
||||
channelName?: string;
|
||||
branchName?: string;
|
||||
nonInteractive: boolean;
|
||||
projectId: string;
|
||||
channel: UpdateChannelByNameObject;
|
||||
}): Promise<{
|
||||
async function endRolloutAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
channelName,
|
||||
branchName,
|
||||
nonInteractive,
|
||||
projectId,
|
||||
channel,
|
||||
}: {
|
||||
channelName?: string;
|
||||
branchName?: string;
|
||||
nonInteractive: boolean;
|
||||
projectId: string;
|
||||
channel: UpdateChannelByNameObject;
|
||||
}
|
||||
): Promise<{
|
||||
newChannelInfo: {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -219,7 +229,7 @@ async function endRolloutAsync({
|
||||
|
||||
let endOnNewBranch;
|
||||
if (branchName) {
|
||||
const branch = await BranchQuery.getBranchByNameAsync({
|
||||
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: branchName,
|
||||
});
|
||||
@@ -270,7 +280,7 @@ async function endRolloutAsync({
|
||||
],
|
||||
};
|
||||
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync({
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(newBranchMapping),
|
||||
});
|
||||
@@ -313,6 +323,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -328,6 +339,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
} = await this.parse(ChannelRollout);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelRollout, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -335,9 +347,9 @@ export default class ChannelRollout extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync({
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: channelName!,
|
||||
});
|
||||
@@ -382,7 +394,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
logMessage: string;
|
||||
};
|
||||
if (!isRollout) {
|
||||
rolloutMutationResult = await startRolloutAsync({
|
||||
rolloutMutationResult = await startRolloutAsync(graphqlClient, {
|
||||
channelName,
|
||||
branchName: branchName ?? (await promptForBranchNameAsync(channelName, nonInteractive)),
|
||||
percent,
|
||||
@@ -393,7 +405,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
channel,
|
||||
});
|
||||
} else if (endFlag) {
|
||||
rolloutMutationResult = await endRolloutAsync({
|
||||
rolloutMutationResult = await endRolloutAsync(graphqlClient, {
|
||||
channelName,
|
||||
branchName,
|
||||
nonInteractive,
|
||||
@@ -401,7 +413,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
channel,
|
||||
});
|
||||
} else {
|
||||
rolloutMutationResult = await editRolloutAsync({
|
||||
rolloutMutationResult = await editRolloutAsync(graphqlClient, {
|
||||
channelName,
|
||||
percent,
|
||||
nonInteractive,
|
||||
|
||||
@@ -27,6 +27,7 @@ export default class ChannelView extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -38,6 +39,7 @@ export default class ChannelView extends EasCommand {
|
||||
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelView, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -50,7 +52,7 @@ export default class ChannelView extends EasCommand {
|
||||
if (nonInteractive) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
const selectedUpdateChannel = await selectChannelOnAppAsync({
|
||||
const selectedUpdateChannel = await selectChannelOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
selectionPromptTitle: 'Select a channel to view',
|
||||
paginatedQueryOptions: {
|
||||
@@ -63,7 +65,7 @@ export default class ChannelView extends EasCommand {
|
||||
assert(channelName, `A channel must be selected.`);
|
||||
}
|
||||
|
||||
await listAndRenderBranchesAndUpdatesOnChannelAsync({
|
||||
await listAndRenderBranchesAndUpdatesOnChannelAsync(graphqlClient, {
|
||||
projectId,
|
||||
channelName,
|
||||
paginatedQueryOptions,
|
||||
|
||||
@@ -17,10 +17,18 @@ export default class Credentials extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(Credentials);
|
||||
const { actor, projectConfig } = await this.getContextAsync(Credentials, {
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectConfig,
|
||||
} = await this.getContextAsync(Credentials, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
await new SelectPlatform(actor, projectConfig ?? null, flags.platform).runAsync();
|
||||
await new SelectPlatform(
|
||||
actor,
|
||||
graphqlClient,
|
||||
projectConfig ?? null,
|
||||
flags.platform
|
||||
).runAsync();
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,17 @@ export default class DeviceCreate extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
// this command is interactive by design
|
||||
const { actor, projectConfig } = await this.getContextAsync(DeviceCreate, {
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectConfig,
|
||||
} = await this.getContextAsync(DeviceCreate, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const ctx = await createContextAsync({
|
||||
appStore: new AppStoreApi(),
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
projectId: projectConfig?.projectId,
|
||||
});
|
||||
const manager = new DeviceManager(ctx);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Flags } from '@oclif/core';
|
||||
import assert from 'assert';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { PaginatedQueryOptions, getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
|
||||
@@ -34,6 +35,7 @@ export default class DeviceDelete extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -42,10 +44,11 @@ export default class DeviceDelete extends EasCommand {
|
||||
let { 'apple-team-id': appleTeamIdentifier, udid } = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(DeviceDelete, {
|
||||
nonInteractive: paginatedQueryOptions.nonInteractive,
|
||||
});
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
let appleTeamName;
|
||||
|
||||
if (paginatedQueryOptions.json) {
|
||||
@@ -53,7 +56,7 @@ export default class DeviceDelete extends EasCommand {
|
||||
}
|
||||
|
||||
if (!appleTeamIdentifier) {
|
||||
const appleTeam = await selectAppleTeamOnAccountAsync({
|
||||
const appleTeam = await selectAppleTeamOnAccountAsync(graphqlClient, {
|
||||
accountName: account.name,
|
||||
selectionPromptTitle: `What Apple team would you like to list devices for?`,
|
||||
paginatedQueryOptions,
|
||||
@@ -65,8 +68,8 @@ export default class DeviceDelete extends EasCommand {
|
||||
assert(appleTeamIdentifier, 'No team identifier is specified');
|
||||
|
||||
const chosenDevice = udid
|
||||
? await AppleDeviceQuery.getByDeviceIdentifierAsync(account.name, udid)
|
||||
: await selectAppleDeviceOnAppleTeamAsync({
|
||||
? await AppleDeviceQuery.getByDeviceIdentifierAsync(graphqlClient, account.name, udid)
|
||||
: await selectAppleDeviceOnAppleTeamAsync(graphqlClient, {
|
||||
accountName: account.name,
|
||||
appleTeamIdentifier,
|
||||
selectionPromptTitle: `Which device would you like to disable?`,
|
||||
@@ -79,7 +82,7 @@ export default class DeviceDelete extends EasCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.removeDeviceFromExpoAsync(chosenDevice);
|
||||
await this.removeDeviceFromExpoAsync(graphqlClient, chosenDevice);
|
||||
|
||||
if (await this.shouldDisableDeviceOnAppleAsync(paginatedQueryOptions)) {
|
||||
await this.disableDeviceOnAppleAsync(chosenDevice, appleTeamIdentifier);
|
||||
@@ -135,11 +138,12 @@ export default class DeviceDelete extends EasCommand {
|
||||
}
|
||||
|
||||
async removeDeviceFromExpoAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
chosenDevice: AppleDevice | AppleDeviceQueryResult
|
||||
): Promise<void> {
|
||||
const removalSpinner = ora(`Removing Apple device on Expo`).start();
|
||||
try {
|
||||
await AppleDeviceMutation.deleteAppleDeviceAsync(chosenDevice.id);
|
||||
await AppleDeviceMutation.deleteAppleDeviceAsync(graphqlClient, chosenDevice.id);
|
||||
removalSpinner.succeed('Removed Apple device from Expo');
|
||||
} catch (err) {
|
||||
removalSpinner.fail();
|
||||
|
||||
@@ -22,6 +22,7 @@ export default class BuildList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -29,6 +30,7 @@ export default class BuildList extends EasCommand {
|
||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(BuildList, {
|
||||
nonInteractive: paginatedQueryOptions.nonInteractive,
|
||||
});
|
||||
@@ -38,11 +40,11 @@ export default class BuildList extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
// if they don't provide a team id, fetch devices on their account
|
||||
if (!appleTeamIdentifier) {
|
||||
const selectedAppleTeam = await selectAppleTeamOnAccountAsync({
|
||||
const selectedAppleTeam = await selectAppleTeamOnAccountAsync(graphqlClient, {
|
||||
accountName: account.name,
|
||||
paginatedQueryOptions: {
|
||||
...paginatedQueryOptions,
|
||||
@@ -56,7 +58,7 @@ export default class BuildList extends EasCommand {
|
||||
}
|
||||
|
||||
assert(appleTeamIdentifier, 'No team identifier is specified');
|
||||
await listAndRenderAppleDevicesOnAppleTeamAsync({
|
||||
await listAndRenderAppleDevicesOnAppleTeamAsync(graphqlClient, {
|
||||
accountName: account.name,
|
||||
appleTeam: {
|
||||
appleTeamIdentifier,
|
||||
|
||||
@@ -12,6 +12,7 @@ export default class DeviceView extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -32,15 +33,20 @@ If you are not sure what is the UDID of the device you are looking for, run:
|
||||
}
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(DeviceView, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const spinner = ora().start(`Fetching device details for ${UDID}…`);
|
||||
|
||||
try {
|
||||
const device = await AppleDeviceQuery.getByDeviceIdentifierAsync(account.name, UDID);
|
||||
const device = await AppleDeviceQuery.getByDeviceIdentifierAsync(
|
||||
graphqlClient,
|
||||
account.name,
|
||||
UDID
|
||||
);
|
||||
|
||||
if (device) {
|
||||
spinner.succeed('Fetched device details');
|
||||
|
||||
@@ -31,7 +31,7 @@ export default class MetadataPull extends EasCommand {
|
||||
|
||||
const { flags } = await this.parse(MetadataPull);
|
||||
const {
|
||||
actor,
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectConfig: { exp, projectId, projectDir },
|
||||
} = await this.getContextAsync(MetadataPull, {
|
||||
nonInteractive: false,
|
||||
@@ -44,6 +44,7 @@ export default class MetadataPull extends EasCommand {
|
||||
projectInfo: { exp, projectId },
|
||||
projectDir,
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class MetadataPush extends EasCommand {
|
||||
|
||||
const { flags } = await this.parse(MetadataPush);
|
||||
const {
|
||||
actor,
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectConfig: { exp, projectId, projectDir },
|
||||
} = await this.getContextAsync(MetadataPush, {
|
||||
nonInteractive: false,
|
||||
@@ -42,6 +42,7 @@ export default class MetadataPush extends EasCommand {
|
||||
projectInfo: { exp, projectId },
|
||||
projectDir,
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -10,17 +10,19 @@ export default class Open extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
// this command is interactive by nature (only really run by humans in a terminal)
|
||||
const {
|
||||
projectConfig: { projectId, exp },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(Open, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const projectName = exp.slug;
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { AppJSONConfig, PackageJSONConfig, getConfig } from '@expo/config';
|
||||
import chalk from 'chalk';
|
||||
import { vol } from 'memfs';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import ActorContextField from '../../../commandUtils/context/ActorContextField';
|
||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||
import ProjectDirContextField from '../../../commandUtils/context/ProjectDirContextField';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { saveProjectIdToAppConfigAsync } from '../../../commandUtils/context/contextUtils/getProjectIdAsync';
|
||||
import { jester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { AppMutation } from '../../../graphql/mutations/AppMutation';
|
||||
@@ -71,10 +73,13 @@ function mockTestProject(options: {
|
||||
);
|
||||
|
||||
const mockManifest = { exp: appJSON.expo };
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
|
||||
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
||||
jest.spyOn(ProjectDirContextField.prototype, 'getValueAsync').mockResolvedValue('/test-project');
|
||||
jest.spyOn(ActorContextField.prototype, 'getValueAsync').mockResolvedValue(jester);
|
||||
jest
|
||||
.spyOn(LoggedInContextField.prototype, 'getValueAsync')
|
||||
.mockResolvedValue({ actor: jester, graphqlClient });
|
||||
}
|
||||
|
||||
const commandOptions = { root: '/test-project' } as any;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { AppInfoQuery, AppInfoQueryVariables } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
|
||||
async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
|
||||
async function projectInfoByIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
appId: string
|
||||
): Promise<AppInfoQuery> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<AppInfoQuery, AppInfoQueryVariables>(
|
||||
@@ -34,16 +38,18 @@ export default class ProjectInfo extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ProjectInfo, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const { app } = await projectInfoByIdAsync(projectId);
|
||||
const { app } = await projectInfoByIdAsync(graphqlClient, projectId);
|
||||
if (!app) {
|
||||
throw new Error(`Could not find project with id: ${projectId}`);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import terminalLink from 'terminal-link';
|
||||
|
||||
import { getProjectDashboardUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { saveProjectIdToAppConfigAsync } from '../../commandUtils/context/contextUtils/getProjectIdAsync';
|
||||
import { AppPrivacy, Role } from '../../graphql/generated';
|
||||
import { AppMutation } from '../../graphql/mutations/AppMutation';
|
||||
@@ -100,6 +101,7 @@ export default class ProjectInit extends EasCommand {
|
||||
}
|
||||
|
||||
private static async initializeWithInteractiveSelectionAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
actor: Actor,
|
||||
projectDir: string
|
||||
): Promise<void> {
|
||||
@@ -159,6 +161,7 @@ export default class ProjectInit extends EasCommand {
|
||||
const projectName = exp.slug;
|
||||
const projectFullName = `@${accountName}/${projectName}`;
|
||||
const existingProjectIdOnServer = await findProjectIdByAccountNameAndSlugNullableAsync(
|
||||
graphqlClient,
|
||||
accountName,
|
||||
projectName
|
||||
);
|
||||
@@ -200,7 +203,7 @@ export default class ProjectInit extends EasCommand {
|
||||
const spinner = ora(`Creating ${chalk.bold(projectFullName)}`).start();
|
||||
let createdProjectId: string;
|
||||
try {
|
||||
createdProjectId = await AppMutation.createAppAsync({
|
||||
createdProjectId = await AppMutation.createAppAsync(graphqlClient, {
|
||||
accountId: account.id,
|
||||
projectName,
|
||||
privacy: toAppPrivacy(exp.privacy) ?? AppPrivacy.Public,
|
||||
@@ -218,12 +221,15 @@ export default class ProjectInit extends EasCommand {
|
||||
const {
|
||||
flags: { id, force, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(ProjectInit);
|
||||
const { actor, projectDir } = await this.getContextAsync(ProjectInit, { nonInteractive });
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectDir,
|
||||
} = await this.getContextAsync(ProjectInit, { nonInteractive });
|
||||
|
||||
if (id) {
|
||||
await ProjectInit.initializeWithExplicitIDAsync(id, projectDir, { force, nonInteractive });
|
||||
} else {
|
||||
await ProjectInit.initializeWithInteractiveSelectionAsync(actor, projectDir);
|
||||
await ProjectInit.initializeWithInteractiveSelectionAsync(graphqlClient, actor, projectDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -66,12 +67,13 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
} = await this.parse(EnvironmentSecretCreate);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(EnvironmentSecretCreate, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const ownerAccount = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
const ownerAccount = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
if (!scope) {
|
||||
const validationMessage = 'Secret scope may not be empty.';
|
||||
@@ -178,12 +180,13 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
if (scope === EnvironmentSecretScope.PROJECT) {
|
||||
if (force) {
|
||||
const { appSecrets: existingSecrets } = await EnvironmentSecretsQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId
|
||||
);
|
||||
const existingSecret = existingSecrets.find(secret => secret.name === name);
|
||||
|
||||
if (existingSecret) {
|
||||
await EnvironmentSecretMutation.deleteAsync(existingSecret.id);
|
||||
await EnvironmentSecretMutation.deleteAsync(graphqlClient, existingSecret.id);
|
||||
Log.withTick(
|
||||
`Deleting existing secret ${chalk.bold(name)} on project ${chalk.bold(
|
||||
projectDisplayName
|
||||
@@ -193,6 +196,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
}
|
||||
|
||||
const secret = await EnvironmentSecretMutation.createForAppAsync(
|
||||
graphqlClient,
|
||||
{ name, value: secretValue, type: SecretTypeToEnvironmentSecretType[secretType] },
|
||||
projectId
|
||||
);
|
||||
@@ -218,12 +222,13 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
} else if (scope === EnvironmentSecretScope.ACCOUNT) {
|
||||
if (force) {
|
||||
const { accountSecrets: existingSecrets } = await EnvironmentSecretsQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId
|
||||
);
|
||||
const existingSecret = existingSecrets.find(secret => secret.name === name);
|
||||
|
||||
if (existingSecret) {
|
||||
await EnvironmentSecretMutation.deleteAsync(existingSecret.id);
|
||||
await EnvironmentSecretMutation.deleteAsync(graphqlClient, existingSecret.id);
|
||||
|
||||
Log.withTick(
|
||||
`Deleting existing secret ${chalk.bold(name)} on account ${chalk.bold(
|
||||
@@ -234,6 +239,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
||||
}
|
||||
|
||||
const secret = await EnvironmentSecretMutation.createForAccountAsync(
|
||||
graphqlClient,
|
||||
{ name, value: secretValue, type: SecretTypeToEnvironmentSecretType[secretType] },
|
||||
ownerAccount.id
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -31,6 +32,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
|
||||
} = await this.parse(EnvironmentSecretDelete);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(EnvironmentSecretDelete, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -42,7 +44,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectId);
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(graphqlClient, projectId);
|
||||
({ secret } = await promptAsync({
|
||||
type: 'autocomplete',
|
||||
name: 'secret',
|
||||
@@ -83,7 +85,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
await EnvironmentSecretMutation.deleteAsync(id);
|
||||
await EnvironmentSecretMutation.deleteAsync(graphqlClient, id);
|
||||
|
||||
Log.withTick(`️Deleted secret${secret?.name ? ` "${secret?.name}"` : ''} with id "${id}".`);
|
||||
}
|
||||
|
||||
@@ -12,16 +12,18 @@ export default class EnvironmentSecretList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(EnvironmentSecretList, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectId);
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(graphqlClient, projectId);
|
||||
|
||||
const table = new Table({
|
||||
head: ['Name', 'Type', 'Scope', 'ID', 'Updated at'],
|
||||
|
||||
@@ -98,7 +98,7 @@ export default class Submit extends EasCommand {
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags: rawFlags } = await this.parse(Submit);
|
||||
const {
|
||||
actor,
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectConfig: { exp, projectId, projectDir },
|
||||
} = await this.getContextAsync(Submit, {
|
||||
nonInteractive: false,
|
||||
@@ -106,7 +106,7 @@ export default class Submit extends EasCommand {
|
||||
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync([StatuspageServiceName.EasSubmit]);
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasSubmit]);
|
||||
|
||||
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
|
||||
|
||||
@@ -128,6 +128,7 @@ export default class Submit extends EasCommand {
|
||||
archiveFlags: flagsWithPlatform.archiveFlags,
|
||||
nonInteractive: flagsWithPlatform.nonInteractive,
|
||||
actor,
|
||||
graphqlClient,
|
||||
exp,
|
||||
projectId,
|
||||
});
|
||||
@@ -150,7 +151,7 @@ export default class Submit extends EasCommand {
|
||||
printSubmissionDetailsUrls(submissions);
|
||||
|
||||
if (flagsWithPlatform.wait) {
|
||||
const completedSubmissions = await waitToCompleteAsync(submissions, {
|
||||
const completedSubmissions = await waitToCompleteAsync(graphqlClient, submissions, {
|
||||
verbose: flagsWithPlatform.verbose,
|
||||
});
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
||||
|
||||
@@ -34,6 +34,7 @@ export default class UpdateConfigure extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -43,6 +44,7 @@ export default class UpdateConfigure extends EasCommand {
|
||||
const { flags } = await this.parse(UpdateConfigure);
|
||||
const {
|
||||
projectConfig: { projectId, exp, projectDir },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdateConfigure, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
@@ -74,14 +76,14 @@ export default class UpdateConfigure extends EasCommand {
|
||||
[RequestedPlatform.Android, RequestedPlatform.All].includes(platform) &&
|
||||
androidWorkflow === Workflow.GENERIC
|
||||
) {
|
||||
await syncAndroidUpdatesConfigurationAsync(projectDir, updatedExp, projectId);
|
||||
await syncAndroidUpdatesConfigurationAsync(graphqlClient, projectDir, updatedExp, projectId);
|
||||
Log.withTick(`Configured ${chalk.bold('AndroidManifest.xml')} for EAS Update`);
|
||||
}
|
||||
if (
|
||||
[RequestedPlatform.Ios, RequestedPlatform.All].includes(platform) &&
|
||||
iosWorkflow === Workflow.GENERIC
|
||||
) {
|
||||
await syncIosUpdatesConfigurationAsync(projectDir, updatedExp, projectId);
|
||||
await syncIosUpdatesConfigurationAsync(graphqlClient, projectDir, updatedExp, projectId);
|
||||
Log.withTick(`Configured ${chalk.bold('Expo.plist')} for EAS Update`);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import chalk from 'chalk';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
DeleteUpdateGroupMutation,
|
||||
UpdateMutationDeleteUpdateGroupArgs,
|
||||
@@ -12,11 +13,14 @@ import Log from '../../log';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
async function deleteUpdateGroupAsync({
|
||||
group,
|
||||
}: {
|
||||
group: string;
|
||||
}): Promise<DeleteUpdateGroupMutation> {
|
||||
async function deleteUpdateGroupAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
group,
|
||||
}: {
|
||||
group: string;
|
||||
}
|
||||
): Promise<DeleteUpdateGroupMutation> {
|
||||
return await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateGroupMutation, UpdateMutationDeleteUpdateGroupArgs>(
|
||||
@@ -50,12 +54,20 @@ export default class UpdateDelete extends EasCommand {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { groupId: group },
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(UpdateDelete);
|
||||
|
||||
const {
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdateDelete, { nonInteractive });
|
||||
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
@@ -78,7 +90,7 @@ export default class UpdateDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
await deleteUpdateGroupAsync({ group });
|
||||
await deleteUpdateGroupAsync(graphqlClient, { group });
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput({ group });
|
||||
|
||||
@@ -11,6 +11,7 @@ import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import { BranchNotFoundError, getDefaultBranchNameAsync } from '../../branch/utils';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import fetch from '../../fetch';
|
||||
@@ -60,17 +61,20 @@ import { createUpdateChannelOnAppAsync } from '../channel/create';
|
||||
export const defaultPublishPlatforms: PublishPlatform[] = ['android', 'ios'];
|
||||
export type PublishPlatformFlag = PublishPlatform | 'all';
|
||||
|
||||
async function ensureChannelExistsAsync({
|
||||
appId,
|
||||
branchId,
|
||||
channelName,
|
||||
}: {
|
||||
appId: string;
|
||||
branchId: string;
|
||||
channelName: string;
|
||||
}): Promise<void> {
|
||||
async function ensureChannelExistsAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
branchId,
|
||||
channelName,
|
||||
}: {
|
||||
appId: string;
|
||||
branchId: string;
|
||||
channelName: string;
|
||||
}
|
||||
): Promise<void> {
|
||||
try {
|
||||
await createUpdateChannelOnAppAsync({
|
||||
await createUpdateChannelOnAppAsync(graphqlClient, {
|
||||
appId,
|
||||
channelName,
|
||||
branchId,
|
||||
@@ -88,26 +92,29 @@ async function ensureChannelExistsAsync({
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureBranchExistsAsync({
|
||||
appId,
|
||||
name: branchName,
|
||||
}: ViewBranchQueryVariables): Promise<{
|
||||
export async function ensureBranchExistsAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, name: branchName }: ViewBranchQueryVariables
|
||||
): Promise<{
|
||||
branchId: string;
|
||||
}> {
|
||||
try {
|
||||
const updateBranch = await BranchQuery.getBranchByNameAsync({
|
||||
const updateBranch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId,
|
||||
name: branchName,
|
||||
});
|
||||
|
||||
const { id } = updateBranch;
|
||||
await ensureChannelExistsAsync({ appId, branchId: id, channelName: branchName });
|
||||
await ensureChannelExistsAsync(graphqlClient, { appId, branchId: id, channelName: branchName });
|
||||
return { branchId: id };
|
||||
} catch (error) {
|
||||
if (error instanceof BranchNotFoundError) {
|
||||
const newUpdateBranch = await createUpdateBranchOnAppAsync({ appId, name: branchName });
|
||||
const newUpdateBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId,
|
||||
name: branchName,
|
||||
});
|
||||
Log.withTick(`Created branch: ${chalk.bold(branchName)}`);
|
||||
await ensureChannelExistsAsync({
|
||||
await ensureChannelExistsAsync(graphqlClient, {
|
||||
appId,
|
||||
branchId: newUpdateBranch.id,
|
||||
channelName: branchName,
|
||||
@@ -168,6 +175,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -186,7 +194,10 @@ export default class UpdatePublish extends EasCommand {
|
||||
'non-interactive': nonInteractive,
|
||||
json: jsonFlag,
|
||||
} = flags;
|
||||
const { getDynamicProjectConfigAsync } = await this.getContextAsync(UpdatePublish, {
|
||||
const {
|
||||
getDynamicProjectConfigAsync,
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdatePublish, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
@@ -206,7 +217,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
isPublicConfig: false,
|
||||
});
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync([StatuspageServiceName.EasUpdate]);
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
@@ -246,7 +257,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
throw new Error('Must supply --branch or use --auto when in non-interactive mode');
|
||||
} else {
|
||||
try {
|
||||
const branch = await selectBranchOnAppAsync({
|
||||
const branch = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: `Which branch would you like to ${
|
||||
republish ? 'republish' : 'publish'
|
||||
@@ -286,14 +297,16 @@ export default class UpdatePublish extends EasCommand {
|
||||
'group' | 'message' | 'runtimeVersion' | 'manifestFragment' | 'platform'
|
||||
>[];
|
||||
if (group) {
|
||||
const updatesByGroup = await UpdateQuery.viewUpdateGroupAsync({ groupId: group });
|
||||
const updatesByGroup = await UpdateQuery.viewUpdateGroupAsync(graphqlClient, {
|
||||
groupId: group,
|
||||
});
|
||||
updatesToRepublish = updatesByGroup;
|
||||
} else {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply --group when in non-interactive mode');
|
||||
}
|
||||
|
||||
updatesToRepublish = await selectUpdateGroupOnBranchAsync({
|
||||
updatesToRepublish = await selectUpdateGroupOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName,
|
||||
paginatedQueryOptions,
|
||||
@@ -393,6 +406,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
const platforms = platformFlag === 'all' ? defaultPublishPlatforms : [platformFlag];
|
||||
const assets = await collectAssetsAsync({ inputDir: inputDir!, platforms });
|
||||
const uploadResults = await uploadAssetsAsync(
|
||||
graphqlClient,
|
||||
assets,
|
||||
projectId,
|
||||
(totalAssets, missingAssets) => {
|
||||
@@ -423,7 +437,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
.map(pair => pair[0]);
|
||||
}
|
||||
|
||||
const { branchId } = await ensureBranchExistsAsync({
|
||||
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: branchName,
|
||||
});
|
||||
@@ -455,7 +469,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
try {
|
||||
newUpdates = await PublishMutation.publishUpdateGroupAsync(updateGroups);
|
||||
newUpdates = await PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
|
||||
|
||||
if (codeSigningInfo) {
|
||||
Log.log('🔒 Signing updates');
|
||||
@@ -488,7 +502,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
|
||||
const manifestSignature = signManifestBody(manifestBody, codeSigningInfo);
|
||||
|
||||
await PublishMutation.setCodeSigningInfoAsync(newUpdate.id, {
|
||||
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
||||
alg: codeSigningInfo.codeSigningMetadata.alg,
|
||||
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
||||
sig: manifestSignature,
|
||||
@@ -531,7 +545,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
const updateGroupId = newUpdatesForRuntimeVersion[0].group;
|
||||
|
||||
const projectName = exp.slug;
|
||||
const accountName = (await getOwnerAccountForProjectIdAsync(projectId)).name;
|
||||
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
|
||||
const updateGroupUrl = getUpdateGroupUrl(accountName, projectName, updateGroupId);
|
||||
const updateGroupLink = link(updateGroupUrl, { dim: false });
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ export default class UpdateList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -41,6 +42,7 @@ export default class UpdateList extends EasCommand {
|
||||
const { branch: branchFlag, all, json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdateList, {
|
||||
nonInteractive,
|
||||
});
|
||||
@@ -51,10 +53,10 @@ export default class UpdateList extends EasCommand {
|
||||
}
|
||||
|
||||
if (all) {
|
||||
listAndRenderUpdateGroupsOnAppAsync({ projectId, paginatedQueryOptions });
|
||||
listAndRenderUpdateGroupsOnAppAsync(graphqlClient, { projectId, paginatedQueryOptions });
|
||||
} else {
|
||||
if (branchFlag) {
|
||||
listAndRenderUpdateGroupsOnBranchAsync({
|
||||
listAndRenderUpdateGroupsOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName: branchFlag,
|
||||
paginatedQueryOptions,
|
||||
@@ -65,7 +67,7 @@ export default class UpdateList extends EasCommand {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
|
||||
const selectedBranch = await selectBranchOnAppAsync({
|
||||
const selectedBranch = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: 'Which branch would you like to view?',
|
||||
displayTextForListItem: updateBranch => updateBranch.name,
|
||||
@@ -77,7 +79,7 @@ export default class UpdateList extends EasCommand {
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
listAndRenderUpdateGroupsOnBranchAsync({
|
||||
listAndRenderUpdateGroupsOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName: selectedBranch.name,
|
||||
paginatedQueryOptions,
|
||||
|
||||
@@ -24,16 +24,25 @@ export default class UpdateView extends EasCommand {
|
||||
...EasJsonOnlyFlag,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { groupId },
|
||||
flags: { json: jsonFlag },
|
||||
} = await this.parse(UpdateView);
|
||||
|
||||
const {
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdateView, { nonInteractive: true });
|
||||
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const updatesByGroup = await UpdateQuery.viewUpdateGroupAsync({ groupId });
|
||||
const updatesByGroup = await UpdateQuery.viewUpdateGroupAsync(graphqlClient, { groupId });
|
||||
const [updateGroupDescription] = getUpdateGroupDescriptions([updatesByGroup]);
|
||||
|
||||
if (jsonFlag) {
|
||||
|
||||
@@ -27,12 +27,14 @@ export default class WebhookCreate extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(WebhookCreate);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WebhookCreate, {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
});
|
||||
@@ -40,7 +42,7 @@ export default class WebhookCreate extends EasCommand {
|
||||
|
||||
const spinner = ora('Creating a webhook').start();
|
||||
try {
|
||||
await WebhookMutation.createWebhookAsync(projectId, webhookInputParams);
|
||||
await WebhookMutation.createWebhookAsync(graphqlClient, projectId, webhookInputParams);
|
||||
spinner.succeed('Successfully created a webhook');
|
||||
} catch (err) {
|
||||
spinner.fail('Failed to create a webhook');
|
||||
|
||||
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||
import { WebhookFragment } from '../../graphql/generated';
|
||||
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
|
||||
@@ -29,6 +30,7 @@ export default class WebhookDelete extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -38,14 +40,15 @@ export default class WebhookDelete extends EasCommand {
|
||||
} = await this.parse(WebhookDelete);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WebhookDelete, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
let webhook: WebhookFragment | undefined =
|
||||
webhookId && (await WebhookQuery.byIdAsync(webhookId));
|
||||
webhookId && (await WebhookQuery.byIdAsync(graphqlClient, webhookId));
|
||||
if (!webhookId) {
|
||||
const webhooks = await fetchWebhooksByAppIdAsync(projectId);
|
||||
const webhooks = await fetchWebhooksByAppIdAsync(graphqlClient, projectId);
|
||||
if (webhooks.length === 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -86,7 +89,7 @@ export default class WebhookDelete extends EasCommand {
|
||||
|
||||
const spinner = ora('Deleting the webhook').start();
|
||||
try {
|
||||
await WebhookMutation.deleteWebhookAsync(webhookId);
|
||||
await WebhookMutation.deleteWebhookAsync(graphqlClient, webhookId);
|
||||
spinner.succeed('Successfully deleted the webhook');
|
||||
} catch (err) {
|
||||
spinner.fail('Failed to delete the webhook');
|
||||
@@ -95,10 +98,13 @@ export default class WebhookDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchWebhooksByAppIdAsync(appId: string): Promise<WebhookFragment[]> {
|
||||
async function fetchWebhooksByAppIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
appId: string
|
||||
): Promise<WebhookFragment[]> {
|
||||
const spinner = ora('Fetching webhooks').start();
|
||||
try {
|
||||
const webhooks = await WebhookQuery.byAppIdAsync(appId);
|
||||
const webhooks = await WebhookQuery.byAppIdAsync(graphqlClient, appId);
|
||||
if (webhooks.length === 0) {
|
||||
spinner.fail('There are no webhooks on the project');
|
||||
return [];
|
||||
|
||||
@@ -21,6 +21,7 @@ export default class WebhookList extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -29,15 +30,20 @@ export default class WebhookList extends EasCommand {
|
||||
} = await this.parse(WebhookList);
|
||||
const {
|
||||
projectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WebhookList, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(projectId);
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const spinner = ora(`Fetching the list of webhook on project ${projectDisplayName}`).start();
|
||||
try {
|
||||
const webhooks = await WebhookQuery.byAppIdAsync(projectId, event && { event });
|
||||
const webhooks = await WebhookQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
event && { event }
|
||||
);
|
||||
if (webhooks.length === 0) {
|
||||
spinner.fail(`There are no webhooks on project ${projectDisplayName}`);
|
||||
} else {
|
||||
|
||||
@@ -31,12 +31,19 @@ export default class WebhookUpdate extends EasCommand {
|
||||
...EASNonInteractiveFlag,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(WebhookUpdate);
|
||||
const {
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WebhookUpdate, { nonInteractive: flags['non-interactive'] });
|
||||
|
||||
const webhookId = flags.id;
|
||||
|
||||
const webhook = await WebhookQuery.byIdAsync(webhookId);
|
||||
const webhook = await WebhookQuery.byIdAsync(graphqlClient, webhookId);
|
||||
const webhookInputParams = await prepareInputParamsAsync(
|
||||
pick(flags, ['event', 'url', 'secret', 'non-interactive']),
|
||||
webhook
|
||||
@@ -44,7 +51,7 @@ export default class WebhookUpdate extends EasCommand {
|
||||
|
||||
const spinner = ora('Updating a webhook').start();
|
||||
try {
|
||||
await WebhookMutation.updateWebhookAsync(webhookId, webhookInputParams);
|
||||
await WebhookMutation.updateWebhookAsync(graphqlClient, webhookId, webhookInputParams);
|
||||
spinner.succeed('Successfully updated a webhook');
|
||||
} catch (err) {
|
||||
spinner.fail('Failed to update a webhook');
|
||||
|
||||
@@ -15,14 +15,21 @@ export default class WebhookView extends EasCommand {
|
||||
},
|
||||
];
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { ID: webhookId },
|
||||
} = await this.parse(WebhookView);
|
||||
const {
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WebhookView, { nonInteractive: true });
|
||||
|
||||
const spinner = ora(`Fetching the webhook details for ID ${webhookId}`).start();
|
||||
try {
|
||||
const webhook = await WebhookQuery.byIdAsync(webhookId);
|
||||
const webhook = await WebhookQuery.byIdAsync(graphqlClient, webhookId);
|
||||
spinner.succeed(`Found the webhook details`);
|
||||
Log.log(`\n${formatWebhook(webhook)}`);
|
||||
} catch (err) {
|
||||
|
||||
@@ -14,8 +14,12 @@ export class AssignFcm {
|
||||
fcm: AndroidFcmFragment
|
||||
): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
const appCredentials =
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(this.app);
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
const updatedAppCredentials = await ctx.android.updateAndroidAppCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
appCredentials,
|
||||
{
|
||||
androidFcmId: fcm.id,
|
||||
|
||||
@@ -14,8 +14,12 @@ export class AssignGoogleServiceAccountKey {
|
||||
googleServiceAccountKey: GoogleServiceAccountKeyFragment
|
||||
): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
const appCredentials =
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(this.app);
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
const updatedAppCredentials = await ctx.android.updateAndroidAppCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
appCredentials,
|
||||
{
|
||||
googleServiceAccountKeyForSubmissionsId: googleServiceAccountKey.id,
|
||||
|
||||
@@ -21,13 +21,16 @@ export async function canCopyLegacyCredentialsAsync(
|
||||
ctx: CredentialsContext,
|
||||
app: AppLookupParams
|
||||
): Promise<boolean> {
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(app);
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
app
|
||||
);
|
||||
if (appCredentials) {
|
||||
return false; // modern credentials already exist
|
||||
}
|
||||
|
||||
const legacyAppCredentials =
|
||||
await ctx.android.getLegacyAndroidAppCredentialsWithCommonFieldsAsync(app);
|
||||
await ctx.android.getLegacyAndroidAppCredentialsWithCommonFieldsAsync(ctx.graphqlClient, app);
|
||||
return !!legacyAppCredentials; // user has some legacy credentials
|
||||
}
|
||||
|
||||
@@ -44,30 +47,37 @@ export async function promptUserAndCopyLegacyCredentialsAsync(
|
||||
|
||||
try {
|
||||
const legacyAppCredentials =
|
||||
await ctx.android.getLegacyAndroidAppCredentialsWithCommonFieldsAsync(app);
|
||||
await ctx.android.getLegacyAndroidAppCredentialsWithCommonFieldsAsync(ctx.graphqlClient, app);
|
||||
if (!legacyAppCredentials) {
|
||||
return;
|
||||
}
|
||||
|
||||
const appCredentials =
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(app);
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
app
|
||||
);
|
||||
const legacyFcm = legacyAppCredentials.androidFcm;
|
||||
if (legacyFcm) {
|
||||
const clonedFcm = await ctx.android.createFcmAsync(
|
||||
ctx.graphqlClient,
|
||||
app.account,
|
||||
legacyFcm.credential,
|
||||
legacyFcm.version
|
||||
);
|
||||
await ctx.android.updateAndroidAppCredentialsAsync(appCredentials, {
|
||||
await ctx.android.updateAndroidAppCredentialsAsync(ctx.graphqlClient, appCredentials, {
|
||||
androidFcmId: clonedFcm.id,
|
||||
});
|
||||
}
|
||||
|
||||
const legacyBuildCredentials = await ctx.android.getLegacyAndroidAppBuildCredentialsAsync(app);
|
||||
const legacyBuildCredentials = await ctx.android.getLegacyAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
app
|
||||
);
|
||||
const legacyKeystore = legacyBuildCredentials?.androidKeystore ?? null;
|
||||
|
||||
if (legacyKeystore) {
|
||||
const clonedKeystore = await ctx.android.createKeystoreAsync(app.account, {
|
||||
const clonedKeystore = await ctx.android.createKeystoreAsync(ctx.graphqlClient, app.account, {
|
||||
keystore: legacyKeystore.keystore,
|
||||
keystorePassword: legacyKeystore.keystorePassword,
|
||||
keyAlias: legacyKeystore.keyAlias,
|
||||
@@ -93,7 +103,7 @@ export async function getAppLookupParamsFromContextAsync(
|
||||
ctx.ensureProjectContext();
|
||||
const projectName = ctx.exp.slug;
|
||||
const projectId = ctx.projectId;
|
||||
const account = await getOwnerAccountForProjectIdAsync(projectId);
|
||||
const account = await getOwnerAccountForProjectIdAsync(ctx.graphqlClient, projectId);
|
||||
|
||||
const androidApplicationIdentifier = await getApplicationIdAsync(
|
||||
ctx.projectDir,
|
||||
@@ -125,18 +135,23 @@ export async function createOrUpdateDefaultAndroidAppBuildCredentialsAsync(
|
||||
'createOrUpdateDefaultAndroidAppBuildCredentialsAsync must be run in interactive mode'
|
||||
);
|
||||
const existingDefaultBuildCredentials =
|
||||
await ctx.android.getDefaultAndroidAppBuildCredentialsAsync(appLookupParams);
|
||||
await ctx.android.getDefaultAndroidAppBuildCredentialsAsync(ctx.graphqlClient, appLookupParams);
|
||||
if (existingDefaultBuildCredentials) {
|
||||
return await ctx.android.updateAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
existingDefaultBuildCredentials,
|
||||
{ androidKeystoreId }
|
||||
);
|
||||
}
|
||||
return await ctx.android.createAndroidAppBuildCredentialsAsync(appLookupParams, {
|
||||
name: generateRandomName(),
|
||||
isDefault: true,
|
||||
androidKeystoreId,
|
||||
});
|
||||
return await ctx.android.createAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
appLookupParams,
|
||||
{
|
||||
name: generateRandomName(),
|
||||
isDefault: true,
|
||||
androidKeystoreId,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function promptForNameAsync(): Promise<string> {
|
||||
|
||||
@@ -19,6 +19,7 @@ export class CreateFcm {
|
||||
},
|
||||
]);
|
||||
const fcmFragment = await ctx.android.createFcmAsync(
|
||||
ctx.graphqlClient,
|
||||
this.account,
|
||||
fcmApiKey,
|
||||
AndroidFcmVersion.Legacy
|
||||
|
||||
@@ -20,6 +20,7 @@ export class CreateGoogleServiceAccountKey {
|
||||
}
|
||||
const jsonKeyObject = await this.provideAsync(ctx);
|
||||
const gsaKeyFragment = await ctx.android.createGoogleServiceAccountKeyAsync(
|
||||
ctx.graphqlClient,
|
||||
this.account,
|
||||
jsonKeyObject
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { AccountFragment, AndroidKeystoreFragment } from '../../../graphql/generated';
|
||||
import Log from '../../../log';
|
||||
import { CredentialsContext } from '../../context';
|
||||
@@ -15,19 +16,26 @@ export class CreateKeystore {
|
||||
}
|
||||
|
||||
const projectId = ctx.projectId;
|
||||
const keystore = await this.provideOrGenerateAsync(projectId);
|
||||
const keystoreFragment = await ctx.android.createKeystoreAsync(this.account, keystore);
|
||||
const keystore = await this.provideOrGenerateAsync(ctx.graphqlClient, projectId);
|
||||
const keystoreFragment = await ctx.android.createKeystoreAsync(
|
||||
ctx.graphqlClient,
|
||||
this.account,
|
||||
keystore
|
||||
);
|
||||
Log.succeed('Created keystore');
|
||||
return keystoreFragment;
|
||||
}
|
||||
|
||||
private async provideOrGenerateAsync(projectId: string): Promise<KeystoreWithType> {
|
||||
private async provideOrGenerateAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string
|
||||
): Promise<KeystoreWithType> {
|
||||
const providedKeystore = await askForUserProvidedAsync(keystoreSchema);
|
||||
if (providedKeystore) {
|
||||
const providedKeystoreWithType = getKeystoreWithType(providedKeystore);
|
||||
validateKeystore(providedKeystoreWithType);
|
||||
return providedKeystoreWithType;
|
||||
}
|
||||
return await generateRandomKeystoreAsync(projectId);
|
||||
return await generateRandomKeystoreAsync(graphqlClient, projectId);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export class RemoveFcm {
|
||||
);
|
||||
}
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
const fcm = appCredentials?.androidFcm;
|
||||
@@ -33,7 +34,7 @@ export class RemoveFcm {
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.android.deleteFcmAsync(fcm);
|
||||
await ctx.android.deleteFcmAsync(ctx.graphqlClient, fcm);
|
||||
Log.succeed('FCM API Key removed');
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ export class SelectAndRemoveGoogleServiceAccountKey {
|
||||
}
|
||||
|
||||
const gsaKeyFragments = await ctx.android.getGoogleServiceAccountKeysForAccountAsync(
|
||||
ctx.graphqlClient,
|
||||
this.account
|
||||
);
|
||||
if (gsaKeyFragments.length === 0) {
|
||||
@@ -47,6 +48,9 @@ export class RemoveGoogleServiceAccountKey {
|
||||
}
|
||||
|
||||
Log.log('Removing Google Service Account Key.');
|
||||
await ctx.android.deleteGoogleServiceAccountKeyAsync(this.googleServiceAccountKey);
|
||||
await ctx.android.deleteGoogleServiceAccountKeyAsync(
|
||||
ctx.graphqlClient,
|
||||
this.googleServiceAccountKey
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export class RemoveKeystore {
|
||||
}
|
||||
await new BackupKeystore(this.app).runAsync(ctx, buildCredentials);
|
||||
|
||||
await ctx.android.deleteKeystoreAsync(keystore);
|
||||
await ctx.android.deleteKeystoreAsync(ctx.graphqlClient, keystore);
|
||||
Log.succeed('Keystore removed');
|
||||
}
|
||||
|
||||
|
||||
@@ -68,9 +68,14 @@ export class SetUpBuildCredentials {
|
||||
keystore: AndroidKeystoreFragment;
|
||||
}): Promise<AndroidAppBuildCredentialsFragment> {
|
||||
if (name) {
|
||||
return await ctx.android.createOrUpdateAndroidAppBuildCredentialsByNameAsync(app, name, {
|
||||
androidKeystoreId: keystore.id,
|
||||
});
|
||||
return await ctx.android.createOrUpdateAndroidAppBuildCredentialsByNameAsync(
|
||||
ctx.graphqlClient,
|
||||
app,
|
||||
name,
|
||||
{
|
||||
androidKeystoreId: keystore.id,
|
||||
}
|
||||
);
|
||||
}
|
||||
return await createOrUpdateDefaultAndroidAppBuildCredentialsAsync(ctx, app, {
|
||||
androidKeystoreId: keystore.id,
|
||||
@@ -91,6 +96,7 @@ export class SetUpBuildCredentials {
|
||||
}
|
||||
|
||||
const defaultBuildCredentials = await ctx.android.getDefaultAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
app
|
||||
);
|
||||
const defaultKeystore = defaultBuildCredentials?.androidKeystore ?? null;
|
||||
@@ -113,6 +119,7 @@ export class SetUpBuildCredentials {
|
||||
name: string;
|
||||
}): Promise<AndroidAppBuildCredentialsFragment | null> {
|
||||
const maybeBuildCredentials = await ctx.android.getAndroidAppBuildCredentialsByNameAsync(
|
||||
ctx.graphqlClient,
|
||||
app,
|
||||
name
|
||||
);
|
||||
|
||||
+10
-4
@@ -62,6 +62,7 @@ export class SetUpBuildCredentialsFromCredentialsJson {
|
||||
}
|
||||
}
|
||||
const keystoreFragment = await ctx.android.createKeystoreAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app.account,
|
||||
providedKeystoreWithType
|
||||
);
|
||||
@@ -70,12 +71,17 @@ export class SetUpBuildCredentialsFromCredentialsJson {
|
||||
selectBuildCredentialsResult.resultType ===
|
||||
SelectAndroidBuildCredentialsResultType.CREATE_REQUEST
|
||||
) {
|
||||
buildCredentials = await ctx.android.createAndroidAppBuildCredentialsAsync(this.app, {
|
||||
...selectBuildCredentialsResult.result,
|
||||
androidKeystoreId: keystoreFragment.id,
|
||||
});
|
||||
buildCredentials = await ctx.android.createAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app,
|
||||
{
|
||||
...selectBuildCredentialsResult.result,
|
||||
androidKeystoreId: keystoreFragment.id,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
buildCredentials = await ctx.android.updateAndroidAppBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
selectBuildCredentialsResult.result,
|
||||
{
|
||||
androidKeystoreId: keystoreFragment.id,
|
||||
|
||||
@@ -21,7 +21,10 @@ export class SetUpGoogleServiceAccountKey {
|
||||
if (isKeySetup) {
|
||||
Log.succeed('Google Service Account Key already set up.');
|
||||
return nullthrows(
|
||||
await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(this.app),
|
||||
await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
),
|
||||
'androidAppCredentials cannot be null if google service account key is already set up'
|
||||
);
|
||||
}
|
||||
@@ -32,6 +35,7 @@ export class SetUpGoogleServiceAccountKey {
|
||||
}
|
||||
|
||||
const keysForAccount = await ctx.android.getGoogleServiceAccountKeysForAccountAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app.account
|
||||
);
|
||||
let googleServiceAccountKey = null;
|
||||
@@ -47,6 +51,7 @@ export class SetUpGoogleServiceAccountKey {
|
||||
|
||||
private async isGoogleServiceAccountKeySetupAsync(ctx: CredentialsContext): Promise<boolean> {
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
return !!appCredentials?.googleServiceAccountKeyForSubmissions;
|
||||
|
||||
@@ -13,6 +13,7 @@ export class UseExistingGoogleServiceAccountKey {
|
||||
);
|
||||
}
|
||||
const gsaKeyFragments = await ctx.android.getGoogleServiceAccountKeysForAccountAsync(
|
||||
ctx.graphqlClient,
|
||||
this.account
|
||||
);
|
||||
if (gsaKeyFragments.length === 0) {
|
||||
|
||||
@@ -40,6 +40,7 @@ describe('CreateKeystore', () => {
|
||||
|
||||
// expect keystore to be created on expo servers
|
||||
expect(ctx.android.createKeystoreAsync).toHaveBeenCalledWith(
|
||||
ctx.graphqlClient,
|
||||
appLookupParams.account,
|
||||
expect.objectContaining(testKeystore)
|
||||
);
|
||||
|
||||
Loaded 100 of 232 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user