Compare commits

..
Author SHA1 Message Date
Quinlan Jung f20d970616 tmp 2024-05-20 15:55:47 +02:00
Quinlan Jung f3712d5dfb amend apple team 2024-05-20 15:55:47 +02:00
Quinlan Jung cba211d82c manage asc api key 2024-05-20 15:55:19 +02:00
Expo CI 61109ea2c1 update CHANGELOG.md 2024-05-17 07:32:39 +00:00
Szymon Dziedzic f889929f5f v9.0.7 2024-05-17 09:27:12 +02:00
Szymon Dziedzic 4bb314192e upgrade eas-cli-local-build-plugin to 1.0.112 2024-05-17 09:27:05 +02:00
Szymon Dziedzic 1a170283db [eas-cli] add --repack flag to eas build command (#2384)
* [eas-cli] add `--repack` flag to `eas build` command

* update Android prepare job
2024-05-17 09:18:38 +02:00
Expo CI ca803eed3f update CHANGELOG.md 2024-05-15 10:57:34 +00:00
Stanisław Chmiela 4b26472975 v9.0.6 2024-05-15 12:52:40 +02:00
Stanisław Chmiela 70b854abb1 [eas-cli] Add turtleJobRunId to UpdatePublishMutation input (#2381)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

We want to save the relationship between a job run and an update.

# How

See https://github.com/expo/universe/pull/15329.

# Test Plan

Tested with staging.
2024-05-15 12:51:49 +02:00
Expo CI 2576a97704 update CHANGELOG.md 2024-05-13 15:59:00 +00:00
Szymon Dziedzic 15e50b1536 v9.0.5 2024-05-13 17:53:24 +02:00
Szymon Dziedzic d6f3784cc0 [eas-cli] install dependencies before resolving expo config (#2380) 2024-05-13 17:52:23 +02:00
Expo CI a6f7d37d71 update CHANGELOG.md 2024-05-13 14:54:54 +00:00
Szymon Dziedzic 675c36a157 v9.0.4 2024-05-13 16:49:55 +02:00
Bartosz Kaszubowski b2961a275b [eas-cli] diagnostics: add loader and progress information (#2378)
* [eas-cli] diagnostics: add loader and progress information

* add changelog entry
2024-05-13 16:47:13 +02:00
Szymon Dziedzic 57928d1fd6 [eas-cli] create keystore for Android emulator builds in init:onboarding (#2379)
* [eas-cli] create keystore for Android emulator builds in `init:onboarding`

* use correct build profile
2024-05-13 16:46:32 +02:00
Szymon Dziedzic ff9d533248 [eas-cli] add init:onboarding flow for no gh repo connected (#2373)
* [eas-cli] add `init:onboarding` flow for no gh repo connected

* fix types

* don't wait
2024-05-13 16:43:13 +02:00
Expo CI e1ebcc0b8a update CHANGELOG.md 2024-05-10 17:38:09 +00:00
Stanisław Chmiela 628a7262cf v9.0.3 2024-05-10 19:33:08 +02:00
Stanisław Chmiela 05be162ec1 [eas-cli] Prevent ora flooding logs (#2375)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

I noticed logs look off in CI when running `eas update --auto`.

<img width="1000" alt="Zrzut ekranu 2024-05-10 o 17 50 10" src="https://github.com/expo/eas-cli/assets/1151041/ed50d014-62f1-4342-bde2-0673f49d52b4">

# How

I pinpointed it to Ora being restarted on every log.

# Test Plan

Ran `CI=1 easd update --auto` and got ⬇️ which I think is much more readable.

```
CI=1 easd update --auto
- Exporting...
[expo-cli] Starting Metro Bundler
[expo-cli] Android Bundling complete 3492ms
[expo-cli] iOS Bundling complete 3493ms
[expo-cli] iOS Building Hermes bytecode for the bundle
[expo-cli] Android Building Hermes bytecode for the bundle
[expo-cli] 
[expo-cli] Bundle                      Size
[expo-cli] ┌ index.ios.hbc           858 kB
[expo-cli] ├ index.android.hbc       864 kB
[expo-cli] ├ index.ios.hbc.map      3.45 MB
[expo-cli] └ index.android.hbc.map  3.48 MB
[expo-cli] 
[expo-cli] 💡 JavaScript bundle sizes affect startup time. Learn more: https://expo.fyi/javascript-bundle-sizes
[expo-cli] Finished saving JS Bundles
[expo-cli] Dumping asset map
[expo-cli] Dumping source maps
[expo-cli] Preparing additional debugging files
[expo-cli] Export was successful. Your exported files can be found in dist
✔ Exported bundle(s)
- Uploading...
✔ Uploaded 2 app bundles
✔ Uploading assets skipped - no new assets found
ℹ 1 iOS asset, 1 Android asset (maximum: 20000 total per update). Learn more about asset limits: https://expo.fyi/eas-update-asset-limits
- Publishing...
✔ Published!
```
2024-05-10 19:31:55 +02:00
Bartosz Kaszubowski ff2fa425bd [eas-cli] fix typo in onboarding commit message (#2376) 2024-05-10 19:19:13 +02:00
Expo Bot a621e395fc Upgrade eas-build dependencies (#2372) 2024-05-10 17:53:25 +02:00
Stanisław Chmiela 2260f74434 s/Website link/EAS Dashboard when displaying update info (#2374)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Implementing @tchayen's suggestion.

# How

Changed wording

# Test Plan

None.
2024-05-10 17:47:31 +02:00
Szymon Dziedzic 6dc9253d2d [eas-cli] improve prompt asking user to specify target clone dir in eas init:onboarding (#2371) 2024-05-10 14:56:51 +02:00
Expo CI b6570bdfcf update CHANGELOG.md 2024-05-09 17:44:21 +00:00
68 changed files with 7461 additions and 1138 deletions

No files matched your search

+20
View File
@@ -12,6 +12,26 @@ This is the log of notable changes to EAS CLI and related packages.
### 🧹 Chores
## [9.0.7](https://github.com/expo/eas-cli/releases/tag/v9.0.7) - 2024-05-17
## [9.0.6](https://github.com/expo/eas-cli/releases/tag/v9.0.6) - 2024-05-15
## [9.0.5](https://github.com/expo/eas-cli/releases/tag/v9.0.5) - 2024-05-13
## [9.0.4](https://github.com/expo/eas-cli/releases/tag/v9.0.4) - 2024-05-13
### 🧹 Chores
- Add loader and progress information to the `diagnostics` command. ([#2378](https://github.com/expo/eas-cli/pull/2378) by [@simek](https://github.com/simek))
## [9.0.3](https://github.com/expo/eas-cli/releases/tag/v9.0.3) - 2024-05-10
### 🧹 Chores
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2372](https://github.com/expo/eas-cli/pull/2372) by [@expo-bot](https://github.com/expo-bot))
## [9.0.2](https://github.com/expo/eas-cli/releases/tag/v9.0.2) - 2024-05-09
## [9.0.1](https://github.com/expo/eas-cli/releases/tag/v9.0.1) - 2024-05-09
### 🧹 Chores
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "9.0.2",
"version": "9.0.7",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+59 -59
View File
@@ -140,7 +140,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/account/login.ts)_
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -157,7 +157,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -174,7 +174,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/account/view.ts)_
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -188,7 +188,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -240,7 +240,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -261,7 +261,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -281,7 +281,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -301,7 +301,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -324,7 +324,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -361,7 +361,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/index.ts)_
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -380,7 +380,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -397,7 +397,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -416,7 +416,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/delete.ts)_
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:inspect`
@@ -451,7 +451,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -492,7 +492,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/list.ts)_
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -518,7 +518,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/resign.ts)_
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -544,7 +544,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/run.ts)_
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -594,7 +594,7 @@ DESCRIPTION
get the latest version from EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/version/get.ts)_
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -613,7 +613,7 @@ DESCRIPTION
update version of an app
```
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/version/set.ts)_
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -632,7 +632,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -649,7 +649,7 @@ DESCRIPTION
view a build for your project
```
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/build/view.ts)_
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -670,7 +670,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -692,7 +692,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -712,7 +712,7 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -744,7 +744,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally.
```
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/channel/rollout.ts)_
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -767,7 +767,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -788,7 +788,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -805,7 +805,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/credentials/index.ts)_
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -823,7 +823,7 @@ DESCRIPTION
Set up credentials for building your project.
```
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas device:create`
@@ -837,7 +837,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/device/create.ts)_
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -857,7 +857,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -878,7 +878,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/device/list.ts)_
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -899,7 +899,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/device/rename.ts)_
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -913,7 +913,7 @@ DESCRIPTION
view a device for your project
```
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/device/view.ts)_
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -927,7 +927,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -1018,7 +1018,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1035,7 +1035,7 @@ DESCRIPTION
generate the local store configuration from the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1052,7 +1052,7 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas open`
@@ -1066,7 +1066,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1080,7 +1080,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/project/info.ts)_
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1102,7 +1102,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/project/init.ts)_
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -1125,7 +1125,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1143,7 +1143,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1157,7 +1157,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1177,7 +1177,7 @@ DESCRIPTION
read environment secrets from env file and store on the server
```
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/secret/push.ts)_
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/secret/push.ts)_
## `eas submit`
@@ -1208,7 +1208,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1241,7 +1241,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/index.ts)_
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1259,7 +1259,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1280,7 +1280,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1302,7 +1302,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/list.ts)_
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1329,7 +1329,7 @@ DESCRIPTION
roll back to an existing update
```
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded`
@@ -1356,7 +1356,7 @@ DESCRIPTION
roll back to the embedded update
```
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -1375,7 +1375,7 @@ DESCRIPTION
roll back to an embedded update or an existing update
```
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/rollback.ts)_
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -1395,7 +1395,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/update/view.ts)_
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1416,7 +1416,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1436,7 +1436,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1454,7 +1454,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1476,7 +1476,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1493,7 +1493,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v9.0.2/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v9.0.7/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+209
View File
@@ -11923,6 +11923,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "keyP8",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": null,
@@ -22728,6 +22744,49 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeploymentSignedUrlResult",
"description": null,
"fields": [
{
"name": "deploymentId",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": null,
"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": "DeploymentsConnection",
@@ -22789,6 +22848,72 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeploymentsMutation",
"description": null,
"fields": [
{
"name": "createSignedDeploymentUrl",
"description": "Create a signed deployment URL",
"args": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deploymentId",
"description": null,
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeploymentSignedUrlResult",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "DevDomainName",
"description": "A DevDomainName for a deployed serverless app",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DiscordUser",
@@ -29675,6 +29800,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "displayName",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "endedAt",
"description": null,
@@ -29838,6 +29975,38 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateGroups",
"description": null,
"args": [],
"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
}
],
"inputFields": null,
@@ -32886,6 +33055,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "turtleJobRunId",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateInfoGroup",
"description": null,
@@ -33775,6 +33956,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deployments",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeploymentsMutation",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "devDomainName",
"description": "Mutations that assign or modify DevDomainNames for apps",
@@ -38884,6 +39081,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "jobRun",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "JobRun",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "manifestFragment",
"description": null,
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "9.0.2",
"version": "9.0.7",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
@@ -13,8 +13,8 @@
"@expo/config": "8.5.4",
"@expo/config-plugins": "7.8.4",
"@expo/config-types": "50.0.0",
"@expo/eas-build-job": "1.0.107",
"@expo/eas-json": "9.0.1",
"@expo/eas-build-job": "1.0.108",
"@expo/eas-json": "9.0.4",
"@expo/json-file": "8.2.37",
"@expo/logger": "1.0.57",
"@expo/multipart-body-parser": "1.1.0",
@@ -28,7 +28,7 @@
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.0",
"@expo/steps": "1.0.107",
"@expo/steps": "1.0.108",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "^1.26.2",
"@oclif/plugin-autocomplete": "^2.3.10",
@@ -24,6 +24,7 @@ export function getMockAppFragment(): AppFragment {
return {
id: mockProjectId,
slug: 'testapp',
name: 'testapp',
fullName: '@testuser/testpp',
ownerAccount: {
id: 'test-account-id',
@@ -93,13 +93,18 @@ export async function prepareJobAsync(
experimental: {
prebuildCommand: buildProfile.prebuildCommand,
},
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
mode: buildProfile.config || ctx.repack ? BuildMode.CUSTOM : BuildMode.BUILD,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
...(ctx.repack && {
customBuildConfig: {
path: '__eas/repack.yml',
},
}),
loggerLevel: ctx.loggerLevel,
};
+1
View File
@@ -62,4 +62,5 @@ export interface BuildContext<T extends Platform> {
requiredPackageManager: NodePackageManager['name'] | null;
vcsClient: Client;
loggerLevel?: LoggerLevel;
repack: boolean;
}
+21 -1
View File
@@ -16,6 +16,9 @@ import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/An
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { CredentialsContext } from '../credentials/context';
import { AppStoreApiKeyPurpose } from '../credentials/ios/actions/AscApiKeyUtils';
import { getAppFromContextAsync } from '../credentials/ios/actions/BuildCredentialsUtils';
import { GetAscApiKey } from '../credentials/ios/actions/GetAscApiKey';
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
@@ -42,6 +45,7 @@ export async function createBuildContextAsync<T extends Platform>({
customBuildConfigMetadata,
buildLoggerLevel,
freezeCredentials,
repack,
}: {
buildProfileName: string;
buildProfile: BuildProfile<T>;
@@ -62,6 +66,7 @@ export async function createBuildContextAsync<T extends Platform>({
customBuildConfigMetadata?: CustomBuildConfigMetadata;
buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean;
repack: boolean;
}): Promise<BuildContext<T>> {
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
const projectName = exp.slug;
@@ -143,6 +148,7 @@ export async function createBuildContextAsync<T extends Platform>({
developmentClient,
requiredPackageManager,
loggerLevel: buildLoggerLevel,
repack,
};
if (platform === Platform.ANDROID) {
const common = commonContext as CommonContext<Platform.ANDROID>;
@@ -152,10 +158,24 @@ export async function createBuildContextAsync<T extends Platform>({
} as BuildContext<T>;
} else {
const common = commonContext as CommonContext<Platform.IOS>;
return {
const iosBuildCtx = {
...common,
ios: await createIosContextAsync(common),
} as BuildContext<T>;
const appLookupParams = {
...(await getAppFromContextAsync(credentialsCtx)),
bundleIdentifier: (iosBuildCtx as BuildContext<Platform.IOS>).ios.bundleIdentifier,
};
const getAscApiKeyAction = new GetAscApiKey(
appLookupParams,
AppStoreApiKeyPurpose.BUILD_SERVICE
);
const ascApiKeyManagedByExpo = await getAscApiKeyAction.runAsync(credentialsCtx);
if (ascApiKeyManagedByExpo) {
// TODO: set credentialCtx with the correct options
}
return iosBuildCtx;
}
}
+6 -1
View File
@@ -88,13 +88,18 @@ export async function prepareJobAsync(
experimental: {
prebuildCommand: buildProfile.prebuildCommand,
},
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
mode: buildProfile.config || ctx.repack ? BuildMode.CUSTOM : BuildMode.BUILD,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
...(ctx.repack && {
customBuildConfig: {
path: '__eas/repack.yml',
},
}),
loggerLevel: ctx.loggerLevel,
};
return sanitizeBuildJob(job) as Ios.Job;
+1 -1
View File
@@ -6,7 +6,7 @@ import semver from 'semver';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '1.0.107';
const PLUGIN_PACKAGE_VERSION = '1.0.112';
export enum LocalBuildMode {
/**
+1
View File
@@ -34,6 +34,7 @@ export async function collectMetadataAsync<T extends Platform>(
workflow: ctx.workflow,
projectDir: ctx.projectDir,
env: ctx.buildProfile.env,
cwd: ctx.projectDir,
})) ?? undefined,
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
...channelObject,
@@ -95,6 +95,7 @@ export interface BuildFlags {
message?: string;
buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean;
repack: boolean;
}
export async function runBuildAndSubmitAsync(
@@ -105,7 +106,9 @@ export async function runBuildAndSubmitAsync(
flags: BuildFlags,
actor: Actor,
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
): Promise<void> {
): Promise<{
buildIds: string[];
}> {
await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
@@ -213,7 +216,9 @@ export async function runBuildAndSubmitAsync(
}
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
return;
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
@@ -269,14 +274,18 @@ export async function runBuildAndSubmitAsync(
}
if (flags.localBuildOptions.localBuildMode) {
return;
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
if (!flags.wait) {
if (flags.json) {
printJsonOnlyOutput(startedBuilds.map(buildInfo => buildInfo.build));
}
return;
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
const { accountName } = Object.values(buildCtxByPlatform)[0];
@@ -317,6 +326,9 @@ export async function runBuildAndSubmitAsync(
}
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
}
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
async function prepareAndStartBuildAsync({
@@ -364,6 +376,7 @@ async function prepareAndStartBuildAsync({
customBuildConfigMetadata,
buildLoggerLevel: flags.buildLoggerLevel,
freezeCredentials: flags.freezeCredentials,
repack: flags.repack,
});
if (moreBuilds) {
@@ -80,6 +80,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@notnotbrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'notnotbrent' } as any,
});
@@ -100,6 +101,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@notnotbrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'notnotbrent' } as any,
});
@@ -124,6 +126,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@totallybrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'totallybrent' } as any,
});
@@ -172,6 +175,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@totallybrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'totallybrent' } as any,
});
@@ -196,6 +200,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@notnotbrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'notnotbrent' } as any,
});
@@ -265,6 +270,7 @@ describe(getProjectIdAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
fullName: '@notnotbrent/test',
name: 'test',
slug: 'test',
ownerAccount: { name: 'notnotbrent' } as any,
});
@@ -36,6 +36,7 @@ interface RawBuildFlags {
message?: string;
'build-logger-level'?: LoggerLevel;
'freeze-credentials': boolean;
repack: boolean;
}
export default class Build extends EasCommand {
@@ -109,6 +110,11 @@ export default class Build extends EasCommand {
default: false,
description: 'Prevent the build from updating credentials in non-interactive mode',
}),
repack: Flags.boolean({
default: false,
hidden: true,
description: 'Use the golden dev client build repack flow as it works for onboarding',
}),
...EasNonInteractiveAndJsonFlags,
};
@@ -223,6 +229,7 @@ export default class Build extends EasCommand {
message,
buildLoggerLevel: flags['build-logger-level'],
freezeCredentials: flags['freeze-credentials'],
repack: flags.repack,
};
}
@@ -120,6 +120,7 @@ export default class BuildInspect extends EasCommand {
workingdir: tmpWorkingdir,
artifactsDir: path.join(tmpWorkingdir, 'artifacts'),
},
repack: false,
},
actor,
getDynamicPrivateProjectConfigAsync
@@ -86,6 +86,7 @@ export default class BuildInternal extends EasCommand {
localBuildMode: LocalBuildMode.INTERNAL,
},
submitProfile: flags['auto-submit-with-profile'] ?? flags.profile,
repack: false,
},
actor,
getDynamicPrivateProjectConfigAsync
+6 -2
View File
@@ -1,8 +1,10 @@
import { Platform } from '@expo/eas-build-job';
import chalk from 'chalk';
import envinfo from 'envinfo';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import { ora } from '../ora';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { Client } from '../vcs/vcs';
@@ -20,6 +22,7 @@ export default class Diagnostics extends EasCommand {
nonInteractive: true,
});
const spinner = ora().start(`Gathering diagnostic information...`);
const info = await envinfo.run(
{
System: ['OS', 'Shell'],
@@ -40,9 +43,10 @@ export default class Diagnostics extends EasCommand {
npmGlobalPackages: ['eas-cli', 'expo-cli'],
},
{
title: `EAS CLI ${easCliVersion} environment info`,
title: chalk.bold(`EAS CLI ${easCliVersion} environment info`),
}
);
spinner.succeed('All needed information gathered!');
Log.log(info.trimEnd());
await this.printWorkflowAsync(projectDir, vcsClient);
@@ -56,7 +60,7 @@ export default class Diagnostics extends EasCommand {
if (androidWorkflow === iosWorkflow) {
Log.log(` Project workflow: ${androidWorkflow}`);
} else {
Log.log(` Project Workflow:`);
Log.log(` Project workflow:`);
Log.log(` Android: ${androidWorkflow}`);
Log.log(` iOS: ${iosWorkflow}`);
}
@@ -110,6 +110,7 @@ describe(ProjectInit.name, () => {
it('is no-op if already configured for id', async () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
name: 'testing-123',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
@@ -121,6 +122,7 @@ describe(ProjectInit.name, () => {
it('prompts to overwrite when different', async () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
name: 'testing-123',
slug: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
@@ -135,6 +137,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -152,6 +155,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -186,6 +190,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -198,6 +203,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -223,6 +229,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -247,6 +254,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -273,6 +281,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -288,6 +297,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -302,6 +312,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -329,6 +340,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -355,6 +367,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -384,6 +397,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -398,6 +412,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-124',
name: 'testing-123',
fullName: '@jester2/testing-124',
ownerAccount: jester2.accounts[0],
});
@@ -437,6 +452,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -464,6 +480,7 @@ describe(ProjectInit.name, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '0129',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -1,6 +1,12 @@
import { ExpoConfig } from '@expo/config-types';
import { Platform } from '@expo/eas-build-job';
import { EasJson } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { getExpoWebsiteBaseUrl } from '../../api';
import { getEASUpdateURL, getExpoWebsiteBaseUrl } from '../../api';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import { reviewAndCommitChangesAsync } from '../../build/utils/repository';
import EasCommand from '../../commandUtils/EasCommand';
import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjectConfigContextField';
@@ -8,15 +14,23 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
import { validateOrSetProjectIdAsync } from '../../commandUtils/context/contextUtils/getProjectIdAsync';
import { CredentialsContextProjectInfo } from '../../credentials/context';
import { SetUpBuildCredentialsCommandAction } from '../../credentials/manager/SetUpBuildCredentialsCommandAction';
import { AppPlatform, OnboardingDeviceType, OnboardingEnvironment } from '../../graphql/generated';
import {
AppFragment,
AppPlatform,
OnboardingDeviceType,
OnboardingEnvironment,
} from '../../graphql/generated';
import { UserPreferencesMutation } from '../../graphql/mutations/UserPreferencesMutation';
import { AppQuery } from '../../graphql/queries/AppQuery';
import Log, { link } from '../../log';
import Log, { learnMore, link } from '../../log';
import { runGitCloneAsync, runGitPushAsync } from '../../onboarding/git';
import { installDependenciesAsync } from '../../onboarding/installDependencies';
import { runCommandAsync } from '../../onboarding/runCommand';
import { RequestedPlatform } from '../../platform';
import { ExpoConfigOptions, getPrivateExpoConfig } from '../../project/expoConfig';
import { confirmAsync, promptAsync } from '../../prompts';
import { promptAsync } from '../../prompts';
import { Actor } from '../../user/User';
import { easCliVersion } from '../../utils/easCli';
import GitClient from '../../vcs/clients/git';
export default class Onboarding extends EasCommand {
@@ -82,7 +96,6 @@ export default class Onboarding extends EasCommand {
'It seems like you started an onboarding process, but it has been a while since you last used it. If you want to start a new onboarding process, visit https://expo.new.'
);
Log.log();
return;
}
const platform =
@@ -97,8 +110,7 @@ export default class Onboarding extends EasCommand {
: 'expo';
const githubRepositoryName = app.githubRepository
? app.githubRepository.metadata.githubRepoName
: 'expo-default-template';
const initialTargetProjectDir = targetProjectDirInput ?? `./${githubRepositoryName}`;
: 'expo-template-default';
Log.log(`👋 Welcome to Expo, ${actor.username}!`);
Log.log();
@@ -112,35 +124,87 @@ export default class Onboarding extends EasCommand {
} from GitHub and installing dependencies.`
);
Log.log();
const shouldContinueWithDefaultTargetDirectory = await confirmAsync({
message: `Do you want to clone your project into ${initialTargetProjectDir}?`,
});
let targetProjectDir = initialTargetProjectDir;
if (!shouldContinueWithDefaultTargetDirectory) {
const { newTargetProjectDir } = await promptAsync({
let initialTargetProjectDirectory: string;
if (targetProjectDirInput) {
initialTargetProjectDirectory = targetProjectDirInput;
Log.log(`📂 Cloning the project to ${initialTargetProjectDirectory}`);
} else {
const { selectedTargetProjectDirectory } = await promptAsync({
type: 'text',
name: 'newTargetProjectDir',
message: 'Provide a new target directory path:',
validate: (input: string) => input !== '',
name: 'selectedTargetProjectDirectory',
message: app.githubRepository
? '📂 Where would you like to clone the project to?'
: '📂 Where would you like to create your new project directory?',
initial: app.githubRepository
? path.join(process.cwd(), githubRepositoryName)
: path.join(process.cwd(), `${actor.username}-first-project`),
});
targetProjectDir = newTargetProjectDir;
initialTargetProjectDirectory = selectedTargetProjectDirectory;
}
Log.log();
const { targetProjectDir: finalTargetProjectDirectory } = await runGitCloneAsync({
githubUsername,
githubRepositoryName,
targetProjectDir,
targetProjectDir: initialTargetProjectDirectory,
});
const vcsClient = new GitClient(finalTargetProjectDirectory);
if (!app.githubRepository) {
await fs.remove(path.join(finalTargetProjectDirectory, '.git'));
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'git',
args: ['init'],
});
Log.log();
await configureProjectFromBareDefaultExpoTemplateAsync({
app,
vcsClient,
targetDir: finalTargetProjectDirectory,
});
}
await installDependenciesAsync({
projectDir: finalTargetProjectDirectory,
});
const exp = await getPrivateExpoConfigWithProjectIdAsync({
projectDir: finalTargetProjectDirectory,
graphqlClient,
actor,
});
const getDynamicProjectConfigFn = getDynamicPrivateProjectConfigGetter({
projectDir: finalTargetProjectDirectory,
graphqlClient,
actor,
});
if (!app.githubRepository) {
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'npx',
args: ['expo', 'install', 'expo-updates'],
});
Log.log();
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'npx',
args: ['expo', 'install', 'expo-insights'],
});
Log.log();
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'npx',
args: ['expo', 'install', 'expo-dev-client'],
});
Log.log();
}
await vcsClient.trackFileAsync('package-lock.json');
const shouldSetupCredentials =
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Device &&
((platform === Platform.IOS &&
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Device) ||
platform === Platform.ANDROID) &&
actor.preferences.onboarding.environment === OnboardingEnvironment.DevBuild;
if (shouldSetupCredentials) {
Log.log('🔑 Now we need to set up build credentials for your project:');
@@ -149,23 +213,17 @@ export default class Onboarding extends EasCommand {
graphqlClient,
vcsClient,
analytics,
await getPrivateExpoConfigWithProjectIdAsync({
projectDir: finalTargetProjectDirectory,
graphqlClient,
actor,
}),
getDynamicPrivateProjectConfigGetter({
projectDir: finalTargetProjectDirectory,
graphqlClient,
actor,
}),
exp,
getDynamicProjectConfigFn,
platform,
'development',
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Simulator
? 'development-simulator'
: 'development',
finalTargetProjectDirectory
).runAsync();
}
if (await vcsClient.hasUncommittedChangesAsync()) {
if (app.githubRepository && (await vcsClient.hasUncommittedChangesAsync())) {
Log.log(
'📦 We will now commit the changes made by the configuration process and push them to GitHub:'
);
@@ -175,7 +233,7 @@ export default class Onboarding extends EasCommand {
await reviewAndCommitChangesAsync(
vcsClient,
`[eas-onboarding] Install dependencies${
shouldSetupCredentials ? 'and set up build credentials' : ''
shouldSetupCredentials ? ' and set up build credentials' : ''
}`,
{ nonInteractive: false }
);
@@ -183,6 +241,69 @@ export default class Onboarding extends EasCommand {
await runGitPushAsync({
targetProjectDir: finalTargetProjectDirectory,
});
} else if (!app.githubRepository) {
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'git',
args: ['add', '.'],
});
Log.log();
await runCommandAsync({
cwd: finalTargetProjectDirectory,
command: 'git',
args: ['commit', '-m', 'Initial commit'],
});
Log.log();
}
Log.log();
Log.log('🎉 We finished configuring your project.');
Log.log();
if (
app.githubRepository ||
actor.preferences.onboarding.environment === OnboardingEnvironment.ExpoGo
) {
Log.log('🚀 You can now go back to the website to continue:');
const url = new URL(
`/onboarding/develop/set-up-project-on-your-machine?project=${app.slug}&accountId=${app.ownerAccount.id}`,
getExpoWebsiteBaseUrl()
).toString();
Log.log(`👉 ${link(url)}`);
} else {
Log.log('🚀 Now we are going to trigger your first build');
Log.log();
const { buildIds } = await runBuildAndSubmitAsync(
graphqlClient,
analytics,
vcsClient,
finalTargetProjectDirectory,
{
nonInteractive: true,
requestedPlatform:
platform === Platform.ANDROID ? RequestedPlatform.Android : RequestedPlatform.Ios,
profile:
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Simulator
? 'development-simulator'
: 'development',
wait: false,
clearCache: false,
json: false,
autoSubmit: false,
localBuildOptions: {},
freezeCredentials: false,
repack: true,
},
actor,
getDynamicProjectConfigFn
);
const buildId = buildIds[0];
Log.log();
Log.log('🚀 You can now go back to the website to continue:');
const url = new URL(
`/onboarding/develop/set-up-project-on-your-machine?project=${app.slug}&accountId=${app.ownerAccount.id}&buildId=${buildId}`,
getExpoWebsiteBaseUrl()
).toString();
Log.log(`👉 ${link(url)}`);
}
const { __typename, ...previousPreferences } = actor.preferences.onboarding;
@@ -190,16 +311,6 @@ export default class Onboarding extends EasCommand {
...previousPreferences,
appId: app.id,
});
Log.log();
Log.log('🎉 We finished configuring your project.');
Log.log();
Log.log('🚀 You can now go back to the website to continue:');
const url = new URL(
`/onboarding/develop/set-up-project-on-your-machine?project=${app.slug}&accountId=${app.ownerAccount.id}`,
getExpoWebsiteBaseUrl()
).toString();
Log.log(`👉 ${link(url)}`);
}
}
@@ -253,3 +364,112 @@ function getDynamicPrivateProjectConfigGetter({
};
};
}
async function configureProjectFromBareDefaultExpoTemplateAsync({
app,
vcsClient,
targetDir,
}: {
app: AppFragment;
vcsClient: GitClient;
targetDir: string;
}): Promise<void> {
// Android package name requires each component to start with a lowercase letter.
const isUsernameValidSegment = /^[^a-z]/.test(app.ownerAccount.name);
const userPrefix = isUsernameValidSegment ? 'user' : '';
const isSlugValidSegment = /^[^a-z]/.test(app.slug);
const slugPrefix = isSlugValidSegment ? 'app' : '';
const bundleIdentifier = `com.${userPrefix}${stripInvalidCharactersForBundleIdentifier(
app.ownerAccount.name
)}.${slugPrefix}${stripInvalidCharactersForBundleIdentifier(app.slug)}`;
const updateUrl = getEASUpdateURL(app.id);
const easBuildGitHubConfig = {
android: {
image: 'latest',
},
ios: {
image: 'latest',
},
};
const easJson: EasJson = {
cli: {
version: `>= ${easCliVersion}`,
},
build: {
development: {
developmentClient: true,
distribution: 'internal',
channel: 'development',
...easBuildGitHubConfig,
},
'development-simulator': {
extends: 'development',
ios: {
simulator: true,
},
},
preview: {
distribution: 'internal',
channel: 'preview',
...easBuildGitHubConfig,
},
production: {
channel: 'production',
...easBuildGitHubConfig,
},
},
submit: {
production: {},
},
};
const easJsonPath = path.join(targetDir, 'eas.json');
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
await vcsClient.trackFileAsync(easJsonPath);
Log.log(
`✅ Generated ${chalk.bold('eas.json')}. ${learnMore(
'https://docs.expo.dev/build-reference/eas-json/'
)}`
);
Log.log();
const expoConfig: ExpoConfig = {
name: app.name ?? app.slug,
slug: app.slug,
extra: {
eas: {
projectId: app.id,
},
},
owner: app.ownerAccount.name,
updates: {
url: updateUrl,
},
runtimeVersion: {
policy: 'appVersion',
},
ios: {
bundleIdentifier,
},
android: {
package: bundleIdentifier,
},
};
const appJsonPath = path.join(targetDir, 'app.json');
await fs.writeFile(appJsonPath, `${JSON.stringify({ expo: expoConfig }, null, 2)}\n`);
await vcsClient.trackFileAsync(appJsonPath);
Log.log(
`✅ Generated ${chalk.bold('app.json')}. ${learnMore(
'https://docs.expo.dev/versions/latest/config/app/'
)}`
);
Log.log();
}
function stripInvalidCharactersForBundleIdentifier(string: string): string {
return string.replaceAll(/[^A-Za-z0-9]/g, '');
}
@@ -274,6 +274,7 @@ function mockTestProject({
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -367,6 +367,7 @@ function mockTestProject({
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -259,6 +259,7 @@ function mockTestProject({
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -514,7 +514,7 @@ export default class UpdatePublish extends EasCommand {
},
]
: []),
{ label: 'Website link', value: updateGroupLink },
{ label: 'EAS Dashboard', value: updateGroupLink },
])
);
Log.addNewLineIfNone();
@@ -276,7 +276,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
},
]
: []),
{ label: 'Website link', value: updateGroupLink },
{ label: 'EAS Dashboard', value: updateGroupLink },
])
);
Log.addNewLineIfNone();
@@ -55,6 +55,7 @@ export const testPKCS12EmptyPasswordKeystore: Keystore = {
export const testAppFragment: AppFragment = {
id: 'test-app-id',
fullName: '@testuser/testapp',
name: 'testapp',
slug: 'testapp',
ownerAccount: {
id: 'test-account-id',
@@ -80,6 +80,7 @@ export const testAppJsonWithDifferentOwner = {
export const testAppQueryByIdResponse: AppFragment = {
id: testProjectId,
slug: testSlug,
name: 'testing 123',
fullName: testExperienceName,
ownerAccount: jester.accounts[0],
};
@@ -55,6 +55,7 @@ export const testAppleAppIdentifierFragment: AppleAppIdentifierFragment = {
export const testAppFragment: AppFragment = {
id: 'test-app-id',
fullName: '@testuser/testapp',
name: 'testapp',
slug: 'testapp',
ownerAccount: {
id: 'test-account-id',
@@ -7,8 +7,10 @@ import * as AndroidGraphqlClient from './android/api/GraphqlClient';
import * as IosGraphqlClient from './ios/api/GraphqlClient';
import AppStoreApi from './ios/appstore/AppStoreApi';
import { AuthenticationMode } from './ios/appstore/authenticateTypes';
import { MinimalAscApiKey } from './ios/credentials';
import { Analytics } from '../analytics/AnalyticsManager';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { AppStoreConnectApiKeyFragment } from '../graphql/generated';
import Log from '../log';
import { getPrivateExpoConfig } from '../project/expoConfig';
import { confirmAsync } from '../prompts';
@@ -85,6 +87,28 @@ export class CredentialsContext {
getPrivateExpoConfig(this.options.projectDir);
}
public configureAppStoreAuth(ascApiKey: AppStoreConnectApiKeyFragment): void {
/* keyP8: string;
keyId: string;
issuerId: string;
teamId?: string;
teamName?: string;
roles?: UserRole[];
name?: string; */
this.appStore.authCtx = {
ascApiKey: {
keyP8: ascApiKey.keyP8,
keyId: ascApiKey.keyIdentifier,
issuerId: ascApiKey.issuerIdentifier,
},
appleTeam: {
teamIdentifier: ascApiKey.appleTeam?.appleTeamIdentifier ?? '',
teamName: ascApiKey.appleTeam?.appleTeamName,
teamType: ascApiKey.appleTeam,
},
};
}
async bestEffortAppStoreAuthenticateAsync(): Promise<void> {
if (this.appStore.authCtx || !this.shouldAskAuthenticateAppStore) {
// skip prompts if already have apple ctx or already asked about it
@@ -1,3 +1,4 @@
import { UserRole } from '@expo/apple-utils';
import chalk from 'chalk';
import fs from 'fs-extra';
import { nanoid } from 'nanoid';
@@ -24,10 +25,19 @@ import { isAscApiKeyValidAndTrackedAsync } from '../validators/validateAscApiKey
export enum AppStoreApiKeyPurpose {
SUBMISSION_SERVICE = 'EAS Submit',
BUILD_SERVICE = 'EAS Build',
}
export async function promptForAscApiKeyPathAsync(ctx: CredentialsContext): Promise<AscApiKeyPath> {
const { keyId, keyP8Path } = await promptForKeyP8AndIdAsync();
export const PURPOSE_TO_ROLES: Record<AppStoreApiKeyPurpose, UserRole[]> = {
[AppStoreApiKeyPurpose.SUBMISSION_SERVICE]: [UserRole.ADMIN],
[AppStoreApiKeyPurpose.BUILD_SERVICE]: [UserRole.ADMIN],
};
export async function promptForAscApiKeyPathAsync(
ctx: CredentialsContext,
roles: UserRole[]
): Promise<AscApiKeyPath> {
const { keyId, keyP8Path } = await promptForKeyP8AndIdAsync(roles);
const bestEffortIssuerId = await getBestEffortIssuerIdAsync(ctx, keyId);
if (bestEffortIssuerId) {
@@ -60,15 +70,15 @@ export async function getMinimalAscApiKeyAsync(ascApiKey: AscApiKey): Promise<Mi
export async function provideOrGenerateAscApiKeyAsync(
ctx: CredentialsContext,
purpose: AppStoreApiKeyPurpose
roles: UserRole[]
): Promise<MinimalAscApiKey> {
if (ctx.nonInteractive) {
throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
}
const userProvided = await promptForAscApiKeyAsync(ctx);
const userProvided = await promptForAscApiKeyAsync(ctx, roles);
if (!userProvided) {
return await generateAscApiKeyAsync(ctx, purpose);
return await generateAscApiKeyAsync(ctx, roles);
}
if (!ctx.appStore.authCtx) {
@@ -86,39 +96,49 @@ export async function provideOrGenerateAscApiKeyAsync(
if (useUserProvided) {
return userProvided;
}
return await provideOrGenerateAscApiKeyAsync(ctx, purpose);
return await provideOrGenerateAscApiKeyAsync(ctx, roles);
}
async function generateAscApiKeyAsync(
ctx: CredentialsContext,
purpose: AppStoreApiKeyPurpose
roles: UserRole[]
): Promise<MinimalAscApiKey> {
await ctx.appStore.ensureAuthenticatedAsync();
const ascApiKey = await ctx.appStore.createAscApiKeyAsync(ctx.analytics, {
nickname: getAscApiKeyName(purpose),
nickname: getAscApiKeyName(),
roles,
});
return await getMinimalAscApiKeyAsync(ascApiKey);
}
export function getAscApiKeyName(purpose: AppStoreApiKeyPurpose): string {
const nameParts = ['[Expo]', purpose, nanoid(10)];
export function getAscApiKeyName(): string {
const nameParts = ['[Expo]', nanoid(10)];
return nameParts.join(' ');
}
async function promptForAscApiKeyAsync(ctx: CredentialsContext): Promise<MinimalAscApiKey | null> {
async function promptForAscApiKeyAsync(
ctx: CredentialsContext,
roles: UserRole[]
): Promise<MinimalAscApiKey | null> {
const shouldAutoGenerateCredentials = await shouldAutoGenerateCredentialsAsync(ascApiKeyIdSchema);
if (shouldAutoGenerateCredentials) {
return null;
}
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx);
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx, roles);
const { keyP8Path, keyId, issuerId } = ascApiKeyPath;
return { keyP8: await fs.readFile(keyP8Path, 'utf-8'), keyId, issuerId };
}
async function promptForKeyP8AndIdAsync(): Promise<Pick<AscApiKeyPath, 'keyP8Path' | 'keyId'>> {
async function promptForKeyP8AndIdAsync(
roles: UserRole[]
): Promise<Pick<AscApiKeyPath, 'keyP8Path' | 'keyId'>> {
const rolesStringified = roles.map(r => r.toString()).join(', ');
const isPlural = roles.length > 1;
Log.log(
chalk.bold(
'An App Store Connect Api key is required to upload your app to the Apple App Store Connect'
`An App Store Connect Api key with the ${rolesStringified} role${
isPlural ? 's' : ''
} is required to upload your app to the Apple App Store Connect`
)
);
Log.log(
@@ -241,13 +261,16 @@ export function sortAscApiKeysByUpdatedAtDesc(
}
export function formatAscApiKey(ascApiKey: AppStoreConnectApiKeyFragment): string {
const { keyIdentifier, appleTeam, name, updatedAt } = ascApiKey;
const { keyIdentifier, appleTeam, roles, name, updatedAt } = ascApiKey;
let line: string = '';
line += `Key ID: ${keyIdentifier}`;
if (name) {
line += chalk.gray(`\n Name: ${name}`);
}
if (roles && roles.length > 0) {
line += chalk.gray(`\n Roles: ${roles.join(', ')}`);
}
if (appleTeam) {
line += chalk.gray(`\n ${formatAppleTeam(appleTeam)}`);
}
@@ -1,7 +1,9 @@
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { AppStoreApiKeyPurpose } from './AscApiKeyUtils';
import { ChooseAssociatedAppleTeamId } from './ChooseAssociatedAppleTeamId';
import { UpdateAppleTeamInfo } from './UpdateAppleTeamInfo';
import {
AppStoreConnectApiKeyFragment,
AppleTeamFragment,
CommonIosAppCredentialsFragment,
} from '../../../graphql/generated';
import Log from '../../../log';
@@ -16,12 +18,15 @@ export class AssignAscApiKey {
ascApiKey: AppStoreConnectApiKeyFragment,
purpose: AppStoreApiKeyPurpose
): Promise<CommonIosAppCredentialsFragment> {
const appleTeam =
(await resolveAppleTeamIfAuthenticatedAsync(ctx, this.app)) ?? ascApiKey.appleTeam ?? null;
const [ascApiKeyWithAppleTeam, appleTeam] = await this.ensureKeyHasAppleTeamInfoAsync(
ctx,
ascApiKey
);
const appCredentials = await ctx.ios.createOrGetIosAppCredentialsWithCommonFieldsAsync(
ctx.graphqlClient,
this.app,
{ appleTeam: appleTeam ?? undefined }
{ appleTeam }
);
let updatedAppCredentials;
if (purpose === AppStoreApiKeyPurpose.SUBMISSION_SERVICE) {
@@ -29,7 +34,15 @@ export class AssignAscApiKey {
ctx.graphqlClient,
appCredentials,
{
ascApiKeyIdForSubmissions: ascApiKey.id,
ascApiKeyIdForSubmissions: ascApiKeyWithAppleTeam.id,
}
);
} else if (purpose === AppStoreApiKeyPurpose.BUILD_SERVICE) {
updatedAppCredentials = await ctx.ios.updateIosAppCredentialsAsync(
ctx.graphqlClient,
appCredentials,
{
ascApiKeyIdForBuilds: ascApiKeyWithAppleTeam.id,
}
);
} else {
@@ -40,4 +53,28 @@ export class AssignAscApiKey {
);
return updatedAppCredentials;
}
private async ensureKeyHasAppleTeamInfoAsync(
ctx: CredentialsContext,
ascApiKey: AppStoreConnectApiKeyFragment
): Promise<[AppStoreConnectApiKeyFragment, AppleTeamFragment]> {
let appleTeamIdentifier = ascApiKey.appleTeam?.appleTeamIdentifier;
if (!appleTeamIdentifier) {
const chooseAssociatedAppleTeamIdAction = new ChooseAssociatedAppleTeamId(
'App Store Connect API Key'
);
appleTeamIdentifier = await chooseAssociatedAppleTeamIdAction.runAsync(ctx);
}
const updateAppleTeamAction = new UpdateAppleTeamInfo(this.app.account, appleTeamIdentifier);
const appleTeam = await updateAppleTeamAction.runAsync(ctx);
if (!ascApiKey.appleTeam) {
const updatedAscApiKey = await ctx.ios.updateAscApiKeyAsync(
ctx.graphqlClient,
ascApiKey,
appleTeam
);
return [updatedAscApiKey, appleTeam];
}
return [ascApiKey, appleTeam];
}
}
@@ -0,0 +1,48 @@
import { Teams } from '@expo/apple-utils';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
export class ChooseAssociatedAppleTeamId {
constructor(private associatedCredential: string) {}
public async runAsync(ctx: CredentialsContext): Promise<string> {
if (!ctx.appStore.authCtx) {
return await this.promptForAppleTeamIdAsync();
}
const teams = await Teams.getTeamsAsync();
const chosenTeamId = await this.promptForAppleTeamIdFromDeveloperPortalAsync(
teams.map(team => ({ prettyName: `${team.name} (${team.type})`, teamId: team.teamId }))
);
return chosenTeamId ?? (await this.promptForAppleTeamIdAsync());
}
private async promptForAppleTeamIdFromDeveloperPortalAsync(
teamChoices: { prettyName: string; teamId: string }[]
): Promise<string | null> {
if (teamChoices.length === 0) {
return null;
}
const { appleTeamId } = await promptAsync({
type: 'select',
message: `Select the Apple Team associated with your ${this.associatedCredential}:`,
name: 'appleTeamId',
choices: [
...teamChoices.map(({ prettyName, teamId }) => ({ title: prettyName, value: teamId })),
{ title: 'None of the above', value: null },
],
});
return appleTeamId;
}
private async promptForAppleTeamIdAsync(): Promise<string> {
const { appleTeamId } = await promptAsync({
type: 'text',
name: 'appleTeamId',
message: `Input the Apple Team ID associated with your ${this.associatedCredential}:`,
validate: (val: string) => val !== '',
});
return appleTeamId;
}
}
@@ -1,6 +1,17 @@
import { AppStoreApiKeyPurpose, provideOrGenerateAscApiKeyAsync } from './AscApiKeyUtils';
import { AccountFragment, AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import {
AppStoreApiKeyPurpose,
PURPOSE_TO_ROLES,
provideOrGenerateAscApiKeyAsync,
} from './AscApiKeyUtils';
import { ChooseAssociatedAppleTeamId } from './ChooseAssociatedAppleTeamId';
import { UpdateAppleTeamInfo } from './UpdateAppleTeamInfo';
import {
AccountFragment,
AppStoreConnectApiKeyFragment,
AppleTeamFragment,
} from '../../../graphql/generated';
import { CredentialsContext } from '../../context';
import { MinimalAscApiKey } from '../credentials';
export class CreateAscApiKey {
constructor(private account: AccountFragment) {}
@@ -12,8 +23,32 @@ export class CreateAscApiKey {
if (ctx.nonInteractive) {
throw new Error(`A new App Store Connect API Key cannot be created in non-interactive mode.`);
}
const roles = PURPOSE_TO_ROLES[purpose];
if (!roles) {
throw new Error(`Unsupported purpose: ${purpose}`);
}
const ascApiKey = await provideOrGenerateAscApiKeyAsync(ctx, purpose);
return await ctx.ios.createAscApiKeyAsync(ctx.graphqlClient, this.account, ascApiKey);
const ascApiKey = await provideOrGenerateAscApiKeyAsync(ctx, roles);
const appleTeam = await this.ensureKeyHasAppleTeamInfoAsync(ctx, ascApiKey);
return await ctx.ios.createAscApiKeyAsync(ctx.graphqlClient, this.account, {
...ascApiKey,
teamId: appleTeam.appleTeamIdentifier,
});
}
private async ensureKeyHasAppleTeamInfoAsync(
ctx: CredentialsContext,
ascApiKey: MinimalAscApiKey
): Promise<AppleTeamFragment> {
let appleTeamIdentifier = ascApiKey.teamId;
if (!appleTeamIdentifier) {
const chooseAssociatedAppleTeamIdAction = new ChooseAssociatedAppleTeamId(
'App Store Connect API Key'
);
appleTeamIdentifier = await chooseAssociatedAppleTeamIdAction.runAsync(ctx);
}
const updateAppleTeamAction = new UpdateAppleTeamInfo(this.account, appleTeamIdentifier);
return await updateAppleTeamAction.runAsync(ctx);
}
}
@@ -0,0 +1,39 @@
import { AppStoreApiKeyPurpose } from './AscApiKeyUtils';
import { AppStoreConnectApiKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
export class GetAscApiKey {
constructor(
private app: AppLookupParams,
private purpose: AppStoreApiKeyPurpose
) {}
public async runAsync(ctx: CredentialsContext): Promise<AppStoreConnectApiKeyFragment | null> {
const appCredentials = await ctx.ios.getIosAppCredentialsWithCommonFieldsAsync(
ctx.graphqlClient,
this.app
);
if (this.purpose === AppStoreApiKeyPurpose.SUBMISSION_SERVICE) {
return appCredentials?.appStoreConnectApiKeyForSubmissions ?? null;
} else if (this.purpose === AppStoreApiKeyPurpose.BUILD_SERVICE) {
const ascApiKey = appCredentials?.appStoreConnectApiKeyForBuilds;
if (!ascApiKey) {
return null;
}
const appleTeam = ascApiKey.appleTeam;
const hasAppleTeamInfo =
!!appleTeam && !!appleTeam.appleTeamIdentifier && !!appleTeam.appleTeamType;
if (!hasAppleTeamInfo) {
Log.warn(
`App Store Connect API Key for ${this.purpose} is missing Apple Team information. Run "eas credentials" and select "Use an existing API Key" to update the missing information.`
);
return null;
}
return ascApiKey;
} else {
throw new Error(`App Store Connect API Keys are not yet supported for ${this.purpose}.`);
}
}
}
@@ -110,10 +110,13 @@ export class SetUpAscApiKey {
ctx.graphqlClient,
this.app
);
if (purpose !== AppStoreApiKeyPurpose.SUBMISSION_SERVICE) {
if (purpose === AppStoreApiKeyPurpose.SUBMISSION_SERVICE) {
return !!appCredentials?.appStoreConnectApiKeyForSubmissions;
} else if (purpose === AppStoreApiKeyPurpose.BUILD_SERVICE) {
return !!appCredentials?.appStoreConnectApiKeyForBuilds;
} else {
throw new Error(`App Store Connect API Key setup is not yet supported for ${purpose}.`);
}
return !!appCredentials?.appStoreConnectApiKeyForSubmissions;
}
private async processChoicesAsync(
@@ -0,0 +1,88 @@
import { Teams } from '@expo/apple-utils';
import { AccountFragment, AppleTeamFragment, AppleTeamType } from '../../../graphql/generated';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { AppleTeamQuery } from '../api/graphql/queries/AppleTeamQuery';
/* *
* Update the Apple Team for the app.
* If the user is logged in, we sync the Team info from the Developer Portal
* Else we ask the user to choose their Team Type (Company/Organization, Individual, In-House)
*/
export class UpdateAppleTeamInfo {
constructor(
private account: AccountFragment,
private appleTeamIdentifier: string
) {}
public async runAsync(ctx: CredentialsContext): Promise<AppleTeamFragment> {
const appleTeam = await AppleTeamQuery.getByAppleTeamIdentifierAsync(
ctx.graphqlClient,
this.account.id,
this.appleTeamIdentifier
);
const hasAppleTeamInfo = !!appleTeam && !!appleTeam.appleTeamType;
if (hasAppleTeamInfo) {
return appleTeam;
}
if (!ctx.appStore.authCtx) {
return await this.promptAndUpdateAppleTeamTypeAsync(ctx);
}
const teams = await Teams.getTeamsAsync();
const team = teams.find(t => t.teamId === this.appleTeamIdentifier);
if (!team) {
return await this.promptAndUpdateAppleTeamTypeAsync(ctx);
}
const { teamId, type, name } = team;
const teamType = appStoreTeamTypeToEnum(type);
return await ctx.ios.createOrUpdateExistingAppleTeamAsync(ctx.graphqlClient, this.account, {
appleTeamIdentifier: teamId,
appleTeamName: name,
appleTeamType: teamType,
});
}
private async promptAndUpdateAppleTeamTypeAsync(
ctx: CredentialsContext
): Promise<AppleTeamFragment> {
if (ctx.nonInteractive) {
throw new Error(
'Unable to get Apple Team information in non-interactive mode. Please re-run with interactive mode.'
);
}
const teamType = await promptForAppleTeamTypeAsync();
return await ctx.ios.createOrUpdateExistingAppleTeamAsync(ctx.graphqlClient, this.account, {
appleTeamIdentifier: this.appleTeamIdentifier,
appleTeamType: teamType,
});
}
}
function appStoreTeamTypeToEnum(
teamType: 'In-House' | 'Company/Organization' | 'Individual' | string
): AppleTeamType | null {
if (teamType === 'In-House') {
return AppleTeamType.InHouse;
} else if (teamType === 'Company/Organization') {
return AppleTeamType.CompanyOrOrganization;
} else if (teamType === 'Individual') {
return AppleTeamType.Individual;
}
return null;
}
async function promptForAppleTeamTypeAsync(): Promise<AppleTeamType> {
const { appleTeamType } = await promptAsync({
type: 'select',
message: 'Select your Apple Team Type:',
name: 'appleTeamType',
choices: [
{ title: 'Enterprise', value: AppleTeamType.InHouse },
{ title: 'Company/Organization', value: AppleTeamType.CompanyOrOrganization },
{ title: 'Individual', value: AppleTeamType.Individual },
],
});
return appleTeamType;
}
@@ -1,21 +1,15 @@
import { UserRole } from '@expo/apple-utils';
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testAscApiKey } from '../../../__tests__/fixtures-ios';
import { getCredentialsFromUserAsync } from '../../../utils/promptForCredentials';
import {
AppStoreApiKeyPurpose,
getAscApiKeyName,
promptForAscApiKeyPathAsync,
} from '../AscApiKeyUtils';
import { getAscApiKeyName, promptForAscApiKeyPathAsync } from '../AscApiKeyUtils';
jest.mock('../../../../prompts');
jest.mock('../../../utils/promptForCredentials');
function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
return Object.keys(obj) as K[];
}
afterEach(() => {
jest.mocked(promptAsync).mockClear();
jest.mocked(getCredentialsFromUserAsync).mockClear();
@@ -24,11 +18,8 @@ afterEach(() => {
describe(getAscApiKeyName, () => {
// Apple enforces a 30 char limit on this name
it('produces a name under 30 chars', async () => {
for (const value of enumKeys(AppStoreApiKeyPurpose)) {
const purpose = AppStoreApiKeyPurpose[value];
const name = getAscApiKeyName(purpose);
expect(name.length < 30).toBe(true);
}
const name = getAscApiKeyName();
expect(name.length < 30).toBe(true);
});
});
@@ -51,7 +42,7 @@ describe(promptForAscApiKeyPathAsync, () => {
authCtx: null,
},
});
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx);
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx, [UserRole.ADMIN]);
expect(ascApiKeyPath).toEqual({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
@@ -76,7 +67,7 @@ describe(promptForAscApiKeyPathAsync, () => {
getAscApiKeyAsync: jest.fn(() => testAscApiKey),
},
});
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx);
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx, [UserRole.ADMIN]);
expect(ascApiKeyPath).toEqual({
keyId: 'test-key-id',
issuerId: 'test-issuer-id-from-apple',
@@ -38,6 +38,7 @@ import {
AppleDistributionCertificateFragment,
ApplePushKeyFragment,
AppleTeamFragment,
AppleTeamInput,
CommonIosAppCredentialsFragment,
IosAppBuildCredentialsFragment,
IosDistributionType,
@@ -189,28 +190,26 @@ export async function updateIosAppCredentialsAsync(
{
applePushKeyId,
ascApiKeyIdForSubmissions,
ascApiKeyIdForBuilds,
}: {
applePushKeyId?: string;
ascApiKeyIdForSubmissions?: string;
ascApiKeyIdForBuilds?: string;
}
): Promise<CommonIosAppCredentialsFragment> {
let updatedAppCredentials = appCredentials;
if (applePushKeyId) {
updatedAppCredentials = await IosAppCredentialsMutation.setPushKeyAsync(
graphqlClient,
updatedAppCredentials.id,
applePushKeyId
);
const updatedAppCredentials = appCredentials;
if (!applePushKeyId && !ascApiKeyIdForSubmissions && !ascApiKeyIdForBuilds) {
return updatedAppCredentials;
}
if (ascApiKeyIdForSubmissions) {
updatedAppCredentials =
await IosAppCredentialsMutation.setAppStoreConnectApiKeyForSubmissionsAsync(
graphqlClient,
updatedAppCredentials.id,
ascApiKeyIdForSubmissions
);
}
return updatedAppCredentials;
return await IosAppCredentialsMutation.updateIosAppCredentialsAsync(
graphqlClient,
appCredentials.id,
{
pushKeyId: applePushKeyId,
appStoreConnectApiKeyForSubmissionsId: ascApiKeyIdForSubmissions,
appStoreConnectApiKeyForBuildsId: ascApiKeyIdForBuilds,
}
);
}
async function createOrGetExistingIosAppCredentialsWithBuildCredentialsAsync(
@@ -263,7 +262,42 @@ async function createOrGetExistingIosAppCredentialsWithBuildCredentialsAsync(
export async function createOrGetExistingAppleTeamAsync(
graphqlClient: ExpoGraphqlClient,
account: AccountFragment,
{ appleTeamIdentifier, appleTeamName }: { appleTeamIdentifier: string; appleTeamName?: string }
{ appleTeamIdentifier, appleTeamName, appleTeamType }: AppleTeamInput
): Promise<AppleTeamFragment> {
return await createOrManageExistingAppleTeamAsync(
graphqlClient,
account,
{
appleTeamIdentifier,
appleTeamName,
appleTeamType,
},
{ updateMissingFields: false }
);
}
export async function createOrUpdateExistingAppleTeamAsync(
graphqlClient: ExpoGraphqlClient,
account: AccountFragment,
{ appleTeamIdentifier, appleTeamName, appleTeamType }: AppleTeamInput
): Promise<AppleTeamFragment> {
return await createOrManageExistingAppleTeamAsync(
graphqlClient,
account,
{
appleTeamIdentifier,
appleTeamName,
appleTeamType,
},
{ updateMissingFields: true }
);
}
async function createOrManageExistingAppleTeamAsync(
graphqlClient: ExpoGraphqlClient,
account: AccountFragment,
{ appleTeamIdentifier, appleTeamName, appleTeamType }: AppleTeamInput,
{ updateMissingFields }: { updateMissingFields: boolean }
): Promise<AppleTeamFragment> {
const appleTeam = await AppleTeamQuery.getByAppleTeamIdentifierAsync(
graphqlClient,
@@ -271,11 +305,24 @@ export async function createOrGetExistingAppleTeamAsync(
appleTeamIdentifier
);
if (appleTeam) {
const didTeamNameChange = appleTeamName && appleTeam.appleTeamName !== appleTeamName;
const didTeamTypeChange = appleTeamType && appleTeam.appleTeamType !== appleTeamType;
const didFieldsChange = didTeamNameChange || didTeamTypeChange;
if (updateMissingFields && didFieldsChange) {
return await AppleTeamMutation.updateAppleTeamAsync(
graphqlClient,
{
appleTeamName: didTeamNameChange ? appleTeamName : undefined,
appleTeamType: didTeamTypeChange ? appleTeamType : undefined,
},
appleTeam.id
);
}
return appleTeam;
} else {
return await AppleTeamMutation.createAppleTeamAsync(
graphqlClient,
{ appleTeamIdentifier, appleTeamName },
{ appleTeamIdentifier, appleTeamName, appleTeamType },
account.id
);
}
@@ -521,6 +568,20 @@ export async function createAscApiKeyAsync(
);
}
export async function updateAscApiKeyAsync(
graphqlClient: ExpoGraphqlClient,
ascApiKey: AppStoreConnectApiKeyFragment,
appleTeam: AppleTeamFragment
): Promise<AppStoreConnectApiKeyFragment> {
return await AppStoreConnectApiKeyMutation.updateAppStoreConnectApiKeyAsync(
graphqlClient,
{
appleTeamId: appleTeam.id,
},
ascApiKey.id
);
}
export async function getAscApiKeysForAccountAsync(
graphqlClient: ExpoGraphqlClient,
account: AccountFragment
@@ -7,8 +7,10 @@ import { withErrorHandlingAsync } from '../../../../../graphql/client';
import {
AppStoreConnectApiKeyFragment,
AppStoreConnectApiKeyInput,
AppStoreConnectApiKeyUpdateInput,
CreateAppStoreConnectApiKeyMutation,
DeleteAppStoreConnectApiKeyMutation,
UpdateAppStoreConnectApiKeyMutation,
} from '../../../../../graphql/generated';
import { AppStoreConnectApiKeyFragmentNode } from '../../../../../graphql/types/credentials/AppStoreConnectApiKey';
@@ -51,6 +53,44 @@ export const AppStoreConnectApiKeyMutation = {
);
return data.appStoreConnectApiKey.createAppStoreConnectApiKey;
},
async updateAppStoreConnectApiKeyAsync(
graphqlClient: ExpoGraphqlClient,
appStoreConnectApiKeyUpdateInput: AppStoreConnectApiKeyUpdateInput,
appStoreConnectApiKeyId: string
): Promise<AppStoreConnectApiKeyFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<UpdateAppStoreConnectApiKeyMutation>(
gql`
mutation UpdateAppStoreConnectApiKeyMutation(
$appStoreConnectApiKeyUpdateInput: AppStoreConnectApiKeyUpdateInput!
$id: ID!
) {
appStoreConnectApiKey {
updateAppStoreConnectApiKey(
appStoreConnectApiKeyUpdateInput: $appStoreConnectApiKeyUpdateInput
id: $id
) {
id
...AppStoreConnectApiKeyFragment
}
}
}
${print(AppStoreConnectApiKeyFragmentNode)}
`,
{
appStoreConnectApiKeyUpdateInput,
appStoreConnectApiKeyId,
}
)
.toPromise()
);
assert(
data.appStoreConnectApiKey.updateAppStoreConnectApiKey,
'GraphQL: `updateAppStoreConnectApiKey` not defined in server response'
);
return data.appStoreConnectApiKey.updateAppStoreConnectApiKey;
},
async deleteAppStoreConnectApiKeyAsync(
graphqlClient: ExpoGraphqlClient,
appStoreConnectApiKeyId: string
@@ -7,7 +7,9 @@ import {
AccountFragment,
AppleTeamFragment,
AppleTeamInput,
AppleTeamUpdateInput,
CreateAppleTeamMutation,
UpdateAppleTeamMutation,
} from '../../../../../graphql/generated';
import { AccountFragmentNode } from '../../../../../graphql/types/Account';
import { AppleTeamFragmentNode } from '../../../../../graphql/types/credentials/AppleTeam';
@@ -50,4 +52,40 @@ export const AppleTeamMutation = {
);
return data.appleTeam.createAppleTeam;
},
async updateAppleTeamAsync(
graphqlClient: ExpoGraphqlClient,
appleTeamUpdateInput: AppleTeamUpdateInput,
appleTeamId: string
): Promise<AppleTeamMutationResult> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<UpdateAppleTeamMutation>(
gql`
mutation UpdateAppleTeamMutation(
$appleTeamUpdateInput: AppleTeamUpdateInput!
$appleTeamId: ID!
) {
appleTeam {
updateAppleTeam(appleTeamUpdateInput: $appleTeamUpdateInput, id: $appleTeamId) {
id
...AppleTeamFragment
account {
id
...AccountFragment
}
}
}
}
${print(AppleTeamFragmentNode)}
${print(AccountFragmentNode)}
`,
{
appleTeamUpdateInput,
appleTeamId,
}
)
.toPromise()
);
return data.appleTeam.updateAppleTeam;
},
};
@@ -10,6 +10,7 @@ import {
IosAppCredentialsInput,
SetAppStoreConnectApiKeyForSubmissionsMutation,
SetPushKeyMutation,
UpdateIosAppCredentialsMutation,
} from '../../../../../graphql/generated';
import { CommonIosAppCredentialsFragmentNode } from '../../../../../graphql/types/credentials/IosAppCredentials';
@@ -56,6 +57,44 @@ export const IosAppCredentialsMutation = {
);
return data.iosAppCredentials.createIosAppCredentials;
},
async updateIosAppCredentialsAsync(
graphqlClient: ExpoGraphqlClient,
iosAppCredentialsId: string,
iosAppCredentialsInput: IosAppCredentialsInput
): Promise<CommonIosAppCredentialsFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<UpdateIosAppCredentialsMutation>(
gql`
mutation UpdateIosAppCredentialsMutation(
$iosAppCredentialsInput: IosAppCredentialsInput!
$iosAppCredentialsId: ID!
) {
iosAppCredentials {
updateIosAppCredentials(
id: $iosAppCredentialsId
iosAppCredentialsInput: $iosAppCredentialsInput
) {
id
...CommonIosAppCredentialsFragment
}
}
}
${print(CommonIosAppCredentialsFragmentNode)}
`,
{
iosAppCredentialsInput,
iosAppCredentialsId,
}
)
.toPromise()
);
assert(
data.iosAppCredentials.updateIosAppCredentials,
'GraphQL: `updateIosAppCredentials` not defined in server response'
);
return data.iosAppCredentials.updateIosAppCredentials;
},
async setPushKeyAsync(
graphqlClient: ExpoGraphqlClient,
iosAppCredentialsId: string,
@@ -1,4 +1,4 @@
import { ProfileType } from '@expo/app-store';
import { ProfileType, UserRole } from '@expo/app-store';
import {
AscApiKey,
@@ -45,16 +45,23 @@ import { createOrReuseAdhocProvisioningProfileAsync } from './provisioningProfil
import { createPushKeyAsync, listPushKeysAsync, revokePushKeyAsync } from './pushKey';
import { hasAscEnvVars } from './resolveCredentials';
import { Analytics } from '../../../analytics/AnalyticsManager';
import { AppleTeamType } from '../../../graphql/generated';
import Log from '../../../log';
import { MinimalAscApiKey } from '../credentials';
type AscApiKeyAuth = {
ascApiKey: MinimalAscApiKey;
appleTeam: { teamIdentifier: string; teamName?: string; teamType: AppleTeamType };
};
export default class AppStoreApi {
public authCtx?: AuthCtx;
public defaultAuthenticationMode: AuthenticationMode;
public readonly ascApiKeyAuth?: AscApiKeyAuth;
constructor() {
this.defaultAuthenticationMode = hasAscEnvVars()
? AuthenticationMode.API_KEY
: AuthenticationMode.USER;
constructor(options: { ascApiKeyAuth?: AscApiKeyAuth } = {}) {
this.ascApiKeyAuth = options.ascApiKeyAuth;
this.defaultAuthenticationMode =
hasAscEnvVars() || this.ascApiKeyAuth ? AuthenticationMode.API_KEY : AuthenticationMode.USER;
}
public async ensureUserAuthenticatedAsync(options?: AuthenticateOptions): Promise<UserAuthCtx> {
@@ -195,10 +202,10 @@ export default class AppStoreApi {
public async createAscApiKeyAsync(
analytics: Analytics,
{ nickname }: { nickname: string }
{ nickname, roles }: { nickname: string; roles: UserRole[] }
): Promise<AscApiKey> {
const userCtx = await this.ensureUserAuthenticatedAsync();
return await createAscApiKeyAsync(analytics, userCtx, { nickname });
return await createAscApiKeyAsync(analytics, userCtx, { nickname, roles });
}
public async revokeAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo> {
@@ -9,6 +9,7 @@ export enum AuthenticationMode {
USER,
}
// TODO: move this to graphql types
export enum AppleTeamType {
IN_HOUSE = 'IN_HOUSE',
COMPANY_OR_ORGANIZATION = 'COMPANY_OR_ORGANIZATION',
@@ -1,54 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`print credentials prints the IosAppCredentials map 1`] = `
"iOS Credentials
Project @quinlanj/test52
Bundle Identifier com.quinlanj.test52
Apple Team test-apple-identifier
Push Key
Developer Portal ID 9839M6AY8W
Apple Team 77KQ969CHE
Updated 6 months ago
App Store Connect API Key
Developer Portal ID NYN468XNN3
Name Expo Submissions pl94k8_lnS
Issuer ID 69a6de93-e4e4-47e3-e053-5b8c7c11a4d1
Roles ADMIN
Apple Team 77KQ969CHE
Updated 6 months ago
App Store Configuration
Distribution Certificate
Serial Number test-serial
Expiration Date Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 6 months ago
Provisioning Profile
Developer Portal ID test-profile-id
Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
Ad Hoc Configuration
Distribution Certificate
Serial Number test-serial
Expiration Date Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 6 months ago
Provisioning Profile
Developer Portal ID test-profile-id
Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
"
"iOS Credentials
Project @quinlanj/test52
Bundle Identifier com.quinlanj.test52
Apple Team test-apple-identifier
Push Key
Developer Portal ID 9839M6AY8W
Apple Team 77KQ969CHE
Updated 6 months ago
EAS Submit App Store Connect API Key
Developer Portal ID NYN468XNN3
Name Expo Submissions pl94k8_lnS
Issuer ID 69a6de93-e4e4-47e3-e053-5b8c7c11a4d1
Roles ADMIN
Apple Team 77KQ969CHE
Updated 6 months ago
App Store Configuration
Distribution Certificate
Serial Number test-serial
Expiration Date Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 6 months ago
Provisioning Profile
Developer Portal ID test-profile-id
Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
Ad Hoc Configuration
Distribution Certificate
Serial Number test-serial
Expiration Date Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 6 months ago
Provisioning Profile
Developer Portal ID test-profile-id
Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
"
`;
@@ -12,6 +12,7 @@ import { APPLE_DEVICE_CLASS_LABELS } from '../../../graphql/types/credentials/Ap
import Log from '../../../log';
import { fromNow } from '../../../utils/date';
import formatFields from '../../../utils/formatFields';
import { AppStoreApiKeyPurpose } from '../actions/AscApiKeyUtils';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { App, IosAppBuildCredentialsMap, IosAppCredentialsMap, Target } from '../types';
@@ -92,7 +93,12 @@ export function displayIosCredentials(
continue;
}
const { appleTeam, pushKey, appStoreConnectApiKeyForSubmissions } = targetAppCredentials;
const {
appleTeam,
pushKey,
appStoreConnectApiKeyForSubmissions,
appStoreConnectApiKeyForBuilds,
} = targetAppCredentials;
if (appleTeam) {
const { appleTeamIdentifier, appleTeamName } = appleTeam;
fields.push({
@@ -107,7 +113,15 @@ export function displayIosCredentials(
}
if (appStoreConnectApiKeyForSubmissions) {
displayAscApiKey(appStoreConnectApiKeyForSubmissions, fields);
displayAscApiKey(
AppStoreApiKeyPurpose.SUBMISSION_SERVICE,
appStoreConnectApiKeyForSubmissions,
fields
);
}
if (appStoreConnectApiKeyForBuilds) {
displayAscApiKey(AppStoreApiKeyPurpose.BUILD_SERVICE, appStoreConnectApiKeyForBuilds, fields);
}
const sortedIosAppBuildCredentialsList = sortBuildCredentialsByDistributionType(
@@ -236,10 +250,11 @@ function displayApplePushKey(
}
function displayAscApiKey(
purpose: AppStoreApiKeyPurpose,
maybeAscApiKey: AppStoreConnectApiKeyFragment | null,
fields: { label: string; value: string }[]
): void {
fields.push({ label: 'App Store Connect API Key', value: '' });
fields.push({ label: `${purpose.toString()} App Store Connect API Key`, value: '' });
if (maybeAscApiKey) {
const { keyIdentifier, issuerIdentifier, appleTeam, name, roles, updatedAt } = maybeAscApiKey;
fields.push({ label: 'Developer Portal ID', value: keyIdentifier });
@@ -29,7 +29,6 @@ export enum AndroidActionType {
UseExistingGsaKeyForSubmissions,
RemoveGsaKey,
SetUpGsaKeyForSubmissions,
CreateGsaKeyForFcmV1,
UseExistingGsaKeyForFcmV1,
RemoveGsaKeyForFcmV1,
SetUpGsaKeyForFcmV1,
@@ -56,8 +55,8 @@ export enum IosActionType {
CreatePushKey,
UseExistingPushKey,
RemovePushKey,
SetUpAscApiKeyForSubmissions,
UseExistingAscApiKeyForSubmissions,
CreateAscApiKeyForSubmissions,
SetUpAscApiKeyForService,
CreateAscApiKeyForService,
UseExistingAscApiKeyForService,
RemoveAscApiKey,
}
@@ -77,22 +77,22 @@ export function getPushKeyActions(ctx: CredentialsContext): ActionInfo[] {
];
}
export function getAscApiKeyActions(ctx: CredentialsContext): ActionInfo[] {
export function getAscApiKeyActions(): ActionInfo[] {
return [
{
value: IosActionType.SetUpAscApiKeyForSubmissions,
title: 'Set up your project to use an API Key for EAS Submit',
value: IosActionType.SetUpAscApiKeyForService,
title: `Set up your project to use an API Key`,
scope: Scope.Project,
},
{
value: IosActionType.UseExistingAscApiKeyForSubmissions,
title: 'Use an existing API Key for EAS Submit',
value: IosActionType.UseExistingAscApiKeyForService,
title: `Use an existing API Key in your project`,
scope: Scope.Project,
},
{
value: IosActionType.CreateAscApiKeyForSubmissions,
title: 'Add a new API Key For EAS Submit',
scope: ctx.hasProjectContext ? Scope.Project : Scope.Account,
value: IosActionType.CreateAscApiKeyForService,
title: `Add a new API Key to use in your project`,
scope: Scope.Project,
},
{
value: IosActionType.RemoveAscApiKey,
@@ -85,7 +85,7 @@ export class ManageIos {
});
const buildCredentialsActions = getBuildCredentialsActions(ctx);
const pushKeyActions = getPushKeyActions(ctx);
const ascApiKeyActions = getAscApiKeyActions(ctx);
const ascApiKeyActions = getAscApiKeyActions();
await ctx.bestEffortAppStoreAuthenticateAsync();
@@ -226,8 +226,6 @@ export class ManageIos {
await new CreateDistributionCertificate(account).runAsync(ctx);
} else if (action === IosActionType.CreatePushKey) {
await new CreatePushKey(account).runAsync(ctx);
} else if (action === IosActionType.CreateAscApiKeyForSubmissions) {
await new CreateAscApiKey(account).runAsync(ctx, AppStoreApiKeyPurpose.SUBMISSION_SERVICE);
} else if (action === IosActionType.RemovePushKey) {
await new SelectAndRemovePushKey(account).runAsync(ctx);
} else if (action === IosActionType.RemoveAscApiKey) {
@@ -354,40 +352,29 @@ export class ManageIos {
}
return;
}
case IosActionType.SetUpAscApiKeyForSubmissions: {
await new SetUpAscApiKey(
appLookupParams,
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
).runAsync(ctx);
case IosActionType.SetUpAscApiKeyForService: {
const purpose = await this.selectAscApiKeyPurposeAsync();
await new SetUpAscApiKey(appLookupParams, purpose).runAsync(ctx);
return;
}
case IosActionType.UseExistingAscApiKeyForSubmissions: {
case IosActionType.UseExistingAscApiKeyForService: {
const purpose = await this.selectAscApiKeyPurposeAsync();
const ascApiKey = await selectAscApiKeysFromAccountAsync(ctx, appLookupParams.account, {
filterDifferentAppleTeam: true,
});
if (ascApiKey) {
await new AssignAscApiKey(appLookupParams).runAsync(
ctx,
ascApiKey,
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
);
await new AssignAscApiKey(appLookupParams).runAsync(ctx, ascApiKey, purpose);
}
return;
}
case IosActionType.CreateAscApiKeyForSubmissions: {
const ascApiKey = await new CreateAscApiKey(appLookupParams.account).runAsync(
ctx,
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
);
case IosActionType.CreateAscApiKeyForService: {
const purpose = await this.selectAscApiKeyPurposeAsync();
const ascApiKey = await new CreateAscApiKey(appLookupParams.account).runAsync(ctx, purpose);
const confirm = await confirmAsync({
message: `Do you want ${appLookupParams.projectName} to use the new API Key?`,
});
if (confirm) {
await new AssignAscApiKey(appLookupParams).runAsync(
ctx,
ascApiKey,
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
);
await new AssignAscApiKey(appLookupParams).runAsync(ctx, ascApiKey, purpose);
}
return;
}
@@ -431,4 +418,17 @@ export class ManageIos {
}))
);
}
protected async selectAscApiKeyPurposeAsync(): Promise<AppStoreApiKeyPurpose> {
const { keyPurpose } = await promptAsync({
type: 'select',
message: `Select the service you'd like to use your ASC Api Key for:`,
name: 'keyPurpose',
choices: [
{ title: 'EAS Submit', value: AppStoreApiKeyPurpose.SUBMISSION_SERVICE },
{ title: 'EAS Build', value: AppStoreApiKeyPurpose.BUILD_SERVICE },
],
});
return keyPurpose;
}
}
@@ -52,6 +52,7 @@ describe(AccountResolver, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: 'test-project-id',
fullName: '@foo/wat',
name: 'wat',
slug: 'wat',
ownerAccount: {
id: 'account_id_888',
+6049 -820
View File
File diff suppressed because it is too large. Load diff
@@ -14,6 +14,8 @@ import {
} from '../generated';
import { UpdateFragmentNode } from '../types/Update';
const turtleJobRunId = process.env.EAS_BUILD_ID;
export const PublishMutation = {
async getUploadURLsAsync(
graphqlClient: ExpoGraphqlClient,
@@ -57,7 +59,12 @@ export const PublishMutation = {
}
${print(UpdateFragmentNode)}
`,
{ publishUpdateGroupsInput }
{
publishUpdateGroupsInput: publishUpdateGroupsInput.map(input => ({
...input,
turtleJobRunId,
})),
}
)
.toPromise()
);
@@ -5,6 +5,7 @@ import { AccountFragmentNode } from './Account';
export const AppFragmentNode = gql`
fragment AppFragment on App {
id
name
fullName
slug
ownerAccount {
@@ -5,5 +5,6 @@ export const AppleTeamFragmentNode = gql`
id
appleTeamIdentifier
appleTeamName
appleTeamType
}
`;
@@ -30,6 +30,10 @@ export const CommonIosAppCredentialsWithoutBuildCredentialsFragmentNode = gql`
id
...AppStoreConnectApiKeyFragment
}
appStoreConnectApiKeyForBuilds {
id
...AppStoreConnectApiKeyFragment
}
}
${AppFragmentNode}
${AppleTeamFragmentNode}
@@ -67,7 +67,7 @@ export async function runCommandAsync({
}
if (showSpinner) {
spinner?.succeed(`✅ ${chalk.bold(`${command} ${args.join(' ')}`)} succeeded`);
spinner?.succeed(`${chalk.bold(`${command} ${args.join(' ')}`)} succeeded`);
} else {
Log.log(`✅ ${chalk.bold(`${command} ${args.join(' ')}`)} succeeded`);
}
+18 -1
View File
@@ -67,7 +67,24 @@ export function ora(options?: Options | string): Ora {
};
spinner.start = (text): Ora => {
wrapNativeLogs();
// wrapNativeLogs wraps calls to console so they always:
// 1. stop the spinner
// 2. log the message
// 3. start the spinner again
// Every restart of the spinner causes the spinner message to be logged again
// which makes logs look like
//
// - Exporting...
// [expo-cli] Starting Metro Bundler
// - Exporting...
// [expo-cli] Android Bundling complete 3492ms
// - Exporting...
//
// Skipping wrapping native logs removes the repeated interleaved "Exporting..." messages.
if (!disabled) {
wrapNativeLogs();
}
return oraStart(text);
};
@@ -162,6 +162,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -196,6 +197,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -139,6 +139,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -171,6 +172,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
id: '1234',
slug: 'testing-123',
name: 'testing-123',
fullName: '@jester/testing-123',
ownerAccount: jester.accounts[0],
});
@@ -15,12 +15,14 @@ export async function resolveRuntimeVersionAsync({
workflow,
projectDir,
env,
cwd,
}: {
exp: ExpoConfig;
platform: 'ios' | 'android';
workflow: Workflow;
projectDir: string;
env: Env | undefined;
cwd?: string;
}): Promise<string | null> {
if (!(await isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync(projectDir))) {
// fall back to the previous behavior (using the @expo/config-plugins eas-cli dependency rather
@@ -36,7 +38,7 @@ export async function resolveRuntimeVersionAsync({
const resolvedRuntimeVersionJSONResult = await expoUpdatesCommandAsync(
projectDir,
['runtimeversion:resolve', '--platform', platform, '--workflow', workflow, ...extraArgs],
{ env }
{ env, cwd }
);
const runtimeVersionResult = JSON.parse(resolvedRuntimeVersionJSONResult);
@@ -1,3 +1,4 @@
import { UserRole } from '@expo/apple-utils';
import { Platform } from '@expo/eas-build-job';
import fs from 'fs-extra';
import nullthrows from 'nullthrows';
@@ -189,7 +190,7 @@ async function handlePromptSourceAsync(
ctx: SubmissionContext<Platform.IOS>,
_source: AscApiKeyPromptSource
): Promise<AscApiKeyPath> {
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx.credentialsCtx);
const ascApiKeyPath = await promptForAscApiKeyPathAsync(ctx.credentialsCtx, [UserRole.ADMIN]);
return await getAscApiKeyPathAsync(ctx, {
sourceType: AscApiKeySourceType.path,
path: ascApiKeyPath,
+1 -1
View File
@@ -195,7 +195,7 @@ export async function republishAsync({
? [{ label: 'iOS update ID', value: updatesRepublishedByPlatform.ios.id }]
: []),
{ label: 'Message', value: updateMessage },
{ label: 'Website link', value: link(updateGroupUrl, { dim: false }) },
{ label: 'EAS Dashboard', value: link(updateGroupUrl, { dim: false }) },
])
);
}
+2 -1
View File
@@ -11,7 +11,7 @@ export class ExpoUpdatesCLICommandFailedError extends Error {}
export async function expoUpdatesCommandAsync(
projectDir: string,
args: string[],
options: { env: Env | undefined }
options: { env: Env | undefined; cwd?: string }
): Promise<string> {
let expoUpdatesCli;
try {
@@ -34,6 +34,7 @@ export async function expoUpdatesCommandAsync(
await spawnAsync(expoUpdatesCli, args, {
stdio: 'pipe',
env: { ...process.env, ...options.env },
cwd: options.cwd,
})
).stdout;
} catch (e: any) {
+20 -9
View File
@@ -52,7 +52,7 @@ export default class GitClient extends Client {
Errors.exit(1);
}
if (await doesGitRepoExistAsync()) {
if (await doesGitRepoExistAsync(this.maybeCwdOverride)) {
return;
}
@@ -160,8 +160,8 @@ export default class GitClient extends Client {
// file:/// so only file:// needs to be prepended
gitRepoUri = `file://${await this.getRootPathAsync()}`;
}
const isCaseSensitive = await isGitCaseSensitiveAsync();
await setGitCaseSensitivityAsync(true);
const isCaseSensitive = await isGitCaseSensitiveAsync(this.maybeCwdOverride);
await setGitCaseSensitivityAsync(true, this.maybeCwdOverride);
try {
if (await this.hasUncommittedChangesAsync()) {
Log.error('Detected inconsistent filename casing between your local filesystem and git.');
@@ -186,7 +186,7 @@ export default class GitClient extends Client {
}
);
} finally {
await setGitCaseSensitivityAsync(isCaseSensitive);
await setGitCaseSensitivityAsync(isCaseSensitive, this.maybeCwdOverride);
}
}
@@ -343,13 +343,17 @@ async function ensureGitConfiguredAsync({
* - boolean - is git case sensitive
* - undefined - case sensitivity is not configured and git is using default behavior
*/
export async function isGitCaseSensitiveAsync(): Promise<boolean | undefined> {
export async function isGitCaseSensitiveAsync(
cwd: string | undefined
): Promise<boolean | undefined> {
if (process.platform !== 'darwin') {
return undefined;
}
try {
const result = await spawnAsync('git', ['config', '--get', 'core.ignorecase']);
const result = await spawnAsync('git', ['config', '--get', 'core.ignorecase'], {
cwd,
});
const isIgnoreCaseEnabled = result.stdout.trim();
if (isIgnoreCaseEnabled === '') {
return undefined;
@@ -363,15 +367,22 @@ export async function isGitCaseSensitiveAsync(): Promise<boolean | undefined> {
}
}
async function setGitCaseSensitivityAsync(enable: boolean | undefined): Promise<void> {
async function setGitCaseSensitivityAsync(
enable: boolean | undefined,
cwd: string | undefined
): Promise<void> {
// we are assuming that if someone sets that on non-macos device then
// they know what they are doing
if (process.platform !== 'darwin') {
return;
}
if (enable === undefined) {
await spawnAsync('git', ['config', '--unset', 'core.ignorecase']);
await spawnAsync('git', ['config', '--unset', 'core.ignorecase'], {
cwd,
});
} else {
await spawnAsync('git', ['config', 'core.ignorecase', String(!enable)]);
await spawnAsync('git', ['config', 'core.ignorecase', String(!enable)], {
cwd,
});
}
}
+4 -2
View File
@@ -12,9 +12,11 @@ export async function isGitInstalledAsync(): Promise<boolean> {
return true;
}
export async function doesGitRepoExistAsync(): Promise<boolean> {
export async function doesGitRepoExistAsync(cwd: string | undefined): Promise<boolean> {
try {
await spawnAsync('git', ['rev-parse', '--git-dir']);
await spawnAsync('git', ['rev-parse', '--git-dir'], {
cwd,
});
return true;
} catch {
return false;
+2 -6
View File
@@ -10,14 +10,10 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"target": "ES2019",
"target": "ES2021",
"outDir": "build",
"rootDir": "src",
"typeRoots": [
"../../ts-declarations",
"../../node_modules/@types",
"./node_modules/@types"
]
"typeRoots": ["../../ts-declarations", "../../node_modules/@types", "./node_modules/@types"]
},
"include": ["src/**/*"]
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "9.0.1",
"version": "9.0.4",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@babel/code-frame": "7.23.5",
"@expo/eas-build-job": "1.0.107",
"@expo/eas-build-job": "1.0.108",
"chalk": "4.1.2",
"env-string": "1.0.1",
"fs-extra": "11.2.0",
+2 -6
View File
@@ -10,14 +10,10 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"target": "ES2019",
"target": "ES2021",
"outDir": "build",
"rootDir": "src",
"typeRoots": [
"../../node_modules/@types",
"../../ts-declarations",
"./node_modules/@types"
]
"typeRoots": ["../../node_modules/@types", "../../ts-declarations", "./node_modules/@types"]
},
"include": ["src/**/*"]
}
Generated
+37 -12
View File
@@ -1462,10 +1462,10 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/[email protected]7":
version "1.0.107"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.107.tgz#b1d7dc7ae302c21000af35bd6d4bc388ec6a4046"
integrity sha512-tKK0s25TN16t89Nq0XIo9xkyUKyYEv0yJuPT5rBb0KQ6oifnZ5ZfvbZ3RsBjV7dWIZR2sI76ZhrHFSIGCuahiw==
"@expo/[email protected]8":
version "1.0.108"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.108.tgz#198e189beff4ca2405ab68ee04395878cff45084"
integrity sha512-4FjqsK+k2KLjfWBT08ECFmlWH5yE9vw7vQmKzM4N8uQHKA/SbA6mMMto/f8XSbs7f5y6kdGnB7pxcMH91RuxbA==
dependencies:
"@expo/logger" "1.0.57"
joi "^17.11.0"
@@ -1667,12 +1667,12 @@
dependencies:
cross-spawn "^7.0.3"
"@expo/[email protected]7":
version "1.0.107"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.107.tgz#9b22c7aab39564c2f24e1ec0d17bcc17f2182376"
integrity sha512-Pnggyp5Y36m/Xy9hrQZgQ24MES1g8LDnGcpifGYZ848cC6h/ZHSTs74WueFog0Ddkph0bg/JQIbbbLdo5fwJNw==
"@expo/[email protected]8":
version "1.0.108"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.108.tgz#3d06486108ce27c898799ed8e6a33ff4bdf6b8f8"
integrity sha512-MUZE4MkY20Y5y1oKDGvnRFFtue5qtkC/l9gsPW2rAxBe9lXSVIGhfSpIDk0MUgd0sjdJbO/qDmYbrqf09qj/lA==
dependencies:
"@expo/eas-build-job" "1.0.107"
"@expo/eas-build-job" "1.0.108"
"@expo/logger" "1.0.57"
"@expo/spawn-async" "^1.7.2"
arg "^5.0.2"
@@ -12690,7 +12690,16 @@ string-length@^5.0.1:
char-regex "^2.0.0"
strip-ansi "^7.0.1"
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -12807,7 +12816,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -12821,6 +12830,13 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@@ -13982,7 +13998,16 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", [email protected], wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
[email protected], wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==