Compare commits

..
Author SHA1 Message Date
Will Schurman 9147ae631c [rfc][eas-cli] Unify and globalize command non-interactive and json flags 2022-09-14 09:42:45 -07:00
Dominik Sokal 9c09cb9034 Update CHANGELOG.md 2022-09-12 09:28:21 +02:00
Dominik Sokal e52d14586f [ENG-6129] support json5 in eas.json (#1350)
* support json5 in eas.json

* address PR feedback

* address more PR feedback

* fix test
2022-09-12 09:27:01 +02:00
Szymon Dziedzic 0f7646868f [eas-cli][ENG-6229] add -e as a shortcut for --profile flag (#1342)
* [eas-cli] add `-e` flag as a shortcut for `--profile` flag

* revert readme changes

* add description for profile flag on config command

* update CHANGELOG.md
2022-09-07 19:36:23 +02:00
Justin McBride 6fe4511280 Small updates to the language of the Device Create command (#1346) 2022-09-07 09:28:53 +02:00
Expo CI efc1551d62 update CHANGELOG.md 2022-09-05 12:11:53 +00:00
Dominik Sokal 0cc5b74081 v2.1.0 2022-09-05 14:02:46 +02:00
Wojciech Kozyra 6c6f9b079d [eas-cli] resolve paths from root when checking for gitignored values (#1336) 2022-09-05 14:00:26 +02:00
Dominik Sokal 7da0db0237 Update CHANGELOG.md 2022-09-05 13:56:02 +02:00
Dominik SokalandKim Brandwijk 66f916ac02 [ENG-3865] add support for uploading build artifacts (#1321)
* add support for uploading build artifacts

* handle auto-renaming artifactPath -> applicationArchivePath

* update eas.schema.json

* upgrade @expo/eas-build-job

* rename buildArtifactsPaths -> buildArtifactPaths

* Apply suggestions from code review

Co-authored-by: Kim Brandwijk <[email protected]>

* fix ts error

* address PR feedback

Co-authored-by: Kim Brandwijk <[email protected]>
2022-09-05 13:55:07 +02:00
Bartosz Kaszubowski 0e500ec827 [eas-cli] bump oclif help plugin (#1341)
* [eas-cli] bump oclif help plugin

* update CHANGELOG.md

* cleanup changelog
2022-09-05 13:54:26 +02:00
Dominik Sokal 00f1d24aa5 [ENG-6186] add "eas open" command (#1337)
* add "eas open" command

* update CHANGELOG.md
2022-09-05 11:53:56 +02:00
Dominik Sokal b909409c54 Update CHANGELOG.md 2022-09-05 10:28:46 +02:00
HBiedeandKim Brandwijk d18d7a6e21 Fix typos (#1340)
* Fix typos

* Revert “re-authentication”

Co-authored-by: Kim Brandwijk <[email protected]>

Co-authored-by: Kim Brandwijk <[email protected]>
2022-09-05 10:25:14 +02:00
Quinlan Jung 0131cdbf4b [ENG-6070][cli] generate tvOS credentials for adhoc and prod (#1325)
* [cli] generate tvOS credentials for adhoc and prod

* update CHANGELOG.md

* pr feedback: dsokal

* pr feedback: wojtek

* fix tsc

* tricky mocks
2022-09-02 00:45:09 +02:00
Will Schurman 114190c52e [eas-cli] Add noImplicitOverride to tsconfigs (#1327) 2022-09-01 09:23:08 -07:00
HBiede a06352d150 [eas-cli] Fix viewing branch list when a branch has no associated platforms (#1326) 2022-09-01 09:12:22 -07:00
Expo CI 1969eea8a7 update CHANGELOG.md 2022-09-01 11:40:30 +00:00
Dominik Sokal 8c758fa413 v2.0.0 2022-09-01 13:30:03 +02:00
Dominik Sokal 4b2fb24d1d Update CHANGELOG.md 2022-09-01 13:24:46 +02:00
Dominik Sokal 1637573d72 fix typo in promptToConfigurePushNotifications (#1332)
* fix typo in promptToConfigurePushNotifications

* address PR feedback
2022-09-01 13:20:18 +02:00
Will Schurman 367477f75b [eas-cli] Fix dynamic config update warning for update:configure (#1322) 2022-08-31 14:24:40 -07:00
139 changed files with 2384 additions and 942 deletions

No files matched your search

+31 -1
View File
@@ -8,11 +8,41 @@ 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
- Use discriminated union type for BuildContext. ([#1330](https://github.com/expo/eas-cli/pull/1330) by [@wschurman](https://github.com/wschurman))
## [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))
## [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": "1.2.0"
"version": "2.1.0"
}
+65 -50
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,6 +91,7 @@ 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)
@@ -124,7 +125,7 @@ ALIASES
$ eas login
```
_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)_
_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)_
## `eas account:logout`
@@ -141,7 +142,7 @@ ALIASES
$ eas logout
```
_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)_
_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)_
## `eas account:view`
@@ -158,7 +159,7 @@ ALIASES
$ eas whoami
```
_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)_
_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)_
## `eas analytics [STATUS]`
@@ -172,7 +173,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -221,7 +222,7 @@ DESCRIPTION
create a branch
```
_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)_
_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)_
## `eas branch:delete [NAME]`
@@ -241,7 +242,7 @@ DESCRIPTION
delete a branch
```
_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)_
_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)_
## `eas branch:list`
@@ -262,7 +263,7 @@ DESCRIPTION
list all branches
```
_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)_
_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)_
## `eas branch:rename`
@@ -281,7 +282,7 @@ DESCRIPTION
rename a branch
```
_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)_
_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)_
## `eas branch:view [NAME]`
@@ -305,7 +306,7 @@ DESCRIPTION
view a branch
```
_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)_
_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)_
## `eas build`
@@ -335,7 +336,7 @@ DESCRIPTION
start a build
```
_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)_
_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)_
## `eas build:cancel [BUILD_ID]`
@@ -349,7 +350,7 @@ DESCRIPTION
cancel a build
```
_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)_
_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)_
## `eas build:configure`
@@ -366,7 +367,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_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)_
_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)_
## `eas build:inspect`
@@ -401,7 +402,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/v1.2.0/packages/eas-cli/src/commands/build/inspect.ts)_
_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)_
## `eas build:list`
@@ -434,7 +435,7 @@ DESCRIPTION
list all builds for your project
```
_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)_
_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)_
## `eas build:submit`
@@ -481,7 +482,7 @@ DESCRIPTION
Update version of an app.
```
_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)_
_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)_
## `eas build:version:sync`
@@ -500,7 +501,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/v1.2.0/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -517,7 +518,7 @@ DESCRIPTION
view a build for your project
```
_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)_
_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)_
## `eas channel:create [NAME]`
@@ -537,7 +538,7 @@ DESCRIPTION
create a channel
```
_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)_
_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)_
## `eas channel:edit [NAME]`
@@ -558,7 +559,7 @@ DESCRIPTION
point a channel at a new branch
```
_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)_
_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)_
## `eas channel:list`
@@ -575,7 +576,7 @@ DESCRIPTION
list all channels
```
_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)_
_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)_
## `eas channel:view [NAME]`
@@ -595,7 +596,7 @@ DESCRIPTION
view a channel
```
_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)_
_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)_
## `eas config`
@@ -613,7 +614,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -630,7 +631,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -644,7 +645,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/v1.2.0/packages/eas-cli/src/commands/device/create.ts)_
_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)_
## `eas device:delete`
@@ -662,7 +663,7 @@ DESCRIPTION
remove a registered device from your account
```
_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)_
_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)_
## `eas device:list`
@@ -679,7 +680,7 @@ DESCRIPTION
list all registered devices for your account
```
_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)_
_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)_
## `eas device:view [UDID]`
@@ -693,7 +694,7 @@ DESCRIPTION
view a device for your project
```
_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)_
_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)_
## `eas diagnostics`
@@ -707,11 +708,11 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
Display help for eas.
display help for eas-cli
```
USAGE
@@ -724,10 +725,10 @@ FLAGS
-n, --nested-commands Include all nested commands in the output.
DESCRIPTION
Display help for eas.
display help for eas-cli
```
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.2.0/src/commands/help.ts)_
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.3.0/src/commands/help.ts)_
## `eas init`
@@ -789,7 +790,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/v1.2.0/packages/eas-cli/src/commands/metadata/pull.ts)_
_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)_
## `eas metadata:push`
@@ -806,7 +807,21 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_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)_
_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)_
## `eas project:info`
@@ -820,7 +835,7 @@ DESCRIPTION
information about the current project
```
_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)_
_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)_
## `eas project:init`
@@ -837,7 +852,7 @@ ALIASES
$ eas init
```
_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)_
_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)_
## `eas secret:create`
@@ -857,7 +872,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/v1.2.0/packages/eas-cli/src/commands/secret/create.ts)_
_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)_
## `eas secret:delete`
@@ -874,7 +889,7 @@ DESCRIPTION
delete an environment secret by ID
```
_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)_
_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)_
## `eas secret:list`
@@ -888,7 +903,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_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)_
_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)_
## `eas submit`
@@ -918,7 +933,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v2.1.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -948,7 +963,7 @@ DESCRIPTION
publish an update group
```
_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)_
_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)_
## `eas update:configure`
@@ -965,7 +980,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_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)_
_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)_
## `eas update:delete GROUPID`
@@ -985,7 +1000,7 @@ DESCRIPTION
delete all the updates in an update group
```
_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)_
_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)_
## `eas update:list`
@@ -1004,7 +1019,7 @@ DESCRIPTION
view the recent updates for a branch
```
_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)_
_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)_
## `eas update:view GROUPID`
@@ -1024,7 +1039,7 @@ DESCRIPTION
update group details
```
_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)_
_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)_
## `eas webhook:create`
@@ -1044,7 +1059,7 @@ DESCRIPTION
create a webhook
```
_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)_
_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)_
## `eas webhook:delete [ID]`
@@ -1061,7 +1076,7 @@ DESCRIPTION
delete a webhook
```
_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)_
_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)_
## `eas webhook:list`
@@ -1078,7 +1093,7 @@ DESCRIPTION
list webhooks
```
_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)_
_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)_
## `eas webhook:update`
@@ -1099,7 +1114,7 @@ DESCRIPTION
update a webhook
```
_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)_
_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)_
## `eas webhook:view ID`
@@ -1116,7 +1131,7 @@ DESCRIPTION
view a webhook
```
_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)_
_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)_
## `eas whoami`
+418 -4
View File
@@ -4995,7 +4995,7 @@
"fields": null,
"inputFields": [
{
"name": "artifactPath",
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
@@ -5006,6 +5006,38 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"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": "",
@@ -5269,7 +5301,7 @@
"fields": null,
"inputFields": [
{
"name": "artifactPath",
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
@@ -5280,6 +5312,38 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"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": "",
@@ -6062,6 +6126,26 @@
"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": "",
@@ -7439,6 +7523,83 @@
"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",
@@ -12373,6 +12534,30 @@
"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": "",
@@ -15834,6 +16019,33 @@
"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",
@@ -18850,7 +19062,7 @@
"fields": null,
"inputFields": [
{
"name": "artifactPath",
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
@@ -18861,6 +19073,38 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"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": "",
@@ -19089,7 +19333,7 @@
"fields": null,
"inputFields": [
{
"name": "artifactPath",
"name": "applicationArchivePath",
"description": "",
"type": {
"kind": "SCALAR",
@@ -19100,6 +19344,38 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "artifactPath",
"description": "@deprecated",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"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": "",
@@ -21360,6 +21636,18 @@
"name": "PublicArtifacts",
"description": "",
"fields": [
{
"name": "applicationArchiveUrl",
"description": "",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildUrl",
"description": "",
@@ -23473,6 +23761,55 @@
"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": "",
@@ -25760,6 +26097,83 @@
"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": "",
+5 -4
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "1.2.0",
"version": "2.1.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.85",
"@expo/eas-json": "1.2.0",
"@expo/eas-build-job": "0.2.90",
"@expo/eas-json": "2.1.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.2.0",
"@expo/plugin-help": "5.3.0",
"@expo/plugin-warn-if-update-available": "2.3.0",
"@expo/prebuild-config": "5.0.3",
"@expo/results": "1.0.0",
@@ -32,6 +32,7 @@
"@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 {
readonly name = 'ApiV2Error';
override readonly name = 'ApiV2Error';
readonly expoApiV2ErrorCode: string;
readonly expoApiV2ErrorDetails?: JSONValue;
readonly expoApiV2ErrorServerStack?: string;
+15 -10
View File
@@ -89,16 +89,21 @@ function renderPageOfBranches(
});
table.push(
...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,
}),
])
...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',
];
})
);
Log.addNewLineIfNone();
@@ -73,17 +73,17 @@ describe(bumpVersionAsync, () => {
await bumpVersionAsync({
bumpStrategy: BumpStrategy.VERSION_CODE,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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('/repo');
const buildGradle = await getAppBuildGradleAsync('/app');
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: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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('/repo');
const buildGradle = await getAppBuildGradleAsync('/app');
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: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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('/repo');
const buildGradle = await getAppBuildGradleAsync('/app');
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: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/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(
'/repo',
'/app',
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(
'/repo',
'/app',
exp,
{} as BuildProfile<Platform.ANDROID>
);
@@ -240,7 +240,7 @@ function initBareWorkflowProject({
}`,
'./android/app/src/main/AndroidManifest.xml': 'fake',
},
'/repo'
'/app'
);
return fakeExp;
@@ -261,7 +261,7 @@ function initManagedProject(): ExpoConfig {
expo: fakeExp,
}),
},
'/repo'
'/app'
);
return fakeExp;
+7 -7
View File
@@ -22,7 +22,7 @@ import {
JobData,
prepareBuildRequestForPlatformAsync,
} from '../build';
import { AndroidBuildContext, AndroidCommonContext, BuildContextAndroid } from '../context';
import { AndroidBuildContext, BuildContext, CommonContext } 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: AndroidCommonContext
ctx: CommonContext<Platform.ANDROID>
): 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: BuildContextAndroid
ctx: BuildContext<Platform.ANDROID>
): Promise<BuildRequestSender> {
return await prepareBuildRequestForPlatformAsync({
ctx,
ensureCredentialsAsync: async (ctx: BuildContextAndroid) => {
ensureCredentialsAsync: async (ctx: BuildContext<Platform.ANDROID>) => {
return await ensureAndroidCredentialsAsync(ctx);
},
syncProjectConfigurationAsync: async () => {
@@ -95,7 +95,7 @@ export async function prepareAndroidBuildAsync(
});
},
prepareJobAsync: async (
ctx: BuildContextAndroid,
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData<AndroidCredentials>
): Promise<Job> => {
return await prepareJobAsync(ctx, jobData);
@@ -118,12 +118,12 @@ export async function prepareAndroidBuildAsync(
});
}
function shouldProvideCredentials(ctx: BuildContextAndroid): boolean {
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
return !ctx.buildProfile.withoutCredentials;
}
async function ensureAndroidCredentialsAsync(
ctx: BuildContextAndroid
ctx: BuildContext<Platform.ANDROID>
): Promise<CredentialsResult<AndroidCredentials> | undefined> {
if (!shouldProvideCredentials(ctx)) {
return;
@@ -15,7 +15,8 @@ export function transformJob(job: Android.Job): AndroidJobInput {
cache: job.cache,
version: job.version?.versionCode ? { versionCode: job.version.versionCode } : undefined,
gradleCommand: job.gradleCommand,
artifactPath: job.artifactPath,
applicationArchivePath: job.applicationArchivePath,
buildArtifactPaths: job.buildArtifactPaths,
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 { BuildContextAndroid } from '../context';
import { BuildContext } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -20,7 +20,10 @@ const cacheDefaults = {
cacheDefaultPaths: true,
};
export async function prepareJobAsync(ctx: BuildContextAndroid, jobData: JobData): Promise<Job> {
export async function prepareJobAsync(
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData
): Promise<Job> {
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
const projectRootDirectory =
@@ -69,7 +72,8 @@ export async function prepareJobAsync(ctx: BuildContextAndroid, jobData: JobData
updates: { channel: ctx.buildProfile.channel },
developmentClient: buildProfile.developmentClient,
gradleCommand: buildProfile.gradleCommand,
artifactPath: buildProfile.artifactPath,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
buildArtifactPaths: buildProfile.buildArtifactPaths,
buildType,
username,
...(ctx.android.versionCodeOverride && {
+18 -12
View File
@@ -46,12 +46,14 @@ export interface JobData<Credentials> {
projectArchive: ArchiveSource;
}
interface Builder<Credentials, TJob extends Job> {
ctx: BuildContext;
interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
ctx: BuildContext<TPlatform>;
ensureCredentialsAsync(ctx: BuildContext): Promise<CredentialsResult<Credentials> | undefined>;
syncProjectConfigurationAsync(ctx: BuildContext): Promise<void>;
prepareJobAsync(ctx: BuildContext, jobData: JobData<Credentials>): Promise<Job>;
ensureCredentialsAsync(
ctx: BuildContext<TPlatform>
): Promise<CredentialsResult<Credentials> | undefined>;
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
sendBuildRequestAsync(
appId: string,
job: TJob,
@@ -62,15 +64,17 @@ interface Builder<Credentials, TJob extends Job> {
export type BuildRequestSender = () => Promise<BuildFragment | undefined>;
function resolveBuildParamsInput(ctx: BuildContext): BuildParamsInput {
function resolveBuildParamsInput<T extends Platform>(ctx: BuildContext<T>): BuildParamsInput {
return {
resourceClass: ctx.resourceClass,
};
}
export async function prepareBuildRequestForPlatformAsync<Credentials, TJob extends Job>(
builder: Builder<Credentials, TJob>
): Promise<BuildRequestSender> {
export async function prepareBuildRequestForPlatformAsync<
TPlatform extends Platform,
Credentials,
TJob extends Job
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
const { ctx } = builder;
const credentialsResult = await withAnalyticsAsync(
async () => await builder.ensureCredentialsAsync(ctx),
@@ -159,7 +163,9 @@ function handleBuildRequestError(error: any, platform: Platform): never {
throw error;
}
async function uploadProjectAsync(ctx: BuildContext): Promise<string> {
async function uploadProjectAsync<TPlatform extends Platform>(
ctx: BuildContext<TPlatform>
): Promise<string> {
let projectTarballPath;
try {
return await withAnalyticsAsync(
@@ -201,8 +207,8 @@ async function uploadProjectAsync(ctx: BuildContext): Promise<string> {
}
}
async function sendBuildRequestAsync<Credentials, TJob extends Job>(
builder: Builder<Credentials, TJob>,
async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJob extends Job>(
builder: Builder<TPlatform, 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, EasJsonReader } from '@expo/eas-json';
import { EasJson, EasJsonAccessor } 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(EasJsonReader.formatEasJsonPath(configureParams.projectDir))) {
if (await fs.pathExists(EasJsonAccessor.formatEasJsonPath(configureParams.projectDir))) {
return false;
}
@@ -89,7 +89,7 @@ const EAS_JSON_BARE_DEFAULT: EasJson = {
};
async function createEasJsonAsync(projectDir: string): Promise<void> {
const easJsonPath = EasJsonReader.formatEasJsonPath(projectDir);
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
const hasAndroidNativeProject =
(await resolveWorkflowAsync(projectDir, Platform.ANDROID)) === Workflow.GENERIC;
+6 -18
View File
@@ -11,9 +11,7 @@ import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { LocalBuildOptions } from './local';
export type AndroidCommonContext = Omit<BuildContextAndroid, 'android'>;
export type IosCommonContext = Omit<BuildContextIos, 'ios'>;
export type CommonContext = AndroidCommonContext | IosCommonContext;
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
export interface AndroidBuildContext {
applicationId: string;
@@ -29,9 +27,10 @@ export interface IosBuildContext {
buildNumberOverride?: string;
}
interface BuildContextBase {
export interface BuildContext<T extends Platform> {
accountName: string;
easJsonCliConfig: EasJson['cli'];
buildProfile: BuildProfile<T>;
buildProfileName: string;
resourceClass: BuildResourceClass;
clearCache: boolean;
@@ -41,6 +40,7 @@ interface BuildContextBase {
nonInteractive: boolean;
noWait: boolean;
runFromCI: boolean;
platform: T;
projectDir: string;
projectId: string;
projectName: string;
@@ -48,18 +48,6 @@ interface BuildContextBase {
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 { AndroidCommonContext, BuildContext, IosCommonContext } from './context';
import { BuildContext, CommonContext } 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> {
}): Promise<BuildContext<T>> {
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 = {
const commonContext: CommonContext<T> = {
accountName,
buildProfile,
buildProfileName,
@@ -103,17 +103,17 @@ export async function createBuildContextAsync<T extends Platform>({
runFromCI,
};
if (platform === Platform.ANDROID) {
const common = commonContext as AndroidCommonContext;
const common = commonContext as CommonContext<Platform.ANDROID>;
return {
...common,
android: await createAndroidContextAsync(common),
};
} as BuildContext<T>;
} else {
const common = commonContext as IosCommonContext;
const common = commonContext as CommonContext<Platform.IOS>;
return {
...common,
ios: await createIosContextAsync(common),
};
} as BuildContext<T>;
}
}
+10 -6
View File
@@ -1,4 +1,4 @@
import { Ios, Job, Metadata, Workflow } from '@expo/eas-build-job';
import { Ios, Job, Metadata, Platform, 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 { BuildContextIos, IosBuildContext, IosCommonContext } from '../context';
import { BuildContext, CommonContext, IosBuildContext } from '../context';
import { transformMetadata } from '../graphql';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { ensureIosCredentialsAsync } from './credentials';
@@ -17,7 +17,9 @@ import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteBuildNumberAsync } from './version';
export async function createIosContextAsync(ctx: IosCommonContext): Promise<IosBuildContext> {
export async function createIosContextAsync(
ctx: CommonContext<Platform.IOS>
): Promise<IosBuildContext> {
const { buildProfile } = ctx;
if (ctx.workflow === Workflow.MANAGED) {
@@ -61,10 +63,12 @@ export async function createIosContextAsync(ctx: IosCommonContext): Promise<IosB
};
}
export async function prepareIosBuildAsync(ctx: BuildContextIos): Promise<BuildRequestSender> {
export async function prepareIosBuildAsync(
ctx: BuildContext<Platform.IOS>
): Promise<BuildRequestSender> {
return await prepareBuildRequestForPlatformAsync({
ctx,
ensureCredentialsAsync: async (ctx: BuildContextIos) => {
ensureCredentialsAsync: async (ctx: BuildContext<Platform.IOS>) => {
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
},
syncProjectConfigurationAsync: async () => {
@@ -79,7 +83,7 @@ export async function prepareIosBuildAsync(ctx: BuildContextIos): Promise<BuildR
});
},
prepareJobAsync: async (
ctx: BuildContextIos,
ctx: BuildContext<Platform.IOS>,
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 { BuildContextIos } from '../context';
import { BuildContext } from '../context';
import { logCredentialsSource } from '../utils/credentials';
export async function ensureIosCredentialsAsync(
buildCtx: BuildContextIos,
buildCtx: BuildContext<Platform.IOS>,
targets: Target[]
): Promise<CredentialsResult<IosCredentials> | undefined> {
if (!shouldProvideCredentials(buildCtx)) {
@@ -31,6 +31,6 @@ export async function ensureIosCredentialsAsync(
};
}
function shouldProvideCredentials(buildCtx: BuildContextIos): boolean {
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
return !buildCtx.buildProfile.simulator;
}
+2 -1
View File
@@ -17,7 +17,8 @@ export function transformJob(job: Ios.Job): IosJobInput {
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
scheme: job.scheme,
buildConfiguration: job.buildConfiguration,
artifactPath: job.artifactPath,
applicationArchivePath: job.applicationArchivePath,
buildArtifactPaths: job.buildArtifactPaths,
username: job.username,
developmentClient: job.developmentClient,
simulator: job.simulator,
+8 -3
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 { BuildContextIos } from '../context';
import { BuildContext } from '../context';
interface JobData {
projectArchive: ArchiveSource;
@@ -20,7 +20,10 @@ const cacheDefaults = {
cacheDefaultPaths: true,
};
export async function prepareJobAsync(ctx: BuildContextIos, jobData: JobData): Promise<Job> {
export async function prepareJobAsync(
ctx: BuildContext<Platform.IOS>,
jobData: JobData
): Promise<Job> {
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
@@ -61,7 +64,9 @@ export async function prepareJobAsync(ctx: BuildContextIos, jobData: JobData): P
simulator: ctx.buildProfile.simulator,
scheme: jobData.buildScheme,
buildConfiguration: ctx.buildProfile.buildConfiguration,
artifactPath: ctx.buildProfile.artifactPath,
applicationArchivePath:
ctx.buildProfile.applicationArchivePath ?? ctx.buildProfile.artifactPath,
buildArtifactPaths: ctx.buildProfile.buildArtifactPaths,
username,
...(ctx.ios.buildNumberOverride && {
version: {
+39 -25
View File
@@ -18,10 +18,12 @@ import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext, BuildContextIos } from './context';
import { BuildContext } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
export async function collectMetadataAsync(ctx: BuildContext): Promise<Metadata> {
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<Metadata> {
const vcsClient = getVcsClient();
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
@@ -50,7 +52,9 @@ export async function collectMetadataAsync(ctx: BuildContext): Promise<Metadata>
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(ctx),
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
ctx as BuildContext<Platform.IOS>
),
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
@@ -58,48 +62,52 @@ export async function collectMetadataAsync(ctx: BuildContext): Promise<Metadata>
return sanitizeMetadata(metadata);
}
async function maybeResolveVersionsAsync(
ctx: BuildContext
async function maybeResolveVersionsAsync<T extends Platform>(
ctx: BuildContext<T>
): 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,
ctx.ios.targets
iosContext.ios.targets
);
if (ctx.ios.buildNumberOverride) {
if (iosContext.ios.buildNumberOverride) {
return {
...resolvedVersion,
appBuildVersion: ctx.ios.buildNumberOverride,
appBuildVersion: iosContext.ios.buildNumberOverride,
};
}
return resolvedVersion;
} else {
} else if (ctx.platform === Platform.ANDROID) {
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
ctx.projectDir,
ctx.exp,
ctx.buildProfile
androidCtx.buildProfile
);
if (ctx.android.versionCodeOverride) {
if (androidCtx.android.versionCodeOverride) {
return {
...resolvedVersion,
appBuildVersion: ctx.android.versionCodeOverride,
appBuildVersion: androidCtx.android.versionCodeOverride,
};
}
return resolvedVersion;
}
}
function resolveAppIdentifier(ctx: BuildContext): string {
if (ctx.platform === Platform.IOS) {
return ctx.ios.bundleIdentifier;
} else {
return ctx.android.applicationId;
throw new Error(`Unsupported platform ${ctx.platform}`);
}
}
async function resolveChannelOrReleaseChannelAsync(
ctx: BuildContext
function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string {
if (ctx.platform === Platform.IOS) {
return (ctx as BuildContext<Platform.IOS>).ios.bundleIdentifier;
} else {
return (ctx as BuildContext<Platform.ANDROID>).android.applicationId;
}
}
async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<{ channel: string } | { releaseChannel: string } | null> {
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
return null;
@@ -118,7 +126,9 @@ async function resolveChannelOrReleaseChannelAsync(
return { releaseChannel };
}
async function getNativeReleaseChannelAsync(ctx: BuildContext): Promise<string> {
async function getNativeReleaseChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<string> {
switch (ctx.platform) {
case Platform.ANDROID: {
return (await readAndroidReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
@@ -131,7 +141,9 @@ async function getNativeReleaseChannelAsync(ctx: BuildContext): Promise<string>
}
}
async function getNativeChannelAsync(ctx: BuildContext): Promise<string | undefined> {
async function getNativeChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<string | undefined> {
switch (ctx.platform) {
case Platform.ANDROID: {
return (await readAndroidChannelSafelyAsync(ctx.projectDir)) ?? undefined;
@@ -144,7 +156,9 @@ async function getNativeChannelAsync(ctx: BuildContext): Promise<string | undefi
return undefined;
}
async function getReactNativeVersionAsync(ctx: BuildContext): Promise<string | undefined> {
async function getReactNativeVersionAsync(
ctx: BuildContext<Platform>
): Promise<string | undefined> {
try {
const reactNativePackageJsonPath = resolveFrom(ctx.projectDir, 'react-native/package.json');
return (await fs.readJson(reactNativePackageJsonPath)).version;
@@ -156,7 +170,7 @@ async function getReactNativeVersionAsync(ctx: BuildContext): Promise<string | u
}
function resolveIosEnterpriseProvisioning(
ctx: BuildContextIos
ctx: BuildContext<Platform.IOS>
): IosEnterpriseProvisioning | undefined {
return ctx.buildProfile.enterpriseProvisioning;
}
+14 -15
View File
@@ -3,7 +3,8 @@ import {
AppVersionSource,
BuildProfile,
EasJson,
EasJsonReader,
EasJsonAccessor,
EasJsonUtils,
SubmitProfile,
} from '@expo/eas-json';
import chalk from 'chalk';
@@ -94,13 +95,14 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
projectDir,
nonInteractive: flags.nonInteractive,
});
const easJsonReader = new EasJsonReader(projectDir);
const easJsonCliConfig: EasJson['cli'] = (await easJsonReader.getCliConfigAsync()) ?? {};
const easJsonAccessor = new EasJsonAccessor(projectDir);
const easJsonCliConfig: EasJson['cli'] =
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
const platforms = toPlatforms(flags.requestedPlatform);
const buildProfiles = await getProfilesAsync({
type: 'build',
easJsonReader,
easJsonAccessor,
platforms,
profileName: flags.profile ?? undefined,
});
@@ -119,7 +121,7 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
build: BuildWithSubmissionsFragment | BuildFragment;
buildProfile: ProfileData<'build'>;
}[] = [];
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext } = {};
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
for (const buildProfile of buildProfiles) {
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
@@ -150,7 +152,7 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
const submissions: SubmissionFragment[] = [];
if (flags.autoSubmit) {
const submitProfiles = await getProfilesAsync({
easJsonReader,
easJsonAccessor,
platforms,
profileName: flags.submitProfile,
type: 'submit',
@@ -232,7 +234,7 @@ async function prepareAndStartBuildAsync({
buildProfile: ProfileData<'build'>;
resourceClass: BuildResourceClass;
easJsonCliConfig: EasJson['cli'];
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext }> {
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
const buildCtx = await createBuildContextAsync({
buildProfileName: buildProfile.profileName,
resourceClass,
@@ -278,12 +280,12 @@ async function prepareAndStartBuildAsync({
};
}
async function startBuildAsync(ctx: BuildContext): Promise<BuildFragment | undefined> {
async function startBuildAsync(ctx: BuildContext<Platform>): Promise<BuildFragment | undefined> {
let sendBuildRequestAsync: BuildRequestSender;
if (ctx.platform === Platform.ANDROID) {
sendBuildRequestAsync = await prepareAndroidBuildAsync(ctx);
sendBuildRequestAsync = await prepareAndroidBuildAsync(ctx as BuildContext<Platform.ANDROID>);
} else {
sendBuildRequestAsync = await prepareIosBuildAsync(ctx);
sendBuildRequestAsync = await prepareIosBuildAsync(ctx as BuildContext<Platform.IOS>);
}
return await sendBuildRequestAsync();
}
@@ -298,7 +300,7 @@ async function prepareAndStartSubmissionAsync({
nonInteractive,
}: {
build: BuildFragment;
buildCtx: BuildContext;
buildCtx: BuildContext<Platform>;
moreBuilds: boolean;
projectDir: string;
buildProfile: BuildProfile;
@@ -315,10 +317,7 @@ async function prepareAndStartSubmissionAsync({
nonInteractive,
env: buildProfile.env,
credentialsCtx: buildCtx.credentialsCtx,
applicationIdentifier:
buildCtx.platform === Platform.ANDROID
? buildCtx.android.applicationId
: buildCtx.ios.bundleIdentifier,
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
});
if (moreBuilds) {
+5 -3
View File
@@ -1,4 +1,4 @@
import { Workflow } from '@expo/eas-build-job';
import { Platform, 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): void {
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): 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,7 +15,9 @@ export function checkNodeEnvVariable(ctx: CommonContext): void {
}
}
export async function checkGoogleServicesFileAsync(ctx: CommonContext): Promise<void> {
export async function checkGoogleServicesFileAsync<T extends Platform>(
ctx: CommonContext<T>
): Promise<void> {
if (ctx.workflow === Workflow.GENERIC || ctx.buildProfile?.env?.GOOGLE_SERVICES_FILE) {
return;
}
+92 -10
View File
@@ -1,8 +1,10 @@
import { EasJsonReader } from '@expo/eas-json';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import * as PackageManagerUtils from '@expo/package-manager';
import { Command } from '@oclif/core';
import { Command, Flags } 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';
@@ -12,15 +14,48 @@ import {
initAsync as initAnalyticsAsync,
logEvent,
} from '../analytics/rudderstackClient';
import { learnMore } from '../log';
import Log, { learnMore } from '../log';
import { interactiveOra, nonInteractiveOra } from '../ora';
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
@@ -28,10 +63,26 @@ export default abstract class EasCommand extends Command {
protected requiresAuthentication = true;
protected mustBeRunInsideProject = true;
protected abstract runAsync(): Promise<any>;
protected abstract runAsync({
nonInteractive,
json,
logger,
prompts: {
promptAsync,
confirmAsync,
selectAsync,
toggleConfirmAsync,
pressAnyKeyToContinueAsync,
},
ora,
}: CommandContext): Promise<{ jsonOutput: object }>;
// 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) {
@@ -41,8 +92,6 @@ 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();
@@ -52,18 +101,29 @@ export default abstract class EasCommand extends Command {
// commands/submit === submit, commands/build/list === build:list
action: `eas ${this.id}`,
});
return this.runAsync();
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);
}
}
// eslint-disable-next-line async-protect/async-suffix
async finally(err: Error): Promise<any> {
override async finally(err: Error): Promise<any> {
await flushAnalyticsAsync();
return super.finally(err);
}
private async applyCliConfigAsync(projectDir: string): Promise<void> {
const easJsonReader = new EasJsonReader(projectDir);
const config = await easJsonReader.getCliConfigAsync();
const easJsonAccessor = new EasJsonAccessor(projectDir);
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
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})`
@@ -119,4 +179,26 @@ 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 {
requiresAuthentication = authValue;
override requiresAuthentication = authValue;
async runAsync(): Promise<void> {}
}
@@ -1,12 +1,15 @@
import { Flags } from '@oclif/core';
export const getPaginatedQueryOptions = (
flags: Record<keyof typeof EasPaginatedQueryFlags, any>
): PaginatedQueryOptions => {
export const getPaginatedQueryOptions = (flags: {
json: boolean;
'non-interactive': boolean;
offset: number | undefined;
limit: number | undefined;
}): PaginatedQueryOptions => {
return {
json: flags.json ?? false,
json: flags.json,
offset: flags.offset ?? 0,
nonInteractive: flags['non-interactive'] ?? false,
nonInteractive: flags['non-interactive'],
...('limit' in flags && { limit: flags.limit }),
};
};
@@ -40,12 +43,6 @@ 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 description = 'log in with your Expo account';
static aliases = ['login'];
static override description = 'log in with your Expo account';
static override aliases = ['login'];
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
protected override mustBeRunInsideProject = false;
protected override 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 description = 'log out';
static aliases = ['logout'];
static override description = 'log out';
static override aliases = ['logout'];
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
protected override mustBeRunInsideProject = false;
protected override 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 description = 'show the username you are logged in as';
static aliases = ['whoami'];
static override description = 'show the username you are logged in as';
static override aliases = ['whoami'];
protected mustBeRunInsideProject = false;
protected requiresAuthentication = false;
protected override mustBeRunInsideProject = false;
protected override 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 description = 'display or change analytics settings';
static override description = 'display or change analytics settings';
static args = [{ name: 'STATUS', options: ['on', 'off'] }];
static override args = [{ name: 'STATUS', options: ['on', 'off'] }];
protected requiresAuthentication = false;
protected override 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 description = 'create a branch';
static override description = 'create a branch';
static args = [
static override args = [
{
name: 'name',
required: false,
@@ -62,7 +62,7 @@ export default class BranchCreate extends EasCommand {
},
];
static flags = {
static override flags = {
json: Flags.boolean({
description: 'return a json with the new branch ID and name.',
default: false,
+20 -32
View File
@@ -1,8 +1,7 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import EasCommand, { CommandContext } from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateBranchMutation,
@@ -11,15 +10,12 @@ 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,
@@ -77,30 +73,24 @@ async function deleteBranchOnAppAsync({
}
export default class BranchDelete extends EasCommand {
static description = 'delete a branch';
static override description = 'delete a branch';
static args = [
static override 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,
}),
};
async runAsync(): Promise<void> {
protected async runAsync({
nonInteractive,
logger,
prompts: { promptAsync, toggleConfirmAsync },
}: CommandContext): Promise<{ jsonOutput: object }> {
let {
args: { name },
flags: { json: jsonFlag },
} = await this.parse(BranchDelete);
if (jsonFlag) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
@@ -109,7 +99,7 @@ export default class BranchDelete extends EasCommand {
if (!name) {
const validationMessage = 'branch name may not be empty.';
if (jsonFlag) {
if (nonInteractive) {
throw new Error(validationMessage);
}
({ name } = await promptAsync({
@@ -126,16 +116,16 @@ export default class BranchDelete extends EasCommand {
throw new Error(`Could not find branch ${name} on ${fullName}`);
}
if (!jsonFlag) {
Log.addNewLineIfNone();
Log.warn(
`You are about to permamently delete branch: "${name}" and all of the updates published on it.` +
if (!nonInteractive) {
logger.addNewLineIfNone();
logger.warn(
`You are about to permanently delete branch: "${name}" and all of the updates published on it.` +
`\nThis action is irreversible.`
);
Log.newLine();
logger.newLine();
const confirmed = await toggleConfirmAsync({ message: 'Are you sure you wish to proceed?' });
if (!confirmed) {
Log.error(`Cancelled deletion of branch: "${name}".`);
logger.error(`Cancelled deletion of branch: "${name}".`);
process.exit(1);
}
}
@@ -144,12 +134,10 @@ export default class BranchDelete extends EasCommand {
branchId,
});
if (jsonFlag) {
printJsonOnlyOutput(deletionResult);
} else {
Log.withTick(
`️Deleted branch "${name}" and all of its updates on project ${chalk.bold(fullName)}.`
);
}
logger.withTick(
`Deleted branch "${name}" and all of its updates on project ${chalk.bold(fullName)}.`
);
return { jsonOutput: deletionResult };
}
}
+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 description = 'list all branches';
static override description = 'list all branches';
static flags = {
static override flags = {
...EasPaginatedQueryFlags,
};
@@ -1,8 +1,8 @@
import EasCommand from '../../commandUtils/EasCommand';
export default class BranchPublish extends EasCommand {
static description = 'deprecated, use "eas update"';
static hidden = true;
static override description = 'deprecated, use "eas update"';
static override 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 description = 'rename a branch';
static override description = 'rename a branch';
static flags = {
static override 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 description = 'view a branch';
static override description = 'view a branch';
static args = [
static override args = [
{
name: 'name',
required: false,
@@ -17,7 +17,7 @@ export default class BranchView extends EasCommand {
},
];
static flags = {
static override flags = {
...EasPaginatedQueryFlags,
};
@@ -110,9 +110,9 @@ async function ensureBuildExistsAsync(buildId: string): Promise<void> {
}
export default class BuildCancel extends EasCommand {
static description = 'cancel a build';
static override description = 'cancel a build';
static args = [{ name: 'BUILD_ID' }];
static override 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 description = 'configure the project to support EAS Build';
static override description = 'configure the project to support EAS Build';
static flags = {
static override flags = {
platform: Flags.enum({
description: 'Platform to configure',
char: 'p',
+87 -3
View File
@@ -1,13 +1,19 @@
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 from '../../log';
import Log, { link } 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';
@@ -29,9 +35,9 @@ interface RawBuildFlags {
}
export default class Build extends EasCommand {
static description = 'start a build';
static override description = 'start a build';
static flags = {
static override flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios', 'all'],
@@ -49,6 +55,7 @@ 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',
@@ -105,6 +112,8 @@ export default class Build extends EasCommand {
const projectDir = await findProjectRootAsync();
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
if (!flags.localBuildOptions.enable) {
await maybeWarnAboutEasOutagesAsync(
flags.autoSubmit
@@ -208,3 +217,78 @@ 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,16 +25,17 @@ 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 description =
static override description =
'inspect the state of the project at specific build stages, useful for troubleshooting';
static flags = {
static override 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 description = 'list all builds for your project';
static override description = 'list all builds for your project';
static flags = {
static override 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 { EasJsonReader } from '@expo/eas-json';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -27,14 +27,15 @@ import {
import { promptAsync } from '../../../prompts';
export default class BuildVersionSetView extends EasCommand {
static description = 'Update version of an app.';
static override description = 'Update version of an app.';
static flags = {
static override flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
@@ -47,9 +48,13 @@ export default class BuildVersionSetView extends EasCommand {
const projectDir = await findProjectRootAsync();
const platform = await selectPlatformAsync(flags.platform);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const profile = await easJsonReader.getBuildProfileAsync(platform, flags.profile ?? undefined);
const easJsonAccessor = new EasJsonAccessor(projectDir);
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
const profile = await EasJsonUtils.getBuildProfileAsync(
easJsonAccessor,
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, EasJsonReader } from '@expo/eas-json';
import { BuildProfile, EasJsonAccessor } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -40,14 +40,16 @@ interface SyncContext<T extends Platform> {
}
export default class BuildVersionSyncView extends EasCommand {
public static description = 'Update a version in native code with a value stored on EAS servers';
public static override description =
'Update a version in native code with a value stored on EAS servers';
public static flags = {
public static override 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',
@@ -59,13 +61,13 @@ export default class BuildVersionSyncView extends EasCommand {
const projectDir = await findProjectRootAsync();
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const easJsonAccessor = new EasJsonAccessor(projectDir);
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
const platforms = toPlatforms(requestedPlatform);
const buildProfiles = await getProfilesAsync({
type: 'build',
easJsonReader,
easJsonAccessor,
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 description = 'view a build for your project';
static override description = 'view a build for your project';
static args = [{ name: 'BUILD_ID' }];
static override args = [{ name: 'BUILD_ID' }];
static flags = {
static override 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 description = 'create a channel';
static override description = 'create a channel';
static args = [
static override args = [
{
name: 'name',
required: false,
@@ -70,7 +70,7 @@ export default class ChannelCreate extends EasCommand {
},
];
static flags = {
static override 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 hidden = true;
static description = 'Delete a channel';
static override hidden = true;
static override description = 'Delete a channel';
static args = [
static override args = [
{
name: 'name',
required: false,
description: 'Name of the channel to delete',
},
];
static flags = {
static override 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 permamently delete channel: "${name}".\nThis action is irreversible.`
`You are about to permanently 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 description = 'point a channel at a new branch';
static override description = 'point a channel at a new branch';
static args = [
static override args = [
{
name: 'name',
required: false,
@@ -103,7 +103,7 @@ export default class ChannelEdit extends EasCommand {
},
];
static flags = {
static override 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 description = 'list all channels';
static override description = 'list all channels';
static flags = {
static override 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 hidden = true;
static description = 'Rollout a new branch out to a channel incrementally.';
static override hidden = true;
static override description = 'Rollout a new branch out to a channel incrementally.';
static args = [
static override args = [
{
name: 'channel',
required: true,
@@ -315,7 +315,7 @@ export default class ChannelRollout extends EasCommand {
},
];
static flags = {
static override 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 description = 'view a channel';
static override description = 'view a channel';
static args = [
static override args = [
{
name: 'name',
required: false,
@@ -148,7 +148,7 @@ export default class ChannelView extends EasCommand {
},
];
static flags = {
static override flags = {
json: Flags.boolean({
description: 'print output as a JSON object with the channel ID, name and branch mapping.',
default: false,
+13 -8
View File
@@ -1,6 +1,6 @@
import { getProjectConfigDescription } from '@expo/config';
import { Platform } from '@expo/eas-build-job';
import { EasJsonReader } from '@expo/eas-json';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -13,14 +13,19 @@ import { findProjectRootAsync } from '../project/projectUtils';
import { selectAsync } from '../prompts';
export default class Config extends EasCommand {
static description = 'display project configuration (app.json + eas.json)';
static override description = 'display project configuration (app.json + eas.json)';
static flags = {
static override flags = {
platform: Flags.enum({ char: 'p', options: ['android', 'ios'] }),
profile: Flags.string(),
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',
}),
};
protected requiresAuthentication = false;
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const { flags } = await this.parse(Config);
@@ -31,13 +36,13 @@ export default class Config extends EasCommand {
const projectDir = await findProjectRootAsync();
const reader = new EasJsonReader(projectDir);
const accessor = new EasJsonAccessor(projectDir);
const profileName =
maybeProfile ??
(await selectAsync(
'Select build profile',
(
await reader.getBuildProfileNamesAsync()
await EasJsonUtils.getBuildProfileNamesAsync(accessor)
).map(profileName => ({
title: profileName,
value: profileName,
@@ -56,7 +61,7 @@ export default class Config extends EasCommand {
},
]));
const profile = await reader.getBuildProfileAsync(platform, profileName);
const profile = await EasJsonUtils.getBuildProfileAsync(accessor, 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 description = 'manage credentials';
static override description = 'manage credentials';
static flags = {
static override 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 description = 'register new Apple Devices to use for internal distribution';
static override description = 'register new Apple Devices to use for internal distribution';
async runAsync(): Promise<void> {
const user = await ensureLoggedInAsync();
+65 -40
View File
@@ -2,7 +2,7 @@ import { Device, DeviceStatus } from '@expo/apple-utils';
import { Flags } from '@oclif/core';
import assert from 'assert';
import EasCommand from '../../commandUtils/EasCommand';
import EasCommand, { CommandContext } from '../../commandUtils/EasCommand';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import {
@@ -14,21 +14,18 @@ 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 description = 'remove a registered device from your account';
static override description = 'remove a registered device from your account';
static flags = {
static override flags = {
'apple-team-id': Flags.string(),
udid: Flags.string({ multiple: true }),
};
async runAsync(): Promise<void> {
protected async runAsync(commandContext: CommandContext): Promise<{ jsonOutput: object }> {
let {
flags: { 'apple-team-id': appleTeamIdentifier, udid: udids },
} = await this.parse(DeviceDelete);
@@ -38,46 +35,61 @@ export default class DeviceDelete extends EasCommand {
const accountName = await getProjectAccountNameAsync(exp);
if (!appleTeamIdentifier) {
appleTeamIdentifier = await this.askForAppleTeamAsync(accountName);
if (commandContext.nonInteractive) {
throw new Error('Must supply `apple-team-id` in non-interactive mode');
}
appleTeamIdentifier = await this.askForAppleTeamAsync(commandContext, accountName);
}
assert(appleTeamIdentifier, 'No team identifier is specified');
const appleDevicesResult = await this.getDevicesForTeamAsync(accountName, appleTeamIdentifier);
const appleDevicesResult = await this.getDevicesForTeamAsync(
commandContext,
accountName,
appleTeamIdentifier
);
if (!appleDevicesResult) {
return;
return { jsonOutput: [] };
}
const { appleTeamName, appleDevices } = appleDevicesResult;
const chosenDevices = await this.chooseDevicesToDeleteAsync(appleDevices, udids);
const chosenDevices = await this.chooseDevicesToDeleteAsync(
commandContext,
appleDevices,
udids
);
if (chosenDevices.length === 0) {
Log.newLine();
Log.warn('No devices were chosen to be removed.');
return;
commandContext.logger.newLine();
commandContext.logger.warn('No devices were chosen to be removed.');
return { jsonOutput: [] };
}
this.logChosenDevices(chosenDevices, appleTeamName, appleTeamIdentifier);
const hasRemoved = await this.askAndRemoveFromExpoAsync(chosenDevices);
this.logChosenDevices(commandContext, chosenDevices, appleTeamName, appleTeamIdentifier);
const hasRemoved = await this.askAndRemoveFromExpoAsync(commandContext, chosenDevices);
if (!hasRemoved) {
return;
return { jsonOutput: [] };
}
await this.askAndDisableOnAppleAsync(chosenDevices, appleTeamIdentifier);
await this.askAndDisableOnAppleAsync(commandContext, chosenDevices, appleTeamIdentifier);
return { jsonOutput: chosenDevices.map(it => it.id) };
}
async askAndDisableOnAppleAsync(
{ nonInteractive, logger, prompts: { toggleConfirmAsync }, ora }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[],
appleTeamIdentifier: string
): Promise<void> {
Log.newLine();
const deleteOnApple = await toggleConfirmAsync({
message: 'Do you want to disable these devices on your Apple account as well?',
});
logger.newLine();
const deleteOnApple = nonInteractive
? true
: await toggleConfirmAsync({
message: 'Do you want to disable these devices on your Apple account as well?',
});
if (!deleteOnApple) {
return;
@@ -86,12 +98,13 @@ export default class DeviceDelete extends EasCommand {
const ctx = await authenticateAsync({ teamId: appleTeamIdentifier });
const context = getRequestContext(ctx);
Log.addNewLineIfNone();
logger.addNewLineIfNone();
const removeAppleSpinner = ora('Disabling devices on Apple').start();
try {
let realDevices = await Device.getAllIOSProfileDevicesAsync(context);
realDevices = realDevices.filter(d =>
chosenDevices.map(cd => cd.identifier).includes(d.attributes.udid)
const chosenDeviceIdentifiers = chosenDevices.map(cd => cd.identifier);
const allDevices = await Device.getAsync(context);
const realDevices = allDevices.filter(d =>
chosenDeviceIdentifiers.includes(d.attributes.udid)
);
for (const device of realDevices) {
@@ -106,18 +119,21 @@ export default class DeviceDelete extends EasCommand {
}
async askAndRemoveFromExpoAsync(
{ nonInteractive, logger, prompts: { toggleConfirmAsync }, ora }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[]
): Promise<boolean> {
Log.warn(
logger.warn(
`You are about to remove the Apple device${
chosenDevices.length > 1 ? 's' : ''
} listed above from your Expo account.`
);
Log.newLine();
logger.newLine();
const confirmed = await toggleConfirmAsync({
message: 'Are you sure you wish to proceed?',
});
const confirmed = nonInteractive
? true
: await toggleConfirmAsync({
message: 'Are you sure you wish to proceed?',
});
if (confirmed) {
const removalSpinner = ora(`Removing Apple devices on Expo`).start();
@@ -136,49 +152,55 @@ export default class DeviceDelete extends EasCommand {
}
logChosenDevices(
{ logger }: CommandContext,
chosenDevices: (AppleDevice | AppleDeviceQueryResult)[],
appleTeamName: Maybe<string> | undefined,
appleTeamIdentifier: string
): void {
Log.addNewLineIfNone();
logger.addNewLineIfNone();
chosenDevices.forEach(device => {
Log.log(
logger.log(
formatDevice(device, {
appleTeamName,
appleTeamIdentifier: appleTeamIdentifier!,
})
);
Log.newLine();
logger.newLine();
});
}
async chooseDevicesToDeleteAsync(
{ nonInteractive, logger }: CommandContext,
appleDevices: AppleDeviceQueryResult[],
udids: string[]
): Promise<(AppleDevice | AppleDeviceQueryResult)[]> {
let chosenDevices: (AppleDeviceQueryResult | AppleDevice)[] = [];
Log.newLine();
logger.newLine();
if (udids) {
udids.forEach(udid => {
const foundDevice = appleDevices.find(device => device.identifier === udid);
if (foundDevice) {
chosenDevices.push(foundDevice);
} else {
Log.warn(`No device found with UDID ${udid}.`);
logger.warn(`No device found with UDID ${udid}.`);
}
});
}
if (chosenDevices.length === 0) {
Log.addNewLineIfNone();
if (nonInteractive) {
throw new Error('Must supply `udid` paramter in non-interactive mode');
}
logger.addNewLineIfNone();
chosenDevices = await chooseDevicesToDeleteAsync(appleDevices);
Log.newLine();
logger.newLine();
}
return chosenDevices;
}
async getDevicesForTeamAsync(
{ ora }: CommandContext,
accountName: string,
appleTeamIdentifier: string
): Promise<AppleDevicesByTeamIdentifierQueryResult | undefined> {
@@ -208,7 +230,10 @@ export default class DeviceDelete extends EasCommand {
}
}
async askForAppleTeamAsync(accountName: string): Promise<string | undefined> {
async askForAppleTeamAsync(
{ prompts: { promptAsync }, ora }: CommandContext,
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 description = 'list all registered devices for your account';
static override description = 'list all registered devices for your account';
static flags = {
static override 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 description = 'view a device for your project';
static override description = 'view a device for your project';
static args = [{ name: 'UDID' }];
static override 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 description = 'display environment info';
static override description = 'display environment info';
protected requiresAuthentication = false;
protected override requiresAuthentication = false;
async runAsync(): Promise<void> {
const info = await envinfo.run(
@@ -14,10 +14,11 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPull extends EasCommand {
static description = 'generate the local store configuration from the app stores';
static override description = 'generate the local store configuration from the app stores';
static flags = {
static override flags = {
profile: Flags.string({
char: 'e',
description:
'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
}),
@@ -12,10 +12,11 @@ import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUt
import { ensureLoggedInAsync } from '../../user/actions';
export default class MetadataPush extends EasCommand {
static description = 'sync the local store configuration to the app stores';
static override description = 'sync the local store configuration to the app stores';
static flags = {
static override 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
@@ -0,0 +1,45 @@
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 description = 'information about the current project';
static override 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 description = 'create or link an EAS project';
static aliases = ['init'];
static override description = 'create or link an EAS project';
static override aliases = ['init'];
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
@@ -20,9 +20,10 @@ import { getActorDisplayName } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretCreate extends EasCommand {
static description = 'create an environment secret on the current project or owner account';
static override description =
'create an environment secret on the current project or owner account';
static flags = {
static override 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 description = 'delete an environment secret by ID';
static override description = 'delete an environment secret by ID';
static flags = {
static override 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 permamently delete secret${
`You are about to permanently 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 description = 'list environment secrets available for your current app';
static override description = 'list environment secrets available for your current app';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
+6 -5
View File
@@ -1,4 +1,4 @@
import { EasJsonReader } from '@expo/eas-json';
import { EasJsonAccessor } from '@expo/eas-json';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
@@ -47,15 +47,16 @@ interface CommandFlags {
}
export default class Submit extends EasCommand {
static description = 'submit app binary to App Store and/or Play Store';
static aliases = ['build:submit'];
static override description = 'submit app binary to App Store and/or Play Store';
static override aliases = ['build:submit'];
static flags = {
static override 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.',
}),
@@ -106,7 +107,7 @@ export default class Submit extends EasCommand {
const platforms = toPlatforms(flagsWithPlatform.requestedPlatform);
const submissionProfiles = await getProfilesAsync({
type: 'submit',
easJsonReader: new EasJsonReader(projectDir),
easJsonAccessor: new EasJsonAccessor(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 description = 'configure the project to support EAS Update';
static override description = 'configure the project to support EAS Update';
static flags = {
static override flags = {
platform: Flags.enum({
description: 'Platform to configure',
char: 'p',
@@ -117,6 +117,7 @@ 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)) {
@@ -129,6 +130,26 @@ 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
@@ -142,6 +163,22 @@ 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;
}
@@ -153,6 +190,14 @@ async function configureAppJSONForEASUpdateAsync({
},
updates,
};
newConfigOnlyAddedValues = {
android: {
runtimeVersion: newAndroidRuntimeVersion,
},
updates: {
url: easUpdateURL,
},
};
break;
}
case RequestedPlatform.Ios: {
@@ -163,6 +208,14 @@ async function configureAppJSONForEASUpdateAsync({
},
updates,
};
newConfigOnlyAddedValues = {
ios: {
runtimeVersion: newIosRuntimeVersion,
},
updates: {
url: easUpdateURL,
},
};
break;
}
default: {
@@ -172,8 +225,8 @@ async function configureAppJSONForEASUpdateAsync({
const result = await modifyConfigAsync(projectDir, newConfig);
const prexistingAndroidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
const prexistingIosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
const preexistingAndroidRuntimeVersion = exp.android?.runtimeVersion ?? exp.runtimeVersion;
const preexistingIosRuntimeVersion = exp.ios?.runtimeVersion ?? exp.runtimeVersion;
switch (result.type) {
case 'success':
if (exp.updates?.url) {
@@ -187,7 +240,7 @@ async function configureAppJSONForEASUpdateAsync({
}
if (
!prexistingAndroidRuntimeVersion &&
!preexistingAndroidRuntimeVersion &&
[RequestedPlatform.Android, RequestedPlatform.All].includes(platform)
) {
Log.withTick(
@@ -197,7 +250,7 @@ async function configureAppJSONForEASUpdateAsync({
);
}
if (
!prexistingIosRuntimeVersion &&
!preexistingIosRuntimeVersion &&
[RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)
) {
Log.withTick(
@@ -219,7 +272,7 @@ async function configureAppJSONForEASUpdateAsync({
'https://expo.fyi/eas-update-config.md'
)}\n`
);
Log.log(chalk.bold(JSON.stringify(newConfig, null, 2)));
Log.log(chalk.bold(JSON.stringify(newConfigOnlyAddedValues, 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 description = 'delete all the updates in an update group';
static override description = 'delete all the updates in an update group';
static args = [
static override args = [
{
name: 'groupId',
required: true,
@@ -46,7 +46,7 @@ export default class UpdateDelete extends EasCommand {
},
];
static flags = {
static override 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 cacheing 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 caching 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 description = 'publish an update group';
static override description = 'publish an update group';
static flags = {
static override 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 description = 'view the recent updates for a branch';
static override description = 'view the recent updates for a branch';
static flags = {
static override 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 description = 'update group details';
static override description = 'update group details';
static args = [
static override args = [
{
name: 'groupId',
required: true,
@@ -61,7 +61,7 @@ export default class UpdateView extends EasCommand {
},
];
static flags = {
static override 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 description = 'create a webhook';
static override description = 'create a webhook';
static flags = {
static override 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 description = 'delete a webhook';
static override description = 'delete a webhook';
static args = [
static override 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 permamently delete this webhook.\nThis action is irreversible.`);
Log.warn(`You are about to permanently 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 description = 'list webhooks';
static override description = 'list webhooks';
static flags = {
static override 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 description = 'update a webhook';
static override description = 'update a webhook';
static flags = {
static override 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 description = 'view a webhook';
static override description = 'view a webhook';
static args = [
static override args = [
{
name: 'ID',
required: true,
@@ -76,9 +76,13 @@ export const testPushKey: ApplePushKeyFragment = {
iosAppCredentialsList: [],
};
export const testTargets: Target[] = [
{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com', entitlements: {} },
];
export const testTarget = {
targetName: 'testapp',
bundleIdentifier: 'foo.bar.com',
entitlements: {},
};
export const testTargets: Target[] = [testTarget];
export const testProvisioningProfileFragment: AppleProvisioningProfileFragment = {
id: 'test-prov-prof-id-1',
@@ -2,10 +2,9 @@ import { Platform } from '@expo/eas-build-job';
import {
CredentialsSource,
DistributionType,
EasJsonReader,
EasJsonAccessor,
IosEnterpriseProvisioning,
} from '@expo/eas-json';
import fs from 'fs-extra';
import { CommonIosAppCredentialsFragment } from '../../graphql/generated';
import Log from '../../log';
@@ -101,7 +100,7 @@ export default class IosCredentialsProvider {
return null;
}
if (ctx.easJsonCliConfig?.promptToConfigurePushNotfications === false) {
if (ctx.easJsonCliConfig?.promptToConfigurePushNotifications === false) {
return null;
}
@@ -129,10 +128,16 @@ export default class IosCredentialsProvider {
private async disablePushNotificationsSetupInEasJsonAsync(
ctx: CredentialsContext
): Promise<void> {
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`);
const easJsonAccessor = new EasJsonAccessor(ctx.projectDir);
await easJsonAccessor.readRawJsonAsync();
easJsonAccessor.patch(easJsonRawObject => {
easJsonRawObject.cli = {
...easJsonRawObject?.cli,
promptToConfigurePushNotifications: false,
};
return easJsonRawObject;
});
await easJsonAccessor.writeAsync();
Log.withTick('Updated eas.json');
}
@@ -17,6 +17,7 @@ jest.mock('fs');
jest.mock('../validators/validateProvisioningProfile', () => ({
validateProvisioningProfileAsync: async (
_ctx: any,
_target: any,
_app: any,
buildCredentials: Partial<IosAppBuildCredentialsFragment> | null
): Promise<boolean> => {
@@ -7,16 +7,19 @@ 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
) {}
@@ -43,8 +46,10 @@ export class ConfigureProvisioningProfile {
return null;
}
const applePlatform = await getApplePlatformFromSdkRoot(this.target);
const profilesFromApple = await ctx.appStore.listProvisioningProfilesAsync(
this.app.bundleIdentifier
this.app.bundleIdentifier,
applePlatform
);
const [matchingProfile] = profilesFromApple.filter(appleInfo =>
developerPortalIdentifier
@@ -11,12 +11,14 @@ 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
) {}
@@ -63,7 +65,7 @@ export class CreateProvisioningProfile {
this.distributionCertificate.certificatePassword,
'Distribution Certificate must have certificate password'
);
return await generateProvisioningProfileAsync(ctx, this.app.bundleIdentifier, {
return await generateProvisioningProfileAsync(ctx, this.target, this.app.bundleIdentifier, {
certId: this.distributionCertificate.developerPortalIdentifier ?? undefined,
certP12: this.distributionCertificate.certificateP12,
certPassword: this.distributionCertificate.certificatePassword,
@@ -1,11 +1,13 @@
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
@@ -20,11 +22,18 @@ 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 (!!)
return await ctx.appStore.createProvisioningProfileAsync(bundleIdentifier, distCert, profileName);
const applePlatform = await getApplePlatformFromSdkRoot(target);
return await ctx.appStore.createProvisioningProfileAsync(
bundleIdentifier,
distCert,
profileName,
applePlatform
);
}
@@ -1,3 +1,4 @@
import { ProfileType } from '@expo/apple-utils';
import assert from 'assert';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
@@ -12,11 +13,14 @@ 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';
@@ -29,12 +33,18 @@ enum ReuseAction {
No,
}
interface Options {
app: AppLookupParams;
target: Target;
}
export class SetUpAdhocProvisioningProfile {
constructor(private app: AppLookupParams) {}
constructor(private options: Options) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const { app } = this.options;
const distCert = await new SetUpDistributionCertificate(
this.app,
app,
IosDistributionType.AdHoc
).runAsync(ctx);
@@ -42,7 +52,7 @@ export class SetUpAdhocProvisioningProfile {
if (ctx.nonInteractive) {
if (areBuildCredentialsSetup) {
return nullthrows(await getBuildCredentialsAsync(ctx, this.app, IosDistributionType.AdHoc));
return nullthrows(await getBuildCredentialsAsync(ctx, app, IosDistributionType.AdHoc));
} else {
throw new MissingCredentialsNonInteractiveError(
'Provisioning profile is not configured correctly. Run this command again in interactive mode.'
@@ -52,7 +62,7 @@ export class SetUpAdhocProvisioningProfile {
const currentBuildCredentials = await getBuildCredentialsAsync(
ctx,
this.app,
app,
IosDistributionType.AdHoc
);
if (areBuildCredentialsSetup) {
@@ -69,9 +79,11 @@ export class SetUpAdhocProvisioningProfile {
ctx: CredentialsContext,
distCert: AppleDistributionCertificateFragment
): Promise<IosAppBuildCredentialsFragment> {
const { app, target } = this.options;
const currentBuildCredentials = await getBuildCredentialsAsync(
ctx,
this.app,
app,
IosDistributionType.AdHoc
);
@@ -80,12 +92,12 @@ export class SetUpAdhocProvisioningProfile {
distCert.appleTeam ?? currentBuildCredentials?.provisioningProfile?.appleTeam ?? null;
if (!appleTeam) {
await ctx.appStore.ensureAuthenticatedAsync();
appleTeam = await resolveAppleTeamIfAuthenticatedAsync(ctx, this.app);
appleTeam = await resolveAppleTeamIfAuthenticatedAsync(ctx, app);
}
assert(appleTeam, 'Apple Team must be defined here');
// 2. Fetch devices registered on EAS servers
let registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(this.app, appleTeam);
let registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(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?`,
@@ -109,16 +121,22 @@ 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),
this.app.bundleIdentifier,
distCert.serialNumber
app.bundleIdentifier,
distCert.serialNumber,
profileType
);
// 5. Create or update the profile on servers
const appleAppIdentifier = await ctx.ios.createOrGetExistingAppleAppIdentifierAsync(
this.app,
app,
appleTeam
);
let appleProvisioningProfile: AppleProvisioningProfileFragment | null = null;
@@ -131,7 +149,7 @@ export class SetUpAdhocProvisioningProfile {
currentBuildCredentials.provisioningProfile.id,
]);
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
this.app,
app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
@@ -143,7 +161,7 @@ export class SetUpAdhocProvisioningProfile {
}
} else {
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
this.app,
app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
@@ -156,7 +174,7 @@ export class SetUpAdhocProvisioningProfile {
assert(appleProvisioningProfile);
return await assignBuildCredentialsAsync(
ctx,
this.app,
app,
IosDistributionType.AdHoc,
distCert,
appleProvisioningProfile,
@@ -165,22 +183,20 @@ export class SetUpAdhocProvisioningProfile {
}
private async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
const buildCredentials = await getBuildCredentialsAsync(
ctx,
this.app,
IosDistributionType.AdHoc
);
return await validateProvisioningProfileAsync(ctx, this.app, buildCredentials);
const { app, target } = this.options;
const buildCredentials = await getBuildCredentialsAsync(ctx, app, IosDistributionType.AdHoc);
return await validateProvisioningProfileAsync(ctx, target, 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(this.app, appleTeam);
const registeredAppleDevices = await ctx.ios.getDevicesForAppleTeamAsync(app, appleTeam);
const provisionedDevices = provisioningProfile.appleDevices;
@@ -254,7 +270,8 @@ export class SetUpAdhocProvisioningProfile {
ctx: CredentialsContext,
appleTeam: AppleTeamFragment
): Promise<AppleDeviceFragment[]> {
const action = new DeviceCreateAction(ctx.appStore, this.app.account, appleTeam);
const { app } = this.options;
const action = new DeviceCreateAction(ctx.appStore, app.account, appleTeam);
const method = await action.runAsync();
while (true) {
@@ -265,7 +282,7 @@ export class SetUpAdhocProvisioningProfile {
}
Log.newLine();
const devices = await ctx.ios.getDevicesForAppleTeamAsync(this.app, appleTeam, {
const devices = await ctx.ios.getDevicesForAppleTeamAsync(app, appleTeam, {
useCache: false,
});
if (devices.length === 0) {
@@ -51,6 +51,7 @@ export class SetUpBuildCredentials {
enterpriseProvisioning: this.options.enterpriseProvisioning,
distribution: this.options.distribution,
entitlements: target.entitlements,
target,
app: {
...this.options.app,
bundleIdentifier: target.bundleIdentifier,
@@ -3,6 +3,7 @@ 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';
@@ -14,11 +15,17 @@ 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 app: AppLookupParams) {}
constructor(private options: Options) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const buildCredentials = await getAllBuildCredentialsAsync(ctx, this.app);
const buildCredentials = await getAllBuildCredentialsAsync(ctx, this.options.app);
const adhocBuildCredentialsExist =
buildCredentials.filter(
@@ -87,15 +94,18 @@ export class SetUpInternalProvisioningProfile {
private async setupAdhocProvisioningProfileAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
return await new SetUpAdhocProvisioningProfile(this.app).runAsync(ctx);
const { app, target } = this.options;
return await new SetUpAdhocProvisioningProfile({ app, target }).runAsync(ctx);
}
private async setupUniversalProvisioningProfileAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
return await new SetUpProvisioningProfile(this.app, IosDistributionType.Enterprise).runAsync(
ctx
);
return await new SetUpProvisioningProfile(
this.options.app,
this.options.target,
IosDistributionType.Enterprise
).runAsync(ctx);
}
private async askForDistributionTypeAndSetupAsync(
@@ -6,11 +6,13 @@ 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,
@@ -26,11 +28,15 @@ import { SetUpDistributionCertificate } from './SetUpDistributionCertificate';
* Sets up either APP_STORE or ENTERPRISE provisioning profiles
*/
export class SetUpProvisioningProfile {
constructor(private app: AppLookupParams, private distributionType: IosDistributionType) {}
constructor(
private app: AppLookupParams,
private target: Target,
private distributionType: IosDistributionType
) {}
async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
const buildCredentials = await getBuildCredentialsAsync(ctx, this.app, this.distributionType);
return await validateProvisioningProfileAsync(ctx, this.app, buildCredentials);
return await validateProvisioningProfileAsync(ctx, this.target, this.app, buildCredentials);
}
async assignNewAndDeleteOldProfileAsync(
@@ -48,9 +54,11 @@ export class SetUpProvisioningProfile {
ctx: CredentialsContext,
distCert: AppleDistributionCertificateFragment
): Promise<IosAppBuildCredentialsFragment> {
const provisioningProfile = await new CreateProvisioningProfile(this.app, distCert).runAsync(
ctx
);
const provisioningProfile = await new CreateProvisioningProfile(
this.app,
this.target,
distCert
).runAsync(ctx);
return await assignBuildCredentialsAsync(
ctx,
this.app,
@@ -67,6 +75,7 @@ export class SetUpProvisioningProfile {
): Promise<IosAppBuildCredentialsFragment | null> {
const profileConfigurator = new ConfigureProvisioningProfile(
this.app,
this.target,
distCert,
originalProvisioningProfile
);
@@ -105,8 +114,10 @@ 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
this.app.bundleIdentifier,
applePlatform
);
const currentProfileFromServer = this.getCurrentProfileStoreInfo(
existingProfiles,
@@ -8,6 +8,7 @@ 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';
@@ -17,6 +18,7 @@ interface Options {
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
entitlements: JSONObject;
target: Target;
}
export class SetUpTargetBuildCredentials {
constructor(private options: Options) {}
@@ -47,22 +49,25 @@ export class SetUpTargetBuildCredentials {
async setupBuildCredentialsAsync(
ctx: CredentialsContext
): Promise<IosAppBuildCredentialsFragment> {
const { app, distribution, enterpriseProvisioning } = this.options;
const { app, distribution, enterpriseProvisioning, target } = this.options;
if (distribution === 'internal') {
if (enterpriseProvisioning === 'adhoc') {
return await new SetUpAdhocProvisioningProfile(app).runAsync(ctx);
return await new SetUpAdhocProvisioningProfile({ app, target }).runAsync(ctx);
} else if (enterpriseProvisioning === 'universal') {
return await new SetUpProvisioningProfile(
app,
target,
GraphQLIosDistributionType.Enterprise
).runAsync(ctx);
} else {
return await new SetUpInternalProvisioningProfile(app).runAsync(ctx);
return await new SetUpInternalProvisioningProfile({ app, target }).runAsync(ctx);
}
} else {
return await new SetUpProvisioningProfile(app, GraphQLIosDistributionType.AppStore).runAsync(
ctx
);
return await new SetUpProvisioningProfile(
app,
target,
GraphQLIosDistributionType.AppStore
).runAsync(ctx);
}
}
}
@@ -6,6 +6,7 @@ import {
testDistCertFragmentNoDependencies,
testProvisioningProfile,
testProvisioningProfileFragment,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -36,6 +37,7 @@ describe('ConfigureProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const provProfConfigurator = new ConfigureProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies,
testProvisioningProfileFragment
);
@@ -53,6 +55,7 @@ describe('ConfigureProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const provProfConfigurator = new ConfigureProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies,
testProvisioningProfileFragment
);
@@ -5,6 +5,7 @@ import { createCtxMock } from '../../../__tests__/fixtures-context';
import {
testDistCertFragmentNoDependencies,
testProvisioningProfile,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -28,6 +29,7 @@ describe('CreateProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const createProvProfAction = new CreateProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies
);
await createProvProfAction.runAsync(ctx);
@@ -44,6 +46,7 @@ describe('CreateProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const createProvProfAction = new CreateProvisioningProfile(
appLookupParams,
testTarget,
testDistCertFragmentNoDependencies
);
await expect(createProvProfAction.runAsync(ctx)).rejects.toThrowError(
@@ -2,6 +2,7 @@ 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';
@@ -41,9 +42,12 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -72,9 +76,12 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -110,9 +117,12 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -141,9 +151,12 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -174,9 +187,12 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -205,9 +221,12 @@ describe(SetUpInternalProvisioningProfile, () => {
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: false,
@@ -235,9 +254,12 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -253,9 +275,12 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -269,9 +294,12 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -291,9 +319,12 @@ describe(SetUpInternalProvisioningProfile, () => {
return buildCredentials;
});
const action = new SetUpInternalProvisioningProfile({
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
app: {
account: { id: 'account-id', name: 'account-name' },
bundleIdentifier: 'com.expo.test',
projectName: 'testproject',
},
target: testTarget,
});
const ctx = createCtxMock({
nonInteractive: true,
@@ -10,6 +10,7 @@ import {
testAppleAppIdentifierFragment,
testCommonIosAppCredentialsFragment,
testIosAppBuildCredentialsFragment,
testTarget,
testTargets,
} from '../../../__tests__/fixtures-ios';
import { MissingCredentialsNonInteractiveError } from '../../../errors';
@@ -55,6 +56,7 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -87,6 +89,7 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -119,6 +122,7 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -147,6 +151,7 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await setupProvisioningProfileAction.runAsync(ctx);
@@ -166,6 +171,7 @@ describe('SetUpProvisioningProfile', () => {
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
appLookupParams,
testTarget,
IosDistributionType.AppStore
);
await expect(setupProvisioningProfileAction.runAsync(ctx)).rejects.toThrowError(
@@ -1,3 +1,5 @@
import { ProfileType } from '@expo/app-store';
import Log from '../../../log';
import {
AscApiKey,
@@ -22,6 +24,7 @@ import {
isUserAuthCtx,
} from './authenticate';
import { AuthCtx, AuthenticationMode, UserAuthCtx } from './authenticateTypes';
import { ApplePlatform } from './constants';
import {
createDistributionCertificateAsync,
listDistributionCertificatesAsync,
@@ -129,16 +132,18 @@ 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, profileClass);
return await listProvisioningProfilesAsync(ctx, bundleIdentifier, applePlatform, profileClass);
}
public async createProvisioningProfileAsync(
bundleIdentifier: string,
distCert: DistributionCertificate,
profileName: string,
applePlatform: ApplePlatform,
profileClass?: ProfileClass
): Promise<ProvisioningProfile> {
const ctx = await this.ensureAuthenticatedAsync();
@@ -147,29 +152,33 @@ 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, profileClass);
return await revokeProvisioningProfileAsync(ctx, bundleIdentifier, applePlatform, profileClass);
}
public async createOrReuseAdhocProvisioningProfileAsync(
udids: string[],
bundleIdentifier: string,
distCertSerialNumber: string
distCertSerialNumber: string,
profileType: ProfileType
): Promise<ProvisioningProfile> {
const ctx = await this.ensureAuthenticatedAsync();
return await createOrReuseAdhocProvisioningProfileAsync(
ctx,
udids,
bundleIdentifier,
distCertSerialNumber
distCertSerialNumber,
profileType
);
}
@@ -1,21 +1,5 @@
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;
@@ -0,0 +1,2 @@
// 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: [CertificateType.IOS_DISTRIBUTION, CertificateType.DISTRIBUTION],
},
},
});
@@ -11,6 +11,7 @@ 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 {
@@ -18,7 +19,37 @@ export enum ProfileClass {
General = 'general',
}
function resolveProfileType(profileClass: ProfileClass, isEnterprise?: boolean): ProfileType {
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 {
if (isEnterprise) {
return profileClass === ProfileClass.Adhoc
? ProfileType.IOS_APP_ADHOC
@@ -141,12 +172,13 @@ 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(profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
const profiles = (await getProfilesForBundleIdAsync(context, bundleIdentifier)).filter(
profile => profile.attributes.profileType === profileType
);
@@ -168,6 +200,7 @@ 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();
@@ -180,7 +213,7 @@ export async function createProvisioningProfileAsync(
}
const context = getRequestContext(authCtx);
const profileType = resolveProfileType(profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
const certificate = await getCertificateBySerialNumberAsync(
context,
@@ -210,6 +243,7 @@ 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();
@@ -217,7 +251,7 @@ export async function revokeProvisioningProfileAsync(
const context = getRequestContext(authCtx);
const profiles = await getProfilesForBundleIdAsync(context, bundleIdentifier);
const profileType = resolveProfileType(profileClass, authCtx.team.inHouse);
const profileType = resolveProfileType(applePlatform, profileClass, authCtx.team.inHouse);
await Promise.all(
profiles
.filter(profile => profile.attributes.profileType === profileType)
@@ -25,10 +25,8 @@ async function registerMissingDevicesAsync(
context: RequestContext,
udids: string[]
): Promise<Device[]> {
const allIosProfileDevices = await Device.getAllIOSProfileDevicesAsync(context);
const alreadyAdded = allIosProfileDevices.filter(device =>
udids.includes(device.attributes.udid)
);
const allDevices = await Device.getAsync(context);
const alreadyAdded = allDevices.filter(device => udids.includes(device.attributes.udid));
const alreadyAddedUdids = alreadyAdded.map(i => i.attributes.udid);
await Promise.all(
@@ -46,17 +44,20 @@ async function registerMissingDevicesAsync(
return alreadyAdded;
}
async function findProfileByBundleIdAsync(
async function findProfileAsync(
context: RequestContext,
bundleId: string,
certSerialNumber: string
{
bundleId,
certSerialNumber,
profileType,
}: { bundleId: string; certSerialNumber: string; profileType: ProfileType }
): Promise<{
profile: Profile | null;
didUpdate: boolean;
}> {
const expoProfiles = (await getProfilesForBundleIdAsync(context, bundleId)).filter(profile => {
return (
profile.attributes.profileType === ProfileType.IOS_APP_ADHOC &&
profile.attributes.profileType === profileType &&
profile.attributes.name.startsWith('*[expo]') &&
profile.attributes.profileState !== ProfileState.EXPIRED
);
@@ -132,11 +133,13 @@ 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.
@@ -155,7 +158,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 findProfileByBundleIdAsync(context, bundleId, certSerialNumber);
const results = await findProfileAsync(context, { bundleId, certSerialNumber, profileType });
existingProfile = results.profile;
didUpdate = results.didUpdate;
}
@@ -192,8 +195,9 @@ async function manageAdHocProfilesAsync(
await existingProfile.regenerateAsync();
}
const updatedProfile = (await findProfileByBundleIdAsync(context, bundleId, certSerialNumber))
.profile;
const updatedProfile = (
await findProfileAsync(context, { bundleId, certSerialNumber, profileType })
).profile;
if (!updatedProfile) {
throw new Error(
`Failed to locate updated profile for bundle identifier "${bundleId}" and serial number "${certSerialNumber}"`
@@ -226,7 +230,7 @@ async function manageAdHocProfilesAsync(
name: `*[expo] ${bundleId} AdHoc ${Date.now()}`,
certificates: [distributionCertificate.id],
devices: devices.map(device => device.id),
profileType: ProfileType.IOS_APP_ADHOC,
profileType,
});
return {
@@ -242,7 +246,8 @@ export async function createOrReuseAdhocProvisioningProfileAsync(
authCtx: AuthCtx,
udids: string[],
bundleIdentifier: string,
distCertSerialNumber: string
distCertSerialNumber: string,
profileType: ProfileType
): Promise<ProvisioningProfile> {
const spinner = ora(`Handling Apple ad hoc provisioning profiles`).start();
try {
@@ -252,6 +257,7 @@ export async function createOrReuseAdhocProvisioningProfileAsync(
udids,
bundleId: bundleIdentifier,
certSerialNumber: distCertSerialNumber,
profileType,
});
if (didCreate) {
@@ -6,14 +6,17 @@ 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> {
@@ -37,7 +40,7 @@ export async function validateProvisioningProfileAsync(
return true;
}
return await validateProvisioningProfileWithAppleAsync(ctx, app, buildCredentials);
return await validateProvisioningProfileWithAppleAsync(ctx, target, app, buildCredentials);
}
function validateProvisioningProfileWithoutApple(
@@ -84,14 +87,17 @@ 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,6 +258,7 @@ export class ManageIos {
}
await this.setupProvisioningProfileWithSpecificDistCertAsync(
ctx,
target,
appLookupParams,
distCert,
distributionType
@@ -274,6 +275,7 @@ export class ManageIos {
if (confirm) {
await this.setupProvisioningProfileWithSpecificDistCertAsync(
ctx,
target,
appLookupParams,
distCert,
distributionType
@@ -377,6 +379,7 @@ export class ManageIos {
private async setupProvisioningProfileWithSpecificDistCertAsync(
ctx: CredentialsContext,
target: Target,
appLookupParams: AppLookupParams,
distCert: AppleDistributionCertificateFragment,
distributionType: IosDistributionTypeGraphql
@@ -384,12 +387,14 @@ 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(
appLookupParams
).runWithDistributionCertificateAsync(ctx, distCert);
return await new SetUpAdhocProvisioningProfile({
app: appLookupParams,
target,
}).runWithDistributionCertificateAsync(ctx, distCert);
} else {
return await new SetUpProvisioningProfile(
appLookupParams,
target,
distributionType
).createAndAssignProfileAsync(ctx, distCert);
}
@@ -1,25 +1,29 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
import { BuildProfile, EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import Log from '../../log';
import { promptAsync } from '../../prompts';
export class SelectBuildProfileFromEasJson<T extends Platform> {
private easJsonReader: EasJsonReader;
private easJsonAccessor: EasJsonAccessor;
constructor(projectDir: string, private platform: T) {
this.easJsonReader = new EasJsonReader(projectDir);
this.easJsonAccessor = new EasJsonAccessor(projectDir);
}
async runAsync(): Promise<BuildProfile<T>> {
const profileName = await this.getProfileNameFromEasConfigAsync();
const easConfig = await this.easJsonReader.getBuildProfileAsync<T>(this.platform, profileName);
const easConfig = await EasJsonUtils.getBuildProfileAsync<T>(
this.easJsonAccessor,
this.platform,
profileName
);
Log.succeed(`Using build profile: ${profileName}`);
return easConfig;
}
async getProfileNameFromEasConfigAsync(): Promise<string> {
const buildProfileNames = await this.easJsonReader.getBuildProfileNamesAsync();
const buildProfileNames = await EasJsonUtils.getBuildProfileNamesAsync(this.easJsonAccessor);
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,11 +93,13 @@ async function findUnregisteredPortalDevicesAsync(
return acc;
}, {} as Record<string, AppleDeviceFragmentWithAppleTeam>);
const portalDevices = await Device.getAllIOSProfileDevicesAsync(getRequestContext(appleAuthCtx));
const portalDevices = await Device.getAsync(getRequestContext(appleAuthCtx));
return portalDevices.filter(
portalDevice =>
!(portalDevice.attributes.udid in expoRegisteredDevicesByUdid) &&
[DeviceClass.IPAD, DeviceClass.IPHONE].includes(portalDevice.attributes.deviceClass)
[DeviceClass.IPAD, DeviceClass.IPHONE, DeviceClass.APPLE_TV].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