Compare commits

..
Author SHA1 Message Date
Will Schurman 378ba974de update CHANGELOG.md 2022-09-01 01:51:13 +00:00
Will Schurman 2faed15944 [eas-cli] Use discriminated union type for BuildContext 2022-08-31 15:38:36 -07:00
139 changed files with 937 additions and 2379 deletions

No files matched your search

+1 -31
View File
@@ -8,41 +8,11 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
- Add `-e` as a shortcut for `--profile` flag. ([#1342](https://github.com/expo/eas-cli/pull/1342) by [@szdziedzic](https://github.com/szdziedzic))
- Support JSON5 in **eas.json**. ([#1350](https://github.com/expo/eas-cli/pull/1350) by [@dsokal](https://github.com/dsokal))
### 🐛 Bug fixes
### 🧹 Chores
## [2.1.0](https://github.com/expo/eas-cli/releases/tag/v2.1.0) - 2022-09-05
### 🎉 New features
- Add tvOS credentials compatibility for Adhoc and App store builds. ([#1325](https://github.com/expo/eas-cli/pull/1325) by [@quinlanj](https://github.com/quinlanj))
- Add `eas open` command for opening project page in web browser. ([#1337](https://github.com/expo/eas-cli/pull/1337) by [@dsokal](https://github.com/dsokal))
- Add support for buildArtifactPaths. Rename artifactPath to applicationArchivePath. ([#1321](https://github.com/expo/eas-cli/pull/1321) by [@dsokal](https://github.com/dsokal))
### 🐛 Bug fixes
- Fix viewing branch list when a branch has no associated platforms. ([#1326](https://github.com/expo/eas-cli/pull/1326) by [@hbiede](https://github.com/hbiede))
- Fix description of `help` command in help prompt. ([#1341](https://github.com/expo/eas-cli/pull/1341) by [@Simek](https://github.com/Simek))
- Resolve paths from the root when checking for gitignored values. ([#1336](https://github.com/expo/eas-cli/pull/1336) by [@wkozyra95](https://github.com/wkozyra95))
### 🧹 Chores
- Add noImplicitOverride to tsconfigs. ([#1327](https://github.com/expo/eas-cli/pull/1327) by [@wschurman](https://github.com/wschurman))
- Fix typos. ([#1340](https://github.com/expo/eas-cli/pull/1340) by [@hbiede](https://github.com/hbiede))
## [2.0.0](https://github.com/expo/eas-cli/releases/tag/v2.0.0) - 2022-09-01
### 🛠 Breaking changes
- Fix typo in `cli.promptToConfigurePushNotifications` field in **eas.json**. EAS CLI will throw an error if the old field name is in **eas.json**. ([#1332](https://github.com/expo/eas-cli/pull/1332) by [@dsokal](https://github.com/dsokal))
### 🐛 Bug fixes
- Fix dynamic config update warning. ([#1322](https://github.com/expo/eas-cli/pull/1322) by [@wschurman](https://github.com/wschurman))
- Use discriminated union type for BuildContext. ([#1330](https://github.com/expo/eas-cli/pull/1330) by [@wschurman](https://github.com/wschurman))
## [1.2.0](https://github.com/expo/eas-cli/releases/tag/v1.2.0) - 2022-08-29
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "2.1.0"
"version": "1.2.0"
}
+50 -65
View File
@@ -32,7 +32,7 @@ An example of **eas.json** that enforces `eas-cli` in version `1.0.0` or newer:
},
"build": {
// build profiles
},
}
"submit": {
// submit profiles
}
@@ -91,7 +91,6 @@ eas --help COMMAND
* [`eas logout`](#eas-logout)
* [`eas metadata:pull`](#eas-metadatapull)
* [`eas metadata:push`](#eas-metadatapush)
* [`eas open`](#eas-open)
* [`eas project:info`](#eas-projectinfo)
* [`eas project:init`](#eas-projectinit)
* [`eas secret:create`](#eas-secretcreate)
@@ -125,7 +124,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -142,7 +141,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -159,7 +158,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -173,7 +172,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -222,7 +221,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -242,7 +241,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -263,7 +262,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -282,7 +281,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -306,7 +305,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -336,7 +335,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -350,7 +349,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -367,7 +366,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -402,7 +401,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.1.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -435,7 +434,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -482,7 +481,7 @@ DESCRIPTION
Update version of an app.
```
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v2.1.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/v1.2.0/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -501,7 +500,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.1.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/v1.2.0/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -518,7 +517,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -538,7 +537,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -559,7 +558,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -576,7 +575,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -596,7 +595,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -614,7 +613,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -631,7 +630,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -645,7 +644,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.1.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -663,7 +662,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -680,7 +679,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -694,7 +693,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -708,11 +707,11 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
display help for eas-cli
Display help for eas.
```
USAGE
@@ -725,10 +724,10 @@ FLAGS
-n, --nested-commands Include all nested commands in the output.
DESCRIPTION
display help for eas-cli
Display help for eas.
```
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.3.0/src/commands/help.ts)_
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.2.0/src/commands/help.ts)_
## `eas init`
@@ -790,7 +789,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.1.0/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -807,21 +806,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.1.0/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas open`
open the project page in a web browser
```
USAGE
$ eas open
DESCRIPTION
open the project page in a web browser
```
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/open.ts)_
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas project:info`
@@ -835,7 +820,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -852,7 +837,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -872,7 +857,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.1.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -889,7 +874,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -903,7 +888,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.1.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -933,7 +918,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -963,7 +948,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -980,7 +965,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1000,7 +985,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.1.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1019,7 +1004,7 @@ DESCRIPTION
view the recent updates for a branch
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -1039,7 +1024,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1059,7 +1044,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1076,7 +1061,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1093,7 +1078,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1114,7 +1099,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1131,7 +1116,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+4 -418
View File
@@ -4994,21 +4994,9 @@
"description": "",
"fields": null,
"inputFields": [
{
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -5018,26 +5006,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildArtifactPaths",
"description": "",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildType",
"description": "",
@@ -5300,21 +5268,9 @@
"description": "",
"fields": null,
"inputFields": [
{
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -5324,26 +5280,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildArtifactPaths",
"description": "",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildType",
"description": "",
@@ -6126,26 +6062,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "filterChannels",
"description": "",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "filterPlatforms",
"description": "",
@@ -7523,83 +7439,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateGroups",
"description": "EAS updates owned by an app grouped by update group",
"args": [
{
"name": "filter",
"description": "",
"type": {
"kind": "INPUT_OBJECT",
"name": "UpdatesFilter",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "limit",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "offset",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Update",
"ofType": null
}
}
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updated",
"description": "Time of last classic update publish",
@@ -12534,30 +12373,6 @@
"name": "BuildArtifacts",
"description": "",
"fields": [
{
"name": "applicationArchiveUrl",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildArtifactsUrl",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildUrl",
"description": "",
@@ -16019,33 +15834,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeployServerlessFunctionResult",
"description": "",
"fields": [
{
"name": "url",
"description": "",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Deployment",
@@ -19061,21 +18849,9 @@
"description": "",
"fields": null,
"inputFields": [
{
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -19085,26 +18861,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildArtifactPaths",
"description": "",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildConfiguration",
"description": "",
@@ -19332,21 +19088,9 @@
"description": "",
"fields": null,
"inputFields": [
{
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"description": "",
"type": {
"kind": "SCALAR",
"name": "String",
@@ -19356,26 +19100,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildArtifactPaths",
"description": "",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildConfiguration",
"description": "",
@@ -21636,18 +21360,6 @@
"name": "PublicArtifacts",
"description": "",
"fields": [
{
"name": "applicationArchiveUrl",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildUrl",
"description": "",
@@ -23761,55 +23473,6 @@
"name": "ServerlessFunctionMutation",
"description": "",
"fields": [
{
"name": "createDeployment",
"description": "",
"args": [
{
"name": "appId",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "serverlessFunctionIdentifierInput",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ServerlessFunctionIdentifierInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeployServerlessFunctionResult",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createUploadPresignedUrl",
"description": "",
@@ -26097,83 +25760,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateGroups",
"description": "",
"args": [
{
"name": "filter",
"description": "",
"type": {
"kind": "INPUT_OBJECT",
"name": "UpdatesFilter",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "limit",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "offset",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Update",
"ofType": null
}
}
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": "",
+4 -5
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "2.1.0",
"version": "1.2.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
@@ -13,15 +13,15 @@
"@expo/config": "7.0.1",
"@expo/config-plugins": "5.0.1",
"@expo/config-types": "46.0.2",
"@expo/eas-build-job": "0.2.90",
"@expo/eas-json": "2.1.0",
"@expo/eas-build-job": "0.2.85",
"@expo/eas-json": "1.2.0",
"@expo/json-file": "8.2.36",
"@expo/multipart-body-parser": "1.1.0",
"@expo/package-manager": "0.0.56",
"@expo/pkcs12": "0.0.8",
"@expo/plist": "0.0.18",
"@expo/plugin-autocomplete": "1.4.0",
"@expo/plugin-help": "5.3.0",
"@expo/plugin-help": "5.2.0",
"@expo/plugin-warn-if-update-available": "2.3.0",
"@expo/prebuild-config": "5.0.3",
"@expo/results": "1.0.0",
@@ -32,7 +32,6 @@
"@urql/core": "2.6.1",
"@urql/exchange-retry": "0.3.3",
"ajv": "^6.12.6",
"better-opn": "3.0.2",
"chalk": "4.1.2",
"cli-progress": "3.11.2",
"cli-table3": "0.6.2",
+1 -1
View File
@@ -4,7 +4,7 @@ import fetch, { RequestError, RequestInit } from './fetch';
import { getAccessToken, getSessionSecret } from './user/sessionStorage';
export class ApiV2Error extends Error {
override readonly name = 'ApiV2Error';
readonly name = 'ApiV2Error';
readonly expoApiV2ErrorCode: string;
readonly expoApiV2ErrorDetails?: JSONValue;
readonly expoApiV2ErrorServerStack?: string;
+10 -15
View File
@@ -89,21 +89,16 @@ function renderPageOfBranches(
});
table.push(
...currentPage.map(branch => {
const update: UpdateBranchFragment['updates'][0] | undefined = branch.updates[0];
return [
branch.name,
formatUpdate(update),
update?.runtimeVersion ?? 'N/A',
update?.group ?? 'N/A',
update?.group
? getPlatformsForGroup({
updates: branch.updates,
group: update.group,
})
: 'N/A',
];
})
...currentPage.map(branch => [
branch.name,
formatUpdate(branch.updates[0]),
branch.updates[0]?.runtimeVersion ?? 'N/A',
branch.updates[0]?.group ?? 'N/A',
getPlatformsForGroup({
updates: branch.updates,
group: branch.updates[0]?.group,
}),
])
);
Log.addNewLineIfNone();
@@ -73,17 +73,17 @@ describe(bumpVersionAsync, () => {
await bumpVersionAsync({
bumpStrategy: BumpStrategy.VERSION_CODE,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('3.0.0');
expect(fakeExp.android?.versionCode).toBe(124);
expect(appJSON.expo.version).toBe('3.0.0');
expect(appJSON.expo.android.versionCode).toBe(124);
const buildGradle = await getAppBuildGradleAsync('/app');
const buildGradle = await getAppBuildGradleAsync('/repo');
assert(buildGradle);
expect(resolveConfigValue(buildGradle, 'versionCode')).toBe('124');
expect(resolveConfigValue(buildGradle, 'versionName')).toBe('3.0.0');
@@ -95,17 +95,17 @@ describe(bumpVersionAsync, () => {
await bumpVersionAsync({
bumpStrategy: BumpStrategy.APP_VERSION,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('3.0.1');
expect(fakeExp.android?.versionCode).toBe(123);
expect(appJSON.expo.version).toBe('3.0.1');
expect(appJSON.expo.android.versionCode).toBe(123);
const buildGradle = await getAppBuildGradleAsync('/app');
const buildGradle = await getAppBuildGradleAsync('/repo');
assert(buildGradle);
expect(resolveConfigValue(buildGradle, 'versionCode')).toBe('123');
expect(resolveConfigValue(buildGradle, 'versionName')).toBe('3.0.1');
@@ -116,17 +116,17 @@ describe(bumpVersionAsync, () => {
await bumpVersionAsync({
bumpStrategy: BumpStrategy.NOOP,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('3.0.0');
expect(fakeExp.android?.versionCode).toBe(123);
expect(appJSON.expo.version).toBe('3.0.0');
expect(appJSON.expo.android.versionCode).toBe(123);
const buildGradle = await getAppBuildGradleAsync('/app');
const buildGradle = await getAppBuildGradleAsync('/repo');
assert(buildGradle);
expect(resolveConfigValue(buildGradle, 'versionCode')).toBe('123');
expect(resolveConfigValue(buildGradle, 'versionName')).toBe('3.0.0');
@@ -140,11 +140,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.VERSION_CODE,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('5.0.0');
expect(fakeExp.android?.versionCode).toBe(127);
expect(appJSON.expo.version).toBe('5.0.0');
@@ -156,11 +156,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.APP_VERSION,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('5.0.1');
expect(fakeExp.android?.versionCode).toBe(126);
expect(appJSON.expo.version).toBe('5.0.1');
@@ -172,11 +172,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.NOOP,
projectDir: '/app',
projectDir: '/repo',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/app/app.json');
const appJSON = await fs.readJSON('/repo/app.json');
expect(fakeExp.version).toBe('5.0.0');
expect(fakeExp.android?.versionCode).toBe(126);
expect(appJSON.expo.version).toBe('5.0.0');
@@ -189,7 +189,7 @@ describe(maybeResolveVersionsAsync, () => {
it('reads the versions from native code', async () => {
const exp = initBareWorkflowProject();
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/app',
'/repo',
exp,
{} as BuildProfile<Platform.ANDROID>
);
@@ -201,7 +201,7 @@ describe(maybeResolveVersionsAsync, () => {
it('reads the versions from expo config', async () => {
const exp = initManagedProject();
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/app',
'/repo',
exp,
{} as BuildProfile<Platform.ANDROID>
);
@@ -240,7 +240,7 @@ function initBareWorkflowProject({
}`,
'./android/app/src/main/AndroidManifest.xml': 'fake',
},
'/app'
'/repo'
);
return fakeExp;
@@ -261,7 +261,7 @@ function initManagedProject(): ExpoConfig {
expo: fakeExp,
}),
},
'/app'
'/repo'
);
return fakeExp;
+7 -7
View File
@@ -22,7 +22,7 @@ import {
JobData,
prepareBuildRequestForPlatformAsync,
} from '../build';
import { AndroidBuildContext, BuildContext, CommonContext } from '../context';
import { AndroidBuildContext, AndroidCommonContext, BuildContextAndroid } from '../context';
import { transformMetadata } from '../graphql';
import { logCredentialsSource } from '../utils/credentials';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
@@ -32,7 +32,7 @@ import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteVersionCodeAsync } from './version';
export async function createAndroidContextAsync(
ctx: CommonContext<Platform.ANDROID>
ctx: AndroidCommonContext
): Promise<AndroidBuildContext> {
const { buildProfile } = ctx;
@@ -77,11 +77,11 @@ This means that it will most likely produce an AAB and you will not be able to i
}
export async function prepareAndroidBuildAsync(
ctx: BuildContext<Platform.ANDROID>
ctx: BuildContextAndroid
): Promise<BuildRequestSender> {
return await prepareBuildRequestForPlatformAsync({
ctx,
ensureCredentialsAsync: async (ctx: BuildContext<Platform.ANDROID>) => {
ensureCredentialsAsync: async (ctx: BuildContextAndroid) => {
return await ensureAndroidCredentialsAsync(ctx);
},
syncProjectConfigurationAsync: async () => {
@@ -95,7 +95,7 @@ export async function prepareAndroidBuildAsync(
});
},
prepareJobAsync: async (
ctx: BuildContext<Platform.ANDROID>,
ctx: BuildContextAndroid,
jobData: JobData<AndroidCredentials>
): Promise<Job> => {
return await prepareJobAsync(ctx, jobData);
@@ -118,12 +118,12 @@ export async function prepareAndroidBuildAsync(
});
}
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
function shouldProvideCredentials(ctx: BuildContextAndroid): boolean {
return !ctx.buildProfile.withoutCredentials;
}
async function ensureAndroidCredentialsAsync(
ctx: BuildContext<Platform.ANDROID>
ctx: BuildContextAndroid
): Promise<CredentialsResult<AndroidCredentials> | undefined> {
if (!shouldProvideCredentials(ctx)) {
return;
@@ -15,8 +15,7 @@ export function transformJob(job: Android.Job): AndroidJobInput {
cache: job.cache,
version: job.version?.versionCode ? { versionCode: job.version.versionCode } : undefined,
gradleCommand: job.gradleCommand,
applicationArchivePath: job.applicationArchivePath,
buildArtifactPaths: job.buildArtifactPaths,
artifactPath: job.artifactPath,
username: job.username,
buildType: job.buildType && transformBuildType(job.buildType),
developmentClient: job.developmentClient,
@@ -7,7 +7,7 @@ import { AndroidCredentials } from '../../credentials/android/AndroidCredentials
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
import { BuildContextAndroid } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -20,10 +20,7 @@ const cacheDefaults = {
cacheDefaultPaths: true,
};
export async function prepareJobAsync(
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData
): Promise<Job> {
export async function prepareJobAsync(ctx: BuildContextAndroid, jobData: JobData): Promise<Job> {
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
const projectRootDirectory =
@@ -72,8 +69,7 @@ export async function prepareJobAsync(
updates: { channel: ctx.buildProfile.channel },
developmentClient: buildProfile.developmentClient,
gradleCommand: buildProfile.gradleCommand,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
buildArtifactPaths: buildProfile.buildArtifactPaths,
artifactPath: buildProfile.artifactPath,
buildType,
username,
...(ctx.android.versionCodeOverride && {
+12 -18
View File
@@ -46,14 +46,12 @@ export interface JobData<Credentials> {
projectArchive: ArchiveSource;
}
interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
ctx: BuildContext<TPlatform>;
interface Builder<Credentials, TJob extends Job> {
ctx: BuildContext;
ensureCredentialsAsync(
ctx: BuildContext<TPlatform>
): Promise<CredentialsResult<Credentials> | undefined>;
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
ensureCredentialsAsync(ctx: BuildContext): Promise<CredentialsResult<Credentials> | undefined>;
syncProjectConfigurationAsync(ctx: BuildContext): Promise<void>;
prepareJobAsync(ctx: BuildContext, jobData: JobData<Credentials>): Promise<Job>;
sendBuildRequestAsync(
appId: string,
job: TJob,
@@ -64,17 +62,15 @@ interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
export type BuildRequestSender = () => Promise<BuildFragment | undefined>;
function resolveBuildParamsInput<T extends Platform>(ctx: BuildContext<T>): BuildParamsInput {
function resolveBuildParamsInput(ctx: BuildContext): BuildParamsInput {
return {
resourceClass: ctx.resourceClass,
};
}
export async function prepareBuildRequestForPlatformAsync<
TPlatform extends Platform,
Credentials,
TJob extends Job
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
export async function prepareBuildRequestForPlatformAsync<Credentials, TJob extends Job>(
builder: Builder<Credentials, TJob>
): Promise<BuildRequestSender> {
const { ctx } = builder;
const credentialsResult = await withAnalyticsAsync(
async () => await builder.ensureCredentialsAsync(ctx),
@@ -163,9 +159,7 @@ function handleBuildRequestError(error: any, platform: Platform): never {
throw error;
}
async function uploadProjectAsync<TPlatform extends Platform>(
ctx: BuildContext<TPlatform>
): Promise<string> {
async function uploadProjectAsync(ctx: BuildContext): Promise<string> {
let projectTarballPath;
try {
return await withAnalyticsAsync(
@@ -207,8 +201,8 @@ async function uploadProjectAsync<TPlatform extends Platform>(
}
}
async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJob extends Job>(
builder: Builder<TPlatform, Credentials, TJob>,
async function sendBuildRequestAsync<Credentials, TJob extends Job>(
builder: Builder<Credentials, TJob>,
job: TJob,
metadata: Metadata,
buildParams: BuildParamsInput
+3 -3
View File
@@ -1,5 +1,5 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
import { EasJson, EasJsonReader } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
@@ -24,7 +24,7 @@ interface ConfigureParams {
export async function ensureProjectConfiguredAsync(
configureParams: ConfigureParams
): Promise<boolean> {
if (await fs.pathExists(EasJsonAccessor.formatEasJsonPath(configureParams.projectDir))) {
if (await fs.pathExists(EasJsonReader.formatEasJsonPath(configureParams.projectDir))) {
return false;
}
@@ -89,7 +89,7 @@ const EAS_JSON_BARE_DEFAULT: EasJson = {
};
async function createEasJsonAsync(projectDir: string): Promise<void> {
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
const easJsonPath = EasJsonReader.formatEasJsonPath(projectDir);
const hasAndroidNativeProject =
(await resolveWorkflowAsync(projectDir, Platform.ANDROID)) === Workflow.GENERIC;
+18 -6
View File
@@ -11,7 +11,9 @@ import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { LocalBuildOptions } from './local';
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
export type AndroidCommonContext = Omit<BuildContextAndroid, 'android'>;
export type IosCommonContext = Omit<BuildContextIos, 'ios'>;
export type CommonContext = AndroidCommonContext | IosCommonContext;
export interface AndroidBuildContext {
applicationId: string;
@@ -27,10 +29,9 @@ export interface IosBuildContext {
buildNumberOverride?: string;
}
export interface BuildContext<T extends Platform> {
interface BuildContextBase {
accountName: string;
easJsonCliConfig: EasJson['cli'];
buildProfile: BuildProfile<T>;
buildProfileName: string;
resourceClass: BuildResourceClass;
clearCache: boolean;
@@ -40,7 +41,6 @@ export interface BuildContext<T extends Platform> {
nonInteractive: boolean;
noWait: boolean;
runFromCI: boolean;
platform: T;
projectDir: string;
projectId: string;
projectName: string;
@@ -48,6 +48,18 @@ export interface BuildContext<T extends Platform> {
trackingCtx: TrackingContext;
user: Actor;
workflow: Workflow;
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
ios: T extends Platform.IOS ? IosBuildContext : undefined;
}
export interface BuildContextAndroid extends BuildContextBase {
buildProfile: BuildProfile<Platform.ANDROID>;
platform: Platform.ANDROID;
android: AndroidBuildContext;
}
export interface BuildContextIos extends BuildContextBase {
buildProfile: BuildProfile<Platform.IOS>;
platform: Platform.IOS;
ios: IosBuildContext;
}
export type BuildContext = BuildContextAndroid | BuildContextIos;
+7 -7
View File
@@ -15,7 +15,7 @@ import { resolveWorkflowAsync } from '../project/workflow';
import { findAccountByName } from '../user/Account';
import { ensureLoggedInAsync } from '../user/actions';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { AndroidCommonContext, BuildContext, IosCommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildOptions } from './local';
@@ -43,7 +43,7 @@ export async function createBuildContextAsync<T extends Platform>({
projectDir: string;
resourceClass: BuildResourceClass;
message?: string;
}): Promise<BuildContext<T>> {
}): Promise<BuildContext> {
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
const user = await ensureLoggedInAsync();
@@ -80,7 +80,7 @@ export async function createBuildContextAsync<T extends Platform>({
};
Analytics.logEvent(BuildEvent.BUILD_COMMAND, trackingCtx);
const commonContext: CommonContext<T> = {
const commonContext = {
accountName,
buildProfile,
buildProfileName,
@@ -103,17 +103,17 @@ export async function createBuildContextAsync<T extends Platform>({
runFromCI,
};
if (platform === Platform.ANDROID) {
const common = commonContext as CommonContext<Platform.ANDROID>;
const common = commonContext as AndroidCommonContext;
return {
...common,
android: await createAndroidContextAsync(common),
} as BuildContext<T>;
};
} else {
const common = commonContext as CommonContext<Platform.IOS>;
const common = commonContext as IosCommonContext;
return {
...common,
ios: await createIosContextAsync(common),
} as BuildContext<T>;
};
}
}
+6 -10
View File
@@ -1,4 +1,4 @@
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { Ios, Job, Metadata, Workflow } from '@expo/eas-build-job';
import { AppVersionSource } from '@expo/eas-json';
import { IosCredentials } from '../../credentials/ios/types';
@@ -8,7 +8,7 @@ import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../pro
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
import { BuildContext, CommonContext, IosBuildContext } from '../context';
import { BuildContextIos, IosBuildContext, IosCommonContext } from '../context';
import { transformMetadata } from '../graphql';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { ensureIosCredentialsAsync } from './credentials';
@@ -17,9 +17,7 @@ import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteBuildNumberAsync } from './version';
export async function createIosContextAsync(
ctx: CommonContext<Platform.IOS>
): Promise<IosBuildContext> {
export async function createIosContextAsync(ctx: IosCommonContext): Promise<IosBuildContext> {
const { buildProfile } = ctx;
if (ctx.workflow === Workflow.MANAGED) {
@@ -63,12 +61,10 @@ export async function createIosContextAsync(
};
}
export async function prepareIosBuildAsync(
ctx: BuildContext<Platform.IOS>
): Promise<BuildRequestSender> {
export async function prepareIosBuildAsync(ctx: BuildContextIos): Promise<BuildRequestSender> {
return await prepareBuildRequestForPlatformAsync({
ctx,
ensureCredentialsAsync: async (ctx: BuildContext<Platform.IOS>) => {
ensureCredentialsAsync: async (ctx: BuildContextIos) => {
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
},
syncProjectConfigurationAsync: async () => {
@@ -83,7 +79,7 @@ export async function prepareIosBuildAsync(
});
},
prepareJobAsync: async (
ctx: BuildContext<Platform.IOS>,
ctx: BuildContextIos,
jobData: JobData<IosCredentials>
): Promise<Job> => {
return await prepareJobAsync(ctx, {
@@ -4,11 +4,11 @@ import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { IosCredentials, Target } from '../../credentials/ios/types';
import { CredentialsResult } from '../build';
import { BuildContext } from '../context';
import { BuildContextIos } from '../context';
import { logCredentialsSource } from '../utils/credentials';
export async function ensureIosCredentialsAsync(
buildCtx: BuildContext<Platform.IOS>,
buildCtx: BuildContextIos,
targets: Target[]
): Promise<CredentialsResult<IosCredentials> | undefined> {
if (!shouldProvideCredentials(buildCtx)) {
@@ -31,6 +31,6 @@ export async function ensureIosCredentialsAsync(
};
}
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
function shouldProvideCredentials(buildCtx: BuildContextIos): boolean {
return !buildCtx.buildProfile.simulator;
}
+1 -2
View File
@@ -17,8 +17,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
scheme: job.scheme,
buildConfiguration: job.buildConfiguration,
applicationArchivePath: job.applicationArchivePath,
buildArtifactPaths: job.buildArtifactPaths,
artifactPath: job.artifactPath,
username: job.username,
developmentClient: job.developmentClient,
simulator: job.simulator,
+3 -8
View File
@@ -6,7 +6,7 @@ import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
import { BuildContextIos } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -20,10 +20,7 @@ const cacheDefaults = {
cacheDefaultPaths: true,
};
export async function prepareJobAsync(
ctx: BuildContext<Platform.IOS>,
jobData: JobData
): Promise<Job> {
export async function prepareJobAsync(ctx: BuildContextIos, jobData: JobData): Promise<Job> {
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
@@ -64,9 +61,7 @@ export async function prepareJobAsync(
simulator: ctx.buildProfile.simulator,
scheme: jobData.buildScheme,
buildConfiguration: ctx.buildProfile.buildConfiguration,
applicationArchivePath:
ctx.buildProfile.applicationArchivePath ?? ctx.buildProfile.artifactPath,
buildArtifactPaths: ctx.buildProfile.buildArtifactPaths,
artifactPath: ctx.buildProfile.artifactPath,
username,
...(ctx.ios.buildNumberOverride && {
version: {
+21 -35
View File
@@ -18,12 +18,10 @@ import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import { BuildContext, BuildContextIos } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<Metadata> {
export async function collectMetadataAsync(ctx: BuildContext): Promise<Metadata> {
const vcsClient = getVcsClient();
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
@@ -52,9 +50,7 @@ export async function collectMetadataAsync<T extends Platform>(
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
ctx as BuildContext<Platform.IOS>
),
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(ctx),
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
@@ -62,52 +58,48 @@ export async function collectMetadataAsync<T extends Platform>(
return sanitizeMetadata(metadata);
}
async function maybeResolveVersionsAsync<T extends Platform>(
ctx: BuildContext<T>
async function maybeResolveVersionsAsync(
ctx: BuildContext
): Promise<{ appBuildVersion?: string; appVersion?: string }> {
if (ctx.platform === Platform.IOS) {
const iosContext = ctx as BuildContext<Platform.IOS>;
const resolvedVersion = await maybeResolveIosVersionsAsync(
ctx.projectDir,
ctx.exp,
iosContext.ios.targets
ctx.ios.targets
);
if (iosContext.ios.buildNumberOverride) {
if (ctx.ios.buildNumberOverride) {
return {
...resolvedVersion,
appBuildVersion: iosContext.ios.buildNumberOverride,
appBuildVersion: ctx.ios.buildNumberOverride,
};
}
return resolvedVersion;
} else if (ctx.platform === Platform.ANDROID) {
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
} else {
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
ctx.projectDir,
ctx.exp,
androidCtx.buildProfile
ctx.buildProfile
);
if (androidCtx.android.versionCodeOverride) {
if (ctx.android.versionCodeOverride) {
return {
...resolvedVersion,
appBuildVersion: androidCtx.android.versionCodeOverride,
appBuildVersion: ctx.android.versionCodeOverride,
};
}
return resolvedVersion;
} else {
throw new Error(`Unsupported platform ${ctx.platform}`);
}
}
function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string {
function resolveAppIdentifier(ctx: BuildContext): string {
if (ctx.platform === Platform.IOS) {
return (ctx as BuildContext<Platform.IOS>).ios.bundleIdentifier;
return ctx.ios.bundleIdentifier;
} else {
return (ctx as BuildContext<Platform.ANDROID>).android.applicationId;
return ctx.android.applicationId;
}
}
async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
ctx: BuildContext<T>
async function resolveChannelOrReleaseChannelAsync(
ctx: BuildContext
): Promise<{ channel: string } | { releaseChannel: string } | null> {
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
return null;
@@ -126,9 +118,7 @@ async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
return { releaseChannel };
}
async function getNativeReleaseChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<string> {
async function getNativeReleaseChannelAsync(ctx: BuildContext): Promise<string> {
switch (ctx.platform) {
case Platform.ANDROID: {
return (await readAndroidReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
@@ -141,9 +131,7 @@ async function getNativeReleaseChannelAsync<T extends Platform>(
}
}
async function getNativeChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<string | undefined> {
async function getNativeChannelAsync(ctx: BuildContext): Promise<string | undefined> {
switch (ctx.platform) {
case Platform.ANDROID: {
return (await readAndroidChannelSafelyAsync(ctx.projectDir)) ?? undefined;
@@ -156,9 +144,7 @@ async function getNativeChannelAsync<T extends Platform>(
return undefined;
}
async function getReactNativeVersionAsync(
ctx: BuildContext<Platform>
): Promise<string | undefined> {
async function getReactNativeVersionAsync(ctx: BuildContext): Promise<string | undefined> {
try {
const reactNativePackageJsonPath = resolveFrom(ctx.projectDir, 'react-native/package.json');
return (await fs.readJson(reactNativePackageJsonPath)).version;
@@ -170,7 +156,7 @@ async function getReactNativeVersionAsync(
}
function resolveIosEnterpriseProvisioning(
ctx: BuildContext<Platform.IOS>
ctx: BuildContextIos
): IosEnterpriseProvisioning | undefined {
return ctx.buildProfile.enterpriseProvisioning;
}
+15 -14
View File
@@ -3,8 +3,7 @@ import {
AppVersionSource,
BuildProfile,
EasJson,
EasJsonAccessor,
EasJsonUtils,
EasJsonReader,
SubmitProfile,
} from '@expo/eas-json';
import chalk from 'chalk';
@@ -95,14 +94,13 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
projectDir,
nonInteractive: flags.nonInteractive,
});
const easJsonAccessor = new EasJsonAccessor(projectDir);
const easJsonCliConfig: EasJson['cli'] =
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
const easJsonReader = new EasJsonReader(projectDir);
const easJsonCliConfig: EasJson['cli'] = (await easJsonReader.getCliConfigAsync()) ?? {};
const platforms = toPlatforms(flags.requestedPlatform);
const buildProfiles = await getProfilesAsync({
type: 'build',
easJsonAccessor,
easJsonReader,
platforms,
profileName: flags.profile ?? undefined,
});
@@ -121,7 +119,7 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
build: BuildWithSubmissionsFragment | BuildFragment;
buildProfile: ProfileData<'build'>;
}[] = [];
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext } = {};
for (const buildProfile of buildProfiles) {
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
@@ -152,7 +150,7 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
const submissions: SubmissionFragment[] = [];
if (flags.autoSubmit) {
const submitProfiles = await getProfilesAsync({
easJsonAccessor,
easJsonReader,
platforms,
profileName: flags.submitProfile,
type: 'submit',
@@ -234,7 +232,7 @@ async function prepareAndStartBuildAsync({
buildProfile: ProfileData<'build'>;
resourceClass: BuildResourceClass;
easJsonCliConfig: EasJson['cli'];
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext }> {
const buildCtx = await createBuildContextAsync({
buildProfileName: buildProfile.profileName,
resourceClass,
@@ -280,12 +278,12 @@ async function prepareAndStartBuildAsync({
};
}
async function startBuildAsync(ctx: BuildContext<Platform>): Promise<BuildFragment | undefined> {
async function startBuildAsync(ctx: BuildContext): Promise<BuildFragment | undefined> {
let sendBuildRequestAsync: BuildRequestSender;
if (ctx.platform === Platform.ANDROID) {
sendBuildRequestAsync = await prepareAndroidBuildAsync(ctx as BuildContext<Platform.ANDROID>);
sendBuildRequestAsync = await prepareAndroidBuildAsync(ctx);
} else {
sendBuildRequestAsync = await prepareIosBuildAsync(ctx as BuildContext<Platform.IOS>);
sendBuildRequestAsync = await prepareIosBuildAsync(ctx);
}
return await sendBuildRequestAsync();
}
@@ -300,7 +298,7 @@ async function prepareAndStartSubmissionAsync({
nonInteractive,
}: {
build: BuildFragment;
buildCtx: BuildContext<Platform>;
buildCtx: BuildContext;
moreBuilds: boolean;
projectDir: string;
buildProfile: BuildProfile;
@@ -317,7 +315,10 @@ async function prepareAndStartSubmissionAsync({
nonInteractive,
env: buildProfile.env,
credentialsCtx: buildCtx.credentialsCtx,
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
applicationIdentifier:
buildCtx.platform === Platform.ANDROID
? buildCtx.android.applicationId
: buildCtx.ios.bundleIdentifier,
});
if (moreBuilds) {
+3 -5
View File
@@ -1,4 +1,4 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { Workflow } from '@expo/eas-build-job';
import fs from 'fs-extra';
import path from 'path';
@@ -6,7 +6,7 @@ import Log, { learnMore } from '../log';
import { getVcsClient } from '../vcs';
import { CommonContext } from './context';
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
export function checkNodeEnvVariable(ctx: CommonContext): void {
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
Log.warn(
'You set NODE_ENV=production in the build profile. Remember that it will be available during the entire build process. In particular, it will make yarn/npm install only production packages.'
@@ -15,9 +15,7 @@ export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
}
}
export async function checkGoogleServicesFileAsync<T extends Platform>(
ctx: CommonContext<T>
): Promise<void> {
export async function checkGoogleServicesFileAsync(ctx: CommonContext): Promise<void> {
if (ctx.workflow === Workflow.GENERIC || ctx.buildProfile?.env?.GOOGLE_SERVICES_FILE) {
return;
}
+10 -92
View File
@@ -1,10 +1,8 @@
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { EasJsonReader } from '@expo/eas-json';
import * as PackageManagerUtils from '@expo/package-manager';
import { Command, Flags } from '@oclif/core';
import { Command } from '@oclif/core';
import chalk from 'chalk';
import fs from 'fs-extra';
// eslint-disable-next-line no-restricted-imports
import { Options, Ora } from 'ora';
import path from 'path';
import semver from 'semver';
@@ -14,48 +12,15 @@ import {
initAsync as initAnalyticsAsync,
logEvent,
} from '../analytics/rudderstackClient';
import Log, { learnMore } from '../log';
import { interactiveOra, nonInteractiveOra } from '../ora';
import { learnMore } from '../log';
import { findProjectRootAsync } from '../project/projectUtils';
import { InteractivePrompts, NonInteractivePrompts, Prompts } from '../prompts';
import { getUserAsync } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { setVcsClient } from '../vcs';
import GitClient from '../vcs/clients/git';
type CommandOptions =
| {
nonInteractive: true;
json: true;
}
| {
nonInteractive: boolean;
json: false;
};
export type CommandContext = Readonly<
CommandOptions & {
logger: Log;
prompts: Prompts;
ora: (options?: Options | string) => Ora;
}
>;
export default abstract class EasCommand extends Command {
static override globalFlags = {
json: Flags.boolean({
description: 'Enable JSON output, non-JSON messages will be printed to stderr.',
dependsOn: ['non-interactive'],
}),
'non-interactive': Flags.boolean({
description: 'Run the command in non-interactive mode.',
}),
};
// disable built in oclif json stuff
enableJsonFlag = false;
/**
* When user data is unavailable locally, determines if the command will
* force the user to log in
@@ -63,26 +28,10 @@ export default abstract class EasCommand extends Command {
protected requiresAuthentication = true;
protected mustBeRunInsideProject = true;
protected abstract runAsync({
nonInteractive,
json,
logger,
prompts: {
promptAsync,
confirmAsync,
selectAsync,
toggleConfirmAsync,
pressAnyKeyToContinueAsync,
},
ora,
}: CommandContext): Promise<{ jsonOutput: object }>;
protected abstract runAsync(): Promise<any>;
// eslint-disable-next-line async-protect/async-suffix
async run(): Promise<any> {
const { flags } = await this.parse();
const json = flags.json ?? false;
const nonInteractive = (flags as any)['non-interactive'] ?? false;
await initAnalyticsAsync();
if (this.mustBeRunInsideProject) {
@@ -92,6 +41,8 @@ export default abstract class EasCommand extends Command {
}
if (this.requiresAuthentication) {
const { flags } = await this.parse();
const nonInteractive = (flags as any)['non-interactive'] ?? false;
await ensureLoggedInAsync({ nonInteractive });
} else {
await getUserAsync();
@@ -101,29 +52,18 @@ export default abstract class EasCommand extends Command {
// commands/submit === submit, commands/build/list === build:list
action: `eas ${this.id}`,
});
const jsonOutput = await this.runAsync({
nonInteractive,
json: json ?? false,
logger: new Log(json),
prompts: nonInteractive ? new InteractivePrompts() : new NonInteractivePrompts(),
ora: nonInteractive ? nonInteractiveOra : interactiveOra,
});
if (json) {
this.printJsonOnlyOutput(jsonOutput);
}
return this.runAsync();
}
// eslint-disable-next-line async-protect/async-suffix
override async finally(err: Error): Promise<any> {
async finally(err: Error): Promise<any> {
await flushAnalyticsAsync();
return super.finally(err);
}
private async applyCliConfigAsync(projectDir: string): Promise<void> {
const easJsonAccessor = new EasJsonAccessor(projectDir);
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
const easJsonReader = new EasJsonReader(projectDir);
const config = await easJsonReader.getCliConfigAsync();
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
throw new Error(
`You are on eas-cli@${easCliVersion} which does not satisfy the CLI version constraint in eas.json (${config.version})`
@@ -179,26 +119,4 @@ export default abstract class EasCommand extends Command {
packageJson?.devDependencies?.['eas-cli'] !== undefined
);
}
private printJsonOnlyOutput(value: object): void {
try {
new Log(false).log(JSON.stringify(this.sanitizeValue(value), null, 2));
} catch {}
}
private sanitizeValue(value: any): unknown {
if (Array.isArray(value)) {
return value.map(val => this.sanitizeValue(val));
} else if (value && typeof value === 'object') {
const result: Record<string, any> = {};
Object.keys(value).forEach(key => {
if (key !== '__typename' && value[key] !== null) {
result[key] = this.sanitizeValue(value[key]);
}
});
return result;
} else {
return value;
}
}
}
@@ -43,7 +43,7 @@ beforeEach(() => {
const createTestEasCommand = (authValue: boolean): typeof EasCommand => {
class TestEasCommand extends EasCommand {
override requiresAuthentication = authValue;
requiresAuthentication = authValue;
async runAsync(): Promise<void> {}
}
@@ -1,15 +1,12 @@
import { Flags } from '@oclif/core';
export const getPaginatedQueryOptions = (flags: {
json: boolean;
'non-interactive': boolean;
offset: number | undefined;
limit: number | undefined;
}): PaginatedQueryOptions => {
export const getPaginatedQueryOptions = (
flags: Record<keyof typeof EasPaginatedQueryFlags, any>
): PaginatedQueryOptions => {
return {
json: flags.json,
json: flags.json ?? false,
offset: flags.offset ?? 0,
nonInteractive: flags['non-interactive'],
nonInteractive: flags['non-interactive'] ?? false,
...('limit' in flags && { limit: flags.limit }),
};
};
@@ -43,6 +40,12 @@ export const EasPaginatedQueryFlags = {
// eslint-disable-next-line async-protect/async-suffix
parse: async input => parseFlagInputStringAsInteger(input, 'limit', 1, 100),
}),
json: Flags.boolean({
description: 'Enable JSON output, non-JSON messages will be printed to stderr.',
}),
'non-interactive': Flags.boolean({
description: 'Run the command in non-interactive mode.',
}),
};
// options required to control a paginated query
@@ -3,11 +3,11 @@ import Log from '../../log';
import { showLoginPromptAsync } from '../../user/actions';
export default class AccountLogin extends EasCommand {
static override description = 'log in with your Expo account';
static override aliases = ['login'];
static description = 'log in with your Expo account';
static aliases = ['login'];
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
await showLoginPromptAsync();
@@ -3,11 +3,11 @@ 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 description = 'log out';
static aliases = ['logout'];
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
await logoutAsync();
@@ -5,11 +5,11 @@ import Log from '../../log';
import { getActorDisplayName, getUserAsync } 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 description = 'show the username you are logged in as';
static aliases = ['whoami'];
protected override mustBeRunInsideProject = false;
protected override requiresAuthentication = false;
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const user = await getUserAsync();
+3 -3
View File
@@ -3,11 +3,11 @@ import Log from '../log';
import UserSettings from '../user/UserSettings';
export default class AnalyticsView extends EasCommand {
static override description = 'display or change analytics settings';
static description = 'display or change analytics settings';
static override args = [{ name: 'STATUS', options: ['on', 'off'] }];
static args = [{ name: 'STATUS', options: ['on', 'off'] }];
protected override requiresAuthentication = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const { STATUS: status } = (await this.parse(AnalyticsView)).args;
@@ -52,9 +52,9 @@ export async function createUpdateBranchOnAppAsync({
}
export default class BranchCreate extends EasCommand {
static override description = 'create a branch';
static description = 'create a branch';
static override args = [
static args = [
{
name: 'name',
required: false,
@@ -62,7 +62,7 @@ export default class BranchCreate extends EasCommand {
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description: 'return a json with the new branch ID and name.',
default: false,
+32 -20
View File
@@ -1,7 +1,8 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand, { CommandContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateBranchMutation,
@@ -10,12 +11,15 @@ import {
GetBranchInfoQuery,
GetBranchInfoQueryVariables,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
async function getBranchInfoAsync({
appId,
@@ -73,24 +77,30 @@ async function deleteBranchOnAppAsync({
}
export default class BranchDelete extends EasCommand {
static override description = 'delete a branch';
static description = 'delete a branch';
static override args = [
static args = [
{
name: 'name',
required: false,
description: 'Name of the branch to delete',
},
];
static flags = {
json: Flags.boolean({
description: `return JSON with the edited branch's ID and name.`,
default: false,
}),
};
protected async runAsync({
nonInteractive,
logger,
prompts: { promptAsync, toggleConfirmAsync },
}: CommandContext): Promise<{ jsonOutput: object }> {
async runAsync(): Promise<void> {
let {
args: { name },
flags: { json: jsonFlag },
} = await this.parse(BranchDelete);
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
@@ -99,7 +109,7 @@ export default class BranchDelete extends EasCommand {
if (!name) {
const validationMessage = 'branch name may not be empty.';
if (nonInteractive) {
if (jsonFlag) {
throw new Error(validationMessage);
}
({ name } = await promptAsync({
@@ -116,16 +126,16 @@ export default class BranchDelete extends EasCommand {
throw new Error(`Could not find branch ${name} on ${fullName}`);
}
if (!nonInteractive) {
logger.addNewLineIfNone();
logger.warn(
`You are about to permanently delete branch: "${name}" and all of the updates published on it.` +
if (!jsonFlag) {
Log.addNewLineIfNone();
Log.warn(
`You are about to permamently delete branch: "${name}" and all of the updates published on it.` +
`\nThis action is irreversible.`
);
logger.newLine();
Log.newLine();
const confirmed = await toggleConfirmAsync({ message: 'Are you sure you wish to proceed?' });
if (!confirmed) {
logger.error(`Cancelled deletion of branch: "${name}".`);
Log.error(`Cancelled deletion of branch: "${name}".`);
process.exit(1);
}
}
@@ -134,10 +144,12 @@ export default class BranchDelete extends EasCommand {
branchId,
});
logger.withTick(
`Deleted branch "${name}" and all of its updates on project ${chalk.bold(fullName)}.`
);
return { jsonOutput: deletionResult };
if (jsonFlag) {
printJsonOnlyOutput(deletionResult);
} else {
Log.withTick(
`️Deleted branch "${name}" and all of its updates on project ${chalk.bold(fullName)}.`
);
}
}
}
+2 -2
View File
@@ -6,9 +6,9 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { enableJsonOutput } from '../../utils/json';
export default class BranchList extends EasCommand {
static override description = 'list all branches';
static description = 'list all branches';
static override flags = {
static flags = {
...EasPaginatedQueryFlags,
};
@@ -1,8 +1,8 @@
import EasCommand from '../../commandUtils/EasCommand';
export default class BranchPublish extends EasCommand {
static override description = 'deprecated, use "eas update"';
static override hidden = true;
static description = 'deprecated, use "eas update"';
static hidden = true;
async runAsync(): Promise<void> {
throw new Error(BranchPublish.description);
@@ -52,9 +52,9 @@ async function renameUpdateBranchOnAppAsync({
}
export default class BranchRename extends EasCommand {
static override description = 'rename a branch';
static description = 'rename a branch';
static override flags = {
static flags = {
from: Flags.string({
description: 'current name of the branch.',
required: false,
+3 -3
View File
@@ -7,9 +7,9 @@ import { listAndRenderUpdatesOnBranchByNameAsync } from '../../update/queries';
import { enableJsonOutput } from '../../utils/json';
export default class BranchView extends EasCommand {
static override description = 'view a branch';
static description = 'view a branch';
static override args = [
static args = [
{
name: 'name',
required: false,
@@ -17,7 +17,7 @@ export default class BranchView extends EasCommand {
},
];
static override flags = {
static flags = {
...EasPaginatedQueryFlags,
};
@@ -110,9 +110,9 @@ async function ensureBuildExistsAsync(buildId: string): Promise<void> {
}
export default class BuildCancel extends EasCommand {
static override description = 'cancel a build';
static description = 'cancel a build';
static override args = [{ name: 'BUILD_ID' }];
static args = [{ name: 'BUILD_ID' }];
async runAsync(): Promise<void> {
const { BUILD_ID: buildIdFromArg } = (await this.parse(BuildCancel)).args;
@@ -16,9 +16,9 @@ import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } fro
import { getVcsClient } from '../../vcs';
export default class BuildConfigure extends EasCommand {
static override description = 'configure the project to support EAS Build';
static description = 'configure the project to support EAS Build';
static override flags = {
static flags = {
platform: Flags.enum({
description: 'Platform to configure',
char: 'p',
+3 -87
View File
@@ -1,19 +1,13 @@
import { Platform } from '@expo/eas-build-job';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import figures from 'figures';
import path from 'path';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import { UserInputResourceClass } from '../../build/types';
import EasCommand from '../../commandUtils/EasCommand';
import { StatuspageServiceName } from '../../graphql/generated';
import Log, { link } from '../../log';
import Log from '../../log';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { selectAsync } from '../../prompts';
import uniq from '../../utils/expodash/uniq';
import { enableJsonOutput } from '../../utils/json';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
@@ -35,9 +29,9 @@ interface RawBuildFlags {
}
export default class Build extends EasCommand {
static override description = 'start a build';
static description = 'start a build';
static override flags = {
static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios', 'all'],
@@ -55,7 +49,6 @@ export default class Build extends EasCommand {
hidden: true,
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
@@ -112,8 +105,6 @@ export default class Build extends EasCommand {
const projectDir = await findProjectRootAsync();
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
if (!flags.localBuildOptions.enable) {
await maybeWarnAboutEasOutagesAsync(
flags.autoSubmit
@@ -217,78 +208,3 @@ export default class Build extends EasCommand {
};
}
}
async function handleDeprecatedEasJsonAsync(
projectDir: string,
nonInteractive: boolean
): Promise<void> {
const easJsonAccessor = new EasJsonAccessor(projectDir);
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
const platformAndProfileNames: [Platform, string][] = profileNames.flatMap(profileName => [
[Platform.ANDROID, profileName],
[Platform.IOS, profileName],
]);
const deprecatedProfiles: [Platform, string][] = [];
for (const [platform, profileName] of platformAndProfileNames) {
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
easJsonAccessor,
platform,
profileName
);
if (buildProfile.artifactPath) {
deprecatedProfiles.push([platform, profileName]);
}
}
if (deprecatedProfiles.length === 0) {
return;
}
const deprecatedProfileNames = uniq(deprecatedProfiles.map(([, profileName]) => profileName));
Log.warn(`Some of your build profiles use deprecated field ${chalk.bold('artifactPath')}:`);
for (const profileName of deprecatedProfileNames) {
Log.warn(`- ${profileName}`);
}
Log.newLine();
if (nonInteractive) {
Log.warn(
`${figures.warning} Action required: rename ${chalk.bold('artifactPath')} to ${chalk.bold(
'applicationArchivePath'
)} in all of the build profiles listed above.`
);
Log.warn(
`See ${link('https://docs.expo.dev/build-reference/eas-json/')} for more information.`
);
Log.warn(
`This warning will become an error in a future EAS CLI release. This build will continue to use the ${chalk.bold(
'artifactPath'
)} setting.`
);
return;
}
const rename = await selectAsync('Do you want us to handle renaming the field for you?', [
{ title: 'Yes', value: true },
{ title: 'No, I will edit eas.json manually (EAS CLI exits)', value: false },
]);
if (!rename) {
Errors.exit(1);
}
await easJsonAccessor.readRawJsonAsync();
for (const [platform, profileName] of deprecatedProfiles) {
easJsonAccessor.patch(easJsonRawObject => {
easJsonRawObject.build[profileName][platform].applicationArchivePath =
easJsonRawObject.build[profileName][platform].artifactPath;
delete easJsonRawObject.build[profileName][platform].artifactPath;
return easJsonRawObject;
});
}
await easJsonAccessor.writeAsync();
Log.withTick('Updated eas.json');
}
@@ -25,17 +25,16 @@ const STAGE_DESCRIPTION = `Stage of the build you want to inspect.
post-build - builds the native project and leaves the output directory for inspection`;
export default class BuildInspect extends EasCommand {
static override description =
static description =
'inspect the state of the project at specific build stages, useful for troubleshooting';
static override flags = {
static flags = {
platform: Flags.enum({
char: 'p',
options: [RequestedPlatform.Android, RequestedPlatform.Ios],
required: true,
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
+2 -2
View File
@@ -22,9 +22,9 @@ import {
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildList extends EasCommand {
static override description = 'list all builds for your project';
static description = 'list all builds for your project';
static override flags = {
static flags = {
platform: Flags.enum({
options: [RequestedPlatform.All, RequestedPlatform.Android, RequestedPlatform.Ios],
}),
@@ -1,6 +1,6 @@
import { getRuntimeVersionNullable } from '@expo/config-plugins/build/utils/Updates';
import { Platform } from '@expo/eas-build-job';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -27,15 +27,14 @@ import {
import { promptAsync } from '../../../prompts';
export default class BuildVersionSetView extends EasCommand {
static override description = 'Update version of an app.';
static description = 'Update version of an app.';
static override flags = {
static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
@@ -48,13 +47,9 @@ export default class BuildVersionSetView extends EasCommand {
const projectDir = await findProjectRootAsync();
const platform = await selectPlatformAsync(flags.platform);
const easJsonAccessor = new EasJsonAccessor(projectDir);
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
const profile = await EasJsonUtils.getBuildProfileAsync(
easJsonAccessor,
platform,
flags.profile ?? undefined
);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const profile = await easJsonReader.getBuildProfileAsync(platform, flags.profile ?? undefined);
const exp = getExpoConfig(projectDir, { env: profile.env });
const projectId = await getProjectIdAsync(exp);
@@ -1,6 +1,6 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJsonAccessor } from '@expo/eas-json';
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -40,16 +40,14 @@ interface SyncContext<T extends Platform> {
}
export default class BuildVersionSyncView extends EasCommand {
public static override description =
'Update a version in native code with a value stored on EAS servers';
public static description = 'Update a version in native code with a value stored on EAS servers';
public static override flags = {
public static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios', 'all'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
@@ -61,13 +59,13 @@ export default class BuildVersionSyncView extends EasCommand {
const projectDir = await findProjectRootAsync();
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
const easJsonAccessor = new EasJsonAccessor(projectDir);
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const platforms = toPlatforms(requestedPlatform);
const buildProfiles = await getProfilesAsync({
type: 'build',
easJsonAccessor,
easJsonReader,
platforms,
profileName: flags.profile ?? undefined,
});
+3 -3
View File
@@ -15,11 +15,11 @@ import {
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildView extends EasCommand {
static override description = 'view a build for your project';
static description = 'view a build for your project';
static override args = [{ name: 'BUILD_ID' }];
static args = [{ name: 'BUILD_ID' }];
static override flags = {
static flags = {
json: Flags.boolean({
description: 'Enable JSON output, non-JSON messages will be printed to stderr',
}),
@@ -60,9 +60,9 @@ export async function createUpdateChannelOnAppAsync({
}
export default class ChannelCreate extends EasCommand {
static override description = 'create a channel';
static description = 'create a channel';
static override args = [
static args = [
{
name: 'name',
required: false,
@@ -70,7 +70,7 @@ export default class ChannelCreate extends EasCommand {
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description:
'print output as a JSON object with the new channel ID, name and branch mapping.',
@@ -21,17 +21,17 @@ import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class ChannelDelete extends EasCommand {
static override hidden = true;
static override description = 'Delete a channel';
static hidden = true;
static description = 'Delete a channel';
static override args = [
static args = [
{
name: 'name',
required: false,
description: 'Name of the channel to delete',
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description: 'print output as a JSON object',
default: false,
@@ -85,7 +85,7 @@ export default class ChannelDelete extends EasCommand {
if (!nonInteractiveFlag) {
Log.addNewLineIfNone();
Log.warn(
`You are about to permanently delete channel: "${name}".\nThis action is irreversible.`
`You are about to permamently delete channel: "${name}".\nThis action is irreversible.`
);
Log.newLine();
const confirmed = await toggleConfirmAsync({ message: 'Are you sure you wish to proceed?' });
@@ -93,9 +93,9 @@ export async function updateChannelBranchMappingAsync({
}
export default class ChannelEdit extends EasCommand {
static override description = 'point a channel at a new branch';
static description = 'point a channel at a new branch';
static override args = [
static args = [
{
name: 'name',
required: false,
@@ -103,7 +103,7 @@ export default class ChannelEdit extends EasCommand {
},
];
static override flags = {
static flags = {
branch: Flags.string({
description: 'Name of the branch to point to',
}),
@@ -67,9 +67,9 @@ async function getAllUpdateChannelForAppAsync({
}
export default class ChannelList extends EasCommand {
static override description = 'list all channels';
static description = 'list all channels';
static override flags = {
static flags = {
json: Flags.boolean({
description: 'print output as a JSON object with the channel ID, name and branch mapping.',
default: false,
@@ -304,10 +304,10 @@ async function endRolloutAsync({
}
export default class ChannelRollout extends EasCommand {
static override hidden = true;
static override description = 'Rollout a new branch out to a channel incrementally.';
static hidden = true;
static description = 'Rollout a new branch out to a channel incrementally.';
static override args = [
static args = [
{
name: 'channel',
required: true,
@@ -315,7 +315,7 @@ export default class ChannelRollout extends EasCommand {
},
];
static override flags = {
static flags = {
branch: Flags.string({
description: 'branch to rollout',
required: false,
@@ -138,9 +138,9 @@ export function logChannelDetails(channel: {
}
export default class ChannelView extends EasCommand {
static override description = 'view a channel';
static description = 'view a channel';
static override args = [
static args = [
{
name: 'name',
required: false,
@@ -148,7 +148,7 @@ export default class ChannelView extends EasCommand {
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description: 'print output as a JSON object with the channel ID, name and branch mapping.',
default: false,
+8 -13
View File
@@ -1,6 +1,6 @@
import { getProjectConfigDescription } from '@expo/config';
import { Platform } from '@expo/eas-build-job';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -13,19 +13,14 @@ import { findProjectRootAsync } from '../project/projectUtils';
import { selectAsync } from '../prompts';
export default class Config extends EasCommand {
static override description = 'display project configuration (app.json + eas.json)';
static description = 'display project configuration (app.json + eas.json)';
static override flags = {
static flags = {
platform: Flags.enum({ char: 'p', options: ['android', 'ios'] }),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
profile: Flags.string(),
};
protected override requiresAuthentication = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const { flags } = await this.parse(Config);
@@ -36,13 +31,13 @@ export default class Config extends EasCommand {
const projectDir = await findProjectRootAsync();
const accessor = new EasJsonAccessor(projectDir);
const reader = new EasJsonReader(projectDir);
const profileName =
maybeProfile ??
(await selectAsync(
'Select build profile',
(
await EasJsonUtils.getBuildProfileNamesAsync(accessor)
await reader.getBuildProfileNamesAsync()
).map(profileName => ({
title: profileName,
value: profileName,
@@ -61,7 +56,7 @@ export default class Config extends EasCommand {
},
]));
const profile = await EasJsonUtils.getBuildProfileAsync(accessor, platform, profileName);
const profile = await reader.getBuildProfileAsync(platform, profileName);
const config = getExpoConfig(projectDir, { env: profile.env, isPublicConfig: true });
Log.addNewLineIfNone();
+2 -2
View File
@@ -4,9 +4,9 @@ import EasCommand from '../commandUtils/EasCommand';
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
export default class Credentials extends EasCommand {
static override description = 'manage credentials';
static description = 'manage credentials';
static override flags = {
static flags = {
platform: Flags.enum({ char: 'p', options: ['android', 'ios'] }),
};
@@ -5,7 +5,7 @@ import DeviceManager from '../../devices/manager';
import { ensureLoggedInAsync } from '../../user/actions';
export default class DeviceCreate extends EasCommand {
static override description = 'register new Apple Devices to use for internal distribution';
static description = 'register new Apple Devices to use for internal distribution';
async runAsync(): Promise<void> {
const user = await ensureLoggedInAsync();
+40 -65
View File
@@ -2,7 +2,7 @@ import { Device, DeviceStatus } from '@expo/apple-utils';
import { Flags } from '@oclif/core';
import assert from 'assert';
import EasCommand, { CommandContext } from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import {
@@ -14,18 +14,21 @@ import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleT
import { authenticateAsync, getRequestContext } from '../../credentials/ios/appstore/authenticate';
import formatDevice from '../../devices/utils/formatDevice';
import { AppleDevice, Maybe } from '../../graphql/generated';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class DeviceDelete extends EasCommand {
static override description = 'remove a registered device from your account';
static description = 'remove a registered device from your account';
static override flags = {
static flags = {
'apple-team-id': Flags.string(),
udid: Flags.string({ multiple: true }),
};
protected async runAsync(commandContext: CommandContext): Promise<{ jsonOutput: object }> {
async runAsync(): Promise<void> {
let {
flags: { 'apple-team-id': appleTeamIdentifier, udid: udids },
} = await this.parse(DeviceDelete);
@@ -35,61 +38,46 @@ export default class DeviceDelete extends EasCommand {
const accountName = await getProjectAccountNameAsync(exp);
if (!appleTeamIdentifier) {
if (commandContext.nonInteractive) {
throw new Error('Must supply `apple-team-id` in non-interactive mode');
}
appleTeamIdentifier = await this.askForAppleTeamAsync(commandContext, accountName);
appleTeamIdentifier = await this.askForAppleTeamAsync(accountName);
}
assert(appleTeamIdentifier, 'No team identifier is specified');
const appleDevicesResult = await this.getDevicesForTeamAsync(
commandContext,
accountName,
appleTeamIdentifier
);
const appleDevicesResult = await this.getDevicesForTeamAsync(accountName, appleTeamIdentifier);
if (!appleDevicesResult) {
return { jsonOutput: [] };
return;
}
const { appleTeamName, appleDevices } = appleDevicesResult;
const chosenDevices = await this.chooseDevicesToDeleteAsync(
commandContext,
appleDevices,
udids
);
const chosenDevices = await this.chooseDevicesToDeleteAsync(appleDevices, udids);
if (chosenDevices.length === 0) {
commandContext.logger.newLine();
commandContext.logger.warn('No devices were chosen to be removed.');
return { jsonOutput: [] };
Log.newLine();
Log.warn('No devices were chosen to be removed.');
return;
}
this.logChosenDevices(commandContext, chosenDevices, appleTeamName, appleTeamIdentifier);
this.logChosenDevices(chosenDevices, appleTeamName, appleTeamIdentifier);
const hasRemoved = await this.askAndRemoveFromExpoAsync(chosenDevices);
const hasRemoved = await this.askAndRemoveFromExpoAsync(commandContext, chosenDevices);
if (!hasRemoved) {
return { jsonOutput: [] };
return;
}
await this.askAndDisableOnAppleAsync(commandContext, chosenDevices, appleTeamIdentifier);
return { jsonOutput: chosenDevices.map(it => it.id) };
await this.askAndDisableOnAppleAsync(chosenDevices, appleTeamIdentifier);
}
async askAndDisableOnAppleAsync(
{ nonInteractive, logger, prompts: { toggleConfirmAsync }, ora }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[],
appleTeamIdentifier: string
): Promise<void> {
logger.newLine();
const deleteOnApple = nonInteractive
? true
: await toggleConfirmAsync({
message: 'Do you want to disable these devices on your Apple account as well?',
});
Log.newLine();
const deleteOnApple = await toggleConfirmAsync({
message: 'Do you want to disable these devices on your Apple account as well?',
});
if (!deleteOnApple) {
return;
@@ -98,13 +86,12 @@ export default class DeviceDelete extends EasCommand {
const ctx = await authenticateAsync({ teamId: appleTeamIdentifier });
const context = getRequestContext(ctx);
logger.addNewLineIfNone();
Log.addNewLineIfNone();
const removeAppleSpinner = ora('Disabling devices on Apple').start();
try {
const chosenDeviceIdentifiers = chosenDevices.map(cd => cd.identifier);
const allDevices = await Device.getAsync(context);
const realDevices = allDevices.filter(d =>
chosenDeviceIdentifiers.includes(d.attributes.udid)
let realDevices = await Device.getAllIOSProfileDevicesAsync(context);
realDevices = realDevices.filter(d =>
chosenDevices.map(cd => cd.identifier).includes(d.attributes.udid)
);
for (const device of realDevices) {
@@ -119,21 +106,18 @@ export default class DeviceDelete extends EasCommand {
}
async askAndRemoveFromExpoAsync(
{ nonInteractive, logger, prompts: { toggleConfirmAsync }, ora }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[]
): Promise<boolean> {
logger.warn(
Log.warn(
`You are about to remove the Apple device${
chosenDevices.length > 1 ? 's' : ''
} listed above from your Expo account.`
);
logger.newLine();
Log.newLine();
const confirmed = nonInteractive
? true
: await toggleConfirmAsync({
message: 'Are you sure you wish to proceed?',
});
const confirmed = await toggleConfirmAsync({
message: 'Are you sure you wish to proceed?',
});
if (confirmed) {
const removalSpinner = ora(`Removing Apple devices on Expo`).start();
@@ -152,55 +136,49 @@ export default class DeviceDelete extends EasCommand {
}
logChosenDevices(
{ logger }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[],
appleTeamName: Maybe<string> | undefined,
appleTeamIdentifier: string
): void {
logger.addNewLineIfNone();
Log.addNewLineIfNone();
chosenDevices.forEach(device => {
logger.log(
Log.log(
formatDevice(device, {
appleTeamName,
appleTeamIdentifier: appleTeamIdentifier!,
})
);
logger.newLine();
Log.newLine();
});
}
async chooseDevicesToDeleteAsync(
{ nonInteractive, logger }: CommandContext,
appleDevices: AppleDeviceQueryResult[],
udids: string[]
): Promise<(AppleDevice | AppleDeviceQueryResult)[]> {
let chosenDevices: (AppleDeviceQueryResult | AppleDevice)[] = [];
logger.newLine();
Log.newLine();
if (udids) {
udids.forEach(udid => {
const foundDevice = appleDevices.find(device => device.identifier === udid);
if (foundDevice) {
chosenDevices.push(foundDevice);
} else {
logger.warn(`No device found with UDID ${udid}.`);
Log.warn(`No device found with UDID ${udid}.`);
}
});
}
if (chosenDevices.length === 0) {
if (nonInteractive) {
throw new Error('Must supply `udid` paramter in non-interactive mode');
}
logger.addNewLineIfNone();
Log.addNewLineIfNone();
chosenDevices = await chooseDevicesToDeleteAsync(appleDevices);
logger.newLine();
Log.newLine();
}
return chosenDevices;
}
async getDevicesForTeamAsync(
{ ora }: CommandContext,
accountName: string,
appleTeamIdentifier: string
): Promise<AppleDevicesByTeamIdentifierQueryResult | undefined> {
@@ -230,10 +208,7 @@ export default class DeviceDelete extends EasCommand {
}
}
async askForAppleTeamAsync(
{ prompts: { promptAsync }, ora }: CommandContext,
accountName: string
): Promise<string | undefined> {
async askForAppleTeamAsync(accountName: string): Promise<string | undefined> {
const teamSpinner = ora().start('Fetching the list of teams for the project…');
try {
+2 -2
View File
@@ -13,9 +13,9 @@ import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/
import { promptAsync } from '../../prompts';
export default class BuildList extends EasCommand {
static override description = 'list all registered devices for your account';
static description = 'list all registered devices for your account';
static override flags = {
static flags = {
'apple-team-id': Flags.string(),
};
+2 -2
View File
@@ -7,9 +7,9 @@ import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
export default class DeviceView extends EasCommand {
static override description = 'view a device for your project';
static description = 'view a device for your project';
static override args = [{ name: 'UDID' }];
static args = [{ name: 'UDID' }];
async runAsync(): Promise<void> {
const { UDID } = (await this.parse(DeviceView)).args;
+2 -2
View File
@@ -8,9 +8,9 @@ import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
export default class Diagnostics extends EasCommand {
static override description = 'display environment info';
static description = 'display environment info';
protected override requiresAuthentication = false;
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const info = await envinfo.run(
@@ -14,11 +14,10 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPull extends EasCommand {
static override description = 'generate the local store configuration from the app stores';
static description = 'generate the local store configuration from the app stores';
static override flags = {
static flags = {
profile: Flags.string({
char: 'e',
description:
'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
}),
@@ -12,11 +12,10 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPush extends EasCommand {
static override description = 'sync the local store configuration to the app stores';
static description = 'sync the local store configuration to the app stores';
static override flags = {
static flags = {
profile: Flags.string({
char: 'e',
description:
'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
}),
-45
View File
@@ -1,45 +0,0 @@
import openBrowserAsync from 'better-opn';
import { getProjectDashboardUrl } from '../build/utils/url';
import EasCommand from '../commandUtils/EasCommand';
import { ora } from '../ora';
import { getExpoConfig } from '../project/expoConfig';
import {
fetchProjectIdFromServerAsync,
findProjectRootAsync,
getProjectAccountName,
} from '../project/projectUtils';
import { ensureLoggedInAsync } from '../user/actions';
export default class Open extends EasCommand {
static override description = 'open the project page in a web browser';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
// this ensures the project exists
await fetchProjectIdFromServerAsync(exp);
const user = await ensureLoggedInAsync();
const accountName = getProjectAccountName(exp, user);
const projectName = exp.slug;
const projectDashboardUrl = getProjectDashboardUrl(accountName, projectName);
const failedMessage = `Unable to open a web browser. Project page is available at: ${projectDashboardUrl}`;
const spinner = ora(`Opening ${projectDashboardUrl}`).start();
try {
const opened = await openBrowserAsync(projectDashboardUrl);
if (opened) {
spinner.succeed(`Opened ${projectDashboardUrl}`);
} else {
spinner.fail(failedMessage);
}
return;
} catch (error) {
spinner.fail(failedMessage);
throw error;
}
}
}
@@ -32,7 +32,7 @@ async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
}
export default class ProjectInfo extends EasCommand {
static override description = 'information about the current project';
static description = 'information about the current project';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
@@ -10,8 +10,8 @@ import {
} from '../../project/projectUtils';
export default class ProjectInit extends EasCommand {
static override description = 'create or link an EAS project';
static override aliases = ['init'];
static description = 'create or link an EAS project';
static aliases = ['init'];
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
@@ -20,10 +20,9 @@ import { getActorDisplayName } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretCreate extends EasCommand {
static override description =
'create an environment secret on the current project or owner account';
static description = 'create an environment secret on the current project or owner account';
static override flags = {
static flags = {
scope: Flags.enum({
description: 'Scope for the secret',
options: [EnvironmentSecretScope.ACCOUNT, EnvironmentSecretScope.PROJECT],
@@ -17,9 +17,9 @@ import {
import { promptAsync, toggleConfirmAsync } from '../../prompts';
export default class EnvironmentSecretDelete extends EasCommand {
static override description = 'delete an environment secret by ID';
static description = 'delete an environment secret by ID';
static override flags = {
static flags = {
id: Flags.string({
description: 'ID of the secret to delete',
}),
@@ -60,7 +60,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
Log.addNewLineIfNone();
Log.warn(
`You are about to permanently delete secret${
`You are about to permamently delete secret${
secret?.name ? ` "${secret?.name}"` : ''
} with id: "${id}".\nThis action is irreversible.`
);
+1 -1
View File
@@ -13,7 +13,7 @@ import {
} from '../../project/projectUtils';
export default class EnvironmentSecretList extends EasCommand {
static override description = 'list environment secrets available for your current app';
static description = 'list environment secrets available for your current app';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
+5 -6
View File
@@ -1,4 +1,4 @@
import { EasJsonAccessor } from '@expo/eas-json';
import { EasJsonReader } from '@expo/eas-json';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -47,16 +47,15 @@ interface CommandFlags {
}
export default class Submit extends EasCommand {
static override description = 'submit app binary to App Store and/or Play Store';
static override aliases = ['build:submit'];
static description = 'submit app binary to App Store and/or Play Store';
static aliases = ['build:submit'];
static override flags = {
static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios', 'all'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
}),
@@ -107,7 +106,7 @@ export default class Submit extends EasCommand {
const platforms = toPlatforms(flagsWithPlatform.requestedPlatform);
const submissionProfiles = await getProfilesAsync({
type: 'submit',
easJsonAccessor: new EasJsonAccessor(projectDir),
easJsonReader: new EasJsonReader(projectDir),
platforms,
profileName: flagsWithPlatform.profile,
});
@@ -24,9 +24,9 @@ const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'sdkVersion' } as const;
const DEFAULT_BARE_RUNTIME_VERSION = '1.0.0' as const;
export default class UpdateConfigure extends EasCommand {
static override description = 'configure the project to support EAS Update';
static description = 'configure the project to support EAS Update';
static override flags = {
static flags = {
platform: Flags.enum({
description: 'Platform to configure',
char: 'p',
@@ -117,7 +117,6 @@ async function configureAppJSONForEASUpdateAsync({
exp.ios?.runtimeVersion ?? exp.runtimeVersion ?? iosDefaultRuntimeVersion;
let newConfig: Partial<ExpoConfig>;
let newConfigOnlyAddedValues: Partial<ExpoConfig>;
switch (platform) {
case RequestedPlatform.All: {
if (isRuntimeEqual(newAndroidRuntimeVersion, newIosRuntimeVersion)) {
@@ -130,26 +129,6 @@ async function configureAppJSONForEASUpdateAsync({
ios: { ...exp.ios, runtimeVersion: undefined },
updates,
};
newConfigOnlyAddedValues = {
runtimeVersion: newAndroidRuntimeVersion,
...(exp.android && 'runtimeVersion' in exp.android
? {
android: {
runtimeVersion: '<remove this key>',
},
}
: {}),
...(exp.ios && 'runtimeVersion' in exp.ios
? {
ios: {
runtimeVersion: '<remove this key>',
},
}
: {}),
updates: {
url: easUpdateURL,
},
};
} else {
newConfig = {
runtimeVersion: undefined, // top level runtime is redundant if it is specified in both android and ios
@@ -163,22 +142,6 @@ async function configureAppJSONForEASUpdateAsync({
},
updates,
};
newConfigOnlyAddedValues = {
...('runtimeVersion' in exp
? {
runtimeVersion: '<remove this key>', // top level runtime is redundant if it is specified in both android and ios
}
: {}),
android: {
runtimeVersion: newAndroidRuntimeVersion,
},
ios: {
runtimeVersion: newIosRuntimeVersion,
},
updates: {
url: easUpdateURL,
},
};
}
break;
}
@@ -190,14 +153,6 @@ async function configureAppJSONForEASUpdateAsync({
},
updates,
};
newConfigOnlyAddedValues = {
android: {
runtimeVersion: newAndroidRuntimeVersion,
},
updates: {
url: easUpdateURL,
},
};
break;
}
case RequestedPlatform.Ios: {
@@ -208,14 +163,6 @@ async function configureAppJSONForEASUpdateAsync({
},
updates,
};
newConfigOnlyAddedValues = {
ios: {
runtimeVersion: newIosRuntimeVersion,
},
updates: {
url: easUpdateURL,
},
};
break;
}
default: {
@@ -225,8 +172,8 @@ async function configureAppJSONForEASUpdateAsync({
const result = await modifyConfigAsync(projectDir, newConfig);
const preexistingAndroidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
const preexistingIosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
const prexistingAndroidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
const prexistingIosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
switch (result.type) {
case 'success':
if (exp.updates?.url) {
@@ -240,7 +187,7 @@ async function configureAppJSONForEASUpdateAsync({
}
if (
!preexistingAndroidRuntimeVersion &&
!prexistingAndroidRuntimeVersion &&
[RequestedPlatform.Android, RequestedPlatform.All].includes(platform)
) {
Log.withTick(
@@ -250,7 +197,7 @@ async function configureAppJSONForEASUpdateAsync({
);
}
if (
!preexistingIosRuntimeVersion &&
!prexistingIosRuntimeVersion &&
[RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)
) {
Log.withTick(
@@ -272,7 +219,7 @@ async function configureAppJSONForEASUpdateAsync({
'https://expo.fyi/eas-update-config.md'
)}\n`
);
Log.log(chalk.bold(JSON.stringify(newConfigOnlyAddedValues, null, 2)));
Log.log(chalk.bold(JSON.stringify(newConfig, null, 2)));
Log.addNewLineIfNone();
if (workflows['android'] === Workflow.GENERIC || workflows['ios'] === Workflow.GENERIC) {
Log.warn(
@@ -36,9 +36,9 @@ async function deleteUpdateGroupAsync({
}
export default class UpdateDelete extends EasCommand {
static override description = 'delete all the updates in an update group';
static description = 'delete all the updates in an update group';
static override args = [
static args = [
{
name: 'groupId',
required: true,
@@ -46,7 +46,7 @@ export default class UpdateDelete extends EasCommand {
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description: `Return a json with the group ID of the deleted updates.`,
default: false,
@@ -69,7 +69,7 @@ export default class UpdateDelete extends EasCommand {
'This is a permanent operation.'
)}\n\n` +
`If you want to revert to a previous publish, you should use 'update --republish' targeted at the last working update group instead.\n\n` +
`An update group should only be deleted in an emergency like an accidental publish of a secret. In this case user 'update --republish' to revert to the last working update group first and then proceed with the deletion. Deleting an update group when it is the latest publish can lead to inconsistent caching behavior by clients.\n\n` +
`An update group should only be deleted in an emergency like an accidental publish of a secret. In this case user 'update --republish' to revert to the last working update group first and then proceed with the deletion. Deleting an update group when it is the latest publish can lead to inconsistent cacheing behavior by clients.\n\n` +
`Would you like to abort?`,
});
@@ -161,9 +161,9 @@ export async function ensureBranchExistsAsync({
}
export default class UpdatePublish extends EasCommand {
static override description = 'publish an update group';
static description = 'publish an update group';
static override flags = {
static flags = {
branch: Flags.string({
description: 'Branch to publish the update group on',
required: false,
+2 -2
View File
@@ -20,9 +20,9 @@ import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { getVcsClient } from '../../vcs';
export default class BranchView extends EasCommand {
static override description = 'view the recent updates for a branch';
static description = 'view the recent updates for a branch';
static override flags = {
static flags = {
branch: Flags.string({
description: 'List all updates on this branch',
exclusive: ['all'],
+3 -3
View File
@@ -51,9 +51,9 @@ export async function viewUpdateAsync({
return data;
}
export default class UpdateView extends EasCommand {
static override description = 'update group details';
static description = 'update group details';
static override args = [
static args = [
{
name: 'groupId',
required: true,
@@ -61,7 +61,7 @@ export default class UpdateView extends EasCommand {
},
];
static override flags = {
static flags = {
json: Flags.boolean({
description: `Return a json with the updates belonging to the group.`,
default: false,
@@ -9,9 +9,9 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookCreate extends EasCommand {
static override description = 'create a webhook';
static description = 'create a webhook';
static override flags = {
static flags = {
event: Flags.enum({
description: 'Event type that triggers the webhook',
options: [WebhookType.Build, WebhookType.Submit],
@@ -14,9 +14,9 @@ import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookDelete extends EasCommand {
static override description = 'delete a webhook';
static description = 'delete a webhook';
static override args = [
static args = [
{
name: 'ID',
required: false,
@@ -57,7 +57,7 @@ export default class WebhookDelete extends EasCommand {
Log.addNewLineIfNone();
Log.log(formatWebhook(webhook));
Log.newLine();
Log.warn(`You are about to permanently delete this webhook.\nThis action is irreversible.`);
Log.warn(`You are about to permamently delete this webhook.\nThis action is irreversible.`);
Log.newLine();
const confirmed = await toggleConfirmAsync({
message: 'Are you sure you wish to proceed?',
@@ -15,9 +15,9 @@ import {
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookList extends EasCommand {
static override description = 'list webhooks';
static description = 'list webhooks';
static override flags = {
static flags = {
event: Flags.enum({
description: 'Event type that triggers the webhook',
options: [WebhookType.Build, WebhookType.Submit],
@@ -9,9 +9,9 @@ import pick from '../../utils/expodash/pick';
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookUpdate extends EasCommand {
static override description = 'update a webhook';
static description = 'update a webhook';
static override flags = {
static flags = {
id: Flags.string({
description: 'Webhook ID',
required: true,
@@ -5,9 +5,9 @@ import { ora } from '../../ora';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookView extends EasCommand {
static override description = 'view a webhook';
static description = 'view a webhook';
static override args = [
static args = [
{
name: 'ID',
required: true,
@@ -76,13 +76,9 @@ export const testPushKey: ApplePushKeyFragment = {
iosAppCredentialsList: [],
};
export const testTarget = {
targetName: 'testapp',
bundleIdentifier: 'foo.bar.com',
entitlements: {},
};
export const testTargets: Target[] = [testTarget];
export const testTargets: Target[] = [
{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com', entitlements: {} },
];
export const testProvisioningProfileFragment: AppleProvisioningProfileFragment = {
id: 'test-prov-prof-id-1',
@@ -2,9 +2,10 @@ import { Platform } from '@expo/eas-build-job';
import {
CredentialsSource,
DistributionType,
EasJsonAccessor,
EasJsonReader,
IosEnterpriseProvisioning,
} from '@expo/eas-json';
import fs from 'fs-extra';
import { CommonIosAppCredentialsFragment } from '../../graphql/generated';
import Log from '../../log';
@@ -100,7 +101,7 @@ export default class IosCredentialsProvider {
return null;
}
if (ctx.easJsonCliConfig?.promptToConfigurePushNotifications === false) {
if (ctx.easJsonCliConfig?.promptToConfigurePushNotfications === false) {
return null;
}
@@ -128,16 +129,10 @@ export default class IosCredentialsProvider {
private async disablePushNotificationsSetupInEasJsonAsync(
ctx: CredentialsContext
): Promise<void> {
const easJsonAccessor = new EasJsonAccessor(ctx.projectDir);
await easJsonAccessor.readRawJsonAsync();
easJsonAccessor.patch(easJsonRawObject => {
easJsonRawObject.cli = {
...easJsonRawObject?.cli,
promptToConfigurePushNotifications: false,
};
return easJsonRawObject;
});
await easJsonAccessor.writeAsync();
const easJsonPath = EasJsonReader.formatEasJsonPath(ctx.projectDir);
const easJson = await fs.readJSON(easJsonPath);
easJson.cli = { ...easJson?.cli, promptToConfigurePushNotfications: false };
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
Log.withTick('Updated eas.json');
}
@@ -17,7 +17,6 @@ jest.mock('fs');
jest.mock('../validators/validateProvisioningProfile', () => ({
validateProvisioningProfileAsync: async (
_ctx: any,
_target: any,
_app: any,
buildCredentials: Partial<IosAppBuildCredentialsFragment> | null
): Promise<boolean> => {
@@ -7,19 +7,16 @@ import {
} from '../../../graphql/generated';
import Log from '../../../log';
import { ora } from '../../../ora';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations/AppleProvisioningProfileMutation';
import { ProvisioningProfileStoreInfo } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticateTypes';
import { Target } from '../types';
export class ConfigureProvisioningProfile {
constructor(
private app: AppLookupParams,
private target: Target,
private distributionCertificate: AppleDistributionCertificateFragment,
private originalProvisioningProfile: AppleProvisioningProfileFragment
) {}
@@ -46,10 +43,8 @@ export class ConfigureProvisioningProfile {
return null;
}
const applePlatform = await getApplePlatformFromSdkRoot(this.target);
const profilesFromApple = await ctx.appStore.listProvisioningProfilesAsync(
this.app.bundleIdentifier,
applePlatform
this.app.bundleIdentifier
);
const [matchingProfile] = profilesFromApple.filter(appleInfo =>
developerPortalIdentifier
@@ -11,14 +11,12 @@ import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations
import { ProvisioningProfile } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticateTypes';
import { provisioningProfileSchema } from '../credentials';
import { Target } from '../types';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { generateProvisioningProfileAsync } from './ProvisioningProfileUtils';
export class CreateProvisioningProfile {
constructor(
private app: AppLookupParams,
private target: Target,
private distributionCertificate: AppleDistributionCertificateFragment
) {}
@@ -65,7 +63,7 @@ export class CreateProvisioningProfile {
this.distributionCertificate.certificatePassword,
'Distribution Certificate must have certificate password'
);
return await generateProvisioningProfileAsync(ctx, this.target, this.app.bundleIdentifier, {
return await generateProvisioningProfileAsync(ctx, this.app.bundleIdentifier, {
certId: this.distributionCertificate.developerPortalIdentifier ?? undefined,
certP12: this.distributionCertificate.certificateP12,
certPassword: this.distributionCertificate.certificatePassword,
@@ -1,13 +1,11 @@
import chalk from 'chalk';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { CredentialsContext } from '../../context';
import {
DistributionCertificate,
ProvisioningProfile,
ProvisioningProfileStoreInfo,
} from '../appstore/Credentials.types';
import { Target } from '../types';
export function formatProvisioningProfileFromApple(
appleInfo: ProvisioningProfileStoreInfo
@@ -22,18 +20,11 @@ export function formatProvisioningProfileFromApple(
export async function generateProvisioningProfileAsync(
ctx: CredentialsContext,
target: Target,
bundleIdentifier: string,
distCert: DistributionCertificate
): Promise<ProvisioningProfile> {
const appleAuthCtx = await ctx.appStore.ensureAuthenticatedAsync();
const type = appleAuthCtx.team.inHouse ? 'Enterprise ' : 'AppStore';
const profileName = `*[expo] ${bundleIdentifier} ${type} ${new Date().toISOString()}`; // Apple drops [ if its the first char (!!)
const applePlatform = await getApplePlatformFromSdkRoot(target);
return await ctx.appStore.createProvisioningProfileAsync(
bundleIdentifier,
distCert,
profileName,
applePlatform
);
return await ctx.appStore.createProvisioningProfileAsync(bundleIdentifier, distCert, profileName);
}
@@ -1,4 +1,3 @@
import { ProfileType } from '@expo/apple-utils';
import assert from 'assert';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
@@ -13,14 +12,11 @@ import {
IosDistributionType,
} from '../../../graphql/generated';
import Log from '../../../log';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { confirmAsync, pressAnyKeyToContinueAsync, promptAsync } from '../../../prompts';
import differenceBy from '../../../utils/expodash/differenceBy';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { ApplePlatform } from '../appstore/constants';
import { Target } from '../types';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { assignBuildCredentialsAsync, getBuildCredentialsAsync } from './BuildCredentialsUtils';
@@ -33,18 +29,12 @@ enum ReuseAction {
No,
}
interface Options {
app: AppLookupParams;
target: Target;
}
export class SetUpAdhocProvisioningProfile {
constructor(private options: Options) {}
constructor(private app: AppLookupParams) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const { app } = this.options;
const distCert = await new SetUpDistributionCertificate(
app,
this.app,
IosDistributionType.AdHoc
).runAsync(ctx);
@@ -52,7 +42,7 @@ export class SetUpAdhocProvisioningProfile {
if (ctx.nonInteractive) {
if (areBuildCredentialsSetup) {
return nullthrows(await getBuildCredentialsAsync(ctx, app, IosDistributionType.AdHoc));
return nullthrows(await getBuildCredentialsAsync(ctx, this.app, IosDistributionType.AdHoc));
} else {
throw new MissingCredentialsNonInteractiveError(
'Provisioning profile is not configured correctly. Run this command again in interactive mode.'
@@ -62,7 +52,7 @@ export class SetUpAdhocProvisioningProfile {
const currentBuildCredentials = await getBuildCredentialsAsync(
ctx,
app,
this.app,
IosDistributionType.AdHoc
);
if (areBuildCredentialsSetup) {
@@ -79,11 +69,9 @@ export class SetUpAdhocProvisioningProfile {
ctx: CredentialsContext,
distCert: AppleDistributionCertificateFragment
): Promise<IosAppBuildCredentialsFragment> {
const { app, target } = this.options;
const currentBuildCredentials = await getBuildCredentialsAsync(
ctx,
app,
this.app,
IosDistributionType.AdHoc
);
@@ -92,12 +80,12 @@ export class SetUpAdhocProvisioningProfile {
distCert.appleTeam ?? currentBuildCredentials?.provisioningProfile?.appleTeam ?? null;
if (!appleTeam) {
await ctx.appStore.ensureAuthenticatedAsync();
appleTeam = await resolveAppleTeamIfAuthenticatedAsync(ctx, app);
appleTeam = await resolveAppleTeamIfAuthenticatedAsync(ctx, this.app);
}
assert(appleTeam, 'Apple Team must be defined here');
// 2. Fetch devices registered on EAS servers
let registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(app, appleTeam);
let registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(this.app, appleTeam);
if (registeredAppleDevices.length === 0) {
const shouldRegisterDevices = await confirmAsync({
message: `You don't have any registered devices yet. Would you like to register them now?`,
@@ -121,22 +109,16 @@ export class SetUpAdhocProvisioningProfile {
);
// 4. Reuse or create the profile on Apple Developer Portal
const applePlatform = await getApplePlatformFromSdkRoot(target);
const profileType =
applePlatform === ApplePlatform.TV_OS
? ProfileType.TVOS_APP_ADHOC
: ProfileType.IOS_APP_ADHOC;
const provisioningProfileStoreInfo =
await ctx.appStore.createOrReuseAdhocProvisioningProfileAsync(
chosenDevices.map(({ identifier }) => identifier),
app.bundleIdentifier,
distCert.serialNumber,
profileType
this.app.bundleIdentifier,
distCert.serialNumber
);
// 5. Create or update the profile on servers
const appleAppIdentifier = await ctx.ios.createOrGetExistingAppleAppIdentifierAsync(
app,
this.app,
appleTeam
);
let appleProvisioningProfile: AppleProvisioningProfileFragment | null = null;
@@ -149,7 +131,7 @@ export class SetUpAdhocProvisioningProfile {
currentBuildCredentials.provisioningProfile.id,
]);
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
app,
this.app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
@@ -161,7 +143,7 @@ export class SetUpAdhocProvisioningProfile {
}
} else {
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
app,
this.app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
@@ -174,7 +156,7 @@ export class SetUpAdhocProvisioningProfile {
assert(appleProvisioningProfile);
return await assignBuildCredentialsAsync(
ctx,
app,
this.app,
IosDistributionType.AdHoc,
distCert,
appleProvisioningProfile,
@@ -183,20 +165,22 @@ export class SetUpAdhocProvisioningProfile {
}
private async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
const { app, target } = this.options;
const buildCredentials = await getBuildCredentialsAsync(ctx, app, IosDistributionType.AdHoc);
return await validateProvisioningProfileAsync(ctx, target, app, buildCredentials);
const buildCredentials = await getBuildCredentialsAsync(
ctx,
this.app,
IosDistributionType.AdHoc
);
return await validateProvisioningProfileAsync(ctx, this.app, buildCredentials);
}
private async shouldUseExistingProfileAsync(
ctx: CredentialsContext,
buildCredentials: IosAppBuildCredentialsFragment
): Promise<boolean> {
const { app } = this.options;
const provisioningProfile = nullthrows(buildCredentials.provisioningProfile);
const appleTeam = nullthrows(provisioningProfile.appleTeam);
const registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(app, appleTeam);
const registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(this.app, appleTeam);
const provisionedDevices = provisioningProfile.appleDevices;
@@ -270,8 +254,7 @@ export class SetUpAdhocProvisioningProfile {
ctx: CredentialsContext,
appleTeam: AppleTeamFragment
): Promise<AppleDeviceFragment[]> {
const { app } = this.options;
const action = new DeviceCreateAction(ctx.appStore, app.account, appleTeam);
const action = new DeviceCreateAction(ctx.appStore, this.app.account, appleTeam);
const method = await action.runAsync();
while (true) {
@@ -282,7 +265,7 @@ export class SetUpAdhocProvisioningProfile {
}
Log.newLine();
const devices = await ctx.ios.getDevicesForAppleTeamAsync(app, appleTeam, {
const devices = await ctx.ios.getDevicesForAppleTeamAsync(this.app, appleTeam, {
useCache: false,
});
if (devices.length === 0) {
@@ -51,7 +51,6 @@ export class SetUpBuildCredentials {
enterpriseProvisioning: this.options.enterpriseProvisioning,
distribution: this.options.distribution,
entitlements: target.entitlements,
target,
app: {
...this.options.app,
bundleIdentifier: target.bundleIdentifier,
@@ -3,7 +3,6 @@ import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/GraphqlClient';
import { Target } from '../types';
import { getAllBuildCredentialsAsync } from './BuildCredentialsUtils';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
@@ -15,17 +14,11 @@ import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
* to choose if they want to set up an adhoc or universal distribution provisioning profile. Otherwise, always
* set up an adhoc provisioning profile.
*/
interface Options {
app: AppLookupParams;
target: Target;
}
export class SetUpInternalProvisioningProfile {
constructor(private options: Options) {}
constructor(private app: AppLookupParams) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const buildCredentials = await getAllBuildCredentialsAsync(ctx, this.options.app);
const buildCredentials = await getAllBuildCredentialsAsync(ctx, this.app);
const adhocBuildCredentialsExist =
buildCredentials.filter(
@@ -94,18 +87,15 @@ export class SetUpInternalProvisioningProfile {
private async setupAdhocProvisioningProfileAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
const { app, target } = this.options;
return await new SetUpAdhocProvisioningProfile({ app, target }).runAsync(ctx);
return await new SetUpAdhocProvisioningProfile(this.app).runAsync(ctx);
}
private async setupUniversalProvisioningProfileAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
return await new SetUpProvisioningProfile(
this.options.app,
this.options.target,
IosDistributionType.Enterprise
).runAsync(ctx);
return await new SetUpProvisioningProfile(this.app, IosDistributionType.Enterprise).runAsync(
ctx
);
}
private async askForDistributionTypeAndSetupAsync(
@@ -6,13 +6,11 @@ import {
IosAppBuildCredentialsFragment,
IosDistributionType,
} from '../../../graphql/generated';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { confirmAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { ProvisioningProfileStoreInfo } from '../appstore/Credentials.types';
import { Target } from '../types';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
import {
assignBuildCredentialsAsync,
@@ -28,15 +26,11 @@ import { SetUpDistributionCertificate } from './SetUpDistributionCertificate';
* Sets up either APP_STORE or ENTERPRISE provisioning profiles
*/
export class SetUpProvisioningProfile {
constructor(
private app: AppLookupParams,
private target: Target,
private distributionType: IosDistributionType
) {}
constructor(private app: AppLookupParams, private distributionType: IosDistributionType) {}
async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
const buildCredentials = await getBuildCredentialsAsync(ctx, this.app, this.distributionType);
return await validateProvisioningProfileAsync(ctx, this.target, this.app, buildCredentials);
return await validateProvisioningProfileAsync(ctx, this.app, buildCredentials);
}
async assignNewAndDeleteOldProfileAsync(
@@ -54,11 +48,9 @@ export class SetUpProvisioningProfile {
ctx: CredentialsContext,
distCert: AppleDistributionCertificateFragment
): Promise<IosAppBuildCredentialsFragment> {
const provisioningProfile = await new CreateProvisioningProfile(
this.app,
this.target,
distCert
).runAsync(ctx);
const provisioningProfile = await new CreateProvisioningProfile(this.app, distCert).runAsync(
ctx
);
return await assignBuildCredentialsAsync(
ctx,
this.app,
@@ -75,7 +67,6 @@ export class SetUpProvisioningProfile {
): Promise<IosAppBuildCredentialsFragment | null> {
const profileConfigurator = new ConfigureProvisioningProfile(
this.app,
this.target,
distCert,
originalProvisioningProfile
);
@@ -114,10 +105,8 @@ export class SetUpProvisioningProfile {
}
// See if the profile we have exists on the Apple Servers
const applePlatform = await getApplePlatformFromSdkRoot(this.target);
const existingProfiles = await ctx.appStore.listProvisioningProfilesAsync(
this.app.bundleIdentifier,
applePlatform
this.app.bundleIdentifier
);
const currentProfileFromServer = this.getCurrentProfileStoreInfo(
existingProfiles,
@@ -8,7 +8,6 @@ import {
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams as GraphQLAppLookupParams } from '../api/GraphqlClient';
import { Target } from '../types';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
import { SetUpInternalProvisioningProfile } from './SetUpInternalProvisioningProfile';
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
@@ -18,7 +17,6 @@ interface Options {
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
entitlements: JSONObject;
target: Target;
}
export class SetUpTargetBuildCredentials {
constructor(private options: Options) {}
@@ -49,25 +47,22 @@ export class SetUpTargetBuildCredentials {
async setupBuildCredentialsAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
const { app, distribution, enterpriseProvisioning, target } = this.options;
const { app, distribution, enterpriseProvisioning } = this.options;
if (distribution === 'internal') {
if (enterpriseProvisioning === 'adhoc') {
return await new SetUpAdhocProvisioningProfile({ app, target }).runAsync(ctx);
return await new SetUpAdhocProvisioningProfile(app).runAsync(ctx);
} else if (enterpriseProvisioning === 'universal') {
return await new SetUpProvisioningProfile(
app,
target,
GraphQLIosDistributionType.Enterprise
).runAsync(ctx);
} else {
return await new SetUpInternalProvisioningProfile({ app, target }).runAsync(ctx);
return await new SetUpInternalProvisioningProfile(app).runAsync(ctx);
}
} else {
return await new SetUpProvisioningProfile(
app,
target,
GraphQLIosDistributionType.AppStore
).runAsync(ctx);
return await new SetUpProvisioningProfile(app, GraphQLIosDistributionType.AppStore).runAsync(
ctx
);
}
}
}
@@ -6,7 +6,6 @@ import {
testDistCertFragmentNoDependencies,
testProvisioningProfile,
testProvisioningProfileFragment,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -37,7 +36,6 @@ describe('ConfigureProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const provProfConfigurator = new ConfigureProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies,
testProvisioningProfileFragment
);
@@ -55,7 +53,6 @@ describe('ConfigureProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const provProfConfigurator = new ConfigureProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies,
testProvisioningProfileFragment
);
@@ -5,7 +5,6 @@ import { createCtxMock } from '../../../__tests__/fixtures-context';
import {
testDistCertFragmentNoDependencies,
testProvisioningProfile,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -29,7 +28,6 @@ describe('CreateProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const createProvProfAction = new CreateProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies
);
await createProvProfAction.runAsync(ctx);
@@ -46,7 +44,6 @@ describe('CreateProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const createProvProfAction = new CreateProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies
);
await expect(createProvProfAction.runAsync(ctx)).rejects.toThrowError(
@@ -2,7 +2,6 @@ import { IosAppBuildCredentialsFragment, IosDistributionType } from '../../../..
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testTarget } from '../../../__tests__/fixtures-ios';
import { getAllBuildCredentialsAsync } from '../BuildCredentialsUtils';
import { SetUpAdhocProvisioningProfile } from '../SetUpAdhocProvisioningProfile';
import { SetUpInternalProvisioningProfile } from '../SetUpInternalProvisioningProfile';
@@ -42,12 +41,9 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -76,12 +72,9 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -117,12 +110,9 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -151,12 +141,9 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -187,12 +174,9 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -221,12 +205,9 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -254,12 +235,9 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -275,12 +253,9 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -294,12 +269,9 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -319,12 +291,9 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -10,7 +10,6 @@ import {
testAppleAppIdentifierFragment,
testCommonIosAppCredentialsFragment,
testIosAppBuildCredentialsFragment,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -56,7 +55,6 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -89,7 +87,6 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -122,7 +119,6 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -151,7 +147,6 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -171,7 +166,6 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await expect(setupProvisioningProfileAction.runAsync(ctx)).rejects.toThrowError(
@@ -1,5 +1,3 @@
import { ProfileType } from '@expo/app-store';
import Log from '../../../log';
import {
AscApiKey,
@@ -24,7 +22,6 @@ import {
isUserAuthCtx,
} from './authenticate';
import { AuthCtx, AuthenticationMode, UserAuthCtx } from './authenticateTypes';
import { ApplePlatform } from './constants';
import {
createDistributionCertificateAsync,
listDistributionCertificatesAsync,
@@ -132,18 +129,16 @@ export default class AppStoreApi {
public async listProvisioningProfilesAsync(
bundleIdentifier: string,
applePlatform: ApplePlatform,
profileClass?: ProfileClass
): Promise<ProvisioningProfileStoreInfo[]> {
const ctx = await this.ensureAuthenticatedAsync();
return await listProvisioningProfilesAsync(ctx, bundleIdentifier, applePlatform, profileClass);
return await listProvisioningProfilesAsync(ctx, bundleIdentifier, profileClass);
}
public async createProvisioningProfileAsync(
bundleIdentifier: string,
distCert: DistributionCertificate,
profileName: string,
applePlatform: ApplePlatform,
profileClass?: ProfileClass
): Promise<ProvisioningProfile> {
const ctx = await this.ensureAuthenticatedAsync();
@@ -152,33 +147,29 @@ export default class AppStoreApi {
bundleIdentifier,
distCert,
profileName,
applePlatform,
profileClass
);
}
public async revokeProvisioningProfileAsync(
bundleIdentifier: string,
applePlatform: ApplePlatform,
profileClass?: ProfileClass
): Promise<void> {
const ctx = await this.ensureAuthenticatedAsync();
return await revokeProvisioningProfileAsync(ctx, bundleIdentifier, applePlatform, profileClass);
return await revokeProvisioningProfileAsync(ctx, bundleIdentifier, profileClass);
}
public async createOrReuseAdhocProvisioningProfileAsync(
udids: string[],
bundleIdentifier: string,
distCertSerialNumber: string,
profileType: ProfileType
distCertSerialNumber: string
): Promise<ProvisioningProfile> {
const ctx = await this.ensureAuthenticatedAsync();
return await createOrReuseAdhocProvisioningProfileAsync(
ctx,
udids,
bundleIdentifier,
distCertSerialNumber,
profileType
distCertSerialNumber
);
}
@@ -1,5 +1,21 @@
import { UserRole } from '@expo/apple-utils';
export interface Device {
id: string;
teamId: string;
identifier: string;
name?: string;
model?: string;
deviceClass?: DeviceClass;
softwareVersion?: string;
enabled: boolean;
}
export enum DeviceClass {
IPHONE = 'iphone',
IPAD = 'ipad',
}
export interface DistributionCertificateStoreInfo {
id: string;
name: string;
@@ -1,2 +0,0 @@
// Renamed to avoid conflation with Android/iOS/Web Platform constants
export { Platform as ApplePlatform } from '@expo/apple-utils';
@@ -33,7 +33,7 @@ export async function getDistributionCertificateAsync(
const certificates = await Certificate.getAsync(context, {
query: {
filter: {
certificateType: [CertificateType.IOS_DISTRIBUTION, CertificateType.DISTRIBUTION],
certificateType: CertificateType.IOS_DISTRIBUTION,
},
},
});
@@ -11,7 +11,6 @@ import {
import { getRequestContext } from './authenticate';
import { AuthCtx } from './authenticateTypes';
import { getBundleIdForIdentifierAsync, getProfilesForBundleIdAsync } from './bundleId';
import { ApplePlatform } from './constants';
import { getCertificateBySerialNumberAsync, transformCertificate } from './distributionCertificate';
export enum ProfileClass {
@@ -19,37 +18,7 @@ export enum ProfileClass {
General = 'general',
}
function resolveProfileType(
applePlatform: ApplePlatform,
profileClass: ProfileClass,
isEnterprise?: boolean
): ProfileType {
switch (applePlatform) {
case ApplePlatform.IOS:
return resolveProfileTypeIos(profileClass, isEnterprise);
case ApplePlatform.TV_OS:
return resolveProfileTypeAppleTv(profileClass, isEnterprise);
case ApplePlatform.MAC_OS:
throw new Error(`${applePlatform} profiles are not supported`);
}
}
function resolveProfileTypeAppleTv(
profileClass: ProfileClass,
isEnterprise?: boolean
): ProfileType {
if (isEnterprise) {
return profileClass === ProfileClass.Adhoc
? ProfileType.TVOS_APP_ADHOC
: ProfileType.TVOS_APP_INHOUSE;
} else {
return profileClass === ProfileClass.Adhoc
? ProfileType.TVOS_APP_ADHOC
: ProfileType.TVOS_APP_STORE;
}
}
function resolveProfileTypeIos(profileClass: ProfileClass, isEnterprise?: boolean): ProfileType {
function resolveProfileType(profileClass: ProfileClass, isEnterprise?: boolean): ProfileType {
if (isEnterprise) {
return profileClass === ProfileClass.Adhoc
? ProfileType.IOS_APP_ADHOC
@@ -172,13 +141,12 @@ export async function useExistingProvisioningProfileAsync(
export async function listProvisioningProfilesAsync(
authCtx: AuthCtx,
bundleIdentifier: string,
applePlatform: ApplePlatform,
profileClass: ProfileClass = ProfileClass.General
): Promise<ProvisioningProfileStoreInfo[]> {
const spinner = ora(`Fetching Apple provisioning profiles`).start();
try {
const context = getRequestContext(authCtx);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(profileClass, authCtx.team.inHouse);
const profiles = (await getProfilesForBundleIdAsync(context, bundleIdentifier)).filter(
profile => profile.attributes.profileType === profileType
);
@@ -200,7 +168,6 @@ export async function createProvisioningProfileAsync(
bundleIdentifier: string,
distCert: DistributionCertificate,
profileName: string,
applePlatform: ApplePlatform,
profileClass: ProfileClass = ProfileClass.General
): Promise<ProvisioningProfile> {
const spinner = ora(`Creating Apple provisioning profile`).start();
@@ -213,7 +180,7 @@ export async function createProvisioningProfileAsync(
}
const context = getRequestContext(authCtx);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(profileClass, authCtx.team.inHouse);
const certificate = await getCertificateBySerialNumberAsync(
context,
@@ -243,7 +210,6 @@ export async function createProvisioningProfileAsync(
export async function revokeProvisioningProfileAsync(
authCtx: AuthCtx,
bundleIdentifier: string,
applePlatform: ApplePlatform,
profileClass: ProfileClass = ProfileClass.General
): Promise<void> {
const spinner = ora(`Revoking Apple provisioning profile`).start();
@@ -251,7 +217,7 @@ export async function revokeProvisioningProfileAsync(
const context = getRequestContext(authCtx);
const profiles = await getProfilesForBundleIdAsync(context, bundleIdentifier);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(profileClass, authCtx.team.inHouse);
await Promise.all(
profiles
.filter(profile => profile.attributes.profileType === profileType)
@@ -25,8 +25,10 @@ async function registerMissingDevicesAsync(
context: RequestContext,
udids: string[]
): Promise<Device[]> {
const allDevices = await Device.getAsync(context);
const alreadyAdded = allDevices.filter(device => udids.includes(device.attributes.udid));
const allIosProfileDevices = await Device.getAllIOSProfileDevicesAsync(context);
const alreadyAdded = allIosProfileDevices.filter(device =>
udids.includes(device.attributes.udid)
);
const alreadyAddedUdids = alreadyAdded.map(i => i.attributes.udid);
await Promise.all(
@@ -44,20 +46,17 @@ async function registerMissingDevicesAsync(
return alreadyAdded;
}
async function findProfileAsync(
async function findProfileByBundleIdAsync(
context: RequestContext,
{
bundleId,
certSerialNumber,
profileType,
}: { bundleId: string; certSerialNumber: string; profileType: ProfileType }
bundleId: string,
certSerialNumber: string
): Promise<{
profile: Profile | null;
didUpdate: boolean;
}> {
const expoProfiles = (await getProfilesForBundleIdAsync(context, bundleId)).filter(profile => {
return (
profile.attributes.profileType === profileType &&
profile.attributes.profileType === ProfileType.IOS_APP_ADHOC &&
profile.attributes.name.startsWith('*[expo]') &&
profile.attributes.profileState !== ProfileState.EXPIRED
);
@@ -133,13 +132,11 @@ async function manageAdHocProfilesAsync(
bundleId,
certSerialNumber,
profileId,
profileType,
}: {
udids: string[];
bundleId: string;
certSerialNumber: string;
profileId?: string;
profileType: ProfileType;
}
): Promise<ProfileResults> {
// We register all missing devices on the Apple Developer Portal. They are identified by UDIDs.
@@ -158,7 +155,7 @@ async function manageAdHocProfilesAsync(
}
} else {
// If no profile id is passed, try to find a suitable provisioning profile for the App ID.
const results = await findProfileAsync(context, { bundleId, certSerialNumber, profileType });
const results = await findProfileByBundleIdAsync(context, bundleId, certSerialNumber);
existingProfile = results.profile;
didUpdate = results.didUpdate;
}
@@ -195,9 +192,8 @@ async function manageAdHocProfilesAsync(
await existingProfile.regenerateAsync();
}
const updatedProfile = (
await findProfileAsync(context, { bundleId, certSerialNumber, profileType })
).profile;
const updatedProfile = (await findProfileByBundleIdAsync(context, bundleId, certSerialNumber))
.profile;
if (!updatedProfile) {
throw new Error(
`Failed to locate updated profile for bundle identifier "${bundleId}" and serial number "${certSerialNumber}"`
@@ -230,7 +226,7 @@ async function manageAdHocProfilesAsync(
name: `*[expo] ${bundleId} AdHoc ${Date.now()}`,
certificates: [distributionCertificate.id],
devices: devices.map(device => device.id),
profileType,
profileType: ProfileType.IOS_APP_ADHOC,
});
return {
@@ -246,8 +242,7 @@ export async function createOrReuseAdhocProvisioningProfileAsync(
authCtx: AuthCtx,
udids: string[],
bundleIdentifier: string,
distCertSerialNumber: string,
profileType: ProfileType
distCertSerialNumber: string
): Promise<ProvisioningProfile> {
const spinner = ora(`Handling Apple ad hoc provisioning profiles`).start();
try {
@@ -257,7 +252,6 @@ export async function createOrReuseAdhocProvisioningProfileAsync(
udids,
bundleId: bundleIdentifier,
certSerialNumber: distCertSerialNumber,
profileType,
});
if (didCreate) {
@@ -6,17 +6,14 @@ import nullthrows from 'nullthrows';
import { IosAppBuildCredentialsFragment, IosDistributionType } from '../../../graphql/generated';
import Log from '../../../log';
import { getApplePlatformFromSdkRoot } from '../../../project/ios/target';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/GraphqlClient';
import { ProfileClass } from '../appstore/provisioningProfile';
import { Target } from '../types';
import { getP12CertFingerprint } from '../utils/p12Certificate';
import { parse as parseProvisioningProfile } from '../utils/provisioningProfile';
export async function validateProvisioningProfileAsync(
ctx: CredentialsContext,
target: Target,
app: AppLookupParams,
buildCredentials: Partial<IosAppBuildCredentialsFragment> | null
): Promise<boolean> {
@@ -40,7 +37,7 @@ export async function validateProvisioningProfileAsync(
return true;
}
return await validateProvisioningProfileWithAppleAsync(ctx, target, app, buildCredentials);
return await validateProvisioningProfileWithAppleAsync(ctx, app, buildCredentials);
}
function validateProvisioningProfileWithoutApple(
@@ -87,17 +84,14 @@ function validateProvisioningProfileWithoutApple(
async function validateProvisioningProfileWithAppleAsync(
ctx: CredentialsContext,
target: Target,
app: AppLookupParams,
buildCredentials: Partial<IosAppBuildCredentialsFragment>
): Promise<boolean> {
assert(buildCredentials.provisioningProfile, 'Provisioning Profile must be defined');
const { developerPortalIdentifier, provisioningProfile } = buildCredentials.provisioningProfile;
const applePlatform = await getApplePlatformFromSdkRoot(target);
const profilesFromApple = await ctx.appStore.listProvisioningProfilesAsync(
app.bundleIdentifier,
applePlatform,
buildCredentials.iosDistributionType === IosDistributionType.AdHoc
? ProfileClass.Adhoc
: ProfileClass.General
@@ -258,7 +258,6 @@ export class ManageIos {
}
await this.setupProvisioningProfileWithSpecificDistCertAsync(
ctx,
target,
appLookupParams,
distCert,
distributionType
@@ -275,7 +274,6 @@ export class ManageIos {
if (confirm) {
await this.setupProvisioningProfileWithSpecificDistCertAsync(
ctx,
target,
appLookupParams,
distCert,
distributionType
@@ -379,7 +377,6 @@ export class ManageIos {
private async setupProvisioningProfileWithSpecificDistCertAsync(
ctx: CredentialsContext,
target: Target,
appLookupParams: AppLookupParams,
distCert: AppleDistributionCertificateFragment,
distributionType: IosDistributionTypeGraphql
@@ -387,14 +384,12 @@ export class ManageIos {
Log.log(`Setting up ${appLookupParams.projectName} to use Distribution Certificate`);
Log.log(`Creating provisioning profile...`);
if (distributionType === IosDistributionTypeGraphql.AdHoc) {
return await new SetUpAdhocProvisioningProfile({
app: appLookupParams,
target,
}).runWithDistributionCertificateAsync(ctx, distCert);
return await new SetUpAdhocProvisioningProfile(
appLookupParams
).runWithDistributionCertificateAsync(ctx, distCert);
} else {
return await new SetUpProvisioningProfile(
appLookupParams,
target,
distributionType
).createAndAssignProfileAsync(ctx, distCert);
}
@@ -1,29 +1,25 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
import Log from '../../log';
import { promptAsync } from '../../prompts';
export class SelectBuildProfileFromEasJson<T extends Platform> {
private easJsonAccessor: EasJsonAccessor;
private easJsonReader: EasJsonReader;
constructor(projectDir: string, private platform: T) {
this.easJsonAccessor = new EasJsonAccessor(projectDir);
this.easJsonReader = new EasJsonReader(projectDir);
}
async runAsync(): Promise<BuildProfile<T>> {
const profileName = await this.getProfileNameFromEasConfigAsync();
const easConfig = await EasJsonUtils.getBuildProfileAsync<T>(
this.easJsonAccessor,
this.platform,
profileName
);
const easConfig = await this.easJsonReader.getBuildProfileAsync<T>(this.platform, profileName);
Log.succeed(`Using build profile: ${profileName}`);
return easConfig;
}
async getProfileNameFromEasConfigAsync(): Promise<string> {
const buildProfileNames = await EasJsonUtils.getBuildProfileNamesAsync(this.easJsonAccessor);
const buildProfileNames = await this.easJsonReader.getBuildProfileNamesAsync();
if (buildProfileNames.length === 0) {
throw new Error(
'You need at least one iOS build profile declared in eas.json. Go to https://docs.expo.dev/build/eas-json/ for more details'
@@ -93,13 +93,11 @@ async function findUnregisteredPortalDevicesAsync(
return acc;
}, {} as Record<string, AppleDeviceFragmentWithAppleTeam>);
const portalDevices = await Device.getAsync(getRequestContext(appleAuthCtx));
const portalDevices = await Device.getAllIOSProfileDevicesAsync(getRequestContext(appleAuthCtx));
return portalDevices.filter(
portalDevice =>
!(portalDevice.attributes.udid in expoRegisteredDevicesByUdid) &&
[DeviceClass.IPAD, DeviceClass.IPHONE, DeviceClass.APPLE_TV].includes(
portalDevice.attributes.deviceClass
)
[DeviceClass.IPAD, DeviceClass.IPHONE].includes(portalDevice.attributes.deviceClass)
);
}
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more