mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bcd94e83f | ||
|
|
da4b56a58d | ||
|
|
1080600af6 | ||
|
|
dcb6713f12 | ||
|
|
24a1d2d581 | ||
|
|
8af88c2d29 | ||
|
|
d416ac2162 | ||
|
|
7bf0493a4d | ||
|
|
91f32cc377 | ||
|
|
cf54f62bac | ||
|
|
3ef3e8447c | ||
|
|
ad64e1afad | ||
|
|
a0800cf7ef | ||
|
|
34436ee674 | ||
|
|
1072b75927 | ||
|
|
c0c9d2e7df | ||
|
|
19b831667f | ||
|
|
09db7da23b | ||
|
|
f08dde2469 | ||
|
|
213b5d240c | ||
|
|
16ebdce913 | ||
|
|
121ff7a718 | ||
|
|
360393f647 | ||
|
|
82d66054dc | ||
|
|
d8849dbf8c | ||
|
|
4d6b5f435b | ||
|
|
8fe7dfea29 | ||
|
|
8750155c25 | ||
|
|
a0fce1c773 |
No files matched your search
@@ -37,7 +37,7 @@ body:
|
||||
required: true
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Run `npx expo-env-info` and `expo doctor`, and paste the output here.
|
||||
description: Run `npx expo-env-info` and `npx expo-doctor`, and paste the output here.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -10,6 +10,56 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [3.9.3](https://github.com/expo/eas-cli/releases/tag/v3.9.3) - 2023-04-18
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix prompts in `eas update`. ([#1797](https://github.com/expo/eas-cli/pull/1797) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
## [3.9.2](https://github.com/expo/eas-cli/releases/tag/v3.9.2) - 2023-04-13
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fixes the rollout percentages when ending a rollout. ([#1781](https://github.com/expo/eas-cli/pull/1781) by [@jonsamp](https://github.com/jonsamp))
|
||||
- Print meaningful error message if `extra.eas.projectId` is not a string. ([#1788](https://github.com/expo/eas-cli/pull/1788) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Show output of `expo install expo-updates`. ([#1782](https://github.com/expo/eas-cli/pull/1782) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Print deprecation warnings for deprecated `eas.json` fields. ([#1768](https://github.com/expo/eas-cli/pull/1768) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [3.9.1](https://github.com/expo/eas-cli/releases/tag/v3.9.1) - 2023-04-10
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Update apple utilities to work around the maintenance error. ([#1780](https://github.com/expo/eas-cli/pull/1780) by [@byCedric](https://github.com/byCedric))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Don't use defaults for the `cache.cacheDefaultPaths` build profile field. ([#1769](https://github.com/expo/eas-cli/pull/1769) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [3.9.0](https://github.com/expo/eas-cli/releases/tag/v3.9.0) - 2023-04-06
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for roll back to embedded updates. ([#1754](https://github.com/expo/eas-cli/pull/1754), [#1755](https://github.com/expo/eas-cli/pull/1755) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Update `expoCommandAsync` to support the new Expo CLI bin path. ([#1772](https://github.com/expo/eas-cli/pull/1772) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Use the `eas-cli` npm tag for checking for the local build plugin updates. ([#1759](https://github.com/expo/eas-cli/pull/1759) by [@dsokal](https://github.com/dsokal))
|
||||
- Ignore `requireCommit` option in `eas build:internal`. ([#1760](https://github.com/expo/eas-cli/pull/1760) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Support parsing eas.json from a string. ([#1766](https://github.com/expo/eas-cli/pull/1766) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
## [3.8.1](https://github.com/expo/eas-cli/releases/tag/v3.8.1) - 2023-03-16
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Ensure public config is used as update. ([#1745](https://github.com/expo/eas-cli/pull/1745) by [@byCedric](https://github.com/byCedric))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "3.8.1",
|
||||
"version": "3.9.3",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+79
-54
@@ -79,6 +79,7 @@ eas --help COMMAND
|
||||
* [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
||||
* [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
||||
* [`eas channel:list`](#eas-channellist)
|
||||
* [`eas channel:rollout [CHANNEL]`](#eas-channelrollout-channel)
|
||||
* [`eas channel:view [NAME]`](#eas-channelview-name)
|
||||
* [`eas config`](#eas-config)
|
||||
* [`eas credentials`](#eas-credentials)
|
||||
@@ -130,7 +131,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -147,7 +148,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -164,7 +165,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -178,7 +179,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -228,7 +229,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -249,7 +250,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -269,7 +270,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -289,7 +290,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -312,7 +313,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -342,7 +343,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -359,7 +360,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -376,7 +377,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -411,7 +412,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -448,7 +449,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -474,7 +475,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -498,7 +499,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -545,7 +546,7 @@ DESCRIPTION
|
||||
Update version of an app.
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -564,7 +565,7 @@ DESCRIPTION
|
||||
Update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -581,7 +582,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -602,7 +603,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -624,7 +625,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -644,7 +645,31 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
Roll a new branch out on a channel incrementally.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas channel:rollout [CHANNEL] [--branch <value>] [--percent <value>] [--end] [--json --non-interactive]
|
||||
|
||||
ARGUMENTS
|
||||
CHANNEL channel on which the rollout should be done
|
||||
|
||||
FLAGS
|
||||
--branch=<value> branch to rollout
|
||||
--end end the rollout
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--percent=<value> percent of users to send to the new branch
|
||||
|
||||
DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -667,7 +692,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -688,7 +713,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -705,7 +730,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -719,7 +744,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -739,7 +764,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -760,7 +785,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -774,7 +799,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -788,7 +813,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -876,7 +901,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -893,7 +918,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -910,7 +935,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -924,7 +949,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -938,7 +963,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -960,7 +985,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -983,7 +1008,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -1001,7 +1026,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1015,7 +1040,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1035,7 +1060,7 @@ DESCRIPTION
|
||||
read environment secrets from env file and store on the server
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1065,7 +1090,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1097,7 +1122,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1115,7 +1140,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1136,7 +1161,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1158,11 +1183,11 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
rollback to an existing update
|
||||
roll back to an existing update
|
||||
|
||||
```
|
||||
USAGE
|
||||
@@ -1179,10 +1204,10 @@ FLAGS
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
DESCRIPTION
|
||||
rollback to an existing update
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1202,7 +1227,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1223,7 +1248,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1243,7 +1268,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1261,7 +1286,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1283,7 +1308,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1300,7 +1325,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.8.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
Generated
+2180
-364
File diff suppressed because it is too large.
Load diff
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "3.8.1",
|
||||
"version": "3.9.3",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "1.1.0",
|
||||
"@expo/apple-utils": "1.2.0",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "7.0.3",
|
||||
"@expo/config-plugins": "5.0.4",
|
||||
"@expo/config-types": "47.0.0",
|
||||
"@expo/eas-build-job": "0.2.106",
|
||||
"@expo/eas-json": "3.8.0",
|
||||
"@expo/eas-build-job": "1.0.7",
|
||||
"@expo/eas-json": "3.9.2",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.0.33",
|
||||
@@ -27,6 +27,7 @@
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.0",
|
||||
"@expo/steps": "1.0.8",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "1.23.2",
|
||||
"@oclif/plugin-autocomplete": "1.3.10",
|
||||
|
||||
@@ -25,7 +25,6 @@ interface JobData {
|
||||
const cacheDefaults = {
|
||||
disabled: false,
|
||||
customPaths: [],
|
||||
cacheDefaultPaths: true,
|
||||
};
|
||||
|
||||
export async function prepareJobAsync(
|
||||
|
||||
@@ -612,7 +612,7 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
|
||||
}
|
||||
|
||||
async function updateIosBuildProfilesToUseM1WorkersAsync(projectDir: string): Promise<void> {
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
|
||||
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
|
||||
|
||||
@@ -28,7 +28,6 @@ interface JobData {
|
||||
const cacheDefaults = {
|
||||
disabled: false,
|
||||
customPaths: [],
|
||||
cacheDefaultPaths: true,
|
||||
};
|
||||
|
||||
export async function prepareJobAsync(
|
||||
|
||||
@@ -99,7 +99,7 @@ export async function runBuildAndSubmitAsync(
|
||||
projectDir,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const easJsonCliConfig: EasJson['cli'] =
|
||||
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ export class DynamicProjectConfigContextField extends ContextField<DynamicConfig
|
||||
nonInteractive,
|
||||
sessionManager,
|
||||
}: ContextOptions): Promise<DynamicConfigContextFn> {
|
||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||
return async (options?: ExpoConfigOptions) => {
|
||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||
const expBefore = getExpoConfig(projectDir, options);
|
||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||
nonInteractive,
|
||||
|
||||
+22
@@ -252,4 +252,26 @@ describe(getProjectIdAsync, () => {
|
||||
getProjectIdAsync(sessionManager, { name: 'test', slug: 'test' }, { nonInteractive: false })
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('throws if extra.eas.projectId is not a string', async () => {
|
||||
jest.mocked(getConfig).mockReturnValue({
|
||||
exp: { name: 'test', slug: 'test', extra: { eas: { projectId: 1234 } } },
|
||||
} as any);
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@notnotbrent/test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'notnotbrent' } as any,
|
||||
});
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'wat', extra: { eas: { projectId: 1234 } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`Project config: "extra.eas.projectId" must be a string, found number. If you're not sure how to set it up on your own, remove the property entirely and it will be automatically configured on the next EAS CLI run.`
|
||||
);
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -12,7 +12,7 @@ import { getVcsClient, setVcsClient } from '../../../vcs';
|
||||
import GitClient from '../../../vcs/clients/git';
|
||||
|
||||
async function applyCliConfigAsync(projectDir: string): Promise<void> {
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
|
||||
throw new Error(
|
||||
|
||||
@@ -89,6 +89,12 @@ export async function getProjectIdAsync(
|
||||
|
||||
const localProjectId = exp.extra?.eas?.projectId;
|
||||
if (localProjectId) {
|
||||
if (typeof localProjectId !== 'string') {
|
||||
throw new Error(
|
||||
`Project config: "extra.eas.projectId" must be a string, found ${typeof localProjectId}. If you're not sure how to set it up on your own, remove the property entirely and it will be automatically configured on the next EAS CLI run.`
|
||||
);
|
||||
}
|
||||
|
||||
// check that the local project ID matches account and slug
|
||||
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, localProjectId);
|
||||
if (exp.owner && exp.owner !== appForProjectId.ownerAccount.name) {
|
||||
|
||||
@@ -251,7 +251,7 @@ export async function handleDeprecatedEasJsonAsync(
|
||||
if (!(await fs.pathExists(EasJsonAccessor.formatEasJsonPath(projectDir)))) {
|
||||
return;
|
||||
}
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
|
||||
const platformAndProfileNames: [Platform, string][] = profileNames.flatMap(profileName => [
|
||||
[Platform.ANDROID, profileName],
|
||||
|
||||
@@ -6,6 +6,8 @@ import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { setVcsClient } from '../../vcs';
|
||||
import GitNoCommitClient from '../../vcs/clients/gitNoCommit';
|
||||
|
||||
/**
|
||||
* This command will be run on the EAS Build workers, when building
|
||||
@@ -51,6 +53,8 @@ export default class BuildInternal extends EasCommand {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
setVcsClient(new GitNoCommitClient());
|
||||
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
|
||||
@@ -121,7 +121,7 @@ export default class BuildResign extends EasCommand {
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasBuild]);
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const easJsonCliConfig: EasJson['cli'] =
|
||||
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
});
|
||||
|
||||
const platform = await selectPlatformAsync(flags.platform);
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
|
||||
const profile = await EasJsonUtils.getBuildProfileAsync(
|
||||
easJsonAccessor,
|
||||
|
||||
@@ -71,7 +71,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
});
|
||||
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
|
||||
|
||||
const platforms = toPlatforms(requestedPlatform);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import { BranchMapping, getBranchMapping } from '../../channel/utils';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
@@ -132,13 +134,11 @@ async function startRolloutAsync(
|
||||
);
|
||||
}
|
||||
|
||||
const logMessage = `️Started a rollout of branch ${chalk.bold(
|
||||
const logMessage = `Started a rollout of branch ${chalk.bold(branchName)} on channel ${chalk.bold(
|
||||
channelName!
|
||||
)}! ${chalk.bold(percent)}% of users will be directed to branch ${chalk.bold(
|
||||
branchName
|
||||
)} onto channel ${chalk.bold(channelName!)}! ${chalk.bold(
|
||||
percent
|
||||
)}% of users will be directed to branch ${chalk.bold(branchName)}, ${chalk.bold(
|
||||
100 - percent
|
||||
)}% to branch ${chalk.bold(oldBranch.name)}.`;
|
||||
)}, ${chalk.bold(100 - percent)}% to branch ${chalk.bold(oldBranch.name)}.`;
|
||||
|
||||
return { newChannelInfo, logMessage };
|
||||
}
|
||||
@@ -190,7 +190,7 @@ async function editRolloutAsync(
|
||||
branchMapping: JSON.stringify(newBranchMapping),
|
||||
});
|
||||
|
||||
const logMessage = `️Rollout of branch ${chalk.bold(newBranch.name)} onto channel ${chalk.bold(
|
||||
const logMessage = `Rollout of branch ${chalk.bold(newBranch.name)} on channel ${chalk.bold(
|
||||
channelName!
|
||||
)} updated from ${chalk.bold(currentPercent)}% to ${chalk.bold(percent)}%. ${chalk.bold(
|
||||
percent
|
||||
@@ -256,11 +256,11 @@ async function endRolloutAsync(
|
||||
'Ending the rollout will send all traffic to a single branch. Which one should that be?',
|
||||
[
|
||||
{
|
||||
title: `${newBranch.name} ${chalk.grey(`- current percent: ${100 - currentPercent}%`)}`,
|
||||
title: `${newBranch.name} ${chalk.grey(`- current percent: ${currentPercent}%`)}`,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
title: `${oldBranch.name} ${chalk.grey(`- current percent: ${currentPercent}%`)}`,
|
||||
title: `${oldBranch.name} ${chalk.grey(`- current percent: ${100 - currentPercent}%`)}`,
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
@@ -284,7 +284,7 @@ async function endRolloutAsync(
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(newBranchMapping),
|
||||
});
|
||||
const logMessage = `️Rollout on channel ${chalk.bold(
|
||||
const logMessage = `Rollout on channel ${chalk.bold(
|
||||
channelName
|
||||
)} ended. All traffic is now sent to branch ${chalk.bold(
|
||||
endOnNewBranch ? newBranch.name : oldBranch.name
|
||||
@@ -294,14 +294,12 @@ async function endRolloutAsync(
|
||||
}
|
||||
|
||||
export default class ChannelRollout extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'Rollout a new branch out to a channel incrementally.';
|
||||
static override description = 'Roll a new branch out on a channel incrementally.';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'channel',
|
||||
required: true,
|
||||
description: 'rollout that the channel is on',
|
||||
description: 'channel on which the rollout should be done',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -311,7 +309,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
required: false,
|
||||
}),
|
||||
percent: Flags.integer({
|
||||
description: 'percent of traffic to redirect to the new branch',
|
||||
description: 'percent of users to send to the new branch',
|
||||
required: false,
|
||||
}),
|
||||
end: Flags.boolean({
|
||||
@@ -328,7 +326,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { channel: channelName },
|
||||
args: { channel: channelNameArg },
|
||||
flags: {
|
||||
json: jsonFlag,
|
||||
end: endFlag,
|
||||
@@ -349,9 +347,23 @@ export default class ChannelRollout extends EasCommand {
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
let channelName: string = channelNameArg;
|
||||
if (!channelName) {
|
||||
const { name } = await selectChannelOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
selectionPromptTitle: 'Select a channel on which to perform a rollout',
|
||||
paginatedQueryOptions: {
|
||||
json: jsonFlag,
|
||||
nonInteractive,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
channelName = name;
|
||||
}
|
||||
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: channelName!,
|
||||
channelName,
|
||||
});
|
||||
if (!channel) {
|
||||
throw new Error(
|
||||
@@ -396,7 +408,15 @@ export default class ChannelRollout extends EasCommand {
|
||||
if (!isRollout) {
|
||||
rolloutMutationResult = await startRolloutAsync(graphqlClient, {
|
||||
channelName,
|
||||
branchName: branchName ?? (await promptForBranchNameAsync(channelName, nonInteractive)),
|
||||
branchName:
|
||||
branchName ??
|
||||
(await promptForBranchNameAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelName,
|
||||
nonInteractive,
|
||||
json: jsonFlag,
|
||||
})),
|
||||
percent,
|
||||
nonInteractive,
|
||||
projectId,
|
||||
@@ -433,18 +453,36 @@ export default class ChannelRollout extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async function promptForBranchNameAsync(
|
||||
channelName: string,
|
||||
nonInteractive: boolean
|
||||
): Promise<string> {
|
||||
async function promptForBranchNameAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelName,
|
||||
nonInteractive,
|
||||
json,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
channelName: string;
|
||||
nonInteractive: boolean;
|
||||
json: boolean;
|
||||
}): Promise<string> {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply branch flag in non-interactive mode');
|
||||
}
|
||||
const { name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: `Select a branch to rollout onto ${channelName}`,
|
||||
validate: value => (value ? true : 'A branch must be specified.'),
|
||||
|
||||
const { name: branchName } = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: `Which branch would you like roll out on ${channelName}?`,
|
||||
displayTextForListItem: updateBranch => ({
|
||||
title: updateBranch.name,
|
||||
}),
|
||||
// discard limit and offset because this query is not their intended target
|
||||
paginatedQueryOptions: {
|
||||
json,
|
||||
nonInteractive,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
return name;
|
||||
|
||||
return branchName;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export default class Config extends EasCommand {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor(projectDir);
|
||||
const accessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const profileName =
|
||||
maybeProfile ??
|
||||
(await selectAsync(
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class MetadataLint extends EasCommand {
|
||||
|
||||
const submitProfiles = await getProfilesAsync({
|
||||
type: 'submit',
|
||||
easJsonAccessor: new EasJsonAccessor(projectDir),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class MetadataPull extends EasCommand {
|
||||
|
||||
const submitProfiles = await getProfilesAsync({
|
||||
type: 'submit',
|
||||
easJsonAccessor: new EasJsonAccessor(projectDir),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class MetadataPush extends EasCommand {
|
||||
|
||||
const submitProfiles = await getProfilesAsync({
|
||||
type: 'submit',
|
||||
easJsonAccessor: new EasJsonAccessor(projectDir),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ export default class Submit extends EasCommand {
|
||||
const platforms = toPlatforms(flagsWithPlatform.requestedPlatform);
|
||||
const submissionProfiles = await getProfilesAsync({
|
||||
type: 'submit',
|
||||
easJsonAccessor: new EasJsonAccessor(projectDir),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms,
|
||||
profileName: flagsWithPlatform.profile,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@expo/config';
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { vol } from 'memfs';
|
||||
import nullthrows from 'nullthrows';
|
||||
import path from 'path';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
@@ -36,6 +37,7 @@ const updateStub: UpdateFragment = {
|
||||
platform: 'ios',
|
||||
gitCommitHash: 'commit',
|
||||
manifestFragment: JSON.stringify({ fake: 'manifest' }),
|
||||
isRollBackToEmbedded: false,
|
||||
manifestPermalink: 'https://expo.dev/fake/manifest/link',
|
||||
codeSigningInfo: null,
|
||||
createdAt: '2022-01-01T12:00:00Z',
|
||||
@@ -90,7 +92,7 @@ describe(UpdatePublish.name, () => {
|
||||
mockTestProject();
|
||||
|
||||
await expect(new UpdatePublish(flags, commandOptions).run()).rejects.toThrow(
|
||||
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto'
|
||||
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto.'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -181,7 +183,7 @@ describe(UpdatePublish.name, () => {
|
||||
// Pull the publish data from the mocked publish function
|
||||
const publishData = jest.mocked(PublishMutation.publishUpdateGroupAsync).mock.calls[0][1][0];
|
||||
// Pull the Expo config from the publish data
|
||||
const expoConfig = publishData.updateInfoGroup.ios!.extra.expoClient;
|
||||
const expoConfig = nullthrows(publishData.updateInfoGroup).ios!.extra.expoClient;
|
||||
|
||||
// Ensure the basic properties are present
|
||||
expect(expoConfig).toHaveProperty('name', appJson.expo.name);
|
||||
|
||||
@@ -27,6 +27,7 @@ const updateStub: UpdateFragment = {
|
||||
platform: 'ios',
|
||||
gitCommitHash: 'commit',
|
||||
manifestFragment: JSON.stringify({ fake: 'manifest' }),
|
||||
isRollBackToEmbedded: false,
|
||||
manifestPermalink: 'https://expo.dev/fake/manifest/link',
|
||||
codeSigningInfo: null,
|
||||
createdAt: '2022-01-01T12:00:00Z',
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
import {
|
||||
AppJSONConfig,
|
||||
ExpoConfig,
|
||||
GetConfigOptions,
|
||||
PackageJSONConfig,
|
||||
getConfig,
|
||||
} from '@expo/config';
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { vol } from 'memfs';
|
||||
import nullthrows from 'nullthrows';
|
||||
import path from 'path';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ensureBranchExistsAsync } from '../../../branch/queries';
|
||||
import { DynamicProjectConfigContextField } from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
||||
import { jester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { UpdateFragment } from '../../../graphql/generated';
|
||||
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
|
||||
import UpdateRollBackToEmbedded from '../roll-back-to-embedded';
|
||||
|
||||
const projectRoot = '/test-project';
|
||||
const commandOptions = { root: projectRoot } as any;
|
||||
const updateStub: UpdateFragment = {
|
||||
id: 'update-1234',
|
||||
group: 'group-1234',
|
||||
branch: { id: 'branch-1234', name: 'main' },
|
||||
message: 'test message',
|
||||
runtimeVersion: 'exposdk:47.0.0',
|
||||
platform: 'ios',
|
||||
gitCommitHash: 'commit',
|
||||
manifestFragment: JSON.stringify({ fake: 'manifest' }),
|
||||
isRollBackToEmbedded: false,
|
||||
manifestPermalink: 'https://expo.dev/fake/manifest/link',
|
||||
codeSigningInfo: null,
|
||||
createdAt: '2022-01-01T12:00:00Z',
|
||||
};
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('@expo/config');
|
||||
jest.mock('@expo/config-plugins');
|
||||
jest.mock('../../../branch/queries');
|
||||
jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync');
|
||||
jest.mock('../../../update/configure');
|
||||
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
|
||||
jest.mock('../../../graphql/mutations/PublishMutation');
|
||||
jest.mock('../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../../graphql/queries/UpdateQuery');
|
||||
jest.mock('../../../ora', () => ({
|
||||
ora: () => ({
|
||||
start: () => ({ succeed: () => {}, fail: () => {}, stop: () => {} }),
|
||||
}),
|
||||
}));
|
||||
jest.mock('../../../project/publish', () => ({
|
||||
...jest.requireActual('../../../project/publish'),
|
||||
buildBundlesAsync: jest.fn(),
|
||||
collectAssetsAsync: jest.fn(),
|
||||
resolveInputDirectoryAsync: jest.fn((inputDir = 'dist') => path.join(projectRoot, inputDir)),
|
||||
uploadAssetsAsync: jest.fn(),
|
||||
}));
|
||||
|
||||
describe(UpdateRollBackToEmbedded.name, () => {
|
||||
afterEach(() => vol.reset());
|
||||
|
||||
it('errors with both --channel and --branch', async () => {
|
||||
const flags = ['--channel=channel123', '--branch=branch123'];
|
||||
|
||||
mockTestProject();
|
||||
|
||||
await expect(new UpdateRollBackToEmbedded(flags, commandOptions).run()).rejects.toThrow(
|
||||
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto.'
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a roll back to embedded with --non-interactive, --branch, and --message', async () => {
|
||||
const flags = ['--non-interactive', '--branch=branch123', '--message=abc'];
|
||||
|
||||
mockTestProject();
|
||||
const platforms = ['android', 'ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
branchId: 'branch123',
|
||||
createdBranch: false,
|
||||
});
|
||||
|
||||
jest
|
||||
.mocked(PublishMutation.publishUpdateGroupAsync)
|
||||
.mockResolvedValue(platforms.map(platform => ({ ...updateStub, platform, runtimeVersion })));
|
||||
|
||||
await new UpdateRollBackToEmbedded(flags, commandOptions).run();
|
||||
|
||||
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates a roll back to embedded with --non-interactive, --channel, and --message', async () => {
|
||||
const flags = ['--non-interactive', '--channel=channel123', '--message=abc'];
|
||||
|
||||
const { projectId } = mockTestProject();
|
||||
const platforms = ['android', 'ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
branchId: 'branch123',
|
||||
createdBranch: false,
|
||||
});
|
||||
|
||||
jest.mocked(PublishMutation.publishUpdateGroupAsync).mockResolvedValue(
|
||||
platforms.map(platform => ({
|
||||
...updateStub,
|
||||
runtimeVersion,
|
||||
platform,
|
||||
}))
|
||||
);
|
||||
|
||||
await new UpdateRollBackToEmbedded(flags, commandOptions).run();
|
||||
|
||||
expect(ensureBranchExistsAsync).toHaveBeenCalledWith(
|
||||
expect.any(Object), // graphql client
|
||||
{
|
||||
appId: projectId,
|
||||
branchName: 'branchFromChannel',
|
||||
}
|
||||
);
|
||||
|
||||
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates a roll back to embedded with the public expo config', async () => {
|
||||
const flags = ['--non-interactive', '--branch=branch123', '--message=abc'];
|
||||
|
||||
// Add configuration to the project that should not be included in the update
|
||||
mockTestProject({
|
||||
expoConfig: {
|
||||
hooks: {
|
||||
postPublish: [
|
||||
{
|
||||
file: 'custom-hook.js',
|
||||
config: { some: 'config' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const platforms = ['ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
// Mock an existing branch, so we don't create a new one
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
branchId: 'branch123',
|
||||
createdBranch: false,
|
||||
});
|
||||
|
||||
jest
|
||||
.mocked(PublishMutation.publishUpdateGroupAsync)
|
||||
.mockResolvedValue(platforms.map(platform => ({ ...updateStub, platform, runtimeVersion })));
|
||||
|
||||
await new UpdateRollBackToEmbedded(flags, commandOptions).run();
|
||||
|
||||
// Pull the publish data from the mocked publish function
|
||||
const publishData = jest.mocked(PublishMutation.publishUpdateGroupAsync).mock.calls[0][1][0];
|
||||
expect(nullthrows(publishData.rollBackToEmbeddedInfoGroup).ios).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/** Create a new in-memory project, based on src/commands/project/__tests__/init.test.ts */
|
||||
function mockTestProject({
|
||||
configuredProjectId = '1234',
|
||||
expoConfig = {},
|
||||
}: {
|
||||
configuredProjectId?: string;
|
||||
expoConfig?: Partial<ExpoConfig>;
|
||||
} = {}): { projectId: string; appJson: AppJSONConfig } {
|
||||
const packageJSON: PackageJSONConfig = {
|
||||
name: 'testing123',
|
||||
version: '0.1.0',
|
||||
description: 'fake description',
|
||||
main: 'index.js',
|
||||
};
|
||||
|
||||
const appJSON: AppJSONConfig = {
|
||||
expo: {
|
||||
name: 'testing 123',
|
||||
version: '0.1.0',
|
||||
slug: 'testing-123',
|
||||
sdkVersion: '33.0.0',
|
||||
owner: jester.accounts[0].name,
|
||||
extra: {
|
||||
eas: {
|
||||
projectId: configuredProjectId,
|
||||
},
|
||||
},
|
||||
...expoConfig,
|
||||
},
|
||||
};
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify(packageJSON),
|
||||
'app.json': JSON.stringify(appJSON),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
const mockManifest = { exp: appJSON.expo };
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
|
||||
|
||||
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
||||
jest
|
||||
.spyOn(DynamicProjectConfigContextField.prototype, 'getValueAsync')
|
||||
.mockResolvedValue(async (options?: GetConfigOptions) => {
|
||||
let exp = { ...mockManifest.exp };
|
||||
|
||||
// Fake a limited public config
|
||||
if (options?.isPublicConfig) {
|
||||
exp = {
|
||||
name: mockManifest.exp.name,
|
||||
version: mockManifest.exp.version,
|
||||
slug: mockManifest.exp.slug,
|
||||
sdkVersion: mockManifest.exp.sdkVersion,
|
||||
owner: mockManifest.exp.owner,
|
||||
extra: mockManifest.exp.extra,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
exp,
|
||||
projectDir: projectRoot,
|
||||
projectId: configuredProjectId,
|
||||
};
|
||||
});
|
||||
|
||||
jest.spyOn(LoggedInContextField.prototype, 'getValueAsync').mockResolvedValue({
|
||||
actor: jester,
|
||||
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
|
||||
graphqlClient,
|
||||
});
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
return { projectId: configuredProjectId, appJson: appJSON };
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
import { ExpoConfig, Platform as PublishPlatform } from '@expo/config';
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Platform as PublishPlatform } from '@expo/config';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ensureBranchExistsAsync, selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import { getDefaultBranchNameAsync } from '../../branch/utils';
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import { ensureChannelExistsAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
@@ -23,32 +19,32 @@ import {
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform, requestedPlatformDisplayNames } from '../../platform';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
ExpoCLIExportPlatformFlag,
|
||||
buildBundlesAsync,
|
||||
buildUnsortedUpdateInfoGroupAsync,
|
||||
collectAssetsAsync,
|
||||
defaultPublishPlatforms,
|
||||
filterExportedPlatformsByFlag,
|
||||
getBranchNameForCommandAsync,
|
||||
getRequestedPlatform,
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions,
|
||||
getRuntimeVersionObjectAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
resolveInputDirectoryAsync,
|
||||
uploadAssetsAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
|
||||
import { getBranchNameFromChannelNameAsync } from '../../update/getBranchNameFromChannelNameAsync';
|
||||
import {
|
||||
formatUpdateMessage,
|
||||
getUpdateGroupJsonInfo,
|
||||
truncateString as truncateUpdateMessage,
|
||||
} from '../../update/utils';
|
||||
import { getUpdateGroupJsonInfo } from '../../update/utils';
|
||||
import { maybeWarnAboutEasPlanOverageAsync } from '../../utils/billing';
|
||||
import {
|
||||
checkManifestBodyAgainstUpdateInfoGroup,
|
||||
getCodeSigningInfoAsync,
|
||||
getManifestBodyAsync,
|
||||
signManifestBody,
|
||||
signBody,
|
||||
} from '../../utils/code-signing';
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
@@ -56,8 +52,6 @@ import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
|
||||
export const defaultPublishPlatforms: Partial<PublishPlatform>[] = ['android', 'ios'];
|
||||
|
||||
type RawUpdateFlags = {
|
||||
auto: boolean;
|
||||
branch?: string;
|
||||
@@ -88,21 +82,6 @@ type UpdateFlags = {
|
||||
nonInteractive: boolean;
|
||||
};
|
||||
|
||||
function getRequestedPlatform(platform: ExpoCLIExportPlatformFlag): RequestedPlatform | null {
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return RequestedPlatform.Android;
|
||||
case 'ios':
|
||||
return RequestedPlatform.Ios;
|
||||
case 'web':
|
||||
return null;
|
||||
case 'all':
|
||||
return RequestedPlatform.All;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default class UpdatePublish extends EasCommand {
|
||||
static override description = 'publish an update group';
|
||||
|
||||
@@ -166,18 +145,18 @@ export default class UpdatePublish extends EasCommand {
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags: rawFlags } = await this.parse(UpdatePublish);
|
||||
const paginatedQueryOptions = getPaginatedQueryOptions(rawFlags);
|
||||
let {
|
||||
const {
|
||||
auto: autoFlag,
|
||||
platform: platformFlag,
|
||||
channelName,
|
||||
updateMessage,
|
||||
channelName: channelNameArg,
|
||||
updateMessage: updateMessageArg,
|
||||
inputDir,
|
||||
skipBundler,
|
||||
privateKeyPath,
|
||||
json: jsonFlag,
|
||||
nonInteractive,
|
||||
branchName: branchNameArg,
|
||||
} = this.sanitizeFlags(rawFlags);
|
||||
let branchName = this.sanitizeFlags(rawFlags).branchName;
|
||||
|
||||
const {
|
||||
getDynamicProjectConfigAsync,
|
||||
@@ -198,10 +177,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
isPublicConfig: true,
|
||||
});
|
||||
|
||||
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
||||
isPublicConfig: false,
|
||||
});
|
||||
|
||||
await maybeWarnAboutEasPlanOverageAsync(graphqlClient, projectId);
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
@@ -212,74 +188,27 @@ export default class UpdatePublish extends EasCommand {
|
||||
});
|
||||
|
||||
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
||||
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
||||
isPublicConfig: false,
|
||||
});
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
let realizedPlatforms: PublishPlatform[] = [];
|
||||
const branchName = await getBranchNameForCommandAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelNameArg,
|
||||
branchNameArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
|
||||
if (channelName && branchName) {
|
||||
throw new Error(
|
||||
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto'
|
||||
);
|
||||
}
|
||||
|
||||
if (channelName) {
|
||||
branchName = await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelName);
|
||||
}
|
||||
|
||||
if (!branchName) {
|
||||
if (autoFlag) {
|
||||
branchName = await getDefaultBranchNameAsync();
|
||||
} else if (nonInteractive) {
|
||||
throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode');
|
||||
} else {
|
||||
try {
|
||||
const branch = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: `Which branch would you like to publish on?`,
|
||||
displayTextForListItem: updateBranch => ({
|
||||
title: `${updateBranch.name} ${chalk.grey(
|
||||
`- current update: ${formatUpdateMessage(updateBranch.updates[0])}`
|
||||
)}`,
|
||||
}),
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
branchName = branch.name;
|
||||
} catch {
|
||||
// unable to select a branch (network error or no branches for project)
|
||||
({ name: branchName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'No branches found. Provide a branch name:',
|
||||
initial: await getDefaultBranchNameAsync(),
|
||||
validate: value => (value ? true : 'Branch name may not be empty.'),
|
||||
}));
|
||||
}
|
||||
|
||||
assert(branchName, 'Branch name must be specified.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!updateMessage && autoFlag) {
|
||||
updateMessage = (await getVcsClient().getLastCommitMessageAsync())?.trim();
|
||||
}
|
||||
|
||||
if (!updateMessage) {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply --message or use --auto when in non-interactive mode');
|
||||
}
|
||||
|
||||
const validationMessage = 'publish message may not be empty.';
|
||||
if (jsonFlag) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
({ updateMessage } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'updateMessage',
|
||||
message: `Provide an update message.`,
|
||||
initial: (await getVcsClient().getLastCommitMessageAsync())?.trim(),
|
||||
validate: (value: any) => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
const updateMessage = await getUpdateMessageForCommandAsync({
|
||||
updateMessageArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
jsonFlag,
|
||||
});
|
||||
|
||||
// build bundle and upload assets for a new publish
|
||||
if (!skipBundler) {
|
||||
@@ -300,6 +229,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
let unsortedUpdateInfoGroups: UpdateInfoGroup = {};
|
||||
let uploadedAssetCount = 0;
|
||||
let assetLimitPerUpdateGroup = 0;
|
||||
let realizedPlatforms: PublishPlatform[] = [];
|
||||
|
||||
try {
|
||||
const collectedAssets = await collectAssetsAsync(distRoot);
|
||||
@@ -351,22 +281,9 @@ export default class UpdatePublish extends EasCommand {
|
||||
throw e;
|
||||
}
|
||||
|
||||
const truncatedMessage = truncateUpdateMessage(updateMessage!, 1024);
|
||||
if (truncatedMessage !== updateMessage) {
|
||||
Log.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
|
||||
}
|
||||
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir);
|
||||
|
||||
const runtimeToPlatformMapping: { runtimeVersion: string; platforms: string[] }[] = [];
|
||||
for (const runtime of runtimeVersions) {
|
||||
const platforms = runtimeVersions
|
||||
.filter(({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion)
|
||||
.map(({ platform }) => platform);
|
||||
if (!runtimeToPlatformMapping.find(item => item.runtimeVersion === runtime.runtimeVersion)) {
|
||||
runtimeToPlatformMapping.push({ runtimeVersion: runtime.runtimeVersion, platforms });
|
||||
}
|
||||
}
|
||||
const runtimeToPlatformMapping =
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
|
||||
|
||||
const { branchId, createdBranch } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
@@ -401,7 +318,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
branchId,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
runtimeVersion,
|
||||
message: truncatedMessage,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
@@ -418,7 +335,10 @@ export default class UpdatePublish extends EasCommand {
|
||||
|
||||
const updatesTemp = [...newUpdates];
|
||||
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
|
||||
const newUpdates = updatesTemp.splice(0, Object.keys(updateGroup.updateInfoGroup).length);
|
||||
const newUpdates = updatesTemp.splice(
|
||||
0,
|
||||
Object.keys(nullthrows(updateGroup.updateInfoGroup)).length
|
||||
);
|
||||
return {
|
||||
updateGroup,
|
||||
newUpdates,
|
||||
@@ -438,11 +358,13 @@ export default class UpdatePublish extends EasCommand {
|
||||
checkManifestBodyAgainstUpdateInfoGroup(
|
||||
manifestBody,
|
||||
nullthrows(
|
||||
updateGroup.updateInfoGroup[newUpdate.platform as keyof UpdateInfoGroup]
|
||||
nullthrows(updateGroup.updateInfoGroup)[
|
||||
newUpdate.platform as keyof UpdateInfoGroup
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
const manifestSignature = signManifestBody(manifestBody, codeSigningInfo);
|
||||
const manifestSignature = signBody(manifestBody, codeSigningInfo);
|
||||
|
||||
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
||||
alg: codeSigningInfo.codeSigningMetadata.alg,
|
||||
@@ -504,7 +426,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
|
||||
: []),
|
||||
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
|
||||
{ label: 'Message', value: truncatedMessage },
|
||||
{ label: 'Message', value: updateMessage },
|
||||
...(gitCommitHash
|
||||
? [
|
||||
{
|
||||
@@ -575,41 +497,3 @@ export default class UpdatePublish extends EasCommand {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Get runtime versions grouped by platform. Runtime version is always `null` on web where the platform is always backwards compatible. */
|
||||
async function getRuntimeVersionObjectAsync(
|
||||
exp: ExpoConfig,
|
||||
platforms: PublishPlatform[],
|
||||
projectDir: string
|
||||
): Promise<{ platform: string; runtimeVersion: string }[]> {
|
||||
for (const platform of platforms) {
|
||||
if (platform === 'web') {
|
||||
continue;
|
||||
}
|
||||
const isPolicy = typeof (exp[platform]?.runtimeVersion ?? exp.runtimeVersion) === 'object';
|
||||
if (isPolicy) {
|
||||
const isManaged =
|
||||
(await resolveWorkflowAsync(projectDir, platform as Platform)) === Workflow.MANAGED;
|
||||
if (!isManaged) {
|
||||
throw new Error(
|
||||
'Runtime version policies are only supported in the managed workflow. In the bare workflow, runtime version needs to be set manually.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(platforms)].map(platform => {
|
||||
if (platform === 'web') {
|
||||
return { platform: 'web', runtimeVersion: 'UNVERSIONED' };
|
||||
}
|
||||
return {
|
||||
platform,
|
||||
runtimeVersion: nullthrows(
|
||||
Updates.getRuntimeVersion(exp, platform),
|
||||
`Unable to determine runtime version for ${
|
||||
requestedPlatformDisplayNames[platform]
|
||||
}. ${learnMore('https://docs.expo.dev/eas-update/runtime-versions/')}`
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -56,7 +56,7 @@ type UpdateToRepublish = {
|
||||
>;
|
||||
|
||||
export default class UpdateRepublish extends EasCommand {
|
||||
static override description = 'rollback to an existing update';
|
||||
static override description = 'roll back to an existing update';
|
||||
|
||||
static override flags = {
|
||||
channel: Flags.string({
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
import { Platform as PublishPlatform } from '@expo/config';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import { ensureChannelExistsAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import fetch from '../../fetch';
|
||||
import {
|
||||
PublishUpdateGroupInput,
|
||||
StatuspageServiceName,
|
||||
UpdatePublishMutation,
|
||||
} from '../../graphql/generated';
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import Log, { link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
ExpoCLIExportPlatformFlag,
|
||||
defaultPublishPlatforms,
|
||||
getBranchNameForCommandAsync,
|
||||
getRequestedPlatform,
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions,
|
||||
getRuntimeVersionObjectAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
} from '../../project/publish';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
|
||||
import { getUpdateGroupJsonInfo } from '../../update/utils';
|
||||
import {
|
||||
CodeSigningInfo,
|
||||
checkDirectiveBodyAgainstUpdateInfoGroup,
|
||||
getCodeSigningInfoAsync,
|
||||
getDirectiveBodyAsync,
|
||||
signBody,
|
||||
} from '../../utils/code-signing';
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
|
||||
type RawUpdateFlags = {
|
||||
auto: boolean;
|
||||
branch?: string;
|
||||
channel?: string;
|
||||
message?: string;
|
||||
platform: string;
|
||||
'private-key-path'?: string;
|
||||
'non-interactive': boolean;
|
||||
json: boolean;
|
||||
};
|
||||
|
||||
type UpdateFlags = {
|
||||
auto: boolean;
|
||||
platform: ExpoCLIExportPlatformFlag;
|
||||
branchName?: string;
|
||||
channelName?: string;
|
||||
updateMessage?: string;
|
||||
privateKeyPath?: string;
|
||||
json: boolean;
|
||||
nonInteractive: boolean;
|
||||
};
|
||||
|
||||
export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
static override hidden = true; // until we launch
|
||||
static override description = 'roll back to the embedded update';
|
||||
|
||||
static override flags = {
|
||||
branch: Flags.string({
|
||||
description: 'Branch to publish the rollback to embedded update group on',
|
||||
required: false,
|
||||
}),
|
||||
channel: Flags.string({
|
||||
description: 'Channel that the published rollback to embedded update should affect',
|
||||
required: false,
|
||||
}),
|
||||
message: Flags.string({
|
||||
description: 'A short message describing the rollback to embedded update',
|
||||
required: false,
|
||||
}),
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: [...defaultPublishPlatforms, 'all'],
|
||||
default: 'all',
|
||||
required: false,
|
||||
}),
|
||||
auto: Flags.boolean({
|
||||
description:
|
||||
'Use the current git branch and commit message for the EAS branch and update message',
|
||||
default: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags: rawFlags } = await this.parse(UpdateRollBackToEmbedded);
|
||||
const paginatedQueryOptions = getPaginatedQueryOptions(rawFlags);
|
||||
const {
|
||||
auto: autoFlag,
|
||||
platform: platformFlag,
|
||||
channelName: channelNameArg,
|
||||
updateMessage: updateMessageArg,
|
||||
privateKeyPath,
|
||||
json: jsonFlag,
|
||||
nonInteractive,
|
||||
branchName: branchNameArg,
|
||||
} = this.sanitizeFlags(rawFlags);
|
||||
|
||||
const {
|
||||
getDynamicProjectConfigAsync,
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(UpdateRollBackToEmbedded, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
projectId,
|
||||
projectDir,
|
||||
} = await getDynamicProjectConfigAsync({
|
||||
isPublicConfig: true,
|
||||
});
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
platform: getRequestedPlatform(platformFlag),
|
||||
projectDir,
|
||||
projectId,
|
||||
});
|
||||
|
||||
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
||||
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
||||
isPublicConfig: false,
|
||||
});
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
const branchName = await getBranchNameForCommandAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelNameArg,
|
||||
branchNameArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
|
||||
const updateMessage = await getUpdateMessageForCommandAsync({
|
||||
updateMessageArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
jsonFlag,
|
||||
});
|
||||
|
||||
const realizedPlatforms: PublishPlatform[] =
|
||||
platformFlag === 'all' ? defaultPublishPlatforms : [platformFlag];
|
||||
|
||||
const { branchId, createdBranch } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchName,
|
||||
});
|
||||
if (createdBranch) {
|
||||
await ensureChannelExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchId,
|
||||
channelName: branchName,
|
||||
});
|
||||
}
|
||||
|
||||
Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`);
|
||||
|
||||
const vcsClient = getVcsClient();
|
||||
|
||||
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
||||
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
||||
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir);
|
||||
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
try {
|
||||
newUpdates = await this.publishRollbacksAsync({
|
||||
graphqlClient,
|
||||
isGitWorkingTreeDirty,
|
||||
gitCommitHash,
|
||||
updateMessage,
|
||||
branchId,
|
||||
codeSigningInfo,
|
||||
runtimeVersions,
|
||||
realizedPlatforms,
|
||||
});
|
||||
publishSpinner.succeed('Published!');
|
||||
} catch (e) {
|
||||
publishSpinner.fail('Failed to publish updates');
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(getUpdateGroupJsonInfo(newUpdates));
|
||||
} else {
|
||||
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
|
||||
);
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
for (const runtime of uniqBy(runtimeVersions, version => version.runtimeVersion)) {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime.runtimeVersion
|
||||
);
|
||||
if (newUpdatesForRuntimeVersion.length === 0) {
|
||||
throw new Error(
|
||||
`Publish response is missing updates with runtime ${runtime.runtimeVersion}.`
|
||||
);
|
||||
}
|
||||
const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
|
||||
const newAndroidUpdate = newUpdatesForRuntimeVersion.find(
|
||||
update => update.platform === 'android'
|
||||
);
|
||||
const newIosUpdate = newUpdatesForRuntimeVersion.find(update => update.platform === 'ios');
|
||||
const updateGroupId = newUpdatesForRuntimeVersion[0].group;
|
||||
|
||||
const projectName = exp.slug;
|
||||
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
|
||||
const updateGroupUrl = getUpdateGroupUrl(accountName, projectName, updateGroupId);
|
||||
const updateGroupLink = link(updateGroupUrl, { dim: false });
|
||||
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Branch', value: branchName },
|
||||
{ label: 'Runtime version', value: runtime.runtimeVersion },
|
||||
{ label: 'Platform', value: platforms.join(', ') },
|
||||
{ label: 'Update group ID', value: updateGroupId },
|
||||
...(newAndroidUpdate
|
||||
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
|
||||
: []),
|
||||
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
|
||||
{ label: 'Message', value: updateMessage },
|
||||
...(gitCommitHash
|
||||
? [
|
||||
{
|
||||
label: 'Commit',
|
||||
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ label: 'Website link', value: updateGroupLink },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async publishRollbacksAsync({
|
||||
graphqlClient,
|
||||
isGitWorkingTreeDirty,
|
||||
gitCommitHash,
|
||||
updateMessage,
|
||||
branchId,
|
||||
codeSigningInfo,
|
||||
runtimeVersions,
|
||||
realizedPlatforms,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
isGitWorkingTreeDirty: boolean | undefined;
|
||||
gitCommitHash: string | undefined;
|
||||
updateMessage: string;
|
||||
branchId: string;
|
||||
codeSigningInfo: CodeSigningInfo | undefined;
|
||||
runtimeVersions: { platform: string; runtimeVersion: string }[];
|
||||
realizedPlatforms: PublishPlatform[];
|
||||
}): Promise<UpdatePublishMutation['updateBranch']['publishUpdateGroups']> {
|
||||
const runtimeToPlatformMapping =
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
|
||||
const rollbackInfoGroups = Object.fromEntries(
|
||||
realizedPlatforms.map(platform => [platform, true])
|
||||
);
|
||||
|
||||
// Sort the updates into different groups based on their platform specific runtime versions
|
||||
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformMapping.map(
|
||||
({ runtimeVersion, platforms }) => {
|
||||
const localRollbackInfoGroup = Object.fromEntries(
|
||||
platforms.map(platform => [platform, rollbackInfoGroups[platform]])
|
||||
);
|
||||
|
||||
return {
|
||||
branchId,
|
||||
rollBackToEmbeddedInfoGroup: localRollbackInfoGroup,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const newUpdates = await PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
|
||||
|
||||
if (codeSigningInfo) {
|
||||
Log.log('🔒 Signing roll back');
|
||||
|
||||
const updatesTemp = [...newUpdates];
|
||||
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
|
||||
const newUpdates = updatesTemp.splice(
|
||||
0,
|
||||
Object.keys(nullthrows(updateGroup.rollBackToEmbeddedInfoGroup)).length
|
||||
);
|
||||
return {
|
||||
updateGroup,
|
||||
newUpdates,
|
||||
};
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
updateGroupsAndTheirUpdates.map(async ({ newUpdates }) => {
|
||||
await Promise.all(
|
||||
newUpdates.map(async newUpdate => {
|
||||
const response = await fetch(newUpdate.manifestPermalink, {
|
||||
method: 'GET',
|
||||
headers: { accept: 'multipart/mixed' },
|
||||
});
|
||||
const directiveBody = nullthrows(await getDirectiveBodyAsync(response));
|
||||
|
||||
checkDirectiveBodyAgainstUpdateInfoGroup(directiveBody);
|
||||
|
||||
const directiveSignature = signBody(directiveBody, codeSigningInfo);
|
||||
|
||||
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
||||
alg: codeSigningInfo.codeSigningMetadata.alg,
|
||||
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
||||
sig: directiveSignature,
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return newUpdates;
|
||||
}
|
||||
|
||||
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
|
||||
const nonInteractive = flags['non-interactive'] ?? false;
|
||||
|
||||
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
|
||||
if (nonInteractive && !auto && !(updateMessage && (branchName || channelName))) {
|
||||
Errors.error(
|
||||
'--branch and --message, or --channel and --message are required when updating in non-interactive mode unless --auto is specified',
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
auto,
|
||||
branchName,
|
||||
channelName,
|
||||
updateMessage,
|
||||
platform: flags.platform as RequestedPlatform,
|
||||
privateKeyPath: flags['private-key-path'],
|
||||
nonInteractive,
|
||||
json: flags.json ?? false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export default class IosCredentialsProvider {
|
||||
private async disablePushNotificationsSetupInEasJsonAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<void> {
|
||||
const easJsonAccessor = new EasJsonAccessor(ctx.projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(ctx.projectDir);
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
easJsonRawObject.cli = {
|
||||
|
||||
@@ -8,7 +8,7 @@ export class SelectBuildProfileFromEasJson<T extends Platform> {
|
||||
private easJsonAccessor: EasJsonAccessor;
|
||||
|
||||
constructor(projectDir: string, private platform: T) {
|
||||
this.easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
this.easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
}
|
||||
|
||||
async runAsync(): Promise<BuildProfile<T>> {
|
||||
|
||||
+327
-60
@@ -87,6 +87,8 @@ export type Account = {
|
||||
appleTeams: Array<AppleTeam>;
|
||||
/** Apps associated with this account */
|
||||
apps: Array<App>;
|
||||
/** Paginated list of apps associated with this account. By default sorted by name. Use filter to adjust the sorting order. */
|
||||
appsPaginated: AccountAppsConnection;
|
||||
/** @deprecated Build packs are no longer supported */
|
||||
availableBuilds?: Maybe<Scalars['Int']>;
|
||||
/** Billing information. Only visible to members with the ADMIN or OWNER role. */
|
||||
@@ -211,6 +213,19 @@ export type AccountAppsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An account is a container owning projects, credentials, billing and other organization
|
||||
* data and settings. Actors may own and be members of accounts.
|
||||
*/
|
||||
export type AccountAppsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<AccountAppsFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An account is a container owning projects, credentials, billing and other organization
|
||||
* data and settings. Actors may own and be members of accounts.
|
||||
@@ -284,6 +299,31 @@ export type AccountTimelineActivityArgs = {
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type AccountAppsConnection = {
|
||||
__typename?: 'AccountAppsConnection';
|
||||
edges: Array<AccountAppsEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AccountAppsEdge = {
|
||||
__typename?: 'AccountAppsEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: App;
|
||||
};
|
||||
|
||||
export type AccountAppsFilterInput = {
|
||||
sortByField: AccountAppsSortByField;
|
||||
};
|
||||
|
||||
export enum AccountAppsSortByField {
|
||||
LatestActivityTime = 'LATEST_ACTIVITY_TIME',
|
||||
/**
|
||||
* Name prefers the display name but falls back to full_name with @account/
|
||||
* part stripped.
|
||||
*/
|
||||
Name = 'NAME'
|
||||
}
|
||||
|
||||
export type AccountDataInput = {
|
||||
name: Scalars['String'];
|
||||
};
|
||||
@@ -295,6 +335,8 @@ export type AccountMutation = {
|
||||
* @deprecated Build packs are no longer supported
|
||||
*/
|
||||
buyProduct?: Maybe<Account>;
|
||||
/** Cancels all subscriptions immediately */
|
||||
cancelAllSubscriptionsImmediately: Account;
|
||||
/** Cancel scheduled subscription change */
|
||||
cancelScheduledSubscriptionChange: Account;
|
||||
/** Cancels the active subscription */
|
||||
@@ -329,6 +371,11 @@ export type AccountMutationBuyProductArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type AccountMutationCancelAllSubscriptionsImmediatelyArgs = {
|
||||
accountID: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type AccountMutationCancelScheduledSubscriptionChangeArgs = {
|
||||
accountID: Scalars['ID'];
|
||||
};
|
||||
@@ -500,6 +547,14 @@ export type AccountSsoConfigurationPublicDataQueryPublicDataByAccountNameArgs =
|
||||
accountName: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AccountUsageEasBuildMetadata = {
|
||||
__typename?: 'AccountUsageEASBuildMetadata';
|
||||
billingResourceClass: EasBuildBillingResourceClass;
|
||||
platform: AppPlatform;
|
||||
};
|
||||
|
||||
export type AccountUsageMetadata = AccountUsageEasBuildMetadata;
|
||||
|
||||
export type AccountUsageMetric = {
|
||||
__typename?: 'AccountUsageMetric';
|
||||
id: Scalars['ID'];
|
||||
@@ -509,19 +564,6 @@ export type AccountUsageMetric = {
|
||||
value: Scalars['Float'];
|
||||
};
|
||||
|
||||
export type AccountUsageMetricAndCost = {
|
||||
__typename?: 'AccountUsageMetricAndCost';
|
||||
id: Scalars['ID'];
|
||||
/** The limit, in units, allowed by this plan */
|
||||
limit: Scalars['Float'];
|
||||
metricType: UsageMetricType;
|
||||
service: EasService;
|
||||
serviceMetric: EasServiceMetric;
|
||||
/** Total cost of this particular metric, in cents */
|
||||
totalCost: Scalars['Float'];
|
||||
value: Scalars['Float'];
|
||||
};
|
||||
|
||||
export type AccountUsageMetrics = {
|
||||
__typename?: 'AccountUsageMetrics';
|
||||
byBillingPeriod: UsageMetricTotal;
|
||||
@@ -954,8 +996,10 @@ export type App = Project & {
|
||||
buildOrBuildJobs: Array<BuildOrBuildJob>;
|
||||
/** (EAS Build) Builds associated with this app */
|
||||
builds: Array<Build>;
|
||||
buildsPaginated: AppBuildsConnection;
|
||||
/** Classic update release channel names that have at least one build */
|
||||
buildsReleaseChannels: Array<Scalars['String']>;
|
||||
channelsPaginated: AppChannelsConnection;
|
||||
deployment?: Maybe<Deployment>;
|
||||
/** Deployments associated with this app */
|
||||
deployments: DeploymentsConnection;
|
||||
@@ -980,6 +1024,8 @@ export type App = Project & {
|
||||
isLikedByMe: Scalars['Boolean'];
|
||||
/** @deprecated No longer supported */
|
||||
lastPublishedTime: Scalars['DateTime'];
|
||||
/** Time of the last user activity (update, branch, submission). */
|
||||
latestActivity: Scalars['DateTime'];
|
||||
latestAppVersionByPlatformAndApplicationIdentifier?: Maybe<AppVersion>;
|
||||
latestReleaseForReleaseChannel?: Maybe<AppRelease>;
|
||||
/** ID of latest classic update release */
|
||||
@@ -1006,11 +1052,13 @@ export type App = Project & {
|
||||
releaseChannels: Array<Scalars['String']>;
|
||||
/** @deprecated Legacy access tokens are deprecated */
|
||||
requiresAccessTokenForPushSecurity: Scalars['Boolean'];
|
||||
scopeKey: Scalars['String'];
|
||||
/** SDK version of the latest classic update publish, 0.0.0 otherwise */
|
||||
sdkVersion: Scalars['String'];
|
||||
slug: Scalars['String'];
|
||||
/** EAS Submissions associated with this app */
|
||||
submissions: Array<Submission>;
|
||||
submissionsPaginated: AppSubmissionsConnection;
|
||||
/** Coalesced project activity for an app using pagination */
|
||||
timelineActivity: TimelineActivityConnection;
|
||||
/** @deprecated 'likes' have been deprecated. */
|
||||
@@ -1090,6 +1138,25 @@ export type AppBuildsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppBuildsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<BuildFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppChannelsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentArgs = {
|
||||
channel: Scalars['String'];
|
||||
@@ -1147,6 +1214,15 @@ export type AppSubmissionsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppSubmissionsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppTimelineActivityArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
@@ -1224,6 +1300,30 @@ export type AppBranchesConnection = {
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AppBuildEdge = {
|
||||
__typename?: 'AppBuildEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: BuildOrBuildJob;
|
||||
};
|
||||
|
||||
export type AppBuildsConnection = {
|
||||
__typename?: 'AppBuildsConnection';
|
||||
edges: Array<AppBuildEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AppChannelEdge = {
|
||||
__typename?: 'AppChannelEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: UpdateChannel;
|
||||
};
|
||||
|
||||
export type AppChannelsConnection = {
|
||||
__typename?: 'AppChannelsConnection';
|
||||
edges: Array<AppChannelEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AppDataInput = {
|
||||
id: Scalars['ID'];
|
||||
privacy?: InputMaybe<Scalars['String']>;
|
||||
@@ -1413,6 +1513,18 @@ export enum AppStoreConnectUserRole {
|
||||
Unknown = 'UNKNOWN'
|
||||
}
|
||||
|
||||
export type AppSubmissionEdge = {
|
||||
__typename?: 'AppSubmissionEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: Submission;
|
||||
};
|
||||
|
||||
export type AppSubmissionsConnection = {
|
||||
__typename?: 'AppSubmissionsConnection';
|
||||
edges: Array<AppSubmissionEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AppUpdateEdge = {
|
||||
__typename?: 'AppUpdateEdge';
|
||||
cursor: Scalars['String'];
|
||||
@@ -1532,6 +1644,8 @@ export type AppleDeviceMutation = {
|
||||
createAppleDevice: AppleDevice;
|
||||
/** Delete an Apple Device */
|
||||
deleteAppleDevice: DeleteAppleDeviceResult;
|
||||
/** Update an Apple Device */
|
||||
updateAppleDevice: AppleDevice;
|
||||
};
|
||||
|
||||
|
||||
@@ -1545,6 +1659,12 @@ export type AppleDeviceMutationDeleteAppleDeviceArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type AppleDeviceMutationUpdateAppleDeviceArgs = {
|
||||
appleDeviceUpdateInput: AppleDeviceUpdateInput;
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type AppleDeviceRegistrationRequest = {
|
||||
__typename?: 'AppleDeviceRegistrationRequest';
|
||||
account: Account;
|
||||
@@ -1574,6 +1694,10 @@ export type AppleDeviceRegistrationRequestQueryByIdArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type AppleDeviceUpdateInput = {
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AppleDistributionCertificate = {
|
||||
__typename?: 'AppleDistributionCertificate';
|
||||
account: Account;
|
||||
@@ -1845,11 +1969,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
__typename?: 'Build';
|
||||
activityTimestamp: Scalars['DateTime'];
|
||||
actor?: Maybe<Actor>;
|
||||
/**
|
||||
* The actual resource class of the builder assigned to the build job
|
||||
* @deprecated Use resourceClassDisplayName instead
|
||||
*/
|
||||
actualResourceClass?: Maybe<BuildResourceClass>;
|
||||
appBuildVersion?: Maybe<Scalars['String']>;
|
||||
appVersion?: Maybe<Scalars['String']>;
|
||||
artifacts?: Maybe<BuildArtifacts>;
|
||||
@@ -1868,6 +1987,8 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
expirationDate?: Maybe<Scalars['DateTime']>;
|
||||
gitCommitHash?: Maybe<Scalars['String']>;
|
||||
gitCommitMessage?: Maybe<Scalars['String']>;
|
||||
gitRef?: Maybe<Scalars['String']>;
|
||||
githubRepositoryOwnerAndName?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
/** Queue position is 1-indexed */
|
||||
initialQueuePosition?: Maybe<Scalars['Int']>;
|
||||
@@ -1886,6 +2007,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
platform: AppPlatform;
|
||||
priority: BuildPriority;
|
||||
project: Project;
|
||||
projectRootDirectory?: Maybe<Scalars['String']>;
|
||||
provisioningStartedAt?: Maybe<Scalars['DateTime']>;
|
||||
/** Queue position is 1-indexed */
|
||||
queuePosition?: Maybe<Scalars['Int']>;
|
||||
@@ -1928,11 +2050,10 @@ export type BuildArtifacts = {
|
||||
};
|
||||
|
||||
export type BuildCacheInput = {
|
||||
cacheDefaultPaths?: InputMaybe<Scalars['Boolean']>;
|
||||
clear?: InputMaybe<Scalars['Boolean']>;
|
||||
customPaths?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
|
||||
disabled?: InputMaybe<Scalars['Boolean']>;
|
||||
key?: InputMaybe<Scalars['String']>;
|
||||
paths?: InputMaybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
export enum BuildCredentialsSource {
|
||||
@@ -1961,6 +2082,12 @@ export type BuildFilter = {
|
||||
status?: InputMaybe<BuildStatus>;
|
||||
};
|
||||
|
||||
export type BuildFilterInput = {
|
||||
channel?: InputMaybe<Scalars['String']>;
|
||||
platforms?: InputMaybe<Array<AppPlatform>>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum BuildIosEnterpriseProvisioning {
|
||||
Adhoc = 'ADHOC',
|
||||
Universal = 'UNIVERSAL'
|
||||
@@ -1996,9 +2123,7 @@ export enum BuildJobLogsFormat {
|
||||
|
||||
export type BuildJobMutation = {
|
||||
__typename?: 'BuildJobMutation';
|
||||
cancel: BuildJob;
|
||||
del?: Maybe<BuildJob>;
|
||||
restart: BuildJob;
|
||||
};
|
||||
|
||||
export type BuildJobQuery = {
|
||||
@@ -2174,7 +2299,9 @@ export type BuildOrBuildJobQueryByIdArgs = {
|
||||
};
|
||||
|
||||
export type BuildParamsInput = {
|
||||
reactNativeVersion?: InputMaybe<Scalars['String']>;
|
||||
resourceClass: BuildResourceClass;
|
||||
sdkVersion?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum BuildPriority {
|
||||
@@ -2261,8 +2388,6 @@ export enum BuildResourceClass {
|
||||
/** @deprecated Use IOS_M_MEDIUM instead */
|
||||
IosM1Large = 'IOS_M1_LARGE',
|
||||
IosM1Medium = 'IOS_M1_MEDIUM',
|
||||
IosM2Medium = 'IOS_M2_MEDIUM',
|
||||
IosM2ProMedium = 'IOS_M2_PRO_MEDIUM',
|
||||
IosMedium = 'IOS_MEDIUM',
|
||||
IosMLarge = 'IOS_M_LARGE',
|
||||
IosMMedium = 'IOS_M_MEDIUM',
|
||||
@@ -2313,27 +2438,6 @@ export type Charge = {
|
||||
wasRefunded: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
/** Represents a client build request */
|
||||
export type ClientBuild = {
|
||||
__typename?: 'ClientBuild';
|
||||
buildJobId?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
manifestPlistUrl?: Maybe<Scalars['String']>;
|
||||
status?: Maybe<Scalars['String']>;
|
||||
userFacingErrorMessage?: Maybe<Scalars['String']>;
|
||||
userId?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ClientBuildQuery = {
|
||||
__typename?: 'ClientBuildQuery';
|
||||
byId: ClientBuild;
|
||||
};
|
||||
|
||||
|
||||
export type ClientBuildQueryByIdArgs = {
|
||||
requestId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type CodeSigningInfo = {
|
||||
__typename?: 'CodeSigningInfo';
|
||||
alg: Scalars['String'];
|
||||
@@ -2464,11 +2568,21 @@ export type DeleteAppleProvisioningProfileResult = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteDiscordUserResult = {
|
||||
__typename?: 'DeleteDiscordUserResult';
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteEnvironmentSecretResult = {
|
||||
__typename?: 'DeleteEnvironmentSecretResult';
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteGitHubUserResult = {
|
||||
__typename?: 'DeleteGitHubUserResult';
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteGoogleServiceAccountKeyResult = {
|
||||
__typename?: 'DeleteGoogleServiceAccountKeyResult';
|
||||
id: Scalars['ID'];
|
||||
@@ -2558,12 +2672,43 @@ export type DeploymentsConnection = {
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type DiscordUser = {
|
||||
__typename?: 'DiscordUser';
|
||||
discordIdentifier: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
metadata?: Maybe<DiscordUserMetadata>;
|
||||
userActor: UserActor;
|
||||
};
|
||||
|
||||
export type DiscordUserMetadata = {
|
||||
__typename?: 'DiscordUserMetadata';
|
||||
discordAvatarUrl: Scalars['String'];
|
||||
discordDiscriminator: Scalars['String'];
|
||||
discordUsername: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DiscordUserMutation = {
|
||||
__typename?: 'DiscordUserMutation';
|
||||
/** Delete a Discord User by ID */
|
||||
deleteDiscordUser: DeleteDiscordUserResult;
|
||||
};
|
||||
|
||||
|
||||
export type DiscordUserMutationDeleteDiscordUserArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export enum DistributionType {
|
||||
Internal = 'INTERNAL',
|
||||
Simulator = 'SIMULATOR',
|
||||
Store = 'STORE'
|
||||
}
|
||||
|
||||
export enum EasBuildBillingResourceClass {
|
||||
Large = 'LARGE',
|
||||
Medium = 'MEDIUM'
|
||||
}
|
||||
|
||||
export type EasBuildDeprecationInfo = {
|
||||
__typename?: 'EASBuildDeprecationInfo';
|
||||
message: Scalars['String'];
|
||||
@@ -2664,6 +2809,30 @@ export enum EnvironmentSecretType {
|
||||
String = 'STRING'
|
||||
}
|
||||
|
||||
export type EstimatedOverageAndCost = {
|
||||
__typename?: 'EstimatedOverageAndCost';
|
||||
id: Scalars['ID'];
|
||||
/** The limit, in units, allowed by this plan */
|
||||
limit: Scalars['Float'];
|
||||
metadata?: Maybe<AccountUsageMetadata>;
|
||||
metricType: UsageMetricType;
|
||||
service: EasService;
|
||||
serviceMetric: EasServiceMetric;
|
||||
/** Total cost of this particular metric, in cents */
|
||||
totalCost: Scalars['Int'];
|
||||
value: Scalars['Float'];
|
||||
};
|
||||
|
||||
export type EstimatedUsage = {
|
||||
__typename?: 'EstimatedUsage';
|
||||
id: Scalars['ID'];
|
||||
limit: Scalars['Float'];
|
||||
metricType: UsageMetricType;
|
||||
service: EasService;
|
||||
serviceMetric: EasServiceMetric;
|
||||
value: Scalars['Float'];
|
||||
};
|
||||
|
||||
export type ExperimentationQuery = {
|
||||
__typename?: 'ExperimentationQuery';
|
||||
/** Get device experimentation config */
|
||||
@@ -2780,6 +2949,17 @@ export enum GitHubAppInstallationStatus {
|
||||
Suspended = 'SUSPENDED'
|
||||
}
|
||||
|
||||
export type GitHubAppMutation = {
|
||||
__typename?: 'GitHubAppMutation';
|
||||
/** Create a GitHub build for an app */
|
||||
createGitHubBuild: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
export type GitHubAppMutationCreateGitHubBuildArgs = {
|
||||
buildInput: GitHubBuildInput;
|
||||
};
|
||||
|
||||
export type GitHubAppQuery = {
|
||||
__typename?: 'GitHubAppQuery';
|
||||
appIdentifier: Scalars['String'];
|
||||
@@ -2801,11 +2981,20 @@ export type GitHubAppQuerySearchRepositoriesArgs = {
|
||||
query: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GitHubBuildInput = {
|
||||
appId: Scalars['ID'];
|
||||
baseDirectory?: InputMaybe<Scalars['String']>;
|
||||
buildProfile: Scalars['String'];
|
||||
gitRef: Scalars['String'];
|
||||
platform: AppPlatform;
|
||||
};
|
||||
|
||||
export type GitHubRepository = {
|
||||
__typename?: 'GitHubRepository';
|
||||
app: App;
|
||||
githubAppInstallation: GitHubAppInstallation;
|
||||
githubRepositoryIdentifier: Scalars['Int'];
|
||||
githubRepositoryUrl?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
metadata?: Maybe<GitHubRepositoryMetadata>;
|
||||
nodeIdentifier: Scalars['String'];
|
||||
@@ -2889,6 +3078,33 @@ export type GitHubRepositorySettingsMutationUpdateGitHubRepositorySettingsArgs =
|
||||
githubRepositorySettingsId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type GitHubUser = {
|
||||
__typename?: 'GitHubUser';
|
||||
githubUserIdentifier: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
metadata?: Maybe<GitHubUserMetadata>;
|
||||
userActor: UserActor;
|
||||
};
|
||||
|
||||
export type GitHubUserMetadata = {
|
||||
__typename?: 'GitHubUserMetadata';
|
||||
avatarUrl: Scalars['String'];
|
||||
login: Scalars['String'];
|
||||
name?: Maybe<Scalars['String']>;
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GitHubUserMutation = {
|
||||
__typename?: 'GitHubUserMutation';
|
||||
/** Delete a GitHub User by ID */
|
||||
deleteGitHubUser: DeleteGitHubUserResult;
|
||||
};
|
||||
|
||||
|
||||
export type GitHubUserMutationDeleteGitHubUserArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type GoogleServiceAccountKey = {
|
||||
__typename?: 'GoogleServiceAccountKey';
|
||||
account: Account;
|
||||
@@ -3325,8 +3541,10 @@ export type MeMutation = {
|
||||
unpublishApp: App;
|
||||
/** Update an App that the current user owns */
|
||||
updateApp: App;
|
||||
/** Update the current user's data */
|
||||
/** Update the current regular user's data */
|
||||
updateProfile: User;
|
||||
/** Update the current SSO user's data */
|
||||
updateSSOProfile: SsoUser;
|
||||
};
|
||||
|
||||
|
||||
@@ -3413,6 +3631,11 @@ export type MeMutationUpdateProfileArgs = {
|
||||
userData: UserDataInput;
|
||||
};
|
||||
|
||||
|
||||
export type MeMutationUpdateSsoProfileArgs = {
|
||||
userData: SsoUserDataInput;
|
||||
};
|
||||
|
||||
export type MeteredBillingStatus = {
|
||||
__typename?: 'MeteredBillingStatus';
|
||||
EAS_BUILD: Scalars['Boolean'];
|
||||
@@ -3616,8 +3839,9 @@ export type PublishUpdateGroupInput = {
|
||||
gitCommitHash?: InputMaybe<Scalars['String']>;
|
||||
isGitWorkingTreeDirty?: InputMaybe<Scalars['Boolean']>;
|
||||
message?: InputMaybe<Scalars['String']>;
|
||||
rollBackToEmbeddedInfoGroup?: InputMaybe<UpdateRollBackToEmbeddedGroup>;
|
||||
runtimeVersion: Scalars['String'];
|
||||
updateInfoGroup: UpdateInfoGroup;
|
||||
updateInfoGroup?: InputMaybe<UpdateInfoGroup>;
|
||||
};
|
||||
|
||||
export type RescindUserInvitationResult = {
|
||||
@@ -3738,16 +3962,22 @@ export type RootMutation = {
|
||||
build: BuildMutation;
|
||||
/** Mutations that modify an BuildJob */
|
||||
buildJob: BuildJobMutation;
|
||||
/** Mutations for Discord users */
|
||||
discordUser: DiscordUserMutation;
|
||||
/** Mutations that modify an EmailSubscription */
|
||||
emailSubscription: EmailSubscriptionMutation;
|
||||
/** Mutations that create and delete EnvironmentSecrets */
|
||||
environmentSecret: EnvironmentSecretMutation;
|
||||
/** Mutations that utilize services facilitated by the GitHub App */
|
||||
githubApp: GitHubAppMutation;
|
||||
/** Mutations for GitHub App installations */
|
||||
githubAppInstallation: GitHubAppInstallationMutation;
|
||||
/** Mutations for GitHub repositories */
|
||||
githubRepository: GitHubRepositoryMutation;
|
||||
/** Mutations for GitHub repository settings */
|
||||
githubRepositorySettings: GitHubRepositorySettingsMutation;
|
||||
/** Mutations for GitHub users */
|
||||
githubUser: GitHubUserMutation;
|
||||
/** Mutations that modify a Google Service Account Key */
|
||||
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
|
||||
/** Mutations that modify the build credentials for an iOS app */
|
||||
@@ -3828,7 +4058,6 @@ export type RootQuery = {
|
||||
/** Top-level query object for querying BuildPublicData publicly. */
|
||||
buildPublicData: BuildPublicDataQuery;
|
||||
builds: BuildQuery;
|
||||
clientBuilds: ClientBuildQuery;
|
||||
/** Top-level query object for querying Experimentation configuration. */
|
||||
experimentation: ExperimentationQuery;
|
||||
/** Top-level query object for querying GitHub App information and resources it has access to. */
|
||||
@@ -3930,6 +4159,8 @@ export type SsoUser = Actor & UserActor & {
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
/** Discord account linked to a user */
|
||||
discordUser?: Maybe<DiscordUser>;
|
||||
displayName: Scalars['String'];
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
@@ -3938,6 +4169,8 @@ export type SsoUser = Actor & UserActor & {
|
||||
featureGates: Scalars['JSONObject'];
|
||||
firstName?: Maybe<Scalars['String']>;
|
||||
fullName?: Maybe<Scalars['String']>;
|
||||
/** GitHub account linked to a user */
|
||||
githubUser?: Maybe<GitHubUser>;
|
||||
githubUsername?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
industry?: Maybe<Scalars['String']>;
|
||||
@@ -3989,6 +4222,15 @@ export type SsoUserSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type SsoUserDataInput = {
|
||||
firstName?: InputMaybe<Scalars['String']>;
|
||||
githubUsername?: InputMaybe<Scalars['String']>;
|
||||
industry?: InputMaybe<Scalars['String']>;
|
||||
lastName?: InputMaybe<Scalars['String']>;
|
||||
location?: InputMaybe<Scalars['String']>;
|
||||
twitterUsername?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type SsoUserQuery = {
|
||||
__typename?: 'SSOUserQuery';
|
||||
/** Query an SSOUser by ID */
|
||||
@@ -4420,6 +4662,7 @@ export type Update = ActivityTimelineProjectActivity & {
|
||||
group: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
isGitWorkingTreeDirty: Scalars['Boolean'];
|
||||
isRollBackToEmbedded: Scalars['Boolean'];
|
||||
manifestFragment: Scalars['String'];
|
||||
manifestPermalink: Scalars['String'];
|
||||
message?: Maybe<Scalars['String']>;
|
||||
@@ -4575,6 +4818,12 @@ export type UpdateMutationSetCodeSigningInfoArgs = {
|
||||
updateId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type UpdateRollBackToEmbeddedGroup = {
|
||||
android?: InputMaybe<Scalars['Boolean']>;
|
||||
ios?: InputMaybe<Scalars['Boolean']>;
|
||||
web?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type UpdatesFilter = {
|
||||
platform?: InputMaybe<AppPlatform>;
|
||||
runtimeVersions?: InputMaybe<Array<Scalars['String']>>;
|
||||
@@ -4602,8 +4851,8 @@ export type UsageMetricTotal = {
|
||||
__typename?: 'UsageMetricTotal';
|
||||
billingPeriod: BillingPeriod;
|
||||
id: Scalars['ID'];
|
||||
overageMetrics: Array<AccountUsageMetricAndCost>;
|
||||
planMetrics: Array<AccountUsageMetricAndCost>;
|
||||
overageMetrics: Array<EstimatedOverageAndCost>;
|
||||
planMetrics: Array<EstimatedUsage>;
|
||||
/** Total cost of overages, in cents */
|
||||
totalCost: Scalars['Float'];
|
||||
};
|
||||
@@ -4642,6 +4891,8 @@ export type User = Actor & UserActor & {
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
/** Discord account linked to a user */
|
||||
discordUser?: Maybe<DiscordUser>;
|
||||
displayName: Scalars['String'];
|
||||
email?: Maybe<Scalars['String']>;
|
||||
emailVerified: Scalars['Boolean'];
|
||||
@@ -4652,6 +4903,8 @@ export type User = Actor & UserActor & {
|
||||
featureGates: Scalars['JSONObject'];
|
||||
firstName?: Maybe<Scalars['String']>;
|
||||
fullName?: Maybe<Scalars['String']>;
|
||||
/** GitHub account linked to a user */
|
||||
githubUser?: Maybe<GitHubUser>;
|
||||
githubUsername?: Maybe<Scalars['String']>;
|
||||
/** Whether this user has any pending user invitations. Only resolves for the viewer. */
|
||||
hasPendingUserInvitations: Scalars['Boolean'];
|
||||
@@ -4728,6 +4981,8 @@ export type UserActor = {
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
/** Discord account linked to a user */
|
||||
discordUser?: Maybe<DiscordUser>;
|
||||
/**
|
||||
* Best-effort human readable name for this human actor for use in user interfaces during action attribution.
|
||||
* For example, when displaying a sentence indicating that actor X created a build or published an update.
|
||||
@@ -4740,6 +4995,8 @@ export type UserActor = {
|
||||
featureGates: Scalars['JSONObject'];
|
||||
firstName?: Maybe<Scalars['String']>;
|
||||
fullName?: Maybe<Scalars['String']>;
|
||||
/** GitHub account linked to a user */
|
||||
githubUser?: Maybe<GitHubUser>;
|
||||
githubUsername?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
industry?: Maybe<Scalars['String']>;
|
||||
@@ -5589,7 +5846,7 @@ export type UpdatePublishMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdatePublishMutation = { __typename?: 'RootMutation', updateBranch: { __typename?: 'UpdateBranchMutation', publishUpdateGroups: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> } };
|
||||
export type UpdatePublishMutation = { __typename?: 'RootMutation', updateBranch: { __typename?: 'UpdateBranchMutation', publishUpdateGroups: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> } };
|
||||
|
||||
export type SetCodeSigningInfoMutationVariables = Exact<{
|
||||
updateId: Scalars['ID'];
|
||||
@@ -5672,6 +5929,14 @@ export type LatestAppVersionQueryVariables = Exact<{
|
||||
|
||||
export type LatestAppVersionQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, latestAppVersionByPlatformAndApplicationIdentifier?: { __typename?: 'AppVersion', id: string, storeVersion: string, buildVersion: string } | null } } };
|
||||
|
||||
export type BillingUpdateByAccountQueryVariables = Exact<{
|
||||
accountId: Scalars['String'];
|
||||
date: Scalars['DateTime'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BillingUpdateByAccountQuery = { __typename?: 'RootQuery', account: { __typename?: 'AccountQuery', byId: { __typename?: 'Account', id: string, billing?: { __typename?: 'Billing', id: string, subscription?: { __typename?: 'SubscriptionDetails', id: string, name?: string | null, meteredBillingStatus: { __typename?: 'MeteredBillingStatus', EAS_UPDATE: boolean } } | null } | null, usageMetrics: { __typename?: 'AccountUsageMetrics', byBillingPeriod: { __typename?: 'UsageMetricTotal', id: string, planMetrics: Array<{ __typename?: 'EstimatedUsage', id: string, serviceMetric: EasServiceMetric, metricType: UsageMetricType, value: number, limit: number }> } } } } };
|
||||
|
||||
export type ViewBranchQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
@@ -5687,7 +5952,7 @@ export type BranchesByAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> }> } } };
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> }> } } };
|
||||
|
||||
export type ViewBranchesOnUpdateChannelQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5697,7 +5962,7 @@ export type ViewBranchesOnUpdateChannelQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewBranchesOnUpdateChannelQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> } | null } } };
|
||||
export type ViewBranchesOnUpdateChannelQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> } | null } } };
|
||||
|
||||
export type BuildsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
@@ -5729,7 +5994,7 @@ export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateChannelOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, name: string, createdAt: any, branchMapping: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> } | null } } };
|
||||
export type ViewUpdateChannelOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, name: string, createdAt: any, branchMapping: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> } | null } } };
|
||||
|
||||
export type ViewUpdateChannelsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5738,7 +6003,7 @@ export type ViewUpdateChannelsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateChannelsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannels: Array<{ __typename?: 'UpdateChannel', id: string, name: string, branchMapping: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> }> } } };
|
||||
export type ViewUpdateChannelsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannels: Array<{ __typename?: 'UpdateChannel', id: string, name: string, branchMapping: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> }> }> } } };
|
||||
|
||||
export type EnvironmentSecretsByAppIdQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5791,7 +6056,7 @@ export type ViewUpdatesByGroupQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdatesByGroupQuery = { __typename?: 'RootQuery', updatesByGroup: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
||||
export type ViewUpdatesByGroupQuery = { __typename?: 'RootQuery', updatesByGroup: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
||||
|
||||
export type ViewUpdateGroupsOnBranchQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5802,7 +6067,7 @@ export type ViewUpdateGroupsOnBranchQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateGroupsOnBranchQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> } | null } } };
|
||||
export type ViewUpdateGroupsOnBranchQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> } | null } } };
|
||||
|
||||
export type ViewUpdateGroupsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5812,7 +6077,7 @@ export type ViewUpdateGroupsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateGroupsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> } } };
|
||||
export type ViewUpdateGroupsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }>> } } };
|
||||
|
||||
export type CurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -5836,6 +6101,8 @@ export type WebhookByIdQuery = { __typename?: 'RootQuery', webhook: { __typename
|
||||
|
||||
export type AccountFragment = { __typename?: 'Account', id: string, name: string, users: Array<{ __typename?: 'UserPermission', role?: Role | null, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> };
|
||||
|
||||
export type AccountUpdatesUsageFragment = { __typename?: 'Account', id: string, billing?: { __typename?: 'Billing', id: string, subscription?: { __typename?: 'SubscriptionDetails', id: string, name?: string | null, meteredBillingStatus: { __typename?: 'MeteredBillingStatus', EAS_UPDATE: boolean } } | null } | null, usageMetrics: { __typename?: 'AccountUsageMetrics', byBillingPeriod: { __typename?: 'UsageMetricTotal', id: string, planMetrics: Array<{ __typename?: 'EstimatedUsage', id: string, serviceMetric: EasServiceMetric, metricType: UsageMetricType, value: number, limit: number }> } } };
|
||||
|
||||
export type AppFragment = { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, users: Array<{ __typename?: 'UserPermission', role?: Role | null, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } };
|
||||
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
@@ -5848,9 +6115,9 @@ export type StatuspageServiceFragment = { __typename?: 'StatuspageService', id:
|
||||
|
||||
export type SubmissionFragment = { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null };
|
||||
|
||||
export type UpdateFragment = { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null };
|
||||
export type UpdateFragment = { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null };
|
||||
|
||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null }> };
|
||||
|
||||
export type WebhookFragment = { __typename?: 'Webhook', id: string, event: WebhookType, url: string, createdAt: any, updatedAt: any };
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import assert from 'assert';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import { BillingUpdateByAccountQuery } from '../generated';
|
||||
|
||||
export const BillingUpdateQuery = {
|
||||
async byAccountIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
accountId: string,
|
||||
date: Date
|
||||
): Promise<BillingUpdateByAccountQuery['account']['byId']> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<BillingUpdateByAccountQuery>(
|
||||
gql`
|
||||
query BillingUpdateByAccountQuery($accountId: String!, $date: DateTime!) {
|
||||
account {
|
||||
byId(accountId: $accountId) {
|
||||
id
|
||||
billing {
|
||||
id
|
||||
subscription {
|
||||
id
|
||||
name
|
||||
meteredBillingStatus {
|
||||
EAS_UPDATE
|
||||
}
|
||||
}
|
||||
}
|
||||
usageMetrics {
|
||||
byBillingPeriod(date: $date, service: UPDATES) {
|
||||
id
|
||||
planMetrics {
|
||||
id
|
||||
serviceMetric
|
||||
metricType
|
||||
value
|
||||
limit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ accountId, date },
|
||||
{
|
||||
additionalTypenames: [
|
||||
'Account',
|
||||
'Billing',
|
||||
'SubscriptionDetails',
|
||||
'AccountUsageMetrics',
|
||||
'UsageMetricTotal',
|
||||
'EstimatedUsage',
|
||||
],
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
assert(data.account, 'GraphQL: `account` not defined in server response');
|
||||
return data.account.byId;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const AccountUpdatesUsageFragmentNode = gql`
|
||||
fragment AccountUpdatesUsageFragment on Account {
|
||||
id
|
||||
billing {
|
||||
id
|
||||
subscription {
|
||||
id
|
||||
name
|
||||
meteredBillingStatus {
|
||||
EAS_UPDATE
|
||||
}
|
||||
}
|
||||
}
|
||||
usageMetrics {
|
||||
byBillingPeriod(date: $date, service: UPDATES) {
|
||||
id
|
||||
planMetrics {
|
||||
id
|
||||
serviceMetric
|
||||
metricType
|
||||
value
|
||||
limit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -9,6 +9,7 @@ export const UpdateFragmentNode = gql`
|
||||
runtimeVersion
|
||||
platform
|
||||
manifestFragment
|
||||
isRollBackToEmbedded
|
||||
manifestPermalink
|
||||
gitCommitHash
|
||||
actor {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { instance, mock } from 'ts-mockito';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { defaultPublishPlatforms } from '../../commands/update';
|
||||
import { AssetMetadataStatus } from '../../graphql/generated';
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import { PublishQuery } from '../../graphql/queries/PublishQuery';
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
buildUnsortedUpdateInfoGroupAsync,
|
||||
collectAssetsAsync,
|
||||
convertAssetToUpdateInfoGroupFormatAsync,
|
||||
defaultPublishPlatforms,
|
||||
filterExportedPlatformsByFlag,
|
||||
filterOutAssetsThatAlreadyExistAsync,
|
||||
getAssetHashFromPath,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import { errors, readAndValidateBuildConfigAsync } from '@expo/steps';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
@@ -12,13 +13,33 @@ export async function validateCustomBuildConfigAsync(
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeBuildConfigPath = getCustomBuildConfigPath(profile.config);
|
||||
const buildConfigPath = path.join(projectDir, relativeBuildConfigPath);
|
||||
if (!(await fs.pathExists(buildConfigPath))) {
|
||||
const relativeConfigPath = getCustomBuildConfigPath(profile.config);
|
||||
const configPath = path.join(projectDir, relativeConfigPath);
|
||||
if (!(await fs.pathExists(configPath))) {
|
||||
throw new Error(
|
||||
`Custom build configuration file ${chalk.bold(relativeBuildConfigPath)} does not exist.`
|
||||
`Custom build configuration file ${chalk.bold(relativeConfigPath)} does not exist.`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await readAndValidateBuildConfigAsync(configPath, { skipNamespacedFunctionsCheck: true });
|
||||
} catch (err) {
|
||||
if (err instanceof errors.BuildConfigYAMLError) {
|
||||
throw new Error(
|
||||
`Custom build configuration file ${chalk.bold(
|
||||
relativeConfigPath
|
||||
)} contains invalid YAML.\n\n${err.message}`
|
||||
);
|
||||
} else if (err instanceof errors.BuildConfigError) {
|
||||
throw new Error(
|
||||
`Custom build configuration file ${chalk.bold(
|
||||
relativeConfigPath
|
||||
)} contains invalid configuration. Please check the docs!\n${err.message}`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getCustomBuildConfigPath(configFilename: string): string {
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
import { ExpoConfig, Platform } from '@expo/config';
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { Platform as EASBuildJobPlatform, Workflow } from '@expo/eas-build-job';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs-extra';
|
||||
import Joi from 'joi';
|
||||
import mime from 'mime';
|
||||
import nullthrows from 'nullthrows';
|
||||
import path from 'path';
|
||||
import promiseLimit from 'promise-limit';
|
||||
|
||||
import { selectBranchOnAppAsync } from '../branch/queries';
|
||||
import { getDefaultBranchNameAsync } from '../branch/utils';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { AssetMetadataStatus, PartialManifestAsset } from '../graphql/generated';
|
||||
import { PublishMutation } from '../graphql/mutations/PublishMutation';
|
||||
import { PublishQuery } from '../graphql/queries/PublishQuery';
|
||||
import Log from '../log';
|
||||
import Log, { learnMore } from '../log';
|
||||
import { RequestedPlatform, requestedPlatformDisplayNames } from '../platform';
|
||||
import { promptAsync } from '../prompts';
|
||||
import { getBranchNameFromChannelNameAsync } from '../update/getBranchNameFromChannelNameAsync';
|
||||
import { formatUpdateMessage, truncateString as truncateUpdateMessage } from '../update/utils';
|
||||
import { PresignedPost, uploadWithPresignedPostWithRetryAsync } from '../uploads';
|
||||
import { expoCommandAsync, shouldUseVersionedExpoCLI } from '../utils/expoCli';
|
||||
import chunk from '../utils/expodash/chunk';
|
||||
import { truthy } from '../utils/expodash/filter';
|
||||
import uniqBy from '../utils/expodash/uniqBy';
|
||||
import { getVcsClient } from '../vcs';
|
||||
import { resolveWorkflowAsync } from './workflow';
|
||||
|
||||
export type ExpoCLIExportPlatformFlag = Platform | 'all';
|
||||
|
||||
@@ -453,3 +467,188 @@ export function isUploadedAssetCountAboveWarningThreshold(
|
||||
const warningThreshold = Math.floor(assetLimitPerUpdateGroup * 0.75);
|
||||
return uploadedAssetCount > warningThreshold;
|
||||
}
|
||||
|
||||
export async function getBranchNameForCommandAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelNameArg,
|
||||
branchNameArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
paginatedQueryOptions,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
channelNameArg: string | undefined;
|
||||
branchNameArg: string | undefined;
|
||||
autoFlag: boolean;
|
||||
nonInteractive: boolean;
|
||||
paginatedQueryOptions: PaginatedQueryOptions;
|
||||
}): Promise<string> {
|
||||
if (channelNameArg && branchNameArg) {
|
||||
throw new Error(
|
||||
'Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto.'
|
||||
);
|
||||
}
|
||||
|
||||
if (channelNameArg) {
|
||||
return await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelNameArg);
|
||||
}
|
||||
|
||||
if (branchNameArg) {
|
||||
return branchNameArg;
|
||||
}
|
||||
|
||||
if (autoFlag) {
|
||||
return await getDefaultBranchNameAsync();
|
||||
} else if (nonInteractive) {
|
||||
throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode.');
|
||||
} else {
|
||||
let branchName: string;
|
||||
|
||||
try {
|
||||
const branch = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: `Which branch would you like to use?`,
|
||||
displayTextForListItem: updateBranch => ({
|
||||
title: `${updateBranch.name} ${chalk.grey(
|
||||
`- current update: ${formatUpdateMessage(updateBranch.updates[0])}`
|
||||
)}`,
|
||||
}),
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
branchName = branch.name;
|
||||
} catch {
|
||||
// unable to select a branch (network error or no branches for project)
|
||||
const { name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'No branches found. Provide a branch name:',
|
||||
initial: await getDefaultBranchNameAsync(),
|
||||
validate: value => (value ? true : 'Branch name may not be empty.'),
|
||||
});
|
||||
branchName = name;
|
||||
}
|
||||
|
||||
assert(branchName, 'Branch name must be specified.');
|
||||
return branchName;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUpdateMessageForCommandAsync({
|
||||
updateMessageArg,
|
||||
autoFlag,
|
||||
nonInteractive,
|
||||
jsonFlag,
|
||||
}: {
|
||||
updateMessageArg: string | undefined;
|
||||
autoFlag: boolean;
|
||||
nonInteractive: boolean;
|
||||
jsonFlag: boolean;
|
||||
}): Promise<string> {
|
||||
let updateMessage = updateMessageArg;
|
||||
if (!updateMessageArg && autoFlag) {
|
||||
updateMessage = (await getVcsClient().getLastCommitMessageAsync())?.trim();
|
||||
}
|
||||
|
||||
if (!updateMessage) {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply --message or use --auto when in non-interactive mode');
|
||||
}
|
||||
|
||||
const validationMessage = 'publish message may not be empty.';
|
||||
if (jsonFlag) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
const { updateMessageLocal } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'updateMessageLocal',
|
||||
message: `Provide an update message:`,
|
||||
initial: (await getVcsClient().getLastCommitMessageAsync())?.trim(),
|
||||
validate: (value: any) => (value ? true : validationMessage),
|
||||
});
|
||||
updateMessage = updateMessageLocal;
|
||||
}
|
||||
|
||||
assert(updateMessage, 'Update message must be specified.');
|
||||
|
||||
const truncatedMessage = truncateUpdateMessage(updateMessage, 1024);
|
||||
if (truncatedMessage !== updateMessage) {
|
||||
Log.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
|
||||
}
|
||||
|
||||
return updateMessage;
|
||||
}
|
||||
|
||||
export const defaultPublishPlatforms: Platform[] = ['android', 'ios'];
|
||||
|
||||
export function getRequestedPlatform(
|
||||
platform: ExpoCLIExportPlatformFlag
|
||||
): RequestedPlatform | null {
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return RequestedPlatform.Android;
|
||||
case 'ios':
|
||||
return RequestedPlatform.Ios;
|
||||
case 'web':
|
||||
return null;
|
||||
case 'all':
|
||||
return RequestedPlatform.All;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get runtime versions grouped by platform. Runtime version is always `null` on web where the platform is always backwards compatible. */
|
||||
export async function getRuntimeVersionObjectAsync(
|
||||
exp: ExpoConfig,
|
||||
platforms: Platform[],
|
||||
projectDir: string
|
||||
): Promise<{ platform: string; runtimeVersion: string }[]> {
|
||||
for (const platform of platforms) {
|
||||
if (platform === 'web') {
|
||||
continue;
|
||||
}
|
||||
const isPolicy = typeof (exp[platform]?.runtimeVersion ?? exp.runtimeVersion) === 'object';
|
||||
if (isPolicy) {
|
||||
const isManaged =
|
||||
(await resolveWorkflowAsync(projectDir, platform as EASBuildJobPlatform)) ===
|
||||
Workflow.MANAGED;
|
||||
if (!isManaged) {
|
||||
throw new Error(
|
||||
'Runtime version policies are only supported in the managed workflow. In the bare workflow, runtime version needs to be set manually.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(platforms)].map(platform => {
|
||||
if (platform === 'web') {
|
||||
return { platform: 'web', runtimeVersion: 'UNVERSIONED' };
|
||||
}
|
||||
return {
|
||||
platform,
|
||||
runtimeVersion: nullthrows(
|
||||
Updates.getRuntimeVersion(exp, platform),
|
||||
`Unable to determine runtime version for ${
|
||||
requestedPlatformDisplayNames[platform]
|
||||
}. ${learnMore('https://docs.expo.dev/eas-update/runtime-versions/')}`
|
||||
),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function getRuntimeToPlatformMappingFromRuntimeVersions(
|
||||
runtimeVersions: { platform: string; runtimeVersion: string }[]
|
||||
): { runtimeVersion: string; platforms: string[] }[] {
|
||||
const runtimeToPlatformMapping: { runtimeVersion: string; platforms: string[] }[] = [];
|
||||
for (const runtime of runtimeVersions) {
|
||||
const platforms = runtimeVersions
|
||||
.filter(({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion)
|
||||
.map(({ platform }) => platform);
|
||||
if (!runtimeToPlatformMapping.find(item => item.runtimeVersion === runtime.runtimeVersion)) {
|
||||
runtimeToPlatformMapping.push({ runtimeVersion: runtime.runtimeVersion, platforms });
|
||||
}
|
||||
}
|
||||
return runtimeToPlatformMapping;
|
||||
}
|
||||
@@ -124,6 +124,7 @@ function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
|
||||
runtime: {} as Runtime, // Temporary fix to resolve type errors
|
||||
platform: 'ios',
|
||||
manifestFragment: '...',
|
||||
isRollBackToEmbedded: false,
|
||||
activityTimestamp: '2022-12-07T02:24:43.487Z',
|
||||
branchId: '',
|
||||
awaitingCodeSigningInfo: false,
|
||||
|
||||
@@ -273,7 +273,7 @@ export async function ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir: stri
|
||||
}
|
||||
|
||||
try {
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
@@ -343,7 +343,7 @@ export async function ensureEASUpdateIsConfiguredAsync(
|
||||
expWithoutUpdates.sdkVersion
|
||||
);
|
||||
if (!hasExpoUpdates) {
|
||||
await installExpoUpdatesAsync(projectDir, { silent: !Log.isDebug });
|
||||
await installExpoUpdatesAsync(projectDir, { silent: false });
|
||||
Log.withTick('Installed expo updates');
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ export type FormattedUpdateGroupDescription = {
|
||||
group: string;
|
||||
platforms: string;
|
||||
runtimeVersion: string;
|
||||
codeSigningKey: string | undefined;
|
||||
isRollBackToEmbedded: boolean;
|
||||
};
|
||||
|
||||
export type FormattedBranchDescription = {
|
||||
@@ -74,6 +76,8 @@ export function formatUpdateGroup(update: FormattedUpdateGroupDescription): stri
|
||||
{ label: 'Platforms', value: update.platforms },
|
||||
{ label: 'Runtime Version', value: update.runtimeVersion },
|
||||
{ label: 'Message', value: update.message },
|
||||
{ label: 'Code Signing Key', value: update.codeSigningKey ?? 'N/A' },
|
||||
{ label: 'Is Roll Back to Embedded', value: update.isRollBackToEmbedded ? 'Yes' : 'No' },
|
||||
{ label: 'Group ID', value: update.group },
|
||||
]);
|
||||
}
|
||||
@@ -203,6 +207,7 @@ export function getUpdateGroupJsonInfo(updateGroups: UpdateFragment[]): UpdateJs
|
||||
runtimeVersion: update.runtimeVersion,
|
||||
platform: update.platform,
|
||||
manifestPermalink: update.manifestPermalink,
|
||||
isRollBackToEmbedded: update.isRollBackToEmbedded,
|
||||
gitCommitHash: update.gitCommitHash,
|
||||
}));
|
||||
}
|
||||
@@ -213,6 +218,8 @@ export function getUpdateGroupDescriptions(
|
||||
return updateGroups.map(updateGroup => ({
|
||||
message: formatUpdateMessage(updateGroup[0]),
|
||||
runtimeVersion: updateGroup[0].runtimeVersion,
|
||||
isRollBackToEmbedded: updateGroup[0].isRollBackToEmbedded,
|
||||
codeSigningKey: updateGroup[0].codeSigningInfo?.keyid,
|
||||
group: updateGroup[0].group,
|
||||
platforms: formatPlatformForUpdateGroup(updateGroup),
|
||||
}));
|
||||
@@ -225,6 +232,8 @@ export function getUpdateGroupDescriptionsWithBranch(
|
||||
branch: updateGroup[0].branch.name,
|
||||
message: formatUpdateMessage(updateGroup[0]),
|
||||
runtimeVersion: updateGroup[0].runtimeVersion,
|
||||
isRollBackToEmbedded: updateGroup[0].isRollBackToEmbedded,
|
||||
codeSigningKey: updateGroup[0].codeSigningInfo?.keyid,
|
||||
group: updateGroup[0].group,
|
||||
platforms: formatPlatformForUpdateGroup(updateGroup),
|
||||
}));
|
||||
@@ -241,6 +250,8 @@ export function getBranchDescription(branch: UpdateBranchFragment): FormattedBra
|
||||
update: {
|
||||
message: formatUpdateMessage(latestUpdate),
|
||||
runtimeVersion: latestUpdate.runtimeVersion,
|
||||
isRollBackToEmbedded: latestUpdate.isRollBackToEmbedded,
|
||||
codeSigningKey: latestUpdate.codeSigningInfo?.keyid,
|
||||
group: latestUpdate.group,
|
||||
platforms: getPlatformsForGroup({
|
||||
group: latestUpdate.group,
|
||||
|
||||
@@ -11,6 +11,7 @@ jest.mock('@expo/eas-json', () => {
|
||||
const EasJsonUtilsMock = {
|
||||
getBuildProfileAsync: jest.fn(),
|
||||
getBuildProfileNamesAsync: jest.fn(),
|
||||
getBuildProfileDeprecationWarnings: jest.fn(() => []),
|
||||
};
|
||||
return {
|
||||
...actual,
|
||||
@@ -29,7 +30,7 @@ describe(getProfilesAsync, () => {
|
||||
});
|
||||
|
||||
it('defaults to production profile', async () => {
|
||||
const easJsonAccessor = new EasJsonAccessor('/fake');
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath('/fake');
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
@@ -51,7 +52,7 @@ describe(getProfilesAsync, () => {
|
||||
|
||||
await expect(
|
||||
getProfilesAsync({
|
||||
easJsonAccessor: new EasJsonAccessor('/fake'),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath('/fake'),
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
@@ -60,7 +61,7 @@ describe(getProfilesAsync, () => {
|
||||
});
|
||||
|
||||
it('gets a specific profile', async () => {
|
||||
const easJsonAccessor = new EasJsonAccessor('/fake');
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath('/fake');
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
@@ -85,7 +86,7 @@ describe(getProfilesAsync, () => {
|
||||
|
||||
await expect(
|
||||
getProfilesAsync({
|
||||
easJsonAccessor: new EasJsonAccessor('/fake'),
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath('/fake'),
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { BillingUpdateQuery } from '../graphql/queries/BillingUpdateQuery';
|
||||
import Log from '../log';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||
|
||||
const WARNING_THRESHOLD = 0.85;
|
||||
export async function maybeWarnAboutEasPlanOverageAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string
|
||||
): Promise<void> {
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const billingUpdateQuery = await BillingUpdateQuery.byAccountIdAsync(
|
||||
graphqlClient,
|
||||
account.id,
|
||||
new Date()
|
||||
);
|
||||
|
||||
const isFree = !billingUpdateQuery.billing; // TODO: no billing means free?
|
||||
const isMetered = !!billingUpdateQuery.billing?.subscription?.meteredBillingStatus?.EAS_UPDATE;
|
||||
// TODO: verify correctness
|
||||
if (!isFree || isMetered) {
|
||||
return;
|
||||
}
|
||||
|
||||
const planMetrics = billingUpdateQuery.usageMetrics.byBillingPeriod.planMetrics;
|
||||
for (const metric of planMetrics) {
|
||||
const { value, limit, serviceMetric } = metric;
|
||||
const percentageUsed = value / limit;
|
||||
if (percentageUsed >= 1) {
|
||||
Log.error(
|
||||
'You have exceeded your EAS Update plan limit. You can create updates but they will no longer be distributed to users.'
|
||||
);
|
||||
Log.error('Please upgrade your plan to continue receiving updates.');
|
||||
return;
|
||||
} else if (percentageUsed >= WARNING_THRESHOLD && percentageUsed < 1) {
|
||||
Log.warn(
|
||||
`You are approaching your EAS Update plan limit. You have used ${Math.round(
|
||||
percentageUsed * 100
|
||||
)}% of your plan's ${serviceMetric}.`
|
||||
);
|
||||
Log.warn('Please upgrade your plan to continue receiving updates.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,9 @@ import nullthrows from 'nullthrows';
|
||||
|
||||
import { Response } from '../fetch';
|
||||
import { PartialManifest, PartialManifestAsset } from '../graphql/generated';
|
||||
import areSetsEqual from './expodash/areSetsEqual';
|
||||
|
||||
type CodeSigningInfo = {
|
||||
export type CodeSigningInfo = {
|
||||
privateKey: PKI.rsa.PrivateKey;
|
||||
certificate: PKI.Certificate;
|
||||
codeSigningMetadata: { alg: string; keyid: string };
|
||||
@@ -101,7 +102,7 @@ export async function getKeyAndCertificateFromPathsAsync({
|
||||
};
|
||||
}
|
||||
|
||||
export async function getManifestBodyAsync(res: Response): Promise<string | null> {
|
||||
async function getMultipartBodyPartAsync(res: Response, partName: string): Promise<string | null> {
|
||||
const contentType = res.headers.get('content-type');
|
||||
if (!contentType) {
|
||||
throw new Error('The multipart manifest response is missing the content-type header');
|
||||
@@ -111,11 +112,19 @@ export async function getManifestBodyAsync(res: Response): Promise<string | null
|
||||
contentType,
|
||||
Buffer.from(bodyBuffer)
|
||||
);
|
||||
const manifestPart = multipartParts.find(part => isMultipartPartWithName(part, 'manifest'));
|
||||
const manifestPart = multipartParts.find(part => isMultipartPartWithName(part, partName));
|
||||
return manifestPart?.body ?? null;
|
||||
}
|
||||
|
||||
export function signManifestBody(body: string, codeSigningInfo: CodeSigningInfo): string {
|
||||
export async function getManifestBodyAsync(res: Response): Promise<string | null> {
|
||||
return await getMultipartBodyPartAsync(res, 'manifest');
|
||||
}
|
||||
|
||||
export async function getDirectiveBodyAsync(res: Response): Promise<string | null> {
|
||||
return await getMultipartBodyPartAsync(res, 'directive');
|
||||
}
|
||||
|
||||
export function signBody(body: string, codeSigningInfo: CodeSigningInfo): string {
|
||||
return signBufferRSASHA256AndVerify(
|
||||
codeSigningInfo.privateKey,
|
||||
codeSigningInfo.certificate,
|
||||
@@ -181,3 +190,20 @@ export function checkManifestBodyAgainstUpdateInfoGroup(
|
||||
assertAssetParity(correspondingManifestResponseBodyAssetJSON, nullthrows(partialManifestAsset));
|
||||
}
|
||||
}
|
||||
|
||||
export function checkDirectiveBodyAgainstUpdateInfoGroup(directiveResponseBody: string): void {
|
||||
const directiveResponseBodyJSON = JSON.parse(directiveResponseBody);
|
||||
|
||||
if (
|
||||
!areSetsEqual(
|
||||
new Set(Object.keys(directiveResponseBodyJSON)),
|
||||
new Set(['extra', 'type', 'parameters'])
|
||||
)
|
||||
) {
|
||||
throw new Error('Code signing directive integrity error: Unexpected keys');
|
||||
}
|
||||
|
||||
if (directiveResponseBodyJSON.type !== 'rollBackToEmbedded') {
|
||||
throw new Error('Code signing directive integrity error: Incorrect directive type');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { ExpoConfig } from '@expo/config-types';
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import chalk from 'chalk';
|
||||
import { boolish } from 'getenv';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import resolveFrom, { silent as silentResolveFrom } from 'resolve-from';
|
||||
import semver from 'semver';
|
||||
|
||||
import Log from '../log';
|
||||
@@ -63,7 +63,8 @@ export async function expoCommandAsync(
|
||||
args: string[],
|
||||
{ silent = false }: { silent?: boolean } = {}
|
||||
): Promise<void> {
|
||||
const expoCliPath = resolveFrom(projectDir, 'expo/bin/cli.js');
|
||||
const expoCliPath =
|
||||
silentResolveFrom(projectDir, 'expo/bin/cli') ?? resolveFrom(projectDir, 'expo/bin/cli.js');
|
||||
const spawnPromise = spawnAsync(expoCliPath, args, {
|
||||
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so user can install a missing expo-cli from inside this command
|
||||
});
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import areSetsEqual from '../areSetsEqual';
|
||||
|
||||
describe(areSetsEqual, () => {
|
||||
it.each([
|
||||
[new Set([1, 2]), new Set([1, 2])],
|
||||
[new Set([1, 2]), new Set([2, 1])],
|
||||
])('equal cases: %p', (a, b) => {
|
||||
expect(areSetsEqual(a, b)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[new Set([1, 2, 3]), new Set([1, 2])],
|
||||
[new Set([1, 2]), new Set([1, 2, 3])],
|
||||
])('non-equal cases: %p', (a, b) => {
|
||||
expect(areSetsEqual(a, b)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function areSetsEqual<T>(a: Set<T>, b: Set<T>): boolean {
|
||||
return a.size === b.size && [...a].every(value => b.has(value));
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
SubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
|
||||
import Log, { learnMore } from '../log';
|
||||
|
||||
type EasProfile<T extends ProfileType> = T extends 'build'
|
||||
? BuildProfile<Platform>
|
||||
: SubmitProfile<Platform>;
|
||||
@@ -57,11 +59,15 @@ async function readProfileAsync<T extends ProfileType>({
|
||||
profileName?: string;
|
||||
}): Promise<EasProfile<T>> {
|
||||
if (type === 'build') {
|
||||
return (await EasJsonUtils.getBuildProfileAsync(
|
||||
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
profileName
|
||||
)) as EasProfile<T>;
|
||||
);
|
||||
|
||||
maybePrintBuildProfileDeprecationWarnings(buildProfile, profileName);
|
||||
|
||||
return buildProfile as EasProfile<T>;
|
||||
} else {
|
||||
return (await EasJsonUtils.getSubmitProfileAsync(
|
||||
easJsonAccessor,
|
||||
@@ -70,3 +76,32 @@ async function readProfileAsync<T extends ProfileType>({
|
||||
)) as EasProfile<T>;
|
||||
}
|
||||
}
|
||||
|
||||
let hasPrintedDeprecationWarnings = false;
|
||||
|
||||
function maybePrintBuildProfileDeprecationWarnings(
|
||||
buildProfile: BuildProfile<Platform>,
|
||||
profileName?: string
|
||||
): void {
|
||||
if (hasPrintedDeprecationWarnings) {
|
||||
return;
|
||||
}
|
||||
const deprecationWarnings = EasJsonUtils.getBuildProfileDeprecationWarnings(
|
||||
buildProfile,
|
||||
profileName
|
||||
);
|
||||
if (deprecationWarnings.length === 0) {
|
||||
return;
|
||||
}
|
||||
Log.newLine();
|
||||
Log.warn('Detected deprecated fields in eas.json:');
|
||||
for (const warning of deprecationWarnings) {
|
||||
const warnlog: string = warning.message.map(line => `\t${line}`).join('\n');
|
||||
Log.warn(warnlog);
|
||||
if (warning.docsUrl) {
|
||||
Log.warn(`\t${learnMore(warning.docsUrl)}`);
|
||||
}
|
||||
Log.newLine();
|
||||
}
|
||||
hasPrintedDeprecationWarnings = true;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "3.8.0",
|
||||
"version": "3.9.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.18.6",
|
||||
"@expo/eas-build-job": "0.2.106",
|
||||
"@expo/eas-build-job": "1.0.7",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "10.1.0",
|
||||
|
||||
@@ -74,11 +74,6 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cacheDefaultPaths": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to cache the recommended set of files, currently only Podfile.lock is cached by default for iOS build and nothing is cached for Android.",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EasJsonAccessor patching JSON file 1`] = `
|
||||
exports[`fromProjectPath patching JSON file 1`] = `
|
||||
"{
|
||||
"build": {
|
||||
"production": {
|
||||
@@ -14,7 +14,7 @@ exports[`EasJsonAccessor patching JSON file 1`] = `
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`EasJsonAccessor patching JSON5 file (preserves comments) 1`] = `
|
||||
exports[`fromProjectPath patching JSON5 file (preserves comments) 1`] = `
|
||||
"{
|
||||
"build": {
|
||||
"production": {
|
||||
|
||||
@@ -15,7 +15,7 @@ beforeEach(async () => {
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
describe(EasJsonAccessor, () => {
|
||||
describe(EasJsonAccessor.fromProjectPath, () => {
|
||||
test('patching JSON file', async () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
@@ -24,7 +24,7 @@ describe(EasJsonAccessor, () => {
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = new EasJsonAccessor(fakeAppPath);
|
||||
const accessor = EasJsonAccessor.fromProjectPath(fakeAppPath);
|
||||
await accessor.readAsync();
|
||||
accessor.patch(o => {
|
||||
o.build.production.env.ABC = '456';
|
||||
@@ -44,7 +44,7 @@ describe(EasJsonAccessor, () => {
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = new EasJsonAccessor(fakeAppPath);
|
||||
const accessor = EasJsonAccessor.fromProjectPath(fakeAppPath);
|
||||
await accessor.readAsync();
|
||||
accessor.patch(o => {
|
||||
o.build.production.env.ABC = '456';
|
||||
@@ -67,7 +67,7 @@ describe(EasJsonAccessor, () => {
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = new EasJsonAccessor(fakeAppPath);
|
||||
const accessor = EasJsonAccessor.fromProjectPath(fakeAppPath);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(
|
||||
/^Found invalid character in .*eas\.json.+/
|
||||
@@ -82,7 +82,57 @@ describe(EasJsonAccessor, () => {
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = new EasJsonAccessor(fakeAppPath);
|
||||
const accessor = EasJsonAccessor.fromProjectPath(fakeAppPath);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(/^.*eas\.json.* is empty\.$/g);
|
||||
});
|
||||
});
|
||||
|
||||
describe(EasJsonAccessor.fromRawString, () => {
|
||||
test('patching JSON file', async () => {
|
||||
const accessor = EasJsonAccessor.fromRawString(
|
||||
await fsReal.readFile(path.join(fixturesDir, 'eas-json.json'), 'utf-8')
|
||||
);
|
||||
await accessor.readAsync();
|
||||
expect(() =>
|
||||
accessor.patch(o => {
|
||||
o.build.production.env.ABC = '456';
|
||||
return o;
|
||||
})
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
test('reading invalid JSON5 object', async () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'./eas.json': await fsReal.readFile(
|
||||
path.join(fixturesDir, 'eas-invalid-json5.json'),
|
||||
'utf-8'
|
||||
),
|
||||
},
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = EasJsonAccessor.fromRawString(
|
||||
await fsReal.readFile(path.join(fixturesDir, 'eas-invalid-json5.json'), 'utf-8')
|
||||
);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(
|
||||
/^Found invalid character in .*eas\.json.+/
|
||||
);
|
||||
});
|
||||
|
||||
test('reading empty JSON file', async () => {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'./eas.json': await fsReal.readFile(path.join(fixturesDir, 'eas-empty.json'), 'utf-8'),
|
||||
},
|
||||
fakeAppPath
|
||||
);
|
||||
|
||||
const accessor = EasJsonAccessor.fromRawString(
|
||||
await fsReal.readFile(path.join(fixturesDir, 'eas-empty.json'), 'utf-8')
|
||||
);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(accessor.readAsync()).rejects.toThrowError(/^.*eas\.json.* is empty\.$/g);
|
||||
});
|
||||
|
||||
@@ -20,7 +20,33 @@ test('minimal valid eas.json for both platforms', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'production'
|
||||
);
|
||||
|
||||
expect(androidProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
|
||||
expect(iosProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
});
|
||||
|
||||
test('minimal valid eas.json for both platforms when reading eas.json from string', async () => {
|
||||
const accessor = EasJsonAccessor.fromRawString(
|
||||
JSON.stringify({
|
||||
build: {
|
||||
production: {},
|
||||
},
|
||||
})
|
||||
);
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -52,7 +78,7 @@ test('valid eas.json for development client builds', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'debug');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -82,7 +108,7 @@ test(`valid eas.json with autoIncrement flag at build profile root`, async () =>
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -111,7 +137,7 @@ test('valid profile for internal distribution on Android', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const profile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'internal');
|
||||
expect(profile).toEqual({
|
||||
distribution: 'internal',
|
||||
@@ -133,7 +159,7 @@ test('valid profile extending other profile', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'base');
|
||||
const extendedProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -176,7 +202,7 @@ test('valid profile extending other profile with platform specific envs', async
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -230,7 +256,6 @@ test('valid profile extending other profile with platform specific caching', asy
|
||||
},
|
||||
android: {
|
||||
cache: {
|
||||
cacheDefaultPaths: false,
|
||||
customPaths: ['somefakepath'],
|
||||
},
|
||||
},
|
||||
@@ -238,7 +263,7 @@ test('valid profile extending other profile with platform specific caching', asy
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
@@ -261,7 +286,6 @@ test('valid profile extending other profile with platform specific caching', asy
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
cache: {
|
||||
cacheDefaultPaths: false,
|
||||
customPaths: ['somefakepath'],
|
||||
},
|
||||
});
|
||||
@@ -282,7 +306,7 @@ test('valid eas.json with missing profile', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const promise = EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'debug');
|
||||
await expect(promise).rejects.toThrowError('Missing build profile in eas.json: debug');
|
||||
});
|
||||
@@ -294,7 +318,7 @@ test('invalid eas.json when using wrong buildType', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const promise = EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(promise).rejects.toThrowError(
|
||||
@@ -305,7 +329,7 @@ test('invalid eas.json when using wrong buildType', async () => {
|
||||
test('empty json', async () => {
|
||||
await fs.writeJson('/project/eas.json', {});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const promise = EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError('Missing build profile in eas.json: production');
|
||||
});
|
||||
@@ -317,7 +341,7 @@ test('invalid semver value', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const promise = EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(InvalidEasJsonError);
|
||||
await expect(promise).rejects.toThrowError(
|
||||
@@ -332,7 +356,7 @@ test('invalid release channel', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const promise = EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(/fails to match the required pattern/);
|
||||
});
|
||||
@@ -345,7 +369,7 @@ test('get profile names', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const allProfileNames = await EasJsonUtils.getBuildProfileNamesAsync(accessor);
|
||||
expect(allProfileNames.sort()).toEqual(['blah', 'production'].sort());
|
||||
});
|
||||
@@ -359,7 +383,7 @@ test('invalid resourceClass at build profile root', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
|
||||
await expect(
|
||||
EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production')
|
||||
@@ -377,7 +401,7 @@ test('iOS-specific resourceClass', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
await expect(
|
||||
EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production')
|
||||
).resolves.not.toThrow();
|
||||
@@ -394,7 +418,7 @@ test('Android-specific resourceClass', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
await expect(
|
||||
EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production')
|
||||
).resolves.not.toThrow();
|
||||
@@ -414,7 +438,7 @@ test('build profile with platform-specific custom build config', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
@@ -442,7 +466,7 @@ test('build profiles with both platform build config', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
|
||||
@@ -19,7 +19,7 @@ test('minimal allowed eas.json for both platforms', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'production');
|
||||
const androidProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
@@ -50,7 +50,7 @@ test('android config with all required values', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const androidProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
@@ -80,7 +80,7 @@ test('android config with serviceAccountKeyPath set to env var', async () => {
|
||||
|
||||
try {
|
||||
process.env.GOOGLE_SERVICE_ACCOUNT = './path.json';
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const androidProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
@@ -114,7 +114,7 @@ test('ios config with all required values', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'production');
|
||||
|
||||
expect(iosProfile).toEqual({
|
||||
@@ -148,7 +148,7 @@ test('ios config with ascApiKey fields set to env var', async () => {
|
||||
process.env.ASC_API_KEY_PATH = './path-ABCD.p8';
|
||||
process.env.ASC_API_KEY_ISSUER_ID = 'abc-123-def-456';
|
||||
process.env.ASC_API_KEY_ID = 'ABCD';
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const iosProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'release');
|
||||
|
||||
expect(iosProfile).toEqual({
|
||||
@@ -189,7 +189,7 @@ test('valid profile extending other profile', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'base');
|
||||
const extendedProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
@@ -233,7 +233,7 @@ test('get profile names', async () => {
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const allProfileNames = await EasJsonUtils.getSubmitProfileNamesAsync(accessor);
|
||||
expect(allProfileNames.sort()).toEqual(['production', 'blah'].sort());
|
||||
});
|
||||
@@ -38,7 +38,7 @@ const customErrorMessageHandlers: ((err: ValidationError) => void)[] = [
|
||||
];
|
||||
|
||||
export class EasJsonAccessor {
|
||||
private easJsonPath: string;
|
||||
private easJsonPath: string | undefined;
|
||||
|
||||
private isJson5 = false;
|
||||
private easJson: EasJson | undefined;
|
||||
@@ -46,8 +46,25 @@ export class EasJsonAccessor {
|
||||
private easJsonRawObject: any | undefined;
|
||||
private easJsonPatched: boolean = false;
|
||||
|
||||
constructor(projectDir: string) {
|
||||
this.easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
|
||||
private constructor({ projectDir }: { projectDir: string });
|
||||
private constructor({ easJsonRawContents }: { easJsonRawContents: string });
|
||||
private constructor({
|
||||
projectDir,
|
||||
easJsonRawContents,
|
||||
}: {
|
||||
projectDir?: string;
|
||||
easJsonRawContents?: string;
|
||||
}) {
|
||||
this.easJsonPath = projectDir && EasJsonAccessor.formatEasJsonPath(projectDir);
|
||||
this.easJsonRawContents = easJsonRawContents;
|
||||
}
|
||||
|
||||
public static fromProjectPath(projectDir: string): EasJsonAccessor {
|
||||
return new EasJsonAccessor({ projectDir });
|
||||
}
|
||||
|
||||
public static fromRawString(easJsonRawContents: string): EasJsonAccessor {
|
||||
return new EasJsonAccessor({ easJsonRawContents });
|
||||
}
|
||||
|
||||
public static formatEasJsonPath(projectDir: string): string {
|
||||
@@ -81,6 +98,9 @@ export class EasJsonAccessor {
|
||||
}
|
||||
|
||||
public async writeAsync(): Promise<void> {
|
||||
if (!this.easJsonPath) {
|
||||
throw new Error('Updates are not supported for EasJsonAccessor created from string.');
|
||||
}
|
||||
if (!this.easJsonPatched) {
|
||||
return;
|
||||
}
|
||||
@@ -89,6 +109,9 @@ export class EasJsonAccessor {
|
||||
}
|
||||
|
||||
public patch(fn: (easJsonRawObject: any) => any): void {
|
||||
if (!this.easJsonPath) {
|
||||
throw new Error('Updates are not supported for EasJsonAccessor created from string.');
|
||||
}
|
||||
assert(
|
||||
this.easJsonRawContents && this.easJsonRawObject,
|
||||
'call readAsync/readRawJsonAsync first'
|
||||
@@ -104,16 +127,24 @@ export class EasJsonAccessor {
|
||||
}
|
||||
|
||||
public async readRawJsonAsync(): Promise<any> {
|
||||
if (!(await fs.pathExists(this.easJsonPath))) {
|
||||
throw new MissingEasJsonError(
|
||||
`${chalk.bold('eas.json')} could not be found at ${
|
||||
this.easJsonPath
|
||||
}. Learn more at https://expo.fyi/eas-json`
|
||||
);
|
||||
if (this.easJsonPath) {
|
||||
if (!(await fs.pathExists(this.easJsonPath))) {
|
||||
throw new MissingEasJsonError(
|
||||
`${chalk.bold('eas.json')} could not be found at ${
|
||||
this.easJsonPath
|
||||
}. Learn more at https://expo.fyi/eas-json`
|
||||
);
|
||||
}
|
||||
this.easJsonRawContents = await fs.readFile(this.easJsonPath, 'utf-8');
|
||||
}
|
||||
return this.parseRawJson();
|
||||
}
|
||||
|
||||
this.easJsonRawContents = await fs.readFile(this.easJsonPath, 'utf-8');
|
||||
|
||||
private parseRawJson(): any {
|
||||
assert(
|
||||
this.easJsonRawContents !== undefined,
|
||||
'easJsonRawContents needs to be set before calling parseRawJson'
|
||||
);
|
||||
if (this.easJsonRawContents.trim().length === 0) {
|
||||
throw new InvalidEasJsonError(`${chalk.bold('eas.json')} is empty.`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { AndroidReleaseStatus, AndroidReleaseTrack, SubmitProfile } from './submit/types';
|
||||
export { getDefaultProfile as getDefaultSubmitProfile } from './submit/resolver';
|
||||
export { EasJson, ProfileType, AppVersionSource } from './types';
|
||||
export { Platform } from '@expo/eas-build-job';
|
||||
export {
|
||||
AndroidVersionAutoIncrement,
|
||||
BuildProfile,
|
||||
|
||||
@@ -8,6 +8,11 @@ import { resolveSubmitProfile } from './submit/resolver';
|
||||
import { SubmitProfile } from './submit/types';
|
||||
import { EasJson } from './types';
|
||||
|
||||
interface EasJsonDeprecationWarning {
|
||||
message: string[];
|
||||
docsUrl?: string;
|
||||
}
|
||||
|
||||
export class EasJsonUtils {
|
||||
public static async getBuildProfileNamesAsync(accessor: EasJsonAccessor): Promise<string[]> {
|
||||
const easJson = await accessor.readAsync();
|
||||
@@ -23,6 +28,26 @@ export class EasJsonUtils {
|
||||
return resolveBuildProfile({ easJson, platform, profileName });
|
||||
}
|
||||
|
||||
public static getBuildProfileDeprecationWarnings(
|
||||
buildProfile: BuildProfile,
|
||||
profileName?: string
|
||||
): EasJsonDeprecationWarning[] {
|
||||
const warnings: EasJsonDeprecationWarning[] = [];
|
||||
|
||||
if (buildProfile.cache?.cacheDefaultPaths !== undefined) {
|
||||
warnings.push({
|
||||
message: [
|
||||
`The "build.${
|
||||
profileName ?? 'production'
|
||||
}.cache.cacheDefaultPaths" field in eas.json is deprecated and will be removed in the future.`,
|
||||
],
|
||||
docsUrl: 'https://docs.expo.dev/build-reference/caching/#ios-dependencies',
|
||||
});
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
public static async getCliConfigAsync(accessor: EasJsonAccessor): Promise<EasJson['cli'] | null> {
|
||||
try {
|
||||
const easJson = await accessor.readAsync();
|
||||
|
||||
@@ -5,9 +5,11 @@ set -eo pipefail
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../.. && pwd )"
|
||||
LOCAL_TS_PATH="$ROOT_DIR/packages/eas-cli/src/build/local.ts"
|
||||
|
||||
LOCAL_BUILD_PLUGIN_NPM_TAG="eas-cli"
|
||||
|
||||
echo "Checking if eas-cli-local-build-plugin upgrade is available"
|
||||
|
||||
plugin_version_latest=$(npm info eas-cli-local-build-plugin@latest version)
|
||||
plugin_version_latest=$(npm info eas-cli-local-build-plugin@$LOCAL_BUILD_PLUGIN_NPM_TAG version)
|
||||
plugin_version_current=$(grep 'const PLUGIN_PACKAGE_VERSION' $LOCAL_TS_PATH | cut -d"'" -f2)
|
||||
|
||||
if [[ "$plugin_version_latest" == "$plugin_version_current" ]]; then
|
||||
|
||||
@@ -1324,10 +1324,10 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@expo/apple-utils@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.1.0.tgz#62cd724436b8eaa9cafcc3648ea211af8409fc71"
|
||||
integrity sha512-caw0o9HeQHX7xeXppkXI3B/PmPyVOkEjWgXdv0dYzgW/rwvZRLwtMNFXX5t+dJy2hd2UpZlrixaoYyvxuXPtbQ==
|
||||
"@expo/apple-utils@1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.2.0.tgz#227d118089a4645b076162f3b513dfc68763ca21"
|
||||
integrity sha512-MotvQqu8CNVlnl9nsISobXDjEzSMfT7U5Zk+1rhOFoJhaZhlEkARxumUFUgH6cYnK7J2mMd3YxM+G769arkEKQ==
|
||||
|
||||
"@expo/bunyan@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1390,10 +1390,10 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@expo/eas-build-job@0.2.106":
|
||||
version "0.2.106"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.106.tgz#a10f791986bfa9777a13b2b0e9c820673e9e38ba"
|
||||
integrity sha512-V7ihSFNFaATM31P1b/tGrGG7sjuXBT5ZAzKnybTWr5Xl1ODOdEpSC1c9bzs9SUr2iWUFVsxyd2/7+f2+7r/l4A==
|
||||
"@expo/eas-build-job@1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.7.tgz#4c50dbc37faf000c5a5d791e69995bbf0eda39ff"
|
||||
integrity sha512-LBYXWS/wp6YnyNDUqt+5+k0J7P6gf3/hjf48yJp+BcLabQbqWqAuz7eqPMEDt9bezMX2fEAiYywSBdnRJKo3Ow==
|
||||
dependencies:
|
||||
joi "^17.7.0"
|
||||
semver "^7.3.8"
|
||||
@@ -1433,6 +1433,13 @@
|
||||
json5 "^2.2.2"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/logger/-/logger-1.0.0.tgz#41f17209e11aa83eabd5722c0efbc5b80efc7c41"
|
||||
integrity sha512-i07o2xUsmtpC2mQ3ZHOJRtFEqWTfQabOb3U5Q0Hwip/BvI0yj8Pweu2ExhKLERbGugt4tSSLdQJUcKQbOrue6g==
|
||||
dependencies:
|
||||
"@expo/bunyan" "^4.0.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/multipart-body-parser/-/multipart-body-parser-1.1.0.tgz#ba402b3047cf14ef17b4d785ec030dd6474ae6e4"
|
||||
@@ -1565,6 +1572,26 @@
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/spawn-async@^1.7.0":
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58"
|
||||
integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.8.tgz#06fe9ca5bde449255661cf090666e70dd2ae5345"
|
||||
integrity sha512-1SxHOtfLwCdKxOUtbXjz548JQlkkVoNjaydNmo7zZgPvD/P4aviaOSCL3StUpQuWNlcERLzjOn8/NgvsdDmMyw==
|
||||
dependencies:
|
||||
"@expo/logger" "1.0.0"
|
||||
"@expo/spawn-async" "^1.7.0"
|
||||
arg "^5.0.2"
|
||||
joi "^17.7.0"
|
||||
this-file "^2.0.3"
|
||||
uuid "^9.0.0"
|
||||
yaml "^2.2.1"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/timeago.js/-/timeago.js-1.0.0.tgz#8fb2b17e93e7a8d28387a4d292af12e071040045"
|
||||
@@ -4832,6 +4859,11 @@ arg@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||
|
||||
arg@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
|
||||
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
@@ -12809,6 +12841,11 @@ thenify-all@^1.0.0:
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
this-file@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/this-file/-/this-file-2.0.3.tgz#13bd2bcfbab2ce86a37a15689df1d14861032b1b"
|
||||
integrity sha512-IdMH1bUkVJdJjM7o8v83Mv4QvVPdkAofur20STl2Bbw9uMuuS/bT/PZURkEdZsy9XC/1ZXWgZ1wIL9nvouGaEg==
|
||||
|
||||
through2@^2.0.0:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||
@@ -13322,7 +13359,7 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c"
|
||||
integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==
|
||||
|
||||
[email protected]:
|
||||
[email protected], uuid@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
|
||||
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
|
||||
@@ -13762,6 +13799,11 @@ yaml@^1.10.0, yaml@^1.7.2:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
||||
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
||||
|
||||
yaml@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4"
|
||||
integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==
|
||||
|
||||
[email protected], yargs-parser@^20.2.2:
|
||||
version "20.2.4"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
|
||||
|
||||
Reference in new issue
Block a user