mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
480bc30468 | ||
|
|
c1c9e06f76 | ||
|
|
5fb68a652b | ||
|
|
056d0b1017 | ||
|
|
cc99794bbd | ||
|
|
04049a8d1f | ||
|
|
5bdd46a2a9 | ||
|
|
a7e88bd7cb | ||
|
|
78b75c6224 | ||
|
|
52f54fc7ae | ||
|
|
7ddd630cfd | ||
|
|
8751a2835f | ||
|
|
da9829a853 | ||
|
|
cc822b18f3 | ||
|
|
bcab3bcb09 | ||
|
|
a5dc0bd27c | ||
|
|
54127c4e37 | ||
|
|
9097ab2537 | ||
|
|
4e2029a5c5 | ||
|
|
ad02a056d7 | ||
|
|
ea9f734b11 | ||
|
|
7db9d79c55 | ||
|
|
2e31b42b76 | ||
|
|
66d02d9724 | ||
|
|
7c96cc10cf | ||
|
|
523a781ba5 | ||
|
|
6cf8bd5c5a | ||
|
|
67e9ea706b | ||
|
|
c22f410889 | ||
|
|
9ff47af4fa | ||
|
|
324dce8fc0 | ||
|
|
b30c86f0c0 | ||
|
|
008279e1dd | ||
|
|
d00814f84b | ||
|
|
81aa127e28 | ||
|
|
d321a0bccb | ||
|
|
a1d86549ed | ||
|
|
f7e9af913c | ||
|
|
137f9d4ac7 | ||
|
|
2ac863a9e3 | ||
|
|
11be9eba49 | ||
|
|
b36db15601 | ||
|
|
93c5dc1a32 | ||
|
|
c86550c0c8 | ||
|
|
36b4296d3c | ||
|
|
8c08541ca3 | ||
|
|
5a1406d39c | ||
|
|
78ea88cc5f | ||
|
|
6cb02d0ccd | ||
|
|
d6c7623718 | ||
|
|
32142b4fe8 | ||
|
|
6bdee0f395 | ||
|
|
baf94a21c3 | ||
|
|
162d64bce6 | ||
|
|
d16c1fc757 | ||
|
|
e42ea7e2ec |
No files matched your search
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Expo Developers Discord
|
||||
url: https://chat.expo.dev/
|
||||
about: Join other developers in discussions regarding Expo and EAS.
|
||||
@@ -8,10 +8,102 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for the Tap to Pay on iPhone iOS entitlement. ([#2069](https://github.com/expo/eas-cli/pull/2069) by [@fobos531](https://github.com/fobos531))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [5.3.1](https://github.com/expo/eas-cli/releases/tag/v5.3.1) - 2023-09-28
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update EAS Build images description in our VSCode plugin. Add new `macos-ventura-13.6-xcode-15.0` image. ([#2068](https://github.com/expo/eas-cli/pull/2068) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [5.3.0](https://github.com/expo/eas-cli/releases/tag/v5.3.0) - 2023-09-25
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Update: expose expo cli `--dev` flag as an argument. ([#2050](https://github.com/expo/eas-cli/pull/2050) by [@nderscore](https://github.com/nderscore))
|
||||
- Support `bun` option in eas.json. ([#2055](https://github.com/expo/eas-cli/pull/2055) by [@kadikraman](https://github.com/kadikraman))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Support node aliases in .nvmrc. ([#2052](https://github.com/expo/eas-cli/pull/2052) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Rollouts: more robust printing function. ([#2047](https://github.com/expo/eas-cli/pull/2047) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [5.2.0](https://github.com/expo/eas-cli/releases/tag/v5.2.0) - 2023-09-05
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Rollouts: json output for ci. ([#2037](https://github.com/expo/eas-cli/pull/2037) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Update: only print channel-branch pairing if we created a channel. ([#2036](https://github.com/expo/eas-cli/pull/2036) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Added rollout tests. ([#2042](https://github.com/expo/eas-cli/pull/2042) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Remove unreachable codesigning option. ([#2041](https://github.com/expo/eas-cli/pull/2041) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Fix generated graphql tsc errors. ([#2039](https://github.com/expo/eas-cli/pull/2039) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Rollouts: view action for CI. ([#2040](https://github.com/expo/eas-cli/pull/2040) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [5.1.0](https://github.com/expo/eas-cli/releases/tag/v5.1.0) - 2023-09-01
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Support `pnpm` option in eas.json. ([#1988](https://github.com/expo/eas-cli/pull/1988) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Make app config error not repeat indefinitely. ([#2020](https://github.com/expo/eas-cli/pull/2020) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
## [5.0.2](https://github.com/expo/eas-cli/releases/tag/v5.0.2) - 2023-08-29
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Add proper expo cli `--platform` flag handling when exporting updates. ([#1939](https://github.com/expo/eas-cli/pull/1939) by [@byCedric](https://github.com/byCedric))
|
||||
|
||||
## [5.0.1](https://github.com/expo/eas-cli/releases/tag/v5.0.1) - 2023-08-28
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Pass `platform` argument to expo-cli correctly when using the `eas update` command. ([#2028](https://github.com/expo/eas-cli/pull/2028) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [5.0.0](https://github.com/expo/eas-cli/releases/tag/v5.0.0) - 2023-08-28
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Only export at most ios and android dist for EAS updates. ([#2002](https://github.com/expo/eas-cli/pull/2002) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add rollback disambiguation command. ([#2004](https://github.com/expo/eas-cli/pull/2004) by [@wschurman](https://github.com/wschurman))
|
||||
- Detect devices that fail to be provisioned, list them to the user and show the explanation message with the link to the devices page to check actual status. ([#2011](https://github.com/expo/eas-cli/pull/2011) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Add info to EAS Update asset upload process about asset counts and limits. ([#2013](https://github.com/expo/eas-cli/pull/2013) by [@wschurman](https://github.com/wschurman))
|
||||
- .nvmrc support for setting node version. ([#1954](https://github.com/expo/eas-cli/pull/1954) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Support republishing roll back to embedded updates. ([#2006](https://github.com/expo/eas-cli/pull/2006) by [@wschurman](https://github.com/wschurman))
|
||||
- Configure updates as well when somebody tries to run a build with channel set. ([#2016](https://github.com/expo/eas-cli/pull/2016) by [@wschurman](https://github.com/wschurman))
|
||||
- Fix printing bug: branch with no update. ([#2023](https://github.com/expo/eas-cli/pull/2023) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- More branch map utility functions. ([#2001](https://github.com/expo/eas-cli/pull/2001) by [@quinlanj](https://github.com/quinlanj))
|
||||
- More branch mapping utils. ([#2003](https://github.com/expo/eas-cli/pull/2003) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Create a linked channel on build if not exists. ([#2017](https://github.com/expo/eas-cli/pull/2017) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add `developmentClient` to metadata. ([#2015](https://github.com/expo/eas-cli/pull/2015) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [4.1.2](https://github.com/expo/eas-cli/releases/tag/v4.1.2) - 2023-08-10
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Make branch mapping utility files have no dependencies. ([#1993](https://github.com/expo/eas-cli/pull/1993) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [4.1.1](https://github.com/expo/eas-cli/releases/tag/v4.1.1) - 2023-08-08
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Instead of opening a GitHub issue for security vulnerabilities, send an email to our secure inbox: secure@expo.io.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "4.1.2",
|
||||
"version": "5.4.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+60
-59
@@ -133,7 +133,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -150,7 +150,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -167,7 +167,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -181,7 +181,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -231,7 +231,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -252,7 +252,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -272,7 +272,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -292,7 +292,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -315,7 +315,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -345,7 +345,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -362,7 +362,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -379,7 +379,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -414,7 +414,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/v4.1.2/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -452,7 +452,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -478,7 +478,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -502,7 +502,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -551,7 +551,7 @@ DESCRIPTION
|
||||
get the latest version from EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
@@ -570,7 +570,7 @@ DESCRIPTION
|
||||
update version of an app
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -589,7 +589,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/v4.1.2/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -606,7 +606,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -627,7 +627,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -649,7 +649,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -669,7 +669,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
@@ -677,7 +677,7 @@ Roll a new branch out on a channel incrementally.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas channel:rollout [CHANNEL] [--action create|edit|end] [--percent <value>] [--outcome
|
||||
$ eas channel:rollout [CHANNEL] [--action create|edit|end|view] [--percent <value>] [--outcome
|
||||
republish-and-revert|revert] [--branch <value>] [--runtime-version <value>] [--private-key-path <value>] [--json
|
||||
--non-interactive]
|
||||
|
||||
@@ -685,7 +685,7 @@ ARGUMENTS
|
||||
CHANNEL channel on which the rollout should be done
|
||||
|
||||
FLAGS
|
||||
--action=(create|edit|end) Rollout action to perform
|
||||
--action=(create|edit|end|view) Rollout action to perform
|
||||
--branch=<value> Branch to roll out. Use with --action=create
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
@@ -701,7 +701,7 @@ DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -724,7 +724,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -745,7 +745,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -762,7 +762,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -776,7 +776,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/v4.1.2/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -796,7 +796,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -817,7 +817,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:rename`
|
||||
|
||||
@@ -838,7 +838,7 @@ DESCRIPTION
|
||||
rename a registered device
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -852,7 +852,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -866,7 +866,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -954,7 +954,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -971,7 +971,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/v4.1.2/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -988,7 +988,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/v4.1.2/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -1002,7 +1002,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1016,7 +1016,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1038,7 +1038,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -1061,7 +1061,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/v4.1.2/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -1079,7 +1079,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1093,7 +1093,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1113,7 +1113,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/v4.1.2/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1143,7 +1143,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1152,8 +1152,8 @@ publish an update group
|
||||
```
|
||||
USAGE
|
||||
$ eas update [--branch <value>] [--channel <value>] [-m <value>] [--republish | --input-dir <value> |
|
||||
--skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--auto] [--private-key-path <value>]
|
||||
[--json --non-interactive]
|
||||
--skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--dev] [--auto] [--private-key-path
|
||||
<value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-m, --message=<value> A short message describing the update
|
||||
@@ -1162,6 +1162,7 @@ FLAGS
|
||||
--branch=<value> Branch to publish the update group on
|
||||
--channel=<value> Channel that the published update should affect
|
||||
--clear-cache Clear the bundler cache before publishing
|
||||
--dev Publish a development bundle
|
||||
--group=<value> Update group to republish (deprecated, see republish command)
|
||||
--input-dir=<value> [default: dist] Location of the bundle
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
@@ -1176,7 +1177,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1194,7 +1195,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1215,7 +1216,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1237,7 +1238,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1264,7 +1265,7 @@ DESCRIPTION
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1284,7 +1285,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1305,7 +1306,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1325,7 +1326,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1343,7 +1344,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1365,7 +1366,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1382,7 +1383,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v4.1.2/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v5.4.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
Generated
+1708
-129
File diff suppressed because it is too large.
Load diff
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "4.1.2",
|
||||
"version": "5.4.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "1.3.1",
|
||||
"@expo/apple-utils": "1.3.2",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "8.1.2",
|
||||
"@expo/config-plugins": "7.2.4",
|
||||
"@expo/config-types": "49.0.0",
|
||||
"@expo/eas-build-job": "1.0.30",
|
||||
"@expo/eas-json": "4.1.0",
|
||||
"@expo/eas-build-job": "1.0.43",
|
||||
"@expo/eas-json": "5.3.1",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.0.33",
|
||||
@@ -73,7 +73,7 @@
|
||||
"prompts": "2.4.2",
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.3.8",
|
||||
"semver": "7.5.2",
|
||||
"slash": "3.0.0",
|
||||
"tar": "6.1.13",
|
||||
"terminal-link": "2.1.1",
|
||||
|
||||
@@ -66,6 +66,8 @@ export async function prepareJobAsync(
|
||||
builderEnvironment: {
|
||||
image: buildProfile.image,
|
||||
node: buildProfile.node,
|
||||
pnpm: buildProfile.pnpm,
|
||||
bun: buildProfile.bun,
|
||||
yarn: buildProfile.yarn,
|
||||
ndk: buildProfile.ndk,
|
||||
expoCli: buildProfile.expoCli,
|
||||
|
||||
@@ -55,4 +55,5 @@ export interface BuildContext<T extends Platform> {
|
||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
||||
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
||||
developmentClient: boolean;
|
||||
}
|
||||
@@ -60,6 +60,12 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
const accountId = account.id;
|
||||
const runFromCI = getenv.boolish('CI', false);
|
||||
const developmentClient =
|
||||
buildProfile.developmentClient ??
|
||||
(platform === Platform.ANDROID
|
||||
? (buildProfile as BuildProfile<Platform.ANDROID>)?.gradleCommand === ':app:assembleDebug'
|
||||
: (buildProfile as BuildProfile<Platform.IOS>)?.buildConfiguration === 'Debug') ??
|
||||
false;
|
||||
|
||||
const credentialsCtx = new CredentialsContext({
|
||||
projectInfo: { exp, projectId },
|
||||
@@ -120,6 +126,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
message,
|
||||
runFromCI,
|
||||
customBuildConfigMetadata,
|
||||
developmentClient,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||
|
||||
@@ -58,6 +58,8 @@ export async function prepareJobAsync(
|
||||
builderEnvironment: {
|
||||
image: buildProfile.image,
|
||||
node: buildProfile.node,
|
||||
pnpm: buildProfile.pnpm,
|
||||
bun: buildProfile.bun,
|
||||
yarn: buildProfile.yarn,
|
||||
bundler: buildProfile.bundler,
|
||||
cocoapods: buildProfile.cocoapods,
|
||||
|
||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.38';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.44';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,7 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
runFromCI: ctx.runFromCI,
|
||||
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
|
||||
developmentClient: ctx.developmentClient,
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
AppVersionSource,
|
||||
@@ -13,6 +14,7 @@ import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { Analytics } from '../analytics/AnalyticsManager';
|
||||
import { createAndLinkChannelAsync, doesChannelExistAsync } from '../channel/queries';
|
||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
@@ -38,9 +40,9 @@ import {
|
||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||
import {
|
||||
installExpoUpdatesAsync,
|
||||
isExpoUpdatesInstalledAsDevDependency,
|
||||
isExpoUpdatesInstalledOrAvailable,
|
||||
isUsingEASUpdate,
|
||||
validateAppVersionRuntimePolicySupportAsync,
|
||||
} from '../project/projectUtils';
|
||||
import {
|
||||
@@ -57,6 +59,7 @@ import {
|
||||
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
|
||||
} from '../submit/submit';
|
||||
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../update/configure';
|
||||
import { validateBuildProfileConfigMatchesProjectConfigAsync } from '../update/utils';
|
||||
import { Actor } from '../user/User';
|
||||
import { downloadAndMaybeExtractAppAsync } from '../utils/download';
|
||||
@@ -117,6 +120,7 @@ export async function runBuildAndSubmitAsync(
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
Log.log(
|
||||
`Loaded "env" configuration for the "${buildProfiles[0].profileName}" profile: ${
|
||||
@@ -183,6 +187,7 @@ export async function runBuildAndSubmitAsync(
|
||||
platforms,
|
||||
profileName: flags.submitProfile,
|
||||
type: 'submit',
|
||||
projectDir,
|
||||
});
|
||||
for (const startedBuild of startedBuilds) {
|
||||
const submitProfile = nullthrows(
|
||||
@@ -320,11 +325,26 @@ async function prepareAndStartBuildAsync({
|
||||
);
|
||||
if (buildProfile.profile.channel) {
|
||||
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
graphqlClient,
|
||||
exp: buildCtx.exp,
|
||||
projectId: buildCtx.projectId,
|
||||
projectDir,
|
||||
sdkVersion: buildCtx.exp.sdkVersion,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
buildProfile,
|
||||
});
|
||||
if (isUsingEASUpdate(buildCtx.exp, buildCtx.projectId)) {
|
||||
const doesChannelExist = await doesChannelExistAsync(graphqlClient, {
|
||||
appId: buildCtx.projectId,
|
||||
channelName: buildProfile.profile.channel,
|
||||
});
|
||||
if (!doesChannelExist) {
|
||||
await createAndLinkChannelAsync(graphqlClient, {
|
||||
appId: buildCtx.projectId,
|
||||
channelName: buildProfile.profile.channel,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
||||
@@ -447,11 +467,17 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
|
||||
}
|
||||
|
||||
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
exp,
|
||||
graphqlClient,
|
||||
projectId,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
nonInteractive,
|
||||
sdkVersion,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
exp: ExpoConfig;
|
||||
projectId: string;
|
||||
projectDir: string;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
nonInteractive: boolean;
|
||||
@@ -467,18 +493,24 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
);
|
||||
} else if (nonInteractive) {
|
||||
Log.warn(
|
||||
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates".`
|
||||
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates" followed by "eas update:configure".`
|
||||
);
|
||||
} else {
|
||||
Log.warn(
|
||||
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package.`
|
||||
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package and run "eas update:configure".`
|
||||
);
|
||||
const installExpoUpdates = await confirmAsync({
|
||||
message: `Would you like to install the "expo-updates" package?`,
|
||||
message: `Would you like to install the "expo-updates" package and configure EAS Update now?`,
|
||||
});
|
||||
if (installExpoUpdates) {
|
||||
await installExpoUpdatesAsync(projectDir, { silent: false });
|
||||
Log.withTick('Installed expo-updates');
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp,
|
||||
projectId,
|
||||
projectDir,
|
||||
platform: RequestedPlatform.All,
|
||||
});
|
||||
Log.withTick('Installed expo-updates and configured EAS Update.');
|
||||
throw new Error('Command must be re-run to pick up new updates configuration.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
assertVersion,
|
||||
getAlwaysTrueBranchMapping,
|
||||
getBranchMapping,
|
||||
getEmptyBranchMapping,
|
||||
getStandardBranchId,
|
||||
isAlwaysTrueBranchMapping,
|
||||
isEmptyBranchMapping,
|
||||
@@ -24,6 +25,12 @@ describe(assertVersion, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(getEmptyBranchMapping, () => {
|
||||
it('gets an empty branch mapping', () => {
|
||||
expect(isEmptyBranchMapping(getEmptyBranchMapping())).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(isEmptyBranchMapping, () => {
|
||||
it('detects empty branch mappings', () => {
|
||||
expect(isEmptyBranchMapping(emptyBranchMapping)).toBe(true);
|
||||
|
||||
@@ -124,6 +124,7 @@ export const testChannelObject: UpdateChannelObject = {
|
||||
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
|
||||
name: 'production',
|
||||
createdAt: '2023-05-16T21:26:16.946Z',
|
||||
updatedAt: '2023-05-16T21:26:16.946Z',
|
||||
branchMapping:
|
||||
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
|
||||
updateBranches: [testUpdateBranch1, testUpdateBranch2],
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { format } from '@expo/timeago.js';
|
||||
|
||||
import { getAlwaysTrueBranchMapping, getEmptyBranchMapping } from '../branch-mapping';
|
||||
import { getDescriptionByBranchId } from '../print-utils';
|
||||
import { testChannelObject, testUpdateBranch1, testUpdateBranch2 } from './fixtures';
|
||||
|
||||
describe(getDescriptionByBranchId, () => {
|
||||
it('should get descriptions for multiple branches with multiple update groups', () => {
|
||||
const descriptionByBranchId = getDescriptionByBranchId(testChannelObject);
|
||||
expect(Object.values(descriptionByBranchId)).toHaveLength(2);
|
||||
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({
|
||||
branch: 'wrong-channel',
|
||||
branchRolloutPercentage: 15,
|
||||
update: {
|
||||
codeSigningKey: undefined,
|
||||
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
|
||||
isRollBackToEmbedded: false,
|
||||
message: `"fix bug" (${format('2023-07-17T22:48:59.278Z', 'en_US')} by quintest113)`,
|
||||
platforms: 'android, ios',
|
||||
runtimeVersion: 'exposdk:48.0.0',
|
||||
},
|
||||
});
|
||||
|
||||
expect(descriptionByBranchId[testUpdateBranch2.id]).toEqual({
|
||||
branch: 'production',
|
||||
branchRolloutPercentage: 85,
|
||||
update: {
|
||||
codeSigningKey: undefined,
|
||||
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
|
||||
isRollBackToEmbedded: false,
|
||||
message: `"fix bug" (${format('2023-06-23T23:37:10.004Z', 'en_US')} by quintest113)`,
|
||||
platforms: 'android, ios',
|
||||
runtimeVersion: 'exposdk:48.0.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
it('should get descriptions for branches with no updates', () => {
|
||||
const noUpdatesBranch = { ...testUpdateBranch1, updateGroups: [] };
|
||||
const oneBranchChannel = {
|
||||
...testChannelObject,
|
||||
updateBranches: [noUpdatesBranch],
|
||||
branchMapping: JSON.stringify(getAlwaysTrueBranchMapping(noUpdatesBranch.id)),
|
||||
};
|
||||
const descriptionByBranchId = getDescriptionByBranchId(oneBranchChannel);
|
||||
expect(Object.values(descriptionByBranchId)).toHaveLength(1);
|
||||
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({ branch: 'wrong-channel' });
|
||||
});
|
||||
it('should get descriptions for no branches', () => {
|
||||
const noBranchChannel = {
|
||||
...testChannelObject,
|
||||
updateBranches: [],
|
||||
branchMapping: JSON.stringify(getEmptyBranchMapping()),
|
||||
};
|
||||
const descriptionByBranchId = getDescriptionByBranchId(noBranchChannel);
|
||||
expect(Object.values(descriptionByBranchId)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -50,6 +50,13 @@ export type EmptyBranchMapping = {
|
||||
data: [];
|
||||
};
|
||||
|
||||
export function getEmptyBranchMapping(): EmptyBranchMapping {
|
||||
return {
|
||||
version: 0,
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function getAlwaysTrueBranchMapping(branchId: string): AlwaysTrueBranchMapping {
|
||||
return {
|
||||
version: 0,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||
@@ -11,17 +10,35 @@ import {
|
||||
} from '../update/utils';
|
||||
import { assertVersion, hasEmptyBranchMap, hasStandardBranchMap } from './branch-mapping';
|
||||
|
||||
/**
|
||||
* Log all the branches associated with the channel and their most recent update group
|
||||
*/
|
||||
export function logChannelDetails(channel: UpdateChannelObject): void {
|
||||
assertVersion(channel, 0);
|
||||
assert(
|
||||
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel),
|
||||
'Only standard branch mappings and rollouts are supported.'
|
||||
);
|
||||
const doesSupportMapping =
|
||||
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel);
|
||||
if (!doesSupportMapping) {
|
||||
Log.log(chalk.dim('Custom branch mapping detected.'));
|
||||
return;
|
||||
}
|
||||
|
||||
const branchDescription = channel.updateBranches.flatMap(branch => {
|
||||
const updateGroupWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
|
||||
branch.updateGroups
|
||||
const branchDescriptionByBranchId = getDescriptionByBranchId(channel);
|
||||
const entries = Object.entries(branchDescriptionByBranchId);
|
||||
if (entries.length === 0) {
|
||||
Log.log(chalk.dim('No branches are pointed to this channel.'));
|
||||
} else {
|
||||
Log.log(
|
||||
entries
|
||||
.map(([_branchId, description]) => formatBranch(description))
|
||||
.join(`\n\n${chalk.dim('———')}\n\n`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDescriptionByBranchId(
|
||||
channel: UpdateChannelObject
|
||||
): Record<string, FormattedBranchDescription> {
|
||||
return channel.updateBranches.reduce((acc, branch) => {
|
||||
const maybeRollout = isRollout(channel) ? getRollout(channel) : null;
|
||||
let maybePercentOnBranch: number | undefined = undefined;
|
||||
if (maybeRollout) {
|
||||
@@ -31,22 +48,21 @@ export function logChannelDetails(channel: UpdateChannelObject): void {
|
||||
: 100 - maybeRollout.percentRolledOut;
|
||||
}
|
||||
|
||||
return updateGroupWithBranchDescriptions.map(
|
||||
({ branch, ...updateGroup }): FormattedBranchDescription => ({
|
||||
branch,
|
||||
branchRolloutPercentage: maybePercentOnBranch,
|
||||
update: updateGroup,
|
||||
})
|
||||
if (branch.updateGroups.length === 0) {
|
||||
acc[branch.id] = { branch: branch.name, branchRolloutPercentage: maybePercentOnBranch };
|
||||
return acc;
|
||||
}
|
||||
const updateGroupsWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
|
||||
branch.updateGroups
|
||||
);
|
||||
});
|
||||
|
||||
if (branchDescription.length === 0) {
|
||||
Log.log(chalk.dim('No branches are pointed to this channel.'));
|
||||
} else {
|
||||
Log.log(
|
||||
branchDescription
|
||||
.map(description => formatBranch(description))
|
||||
.join(`\n\n${chalk.dim('———')}\n\n`)
|
||||
);
|
||||
}
|
||||
// display the most recent update group
|
||||
const updateGroupWithBranchDescription = updateGroupsWithBranchDescriptions[0];
|
||||
const { branch: branchName, ...updateGroup } = updateGroupWithBranchDescription;
|
||||
acc[branch.id] = {
|
||||
branch: branchName,
|
||||
branchRolloutPercentage: maybePercentOnBranch,
|
||||
update: updateGroup,
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<string, FormattedBranchDescription>);
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import chalk from 'chalk';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { createUpdateBranchOnAppAsync } from '../branch/queries';
|
||||
import { BranchNotFoundError } from '../branch/utils';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { withErrorHandlingAsync } from '../graphql/client';
|
||||
@@ -15,13 +17,16 @@ import { BranchQuery, UpdateBranchOnChannelObject } from '../graphql/queries/Bra
|
||||
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
||||
import { UpdateChannelBasicInfoFragmentNode } from '../graphql/types/UpdateChannelBasicInfo';
|
||||
import Log from '../log';
|
||||
import { getDisplayNameForProjectIdAsync } from '../project/projectUtils';
|
||||
import formatFields from '../utils/formatFields';
|
||||
import { printJsonOnlyOutput } from '../utils/json';
|
||||
import {
|
||||
paginatedQueryWithConfirmPromptAsync,
|
||||
paginatedQueryWithSelectPromptAsync,
|
||||
} from '../utils/queries';
|
||||
import { ChannelNotFoundError } from './errors';
|
||||
import { logChannelDetails } from './print-utils';
|
||||
import { ChannelBasicInfo } from './utils';
|
||||
|
||||
export const CHANNELS_LIMIT = 25;
|
||||
|
||||
@@ -264,3 +269,101 @@ export async function ensureChannelExistsAsync(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function doesChannelExistAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, channelName }: { appId: string; channelName: string }
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId,
|
||||
channelName,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof ChannelNotFoundError) {
|
||||
return false;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Creates a channel and links it to a branch with the same name.
|
||||
*
|
||||
* @param appId the app ID, also known as the project ID
|
||||
* @param channelName the name of the channel to create
|
||||
* @param shouldPrintJson print only the JSON output
|
||||
*/
|
||||
export async function createAndLinkChannelAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
channelName,
|
||||
shouldPrintJson,
|
||||
}: { appId: string; channelName: string; shouldPrintJson?: boolean }
|
||||
): Promise<ChannelBasicInfo> {
|
||||
let branchId: string;
|
||||
let branchMessage: string;
|
||||
|
||||
try {
|
||||
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId,
|
||||
name: channelName,
|
||||
});
|
||||
branchId = branch.id;
|
||||
branchMessage = `We found a branch with the same name`;
|
||||
} catch (error) {
|
||||
if (error instanceof BranchNotFoundError) {
|
||||
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId,
|
||||
name: channelName,
|
||||
});
|
||||
branchId = newBranch.id;
|
||||
branchMessage = `We also went ahead and made a branch with the same name`;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
updateChannel: { createUpdateChannelForApp: newChannel },
|
||||
} = await createChannelOnAppAsync(graphqlClient, {
|
||||
appId,
|
||||
channelName,
|
||||
branchId,
|
||||
});
|
||||
|
||||
if (!newChannel) {
|
||||
throw new Error(
|
||||
`Could not create channel with name ${channelName} on project with id ${appId}`
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldPrintJson) {
|
||||
printJsonOnlyOutput(newChannel);
|
||||
} else {
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(
|
||||
`Created a new channel on project ${chalk.bold(
|
||||
await getDisplayNameForProjectIdAsync(graphqlClient, appId)
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: newChannel.name },
|
||||
{ label: 'ID', value: newChannel.id },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(`${branchMessage} and have pointed the channel at it.`);
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: newChannel.name },
|
||||
{ label: 'ID', value: branchId },
|
||||
])
|
||||
);
|
||||
}
|
||||
return newChannel;
|
||||
}
|
||||
@@ -68,6 +68,7 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
const results: { [key in Platform]?: string } = {};
|
||||
for (const { profile, platform } of buildProfiles) {
|
||||
|
||||
@@ -80,6 +80,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
easJsonAccessor,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
for (const profileInfo of buildProfiles) {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { createUpdateBranchOnAppAsync } from '../../branch/queries';
|
||||
import { BranchNotFoundError } from '../../branch/utils';
|
||||
import { createChannelOnAppAsync } from '../../channel/queries';
|
||||
import { createAndLinkChannelAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { BranchQuery } from '../../graphql/queries/BranchQuery';
|
||||
import Log from '../../log';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
export default class ChannelCreate extends EasCommand {
|
||||
static override description = 'create a channel';
|
||||
@@ -60,69 +55,13 @@ export default class ChannelCreate extends EasCommand {
|
||||
}));
|
||||
}
|
||||
|
||||
let branchId: string;
|
||||
let branchMessage: string;
|
||||
|
||||
try {
|
||||
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: channelName,
|
||||
});
|
||||
branchId = branch.id;
|
||||
branchMessage = `We found a branch with the same name`;
|
||||
} catch (error) {
|
||||
if (error instanceof BranchNotFoundError) {
|
||||
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: channelName,
|
||||
});
|
||||
branchId = newBranch.id;
|
||||
branchMessage = `We also went ahead and made a branch with the same name`;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
updateChannel: { createUpdateChannelForApp: newChannel },
|
||||
} = await createChannelOnAppAsync(graphqlClient, {
|
||||
await createAndLinkChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName,
|
||||
branchId,
|
||||
shouldPrintJson: jsonFlag,
|
||||
});
|
||||
|
||||
if (!newChannel) {
|
||||
throw new Error(
|
||||
`Could not create channel with name ${channelName} on project with id ${projectId}`
|
||||
);
|
||||
}
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(newChannel);
|
||||
} else {
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(
|
||||
`Created a new channel on project ${chalk.bold(
|
||||
await getDisplayNameForProjectIdAsync(graphqlClient, projectId)
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: newChannel.name },
|
||||
{ label: 'ID', value: newChannel.id },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(`${branchMessage} and have pointed the channel at it.`);
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: newChannel.name },
|
||||
{ label: 'ID', value: branchId },
|
||||
])
|
||||
);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('You can now update your app by publishing!'));
|
||||
}
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('You can now update your app by publishing!'));
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,13 @@ import {
|
||||
RolloutActions,
|
||||
RolloutMainMenu,
|
||||
} from '../../rollout/actions/RolloutMainMenu';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
enum ActionRawFlagValue {
|
||||
CREATE = 'create',
|
||||
EDIT = 'edit',
|
||||
END = 'end',
|
||||
VIEW = 'view',
|
||||
}
|
||||
type ChannelRolloutRawArgsAndFlags = {
|
||||
channel?: string;
|
||||
@@ -137,8 +139,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
nonInteractive: argsAndFlags.nonInteractive,
|
||||
});
|
||||
if (argsAndFlags.json) {
|
||||
// TODO(quin): implement json output
|
||||
throw new Error('Developer Preview doesnt support JSON output yet');
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const app = { projectId, exp };
|
||||
@@ -168,6 +169,8 @@ export default class ChannelRollout extends EasCommand {
|
||||
return ManageRolloutActions.EDIT;
|
||||
case ActionRawFlagValue.END:
|
||||
return ManageRolloutActions.END;
|
||||
case ActionRawFlagValue.VIEW:
|
||||
return ManageRolloutActions.VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class MetadataLint extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -49,6 +49,7 @@ export default class MetadataPull extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -47,6 +47,7 @@ export default class MetadataPush extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms: [Platform.IOS],
|
||||
profileName: flags.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
if (submitProfiles.length !== 1) {
|
||||
|
||||
@@ -118,6 +118,7 @@ export default class Submit extends EasCommand {
|
||||
easJsonAccessor: EasJsonAccessor.fromProjectPath(projectDir),
|
||||
platforms,
|
||||
profileName: flagsWithPlatform.profile,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
const submissions: SubmissionFragment[] = [];
|
||||
|
||||
@@ -57,12 +57,6 @@ jest.mock('../../../fetch');
|
||||
describe(UpdateRepublish.name, () => {
|
||||
afterEach(() => vol.reset());
|
||||
|
||||
it('errors without --channel, --branch, or --group', async () => {
|
||||
await expect(new UpdateRepublish([], commandOptions).run()).rejects.toThrow(
|
||||
'--channel, --branch, or --group must be specified'
|
||||
);
|
||||
});
|
||||
|
||||
it('errors when providing both --group and --branch', async () => {
|
||||
const flags = ['--group=1234', '--branch=main'];
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
getRuntimeVersionObjectAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
platformDisplayNames,
|
||||
resolveInputDirectoryAsync,
|
||||
uploadAssetsAsync,
|
||||
} from '../../project/publish';
|
||||
@@ -55,6 +56,7 @@ import { getVcsClient } from '../../vcs';
|
||||
|
||||
type RawUpdateFlags = {
|
||||
auto: boolean;
|
||||
dev: boolean;
|
||||
branch?: string;
|
||||
channel?: string;
|
||||
message?: string;
|
||||
@@ -73,6 +75,7 @@ type RawUpdateFlags = {
|
||||
|
||||
type UpdateFlags = {
|
||||
auto: boolean;
|
||||
dev: boolean;
|
||||
platform: ExpoCLIExportPlatformFlag;
|
||||
branchName?: string;
|
||||
channelName?: string;
|
||||
@@ -133,6 +136,10 @@ export default class UpdatePublish extends EasCommand {
|
||||
default: 'all',
|
||||
required: false,
|
||||
}),
|
||||
dev: Flags.boolean({
|
||||
description: 'Publish a development bundle',
|
||||
default: false,
|
||||
}),
|
||||
auto: Flags.boolean({
|
||||
description:
|
||||
'Use the current git branch and commit message for the EAS branch and update message',
|
||||
@@ -157,6 +164,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
auto: autoFlag,
|
||||
platform: platformFlag,
|
||||
channelName: channelNameArg,
|
||||
dev,
|
||||
updateMessage: updateMessageArg,
|
||||
inputDir,
|
||||
skipBundler,
|
||||
@@ -219,7 +227,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
if (!skipBundler) {
|
||||
const bundleSpinner = ora().start('Exporting...');
|
||||
try {
|
||||
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag, clearCache });
|
||||
await buildBundlesAsync({ projectDir, inputDir, dev, exp, platformFlag, clearCache });
|
||||
bundleSpinner.succeed('Exported bundle(s)');
|
||||
} catch (e) {
|
||||
bundleSpinner.fail('Export failed');
|
||||
@@ -325,6 +333,21 @@ export default class UpdatePublish extends EasCommand {
|
||||
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
|
||||
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
|
||||
}
|
||||
|
||||
const platformString = (Object.keys(assets) as PublishPlatform[])
|
||||
.map(platform => {
|
||||
const collectedAssetForPlatform = nullthrows(assets[platform]);
|
||||
const totalAssetsForPlatform = collectedAssetForPlatform.assets.length + 1; // launch asset
|
||||
const assetString = totalAssetsForPlatform === 1 ? 'asset' : 'assets';
|
||||
return `${totalAssetsForPlatform} ${platformDisplayNames[platform]} ${assetString}`;
|
||||
})
|
||||
.join(', ');
|
||||
Log.withInfo(
|
||||
`${platformString} (maximum: ${assetLimitPerUpdateGroup} total per update). ${learnMore(
|
||||
'https://expo.fyi/eas-update-asset-limits',
|
||||
{ learnMoreMessage: 'Learn more about asset limits.' }
|
||||
)}`
|
||||
);
|
||||
} catch (e) {
|
||||
assetSpinner.fail('Failed to upload');
|
||||
throw e;
|
||||
@@ -344,10 +367,11 @@ export default class UpdatePublish extends EasCommand {
|
||||
branchId,
|
||||
channelName: branchName,
|
||||
});
|
||||
Log.withTick(
|
||||
`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`
|
||||
);
|
||||
}
|
||||
|
||||
Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`);
|
||||
|
||||
const vcsClient = getVcsClient();
|
||||
|
||||
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
||||
@@ -507,7 +531,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
|
||||
const nonInteractive = flags['non-interactive'] ?? false;
|
||||
|
||||
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
|
||||
const { auto, branch: branchName, channel: channelName, dev, 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',
|
||||
@@ -536,6 +560,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
auto,
|
||||
branchName,
|
||||
channelName,
|
||||
dev,
|
||||
updateMessage,
|
||||
inputDir: flags['input-dir'],
|
||||
skipBundler: flags['skip-bundler'],
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Platform } from '@expo/config';
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
@@ -151,9 +153,6 @@ export default class UpdateRepublish extends EasCommand {
|
||||
if (nonInteractive && !groupId) {
|
||||
throw new Error('Only --group can be used in non-interactive mode');
|
||||
}
|
||||
if (!groupId && !(branchName || channelName)) {
|
||||
throw new Error(`--channel, --branch, or --group must be specified`);
|
||||
}
|
||||
|
||||
const platform =
|
||||
rawFlags.platform === 'all' ? defaultRepublishPlatforms : ([rawFlags.platform] as Platform[]);
|
||||
@@ -190,6 +189,10 @@ async function getOrAskUpdatesAsync(
|
||||
}));
|
||||
}
|
||||
|
||||
if (flags.nonInteractive) {
|
||||
throw new Error('Must supply --group when in non-interactive mode');
|
||||
}
|
||||
|
||||
if (flags.branchName) {
|
||||
return await askUpdatesFromBranchNameAsync(graphqlClient, {
|
||||
...flags,
|
||||
@@ -206,7 +209,55 @@ async function getOrAskUpdatesAsync(
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error('--channel, --branch, or --group is required');
|
||||
const { choice } = await promptAsync({
|
||||
type: 'select',
|
||||
message: 'Find update by branch or channel?',
|
||||
name: 'choice',
|
||||
choices: [
|
||||
{ title: 'Branch', value: 'branch' },
|
||||
{ title: 'Channel', value: 'channel' },
|
||||
],
|
||||
});
|
||||
|
||||
if (choice === 'channel') {
|
||||
const { name } = await selectChannelOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
selectionPromptTitle: 'Select a channel to view',
|
||||
paginatedQueryOptions: {
|
||||
json: flags.json,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
|
||||
return await askUpdatesFromChannelNameAsync(graphqlClient, {
|
||||
...flags,
|
||||
channelName: name,
|
||||
projectId,
|
||||
});
|
||||
} else if (choice === 'branch') {
|
||||
const { name } = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: 'Select branch from which to choose update',
|
||||
displayTextForListItem: updateBranch => ({
|
||||
title: updateBranch.name,
|
||||
}),
|
||||
// discard limit and offset because this query is not their intended target
|
||||
paginatedQueryOptions: {
|
||||
json: flags.json,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
|
||||
return await askUpdatesFromBranchNameAsync(graphqlClient, {
|
||||
...flags,
|
||||
branchName: name,
|
||||
projectId,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Must choose update via channel or branch');
|
||||
}
|
||||
}
|
||||
|
||||
/** Ask the user which update needs to be republished by branch name, this requires interactive mode */
|
||||
@@ -219,10 +270,6 @@ async function askUpdatesFromBranchNameAsync(
|
||||
nonInteractive,
|
||||
}: { projectId: string; branchName: string; json: boolean; nonInteractive: boolean }
|
||||
): Promise<UpdateToRepublish[]> {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply --group when in non-interactive mode');
|
||||
}
|
||||
|
||||
const updateGroup = await selectUpdateGroupOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName,
|
||||
@@ -246,10 +293,6 @@ async function askUpdatesFromChannelNameAsync(
|
||||
nonInteractive,
|
||||
}: { projectId: string; channelName: string; json: boolean; nonInteractive: boolean }
|
||||
): Promise<UpdateToRepublish[]> {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Must supply --group when in non-interactive mode');
|
||||
}
|
||||
|
||||
const branchName = await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelName);
|
||||
|
||||
return await askUpdatesFromBranchNameAsync(graphqlClient, {
|
||||
|
||||
@@ -86,7 +86,11 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
}),
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: [...defaultPublishPlatforms, 'all'],
|
||||
options: [
|
||||
// TODO: Add web when it's fully supported
|
||||
...defaultPublishPlatforms,
|
||||
'all',
|
||||
],
|
||||
default: 'all',
|
||||
required: false,
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import UpdateRepublish from './republish';
|
||||
import UpdateRollBackToEmbedded from './roll-back-to-embedded';
|
||||
|
||||
export default class UpdateRollback extends EasCommand {
|
||||
static override description = 'roll back to an embedded update or an existing update';
|
||||
|
||||
static override hidden = true;
|
||||
|
||||
static override flags = {
|
||||
'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,
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(UpdateRollback);
|
||||
|
||||
const { choice } = await promptAsync({
|
||||
type: 'select',
|
||||
message: 'Which type of update would you like to roll back to?',
|
||||
name: 'choice',
|
||||
choices: [
|
||||
{ title: 'Published Update', value: 'published' },
|
||||
{ title: 'Embedded Update', value: 'embedded' },
|
||||
],
|
||||
});
|
||||
|
||||
const privateKeyPathArg = flags['private-key-path']
|
||||
? ['--private-key-path', flags['private-key-path']]
|
||||
: [];
|
||||
if (choice === 'published') {
|
||||
await UpdateRepublish.run(privateKeyPathArg);
|
||||
} else {
|
||||
await UpdateRollBackToEmbedded.run(privateKeyPathArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '../../../project/projectUtils';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { AndroidPackageNotDefinedError } from '../../errors';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
|
||||
/**
|
||||
@@ -111,7 +112,7 @@ export async function getAppLookupParamsFromContextAsync(
|
||||
gradleContext
|
||||
);
|
||||
if (!androidApplicationIdentifier) {
|
||||
throw new Error(
|
||||
throw new AndroidPackageNotDefinedError(
|
||||
`android.package needs to be defined in your ${getProjectConfigDescription(
|
||||
ctx.projectDir
|
||||
)} file`
|
||||
|
||||
@@ -15,3 +15,9 @@ export class UnsupportedCredentialsChoiceError extends Error {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export class AndroidPackageNotDefinedError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message ?? 'android.package needs to be defined in your app.json/app.config.js file');
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,23 @@ export class SetUpAdhocProvisioningProfile {
|
||||
);
|
||||
}
|
||||
|
||||
// 6. Create (or update) app build credentials
|
||||
// 6. Compare selected devices with the ones actually provisioned
|
||||
const diffList = differenceBy(
|
||||
chosenDevices,
|
||||
appleProvisioningProfile.appleDevices,
|
||||
'identifier'
|
||||
);
|
||||
if (diffList && diffList.length > 0) {
|
||||
Log.warn(`Failed to provision ${diffList.length} of the selected devices:`);
|
||||
for (const missingDevice of diffList) {
|
||||
Log.warn(`- ${formatDeviceLabel(missingDevice)}`);
|
||||
}
|
||||
Log.log(
|
||||
'Most commonly devices fail to to be provisioned while they are still being processed by Apple, which can take up to 24-72 hours. Check your Apple Developer Portal page at https://developer.apple.com/account/resources/devices/list, the devices in "Processing" status cannot be provisioned yet'
|
||||
);
|
||||
}
|
||||
|
||||
// 7. Create (or update) app build credentials
|
||||
assert(appleProvisioningProfile);
|
||||
return await assignBuildCredentialsAsync(
|
||||
ctx,
|
||||
|
||||
+136
-1
@@ -1,4 +1,38 @@
|
||||
import { doUDIDsMatch } from '../SetUpAdhocProvisioningProfile';
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
import { EasJson } from '@expo/eas-json';
|
||||
|
||||
import { Analytics } from '../../../../analytics/AnalyticsManager';
|
||||
import { ExpoGraphqlClient } from '../../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
Account,
|
||||
AppleAppIdentifierFragment,
|
||||
AppleDevice,
|
||||
AppleDeviceClass,
|
||||
AppleDeviceFragment,
|
||||
AppleDistributionCertificateFragment,
|
||||
IosAppBuildCredentialsFragment,
|
||||
} from '../../../../graphql/generated';
|
||||
import Log from '../../../../log';
|
||||
import { getApplePlatformFromTarget } from '../../../../project/ios/target';
|
||||
import { Actor } from '../../../../user/User';
|
||||
import { CredentialsContext, CredentialsContextProjectInfo } from '../../../context';
|
||||
import { ProvisioningProfile } from '../../appstore/Credentials.types';
|
||||
import { ApplePlatform } from '../../appstore/constants';
|
||||
import { assignBuildCredentialsAsync, getBuildCredentialsAsync } from '../BuildCredentialsUtils';
|
||||
import { chooseDevicesAsync } from '../DeviceUtils';
|
||||
import { SetUpAdhocProvisioningProfile, doUDIDsMatch } from '../SetUpAdhocProvisioningProfile';
|
||||
|
||||
jest.mock('../BuildCredentialsUtils');
|
||||
jest.mock('../../../context');
|
||||
jest.mock('../../../ios/api/GraphqlClient');
|
||||
jest.mock('../DeviceUtils', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
chooseDevicesAsync: jest.fn(),
|
||||
formatDeviceLabel: jest.requireActual('../DeviceUtils').formatDeviceLabel,
|
||||
};
|
||||
});
|
||||
jest.mock('../../../../project/ios/target');
|
||||
|
||||
describe(doUDIDsMatch, () => {
|
||||
it('return false if UDIDs do not match', () => {
|
||||
@@ -12,3 +46,104 @@ describe(doUDIDsMatch, () => {
|
||||
expect(doUDIDsMatch(udidsA, udidsB)).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('runWithDistributionCertificateAsync', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
const setUpAdhocProvisioningProfile = new SetUpAdhocProvisioningProfile({
|
||||
app: { account: {} as Account, projectName: 'projName', bundleIdentifier: 'bundleId' },
|
||||
target: { targetName: 'targetName', bundleIdentifier: 'bundleId', entitlements: {} },
|
||||
});
|
||||
describe('compare chosen and provisioned devices', () => {
|
||||
describe('not all devices provisioned', () => {
|
||||
it('displays warning to the user and lists the missing devices', async () => {
|
||||
const { ctx, distCert } = setUpTest();
|
||||
jest.mocked(getBuildCredentialsAsync).mockResolvedValue({
|
||||
provisioningProfile: {
|
||||
appleTeam: {},
|
||||
appleDevices: [{ identifier: 'id1' }],
|
||||
developerPortalIdentifier: 'provisioningProfileId',
|
||||
},
|
||||
} as IosAppBuildCredentialsFragment);
|
||||
const LogWarnSpy = jest.spyOn(Log, 'warn');
|
||||
const LogLogSpy = jest.spyOn(Log, 'log');
|
||||
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
|
||||
ctx,
|
||||
distCert
|
||||
);
|
||||
expect(result).toEqual({} as IosAppBuildCredentialsFragment);
|
||||
expect(LogWarnSpy).toHaveBeenCalledTimes(3);
|
||||
expect(LogWarnSpy).toHaveBeenCalledWith('Failed to provision 2 of the selected devices:');
|
||||
expect(LogWarnSpy).toHaveBeenCalledWith('- id2 (iPhone) (Device 2)');
|
||||
expect(LogWarnSpy).toHaveBeenCalledWith('- id3 (Mac) (Device 3)');
|
||||
expect(LogLogSpy).toHaveBeenCalledTimes(1);
|
||||
expect(LogLogSpy).toHaveBeenCalledWith(
|
||||
'Most commonly devices fail to to be provisioned while they are still being processed by Apple, which can take up to 24-72 hours. Check your Apple Developer Portal page at https://developer.apple.com/account/resources/devices/list, the devices in "Processing" status cannot be provisioned yet'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('all devices provisioned', () => {
|
||||
it('does not display warning', async () => {
|
||||
const { ctx, distCert } = setUpTest();
|
||||
jest.mocked(getBuildCredentialsAsync).mockResolvedValue({
|
||||
provisioningProfile: {
|
||||
appleTeam: {},
|
||||
appleDevices: [{ identifier: 'id1' }, { identifier: 'id2' }, { identifier: 'id3' }],
|
||||
developerPortalIdentifier: 'provisioningProfileId',
|
||||
},
|
||||
} as IosAppBuildCredentialsFragment);
|
||||
const LogWarnSpy = jest.spyOn(Log, 'warn');
|
||||
const LogLogSpy = jest.spyOn(Log, 'log');
|
||||
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
|
||||
ctx,
|
||||
distCert
|
||||
);
|
||||
expect(result).toEqual({} as IosAppBuildCredentialsFragment);
|
||||
expect(LogWarnSpy).not.toHaveBeenCalled();
|
||||
expect(LogLogSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setUpTest(): { ctx: CredentialsContext; distCert: AppleDistributionCertificateFragment } {
|
||||
const ctx = jest.mocked(
|
||||
new CredentialsContext(
|
||||
{} as {
|
||||
projectInfo: CredentialsContextProjectInfo | null;
|
||||
easJsonCliConfig?: EasJson['cli'];
|
||||
nonInteractive: boolean;
|
||||
projectDir: string;
|
||||
user: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
analytics: Analytics;
|
||||
env?: Env;
|
||||
}
|
||||
)
|
||||
);
|
||||
Object.defineProperty(ctx, 'ios', { value: jest.mock('../../../ios/api/GraphqlClient') });
|
||||
ctx.ios.getDevicesForAppleTeamAsync = jest
|
||||
.fn()
|
||||
.mockResolvedValue([
|
||||
{ identifier: 'id1' },
|
||||
{ identifier: 'id2' },
|
||||
{ identifier: 'id3' },
|
||||
] as AppleDeviceFragment[]);
|
||||
jest.mocked(chooseDevicesAsync).mockResolvedValue([
|
||||
{ identifier: 'id1', name: 'Device 1', deviceClass: AppleDeviceClass.Ipad },
|
||||
{ identifier: 'id2', name: 'Device 2', deviceClass: AppleDeviceClass.Iphone },
|
||||
{ identifier: 'id3', name: 'Device 3', deviceClass: AppleDeviceClass.Mac },
|
||||
] as AppleDevice[]);
|
||||
// @ts-ignore
|
||||
jest.mocked(getApplePlatformFromTarget).mockResolvedValue(ApplePlatform.IOS);
|
||||
Object.defineProperty(ctx, 'appStore', { value: jest.mock('../../appstore/AppStoreApi') });
|
||||
ctx.appStore.createOrReuseAdhocProvisioningProfileAsync = jest.fn().mockResolvedValue({
|
||||
provisioningProfileId: 'provisioningProfileId',
|
||||
} as ProvisioningProfile);
|
||||
ctx.ios.createOrGetExistingAppleAppIdentifierAsync = jest
|
||||
.fn()
|
||||
.mockResolvedValue({} as AppleAppIdentifierFragment);
|
||||
jest.mocked(assignBuildCredentialsAsync).mockResolvedValue({} as IosAppBuildCredentialsFragment);
|
||||
const distCert = {} as AppleDistributionCertificateFragment;
|
||||
return { ctx, distCert };
|
||||
}
|
||||
@@ -733,6 +733,13 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.proximity-reader.payment.acceptance',
|
||||
name: 'Tap to Pay on iPhone',
|
||||
capability: CapabilityType.TAP_TO_PAY_ON_IPHONE,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.matter.allow-setup-payload',
|
||||
name: 'Matter Allow Setup Payload',
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
displayEmptyAndroidCredentials,
|
||||
} from '../android/utils/printCredentials';
|
||||
import { CredentialsContext, CredentialsContextProjectInfo } from '../context';
|
||||
import { AndroidPackageNotDefinedError } from '../errors';
|
||||
import { ActionInfo, AndroidActionType, Scope } from './Actions';
|
||||
import {
|
||||
buildCredentialsActions,
|
||||
@@ -131,6 +132,12 @@ export class ManageAndroid {
|
||||
|
||||
await this.runProjectSpecificActionAsync(ctx, chosenAction, gradleContext);
|
||||
} catch (err) {
|
||||
if (err instanceof AndroidPackageNotDefinedError) {
|
||||
err.message += `\n${learnMore(
|
||||
'https://docs.expo.dev/workflow/configuration/'
|
||||
)} about configuration with app.json/app.config.js`;
|
||||
throw err; // breaks out of the loop to avoid failing again after continuation
|
||||
}
|
||||
Log.error(err);
|
||||
if (process.env.DEBUG) {
|
||||
throw err; // breaks out of the loop so we can get stack trace
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonUtils } from '@expo/eas-json';
|
||||
|
||||
import { Analytics } from '../../../analytics/AnalyticsManager';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { learnMore } from '../../../log';
|
||||
import { getProjectConfigDescription } from '../../../project/projectUtils';
|
||||
import { pressAnyKeyToContinueAsync } from '../../../prompts';
|
||||
import { Actor } from '../../../user/User';
|
||||
import { getAppLookupParamsFromContextAsync } from '../../android/actions/BuildCredentialsUtils';
|
||||
import { CredentialsContextProjectInfo } from '../../context';
|
||||
import { AndroidPackageNotDefinedError } from '../../errors';
|
||||
import { Action } from '../HelperActions';
|
||||
import { ManageAndroid } from '../ManageAndroid';
|
||||
|
||||
jest.mock('../../android/actions/BuildCredentialsUtils');
|
||||
jest.mock('../../../prompts', () => {
|
||||
return {
|
||||
...jest.requireActual('../../../prompts'),
|
||||
pressAnyKeyToContinueAsync: jest.fn(),
|
||||
};
|
||||
});
|
||||
jest.mock('../../../project/projectUtils');
|
||||
|
||||
describe('runAsync', () => {
|
||||
describe('"android.package" missing in app.json', () => {
|
||||
const manageAndroid = new ManageAndroid(
|
||||
{
|
||||
projectInfo: {} as CredentialsContextProjectInfo,
|
||||
actor: {} as Actor,
|
||||
graphqlClient: {} as ExpoGraphqlClient,
|
||||
analytics: {} as Analytics,
|
||||
getDynamicPrivateProjectConfigAsync: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ exp: {}, projectId: '' }),
|
||||
runAsync: jest.fn(),
|
||||
} as Action,
|
||||
''
|
||||
);
|
||||
it('does not repeat the error indefinitely and prints useful error', async () => {
|
||||
jest.spyOn(EasJsonUtils, 'getBuildProfileNamesAsync').mockResolvedValue(['testProfile']);
|
||||
jest
|
||||
.spyOn(EasJsonUtils, 'getBuildProfileAsync')
|
||||
.mockResolvedValue({} as BuildProfile<Platform.ANDROID>);
|
||||
jest.mocked(getProjectConfigDescription).mockReturnValue('app.json');
|
||||
jest.mocked(getAppLookupParamsFromContextAsync).mockImplementation(() => {
|
||||
throw new AndroidPackageNotDefinedError(
|
||||
'Specify "android.package" in app.json and run this command again.'
|
||||
);
|
||||
});
|
||||
const pressAnyKeyToContinueAsyncMock = jest.mocked(pressAnyKeyToContinueAsync);
|
||||
Array.from(Array(100)).map((_, i) => {
|
||||
// continue 101 times if error is rethrown indefinitely
|
||||
pressAnyKeyToContinueAsyncMock.mockResolvedValueOnce();
|
||||
});
|
||||
pressAnyKeyToContinueAsyncMock.mockImplementationOnce(async () => {
|
||||
// fail after 102nd time
|
||||
fail('test should not reach this place - if it does, the error repeats indefinitely');
|
||||
});
|
||||
const reThrownError = new AndroidPackageNotDefinedError(
|
||||
'Specify "android.package" in app.json and run this command again.\n' +
|
||||
`${learnMore(
|
||||
'https://docs.expo.dev/workflow/configuration/'
|
||||
)} about configuration with app.json/app.config.js`
|
||||
);
|
||||
await expect(manageAndroid.runAsync()).rejects.toThrow(reThrownError);
|
||||
});
|
||||
});
|
||||
});
|
||||
+274
-21
@@ -479,17 +479,11 @@ export type AccountSsoConfiguration = {
|
||||
};
|
||||
|
||||
export type AccountSsoConfigurationData = {
|
||||
authEndpoint?: InputMaybe<Scalars['String']>;
|
||||
authProtocol: AuthProtocolType;
|
||||
authProviderIdentifier: Scalars['String'];
|
||||
clientIdentifier: Scalars['String'];
|
||||
clientSecret: Scalars['String'];
|
||||
endSessionEndpoint?: InputMaybe<Scalars['String']>;
|
||||
issuer: Scalars['String'];
|
||||
jwksEndpoint?: InputMaybe<Scalars['String']>;
|
||||
revokeEndpoint?: InputMaybe<Scalars['String']>;
|
||||
tokenEndpoint?: InputMaybe<Scalars['String']>;
|
||||
userInfoEndpoint?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AccountSsoConfigurationMutation = {
|
||||
@@ -602,6 +596,8 @@ export type Actor = {
|
||||
* For example, when displaying a sentence indicating that actor X created a build or published an update.
|
||||
*/
|
||||
displayName: Scalars['String'];
|
||||
/** Experiments associated with this actor */
|
||||
experiments: Array<ActorExperiment>;
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -618,9 +614,33 @@ export type ActorFeatureGatesArgs = {
|
||||
filter?: InputMaybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
export type ActorExperiment = {
|
||||
__typename?: 'ActorExperiment';
|
||||
createdAt: Scalars['DateTime'];
|
||||
enabled: Scalars['Boolean'];
|
||||
experiment: Experiment;
|
||||
id: Scalars['ID'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type ActorExperimentMutation = {
|
||||
__typename?: 'ActorExperimentMutation';
|
||||
/** Create or update the value of a User Experiment */
|
||||
createOrUpdateActorExperiment: ActorExperiment;
|
||||
};
|
||||
|
||||
|
||||
export type ActorExperimentMutationCreateOrUpdateActorExperimentArgs = {
|
||||
enabled: Scalars['Boolean'];
|
||||
experiment: Experiment;
|
||||
};
|
||||
|
||||
export type ActorQuery = {
|
||||
__typename?: 'ActorQuery';
|
||||
/** Query an Actor by ID */
|
||||
/**
|
||||
* Query an Actor by ID
|
||||
* @deprecated Public actor queries are no longer supported
|
||||
*/
|
||||
byId: Actor;
|
||||
};
|
||||
|
||||
@@ -789,11 +809,13 @@ export enum AndroidBuildType {
|
||||
}
|
||||
|
||||
export type AndroidBuilderEnvironmentInput = {
|
||||
bun?: InputMaybe<Scalars['String']>;
|
||||
env?: InputMaybe<Scalars['JSONObject']>;
|
||||
expoCli?: InputMaybe<Scalars['String']>;
|
||||
image?: InputMaybe<Scalars['String']>;
|
||||
ndk?: InputMaybe<Scalars['String']>;
|
||||
node?: InputMaybe<Scalars['String']>;
|
||||
pnpm?: InputMaybe<Scalars['String']>;
|
||||
yarn?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
@@ -1023,6 +1045,7 @@ export type App = Project & {
|
||||
insights: AppInsights;
|
||||
/** iOS app credentials for the project */
|
||||
iosAppCredentials: Array<IosAppCredentials>;
|
||||
isDeleting: Scalars['Boolean'];
|
||||
/** Whether the latest classic update publish is using a deprecated SDK version */
|
||||
isDeprecated: Scalars['Boolean'];
|
||||
/** @deprecated 'likes' have been deprecated. */
|
||||
@@ -1057,6 +1080,8 @@ export type App = Project & {
|
||||
releaseChannels: Array<Scalars['String']>;
|
||||
/** @deprecated Legacy access tokens are deprecated */
|
||||
requiresAccessTokenForPushSecurity: Scalars['Boolean'];
|
||||
/** Runtimes associated with this app */
|
||||
runtimes: RuntimesConnection;
|
||||
scopeKey: Scalars['String'];
|
||||
/** SDK version of the latest classic update publish, 0.0.0 otherwise */
|
||||
sdkVersion: Scalars['String'];
|
||||
@@ -1174,6 +1199,7 @@ export type AppDeploymentArgs = {
|
||||
export type AppDeploymentsArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<DeploymentFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
@@ -1212,6 +1238,15 @@ export type AppLikedByArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppRuntimesArgs = {
|
||||
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 AppSubmissionsArgs = {
|
||||
filter: SubmissionFilter;
|
||||
@@ -2053,6 +2088,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
completedAt?: Maybe<Scalars['DateTime']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
customWorkflowName?: Maybe<Scalars['String']>;
|
||||
developmentClient?: Maybe<Scalars['Boolean']>;
|
||||
distribution?: Maybe<DistributionType>;
|
||||
enqueuedAt?: Maybe<Scalars['DateTime']>;
|
||||
error?: Maybe<BuildError>;
|
||||
@@ -2144,6 +2180,7 @@ export enum BuildCredentialsSource {
|
||||
|
||||
export type BuildError = {
|
||||
__typename?: 'BuildError';
|
||||
buildPhase?: Maybe<BuildPhase>;
|
||||
docsUrl?: Maybe<Scalars['String']>;
|
||||
errorCode: Scalars['String'];
|
||||
message: Scalars['String'];
|
||||
@@ -2269,6 +2306,7 @@ export type BuildMetadataInput = {
|
||||
cliVersion?: InputMaybe<Scalars['String']>;
|
||||
credentialsSource?: InputMaybe<BuildCredentialsSource>;
|
||||
customWorkflowName?: InputMaybe<Scalars['String']>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']>;
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
gitCommitHash?: InputMaybe<Scalars['String']>;
|
||||
gitCommitMessage?: InputMaybe<Scalars['String']>;
|
||||
@@ -2397,6 +2435,50 @@ export type BuildParamsInput = {
|
||||
sdkVersion?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum BuildPhase {
|
||||
BuilderInfo = 'BUILDER_INFO',
|
||||
CleanUpCredentials = 'CLEAN_UP_CREDENTIALS',
|
||||
CompleteBuild = 'COMPLETE_BUILD',
|
||||
ConfigureExpoUpdates = 'CONFIGURE_EXPO_UPDATES',
|
||||
ConfigureXcodeProject = 'CONFIGURE_XCODE_PROJECT',
|
||||
Custom = 'CUSTOM',
|
||||
DownloadApplicationArchive = 'DOWNLOAD_APPLICATION_ARCHIVE',
|
||||
EasBuildInternal = 'EAS_BUILD_INTERNAL',
|
||||
FailBuild = 'FAIL_BUILD',
|
||||
FixGradlew = 'FIX_GRADLEW',
|
||||
InstallCustomTools = 'INSTALL_CUSTOM_TOOLS',
|
||||
InstallDependencies = 'INSTALL_DEPENDENCIES',
|
||||
InstallPods = 'INSTALL_PODS',
|
||||
OnBuildCancelHook = 'ON_BUILD_CANCEL_HOOK',
|
||||
OnBuildCompleteHook = 'ON_BUILD_COMPLETE_HOOK',
|
||||
OnBuildErrorHook = 'ON_BUILD_ERROR_HOOK',
|
||||
OnBuildSuccessHook = 'ON_BUILD_SUCCESS_HOOK',
|
||||
ParseCustomWorkflowConfig = 'PARSE_CUSTOM_WORKFLOW_CONFIG',
|
||||
PostInstallHook = 'POST_INSTALL_HOOK',
|
||||
Prebuild = 'PREBUILD',
|
||||
PrepareArtifacts = 'PREPARE_ARTIFACTS',
|
||||
PrepareCredentials = 'PREPARE_CREDENTIALS',
|
||||
PrepareProject = 'PREPARE_PROJECT',
|
||||
PreInstallHook = 'PRE_INSTALL_HOOK',
|
||||
PreUploadArtifactsHook = 'PRE_UPLOAD_ARTIFACTS_HOOK',
|
||||
Queue = 'QUEUE',
|
||||
ReadAppConfig = 'READ_APP_CONFIG',
|
||||
ReadPackageJson = 'READ_PACKAGE_JSON',
|
||||
RestoreCache = 'RESTORE_CACHE',
|
||||
RunExpoDoctor = 'RUN_EXPO_DOCTOR',
|
||||
RunFastlane = 'RUN_FASTLANE',
|
||||
RunGradlew = 'RUN_GRADLEW',
|
||||
SaveCache = 'SAVE_CACHE',
|
||||
SetUpBuildEnvironment = 'SET_UP_BUILD_ENVIRONMENT',
|
||||
SpinUpBuilder = 'SPIN_UP_BUILDER',
|
||||
StartBuild = 'START_BUILD',
|
||||
Unknown = 'UNKNOWN',
|
||||
UploadApplicationArchive = 'UPLOAD_APPLICATION_ARCHIVE',
|
||||
/** @deprecated No longer supported */
|
||||
UploadArtifacts = 'UPLOAD_ARTIFACTS',
|
||||
UploadBuildArtifacts = 'UPLOAD_BUILD_ARTIFACTS'
|
||||
}
|
||||
|
||||
export type BuildPlanCreditThresholdExceededMetadata = {
|
||||
__typename?: 'BuildPlanCreditThresholdExceededMetadata';
|
||||
account: Account;
|
||||
@@ -2771,8 +2853,10 @@ export type Deployment = {
|
||||
builds: DeploymentBuildsConnection;
|
||||
channel: UpdateChannel;
|
||||
id: Scalars['ID'];
|
||||
/** @deprecated Not required for the new Deployment UI */
|
||||
latestUpdate?: Maybe<Update>;
|
||||
/** Deployment query field */
|
||||
insights: DeploymentInsights;
|
||||
/** Ordered the same way as 'updateBranches' in UpdateChannel */
|
||||
latestUpdatesPerBranch: Array<LatestUpdateOnBranch>;
|
||||
runtime: Runtime;
|
||||
};
|
||||
|
||||
@@ -2785,6 +2869,13 @@ export type DeploymentBuildsArgs = {
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type DeploymentLatestUpdatesPerBranchArgs = {
|
||||
limit: Scalars['Int'];
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type DeploymentBuildEdge = {
|
||||
__typename?: 'DeploymentBuildEdge';
|
||||
cursor: Scalars['String'];
|
||||
@@ -2804,6 +2895,40 @@ export type DeploymentEdge = {
|
||||
node: Deployment;
|
||||
};
|
||||
|
||||
export type DeploymentFilterInput = {
|
||||
channel?: InputMaybe<Scalars['String']>;
|
||||
runtimeVersion?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type DeploymentInsights = {
|
||||
__typename?: 'DeploymentInsights';
|
||||
embeddedUpdateTotalUniqueUsers: Scalars['Int'];
|
||||
embeddedUpdateUniqueUsersOverTime: UniqueUsersOverTimeData;
|
||||
id: Scalars['ID'];
|
||||
mostPopularUpdates: Array<Update>;
|
||||
uniqueUsersOverTime: UniqueUsersOverTimeData;
|
||||
};
|
||||
|
||||
|
||||
export type DeploymentInsightsEmbeddedUpdateTotalUniqueUsersArgs = {
|
||||
timespan: InsightsTimespan;
|
||||
};
|
||||
|
||||
|
||||
export type DeploymentInsightsEmbeddedUpdateUniqueUsersOverTimeArgs = {
|
||||
timespan: InsightsTimespan;
|
||||
};
|
||||
|
||||
|
||||
export type DeploymentInsightsMostPopularUpdatesArgs = {
|
||||
timespan: InsightsTimespan;
|
||||
};
|
||||
|
||||
|
||||
export type DeploymentInsightsUniqueUsersOverTimeArgs = {
|
||||
timespan: InsightsTimespan;
|
||||
};
|
||||
|
||||
/** Represents the connection over the deployments edge of an App */
|
||||
export type DeploymentsConnection = {
|
||||
__typename?: 'DeploymentsConnection';
|
||||
@@ -2972,6 +3097,10 @@ export type EstimatedUsage = {
|
||||
value: Scalars['Float'];
|
||||
};
|
||||
|
||||
export enum Experiment {
|
||||
Orbit = 'ORBIT'
|
||||
}
|
||||
|
||||
export type ExperimentationQuery = {
|
||||
__typename?: 'ExperimentationQuery';
|
||||
/** Get device experimentation config */
|
||||
@@ -3535,6 +3664,7 @@ export enum IosBuildType {
|
||||
}
|
||||
|
||||
export type IosBuilderEnvironmentInput = {
|
||||
bun?: InputMaybe<Scalars['String']>;
|
||||
bundler?: InputMaybe<Scalars['String']>;
|
||||
cocoapods?: InputMaybe<Scalars['String']>;
|
||||
env?: InputMaybe<Scalars['JSONObject']>;
|
||||
@@ -3542,6 +3672,7 @@ export type IosBuilderEnvironmentInput = {
|
||||
fastlane?: InputMaybe<Scalars['String']>;
|
||||
image?: InputMaybe<Scalars['String']>;
|
||||
node?: InputMaybe<Scalars['String']>;
|
||||
pnpm?: InputMaybe<Scalars['String']>;
|
||||
yarn?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
@@ -3668,6 +3799,12 @@ export type KeystoreGenerationUrlMutation = {
|
||||
createKeystoreGenerationUrl: KeystoreGenerationUrl;
|
||||
};
|
||||
|
||||
export type LatestUpdateOnBranch = {
|
||||
__typename?: 'LatestUpdateOnBranch';
|
||||
branchId: Scalars['String'];
|
||||
update?: Maybe<Update>;
|
||||
};
|
||||
|
||||
export type LeaveAccountResult = {
|
||||
__typename?: 'LeaveAccountResult';
|
||||
success: Scalars['Boolean'];
|
||||
@@ -3845,6 +3982,7 @@ export type MeteredBillingStatus = {
|
||||
|
||||
export type Notification = {
|
||||
__typename?: 'Notification';
|
||||
accountName: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
event: NotificationEvent;
|
||||
id: Scalars['ID'];
|
||||
@@ -3852,6 +3990,7 @@ export type Notification = {
|
||||
metadata?: Maybe<NotificationMetadata>;
|
||||
type: NotificationType;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
websiteMessage: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum NotificationEvent {
|
||||
@@ -4084,6 +4223,8 @@ export type Robot = Actor & {
|
||||
accounts: Array<Account>;
|
||||
created: Scalars['DateTime'];
|
||||
displayName: Scalars['String'];
|
||||
/** Experiments associated with this actor */
|
||||
experiments: Array<ActorExperiment>;
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -4155,6 +4296,8 @@ export type RootMutation = {
|
||||
account: AccountMutation;
|
||||
/** Mutations that create, update, and delete an AccountSSOConfiguration */
|
||||
accountSSOConfiguration: AccountSsoConfigurationMutation;
|
||||
/** Mutations for Actor experiments */
|
||||
actorExperiment: ActorExperimentMutation;
|
||||
/** Mutations that modify the build credentials for an Android app */
|
||||
androidAppBuildCredentials: AndroidAppBuildCredentialsMutation;
|
||||
/** Mutations that modify the credentials for an Android app */
|
||||
@@ -4226,10 +4369,14 @@ export type RootMutation = {
|
||||
updateBranch: UpdateBranchMutation;
|
||||
updateChannel: UpdateChannelMutation;
|
||||
uploadSession: UploadSession;
|
||||
/** Mutations that create, update, and delete pinned apps */
|
||||
userAppPins: UserAppPinMutation;
|
||||
/** Mutations that create, delete, and accept UserInvitations */
|
||||
userInvitation: UserInvitationMutation;
|
||||
/** Mutations that create, delete, update Webhooks */
|
||||
webhook: WebhookMutation;
|
||||
/** Mutations that modify a websiteNotification */
|
||||
websiteNotifications: WebsiteNotificationMutation;
|
||||
};
|
||||
|
||||
|
||||
@@ -4263,7 +4410,10 @@ export type RootQuery = {
|
||||
account: AccountQuery;
|
||||
/** Top-level query object for querying AccountSSOConfigurationPublicData */
|
||||
accountSSOConfigurationPublicData: AccountSsoConfigurationPublicDataQuery;
|
||||
/** Top-level query object for querying Actors. */
|
||||
/**
|
||||
* Top-level query object for querying Actors.
|
||||
* @deprecated Public actor queries are no longer supported
|
||||
*/
|
||||
actor: ActorQuery;
|
||||
/**
|
||||
* Public apps in the app directory
|
||||
@@ -4320,7 +4470,10 @@ export type RootQuery = {
|
||||
* @deprecated Public user queries are no longer supported
|
||||
*/
|
||||
user: UserQuery;
|
||||
/** Top-level query object for querying UserActors. */
|
||||
/**
|
||||
* Top-level query object for querying UserActors.
|
||||
* @deprecated Public user queries are no longer supported
|
||||
*/
|
||||
userActor: UserActorQuery;
|
||||
/** Top-level query object for querying UserActorPublicData publicly. */
|
||||
userActorPublicData: UserActorPublicDataQuery;
|
||||
@@ -4386,6 +4539,7 @@ export type RuntimeFilterInput = {
|
||||
branchId?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** Represents the connection over the runtime edge of an App */
|
||||
export type RuntimesConnection = {
|
||||
__typename?: 'RuntimesConnection';
|
||||
edges: Array<RuntimeEdge>;
|
||||
@@ -4410,6 +4564,8 @@ export type SsoUser = Actor & UserActor & {
|
||||
/** Discord account linked to a user */
|
||||
discordUser?: Maybe<DiscordUser>;
|
||||
displayName: Scalars['String'];
|
||||
/** Experiments associated with this actor */
|
||||
experiments: Array<ActorExperiment>;
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -4429,6 +4585,7 @@ export type SsoUser = Actor & UserActor & {
|
||||
/** @deprecated No longer supported */
|
||||
location?: Maybe<Scalars['String']>;
|
||||
notificationSubscriptions: Array<NotificationSubscription>;
|
||||
pinnedApps: Array<App>;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
profilePhoto: Scalars['String'];
|
||||
@@ -4437,8 +4594,7 @@ export type SsoUser = Actor & UserActor & {
|
||||
/** @deprecated No longer supported */
|
||||
twitterUsername?: Maybe<Scalars['String']>;
|
||||
username: Scalars['String'];
|
||||
/** Web notifications linked to a user */
|
||||
websiteNotifications: Array<Notification>;
|
||||
websiteNotificationsPaginated: WebsiteNotificationsConnection;
|
||||
};
|
||||
|
||||
|
||||
@@ -4476,6 +4632,15 @@ export type SsoUserSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human SSO (not robot) actor. */
|
||||
export type SsoUserWebsiteNotificationsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type SsoUserDataInput = {
|
||||
firstName?: InputMaybe<Scalars['String']>;
|
||||
lastName?: InputMaybe<Scalars['String']>;
|
||||
@@ -4670,7 +4835,9 @@ export type StatuspageService = {
|
||||
export enum StatuspageServiceName {
|
||||
EasBuild = 'EAS_BUILD',
|
||||
EasSubmit = 'EAS_SUBMIT',
|
||||
EasUpdate = 'EAS_UPDATE'
|
||||
EasUpdate = 'EAS_UPDATE',
|
||||
GithubApiRequests = 'GITHUB_API_REQUESTS',
|
||||
GithubWebhooks = 'GITHUB_WEBHOOKS'
|
||||
}
|
||||
|
||||
export type StatuspageServiceQuery = {
|
||||
@@ -4904,6 +5071,8 @@ export type Update = ActivityTimelineProjectActivity & {
|
||||
gitCommitHash?: Maybe<Scalars['String']>;
|
||||
group: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
/** Update query field */
|
||||
insights: UpdateInsights;
|
||||
isGitWorkingTreeDirty: Scalars['Boolean'];
|
||||
isRollBackToEmbedded: Scalars['Boolean'];
|
||||
manifestFragment: Scalars['String'];
|
||||
@@ -5064,6 +5233,17 @@ export type UpdateInfoGroup = {
|
||||
web?: InputMaybe<PartialManifest>;
|
||||
};
|
||||
|
||||
export type UpdateInsights = {
|
||||
__typename?: 'UpdateInsights';
|
||||
id: Scalars['ID'];
|
||||
totalUniqueUsers: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type UpdateInsightsTotalUniqueUsersArgs = {
|
||||
timespan: InsightsTimespan;
|
||||
};
|
||||
|
||||
export type UpdateMutation = {
|
||||
__typename?: 'UpdateMutation';
|
||||
/** Delete an EAS update group */
|
||||
@@ -5163,6 +5343,8 @@ export type User = Actor & UserActor & {
|
||||
displayName: Scalars['String'];
|
||||
email: Scalars['String'];
|
||||
emailVerified: Scalars['Boolean'];
|
||||
/** Experiments associated with this actor */
|
||||
experiments: Array<ActorExperiment>;
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -5189,6 +5371,7 @@ export type User = Actor & UserActor & {
|
||||
notificationSubscriptions: Array<NotificationSubscription>;
|
||||
/** Pending UserInvitations for this user. Only resolves for the viewer. */
|
||||
pendingUserInvitations: Array<UserInvitation>;
|
||||
pinnedApps: Array<App>;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
profilePhoto: Scalars['String'];
|
||||
@@ -5199,7 +5382,7 @@ export type User = Actor & UserActor & {
|
||||
/** @deprecated No longer supported */
|
||||
twitterUsername?: Maybe<Scalars['String']>;
|
||||
username: Scalars['String'];
|
||||
websiteNotifications: Array<Notification>;
|
||||
websiteNotificationsPaginated: WebsiteNotificationsConnection;
|
||||
};
|
||||
|
||||
|
||||
@@ -5237,6 +5420,15 @@ export type UserSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human (not robot) actor. */
|
||||
export type UserWebsiteNotificationsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActor = {
|
||||
/** Access Tokens belonging to this user actor */
|
||||
@@ -5261,6 +5453,8 @@ export type UserActor = {
|
||||
* For example, when displaying a sentence indicating that actor X created a build or published an update.
|
||||
*/
|
||||
displayName: Scalars['String'];
|
||||
/** Experiments associated with this actor */
|
||||
experiments: Array<ActorExperiment>;
|
||||
/**
|
||||
* Server feature gate values for this user actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -5280,6 +5474,7 @@ export type UserActor = {
|
||||
/** @deprecated No longer supported */
|
||||
location?: Maybe<Scalars['String']>;
|
||||
notificationSubscriptions: Array<NotificationSubscription>;
|
||||
pinnedApps: Array<App>;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
profilePhoto: Scalars['String'];
|
||||
@@ -5288,8 +5483,7 @@ export type UserActor = {
|
||||
/** @deprecated No longer supported */
|
||||
twitterUsername?: Maybe<Scalars['String']>;
|
||||
username: Scalars['String'];
|
||||
/** Web notifications linked to a user */
|
||||
websiteNotifications: Array<Notification>;
|
||||
websiteNotificationsPaginated: WebsiteNotificationsConnection;
|
||||
};
|
||||
|
||||
|
||||
@@ -5327,6 +5521,15 @@ export type UserActorSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorWebsiteNotificationsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorPublicData = {
|
||||
__typename?: 'UserActorPublicData';
|
||||
@@ -5369,9 +5572,15 @@ export type UserActorPublicDataQueryByUsernameArgs = {
|
||||
|
||||
export type UserActorQuery = {
|
||||
__typename?: 'UserActorQuery';
|
||||
/** Query a UserActor by ID */
|
||||
/**
|
||||
* Query a UserActor by ID
|
||||
* @deprecated Public user actor queries are no longer supported
|
||||
*/
|
||||
byId: UserActor;
|
||||
/** Query a UserActor by username */
|
||||
/**
|
||||
* Query a UserActor by username
|
||||
* @deprecated Public user actor queries are no longer supported
|
||||
*/
|
||||
byUsername: UserActor;
|
||||
};
|
||||
|
||||
@@ -5385,6 +5594,22 @@ export type UserActorQueryByUsernameArgs = {
|
||||
username: Scalars['String'];
|
||||
};
|
||||
|
||||
export type UserAppPinMutation = {
|
||||
__typename?: 'UserAppPinMutation';
|
||||
pinApp: Scalars['ID'];
|
||||
unpinApp?: Maybe<Scalars['ID']>;
|
||||
};
|
||||
|
||||
|
||||
export type UserAppPinMutationPinAppArgs = {
|
||||
appId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type UserAppPinMutationUnpinAppArgs = {
|
||||
appId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type UserDataInput = {
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
firstName?: InputMaybe<Scalars['String']>;
|
||||
@@ -5544,6 +5769,11 @@ export type UserSecondFactorDevice = {
|
||||
user: User;
|
||||
};
|
||||
|
||||
export type WebNotificationUpdateReadStateInput = {
|
||||
id: Scalars['ID'];
|
||||
isRead: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type Webhook = {
|
||||
__typename?: 'Webhook';
|
||||
appId: Scalars['ID'];
|
||||
@@ -5606,6 +5836,29 @@ export enum WebhookType {
|
||||
Submit = 'SUBMIT'
|
||||
}
|
||||
|
||||
export type WebsiteNotificationEdge = {
|
||||
__typename?: 'WebsiteNotificationEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: Notification;
|
||||
};
|
||||
|
||||
export type WebsiteNotificationMutation = {
|
||||
__typename?: 'WebsiteNotificationMutation';
|
||||
updateAllWebsiteNotificationReadStateAsRead: Scalars['Boolean'];
|
||||
updateNotificationReadState: Notification;
|
||||
};
|
||||
|
||||
|
||||
export type WebsiteNotificationMutationUpdateNotificationReadStateArgs = {
|
||||
input: WebNotificationUpdateReadStateInput;
|
||||
};
|
||||
|
||||
export type WebsiteNotificationsConnection = {
|
||||
__typename?: 'WebsiteNotificationsConnection';
|
||||
edges: Array<WebsiteNotificationEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type DeleteAndroidAppBuildCredentialsResult = {
|
||||
__typename?: 'deleteAndroidAppBuildCredentialsResult';
|
||||
id: Scalars['ID'];
|
||||
@@ -6334,7 +6587,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, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, 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, updatedAt: any, 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', username: string, 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'];
|
||||
@@ -6343,7 +6596,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, 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', username: string, 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, updatedAt: any, 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', username: string, 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 ViewUpdateChannelsPaginatedOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
|
||||
@@ -60,6 +60,7 @@ export const ChannelQuery = {
|
||||
updateChannelByName(name: $channelName) {
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
createdAt
|
||||
branchMapping
|
||||
updateBranches(offset: 0, limit: 5) {
|
||||
@@ -104,6 +105,7 @@ export const ChannelQuery = {
|
||||
updateChannels(offset: $offset, limit: $limit) {
|
||||
id
|
||||
name
|
||||
updatedAt
|
||||
createdAt
|
||||
branchMapping
|
||||
updateBranches(offset: 0, limit: 5) {
|
||||
|
||||
@@ -57,6 +57,10 @@ export default class Log {
|
||||
Log.consoleLog(chalk.green(figures.tick), ...args);
|
||||
}
|
||||
|
||||
public static withInfo(...args: any[]): void {
|
||||
Log.consoleLog(chalk.green(figures.info), ...args);
|
||||
}
|
||||
|
||||
private static consoleLog(...args: any[]): void {
|
||||
Log.updateIsLastLineNewLine(args);
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@@ -201,8 +201,14 @@ async function getSuggestedBundleIdentifierAsync(
|
||||
} else {
|
||||
// the only callsite is heavily interactive
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
let possibleId: string;
|
||||
// It's common to use dashes in your node project name, strip them from the suggested package name.
|
||||
const possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
|
||||
if (account.name) {
|
||||
possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
|
||||
} else {
|
||||
possibleId = `com.${exp.slug}`.split('-').join('');
|
||||
}
|
||||
|
||||
if (isBundleIdentifierValid(possibleId)) {
|
||||
return possibleId;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ 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 {
|
||||
expoCommandAsync,
|
||||
shouldUseVersionedExpoCLI,
|
||||
shouldUseVersionedExpoCLIWithExplicitPlatforms,
|
||||
} from '../utils/expoCli';
|
||||
import chunk from '../utils/expodash/chunk';
|
||||
import { truthy } from '../utils/expodash/filter';
|
||||
import uniqBy from '../utils/expodash/uniqBy';
|
||||
@@ -181,32 +185,23 @@ export async function buildBundlesAsync({
|
||||
exp,
|
||||
platformFlag,
|
||||
clearCache,
|
||||
dev = false,
|
||||
}: {
|
||||
projectDir: string;
|
||||
inputDir: string;
|
||||
exp: Pick<ExpoConfig, 'sdkVersion'>;
|
||||
exp: Pick<ExpoConfig, 'sdkVersion' | 'web'>;
|
||||
platformFlag: ExpoCLIExportPlatformFlag;
|
||||
clearCache?: boolean;
|
||||
dev?: boolean;
|
||||
}): Promise<void> {
|
||||
const packageJSON = JsonFile.read(path.resolve(projectDir, 'package.json'));
|
||||
if (!packageJSON) {
|
||||
throw new Error('Could not locate package.json');
|
||||
}
|
||||
|
||||
if (shouldUseVersionedExpoCLI(projectDir, exp)) {
|
||||
await expoCommandAsync(projectDir, [
|
||||
'export',
|
||||
'--output-dir',
|
||||
inputDir,
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
'--platform',
|
||||
platformFlag,
|
||||
...(clearCache ? ['--clear'] : []),
|
||||
]);
|
||||
} else {
|
||||
// Legacy global Expo CLI
|
||||
await expoCommandAsync(projectDir, [
|
||||
// Legacy global Expo CLI
|
||||
if (!shouldUseVersionedExpoCLI(projectDir, exp)) {
|
||||
return await expoCommandAsync(projectDir, [
|
||||
'export',
|
||||
'--output-dir',
|
||||
inputDir,
|
||||
@@ -214,11 +209,52 @@ export async function buildBundlesAsync({
|
||||
'--non-interactive',
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
'--platform',
|
||||
platformFlag,
|
||||
`--platform=${platformFlag}`,
|
||||
...(dev ? ['--dev'] : []),
|
||||
...(clearCache ? ['--clear'] : []),
|
||||
]);
|
||||
}
|
||||
|
||||
// Versioned Expo CLI, with multiple platform flag support
|
||||
if (shouldUseVersionedExpoCLIWithExplicitPlatforms(projectDir)) {
|
||||
// When creating EAS updates, we don't want to build a web bundle
|
||||
const platformArgs =
|
||||
platformFlag === 'all'
|
||||
? ['--platform', 'ios', '--platform', 'android']
|
||||
: ['--platform', platformFlag];
|
||||
|
||||
return await expoCommandAsync(projectDir, [
|
||||
'export',
|
||||
'--output-dir',
|
||||
inputDir,
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
...platformArgs,
|
||||
...(dev ? ['--dev'] : []),
|
||||
...(clearCache ? ['--clear'] : []),
|
||||
]);
|
||||
}
|
||||
|
||||
// Versioned Expo CLI, without multiple platform flag support
|
||||
// Warn users about potential export issues when using Metro web
|
||||
// See: https://github.com/expo/expo/pull/23621
|
||||
if (exp.web?.bundler === 'metro') {
|
||||
Log.warn('Exporting bundle for all platforms, including Metro web.');
|
||||
Log.warn(
|
||||
'If your app is incompatible with web, remove the "expo.web.bundler" property from your app manifest, or upgrade to the latest Expo SDK.'
|
||||
);
|
||||
}
|
||||
|
||||
return await expoCommandAsync(projectDir, [
|
||||
'export',
|
||||
'--output-dir',
|
||||
inputDir,
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
`--platform=${platformFlag}`,
|
||||
...(dev ? ['--dev'] : []),
|
||||
...(clearCache ? ['--clear'] : []),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function resolveInputDirectoryAsync(
|
||||
@@ -682,3 +718,9 @@ export function getRuntimeToPlatformMappingFromRuntimeVersions(
|
||||
}
|
||||
return runtimeToPlatformMapping;
|
||||
}
|
||||
|
||||
export const platformDisplayNames: Record<Platform, string> = {
|
||||
android: 'Android',
|
||||
ios: 'iOS',
|
||||
web: 'Web',
|
||||
};
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
assertRolloutBranchMapping,
|
||||
composeRollout,
|
||||
createRolloutBranchMapping,
|
||||
doesTargetRollout,
|
||||
editRolloutBranchMapping,
|
||||
getRollout,
|
||||
getRolloutInfo,
|
||||
@@ -33,6 +34,17 @@ import {
|
||||
standardBranchMapping,
|
||||
} from './fixtures';
|
||||
|
||||
describe(doesTargetRollout, () => {
|
||||
it('detects whether a runtime targets a constrained rollout', () => {
|
||||
expect(doesTargetRollout(rolloutBranchMapping, '1.0.0')).toBe(true);
|
||||
expect(doesTargetRollout(rolloutBranchMapping, '2.0.0')).toBe(false);
|
||||
});
|
||||
it('should always return true if the rollout is unconstrained', () => {
|
||||
expect(doesTargetRollout(rolloutBranchMappingLegacy, '1.0.0')).toBe(true);
|
||||
expect(doesTargetRollout(rolloutBranchMappingLegacy, '2.0.0')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe(composeRollout, () => {
|
||||
it('composes a rollout', () => {
|
||||
const rollout = getRollout(channelInfoWithBranches);
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
export enum ManageRolloutActions {
|
||||
EDIT = 'Edit',
|
||||
END = 'End',
|
||||
VIEW = 'View',
|
||||
GO_BACK = 'Go back',
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ export class ManageRollout implements EASUpdateAction<EASUpdateAction> {
|
||||
private channelInfo: UpdateChannelBasicInfoFragment,
|
||||
private options: {
|
||||
callingAction?: EASUpdateAction;
|
||||
action?: ManageRolloutActions.EDIT | ManageRolloutActions.END;
|
||||
action?: ManageRolloutActions.EDIT | ManageRolloutActions.END | ManageRolloutActions.VIEW;
|
||||
} & Partial<EditRolloutNonInteractiveOptions> &
|
||||
Partial<EndRolloutNonInteractiveOptions> &
|
||||
EndRolloutGeneralOptions
|
||||
@@ -51,6 +52,9 @@ export class ManageRollout implements EASUpdateAction<EASUpdateAction> {
|
||||
return new EditRollout(this.channelInfo, this.options);
|
||||
case ManageRolloutActions.END:
|
||||
return new EndRollout(this.channelInfo, this.options);
|
||||
case ManageRolloutActions.VIEW:
|
||||
// Rollout is automatically printed in interactive mode
|
||||
return new Noop();
|
||||
case ManageRolloutActions.GO_BACK:
|
||||
assert(this.options.callingAction, 'calling action must be defined');
|
||||
return this.options.callingAction;
|
||||
@@ -94,3 +98,7 @@ export class ManageRollout implements EASUpdateAction<EASUpdateAction> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Noop {
|
||||
public async runAsync(): Promise<void> {}
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
|
||||
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import { CodeSigningInfo } from '../../utils/code-signing';
|
||||
import {
|
||||
ChannelQuery,
|
||||
UpdateBranchObject,
|
||||
UpdateChannelObject,
|
||||
} from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import { printJsonOnlyOutput } from '../../utils/json';
|
||||
import { getRollout, getRolloutInfo, isConstrainedRolloutInfo, isRollout } from '../branch-mapping';
|
||||
import { printRollout } from '../utils';
|
||||
import {
|
||||
CreateRollout,
|
||||
NonInteractiveOptions as CreateRolloutNonInteractiveOptions,
|
||||
@@ -18,6 +25,19 @@ import {
|
||||
import { ManageRolloutActions } from './ManageRollout';
|
||||
import { MainMenuActions, RolloutActions } from './RolloutMainMenu';
|
||||
|
||||
type JSONRolloutOutput = {
|
||||
defaultBranch: UpdateBranchObject;
|
||||
rolledOutBranch: UpdateBranchObject;
|
||||
percentRolledOut: number;
|
||||
runtimeVersion?: string;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
type JSONOutput = {
|
||||
hasRollout: boolean;
|
||||
originalRolloutInfo?: JSONRolloutOutput;
|
||||
currentRolloutInfo?: JSONRolloutOutput;
|
||||
};
|
||||
/**
|
||||
* Control a rollout in non interactive mode.
|
||||
*/
|
||||
@@ -25,7 +45,7 @@ export class NonInteractiveRollout implements EASUpdateAction<void> {
|
||||
constructor(
|
||||
private options: {
|
||||
channelName?: string;
|
||||
codeSigningInfo?: CodeSigningInfo;
|
||||
json?: boolean;
|
||||
action?: RolloutActions;
|
||||
} & Partial<EditRolloutNonInteractiveOptions> &
|
||||
Partial<EndRolloutNonInteractiveOptions> &
|
||||
@@ -49,29 +69,102 @@ export class NonInteractiveRollout implements EASUpdateAction<void> {
|
||||
appId: app.projectId,
|
||||
channelName,
|
||||
});
|
||||
const channelObject = await this.getChannelObjectAsync(ctx, {
|
||||
channelName,
|
||||
runtimeVersion: this.getRuntimeVersion(channelInfo),
|
||||
});
|
||||
|
||||
if (!action) {
|
||||
throw new Error(`--action is required in non-interactive mode.`);
|
||||
}
|
||||
await this.runActionAsync(ctx, action, channelInfo, this.options);
|
||||
const updatedChannelInfo = await this.runActionAsync(ctx, action, channelObject);
|
||||
const updatedChannelObject = await this.getChannelObjectAsync(ctx, {
|
||||
channelName,
|
||||
runtimeVersion: this.getRuntimeVersion(updatedChannelInfo),
|
||||
});
|
||||
if (this.options.json) {
|
||||
const json = await this.getJsonAsync({
|
||||
originalChannelObject: channelObject,
|
||||
updatedChannelObject,
|
||||
});
|
||||
printJsonOnlyOutput(json);
|
||||
}
|
||||
}
|
||||
|
||||
async runActionAsync(
|
||||
ctx: EASUpdateContext,
|
||||
action: RolloutActions,
|
||||
channelInfo: UpdateChannelBasicInfoFragment,
|
||||
options: Partial<EditRolloutNonInteractiveOptions> &
|
||||
Partial<EndRolloutNonInteractiveOptions> &
|
||||
EndRolloutGeneralOptions &
|
||||
Partial<CreateRolloutNonInteractiveOptions>
|
||||
channelObject: UpdateChannelObject
|
||||
): Promise<UpdateChannelBasicInfoFragment> {
|
||||
switch (action) {
|
||||
case MainMenuActions.CREATE_NEW:
|
||||
return await new CreateRollout(channelInfo, options).runAsync(ctx);
|
||||
return await new CreateRollout(channelObject, this.options).runAsync(ctx);
|
||||
case ManageRolloutActions.EDIT:
|
||||
return await new EditRollout(channelInfo, options).runAsync(ctx);
|
||||
return await new EditRollout(channelObject, this.options).runAsync(ctx);
|
||||
case ManageRolloutActions.END:
|
||||
return await new EndRollout(channelInfo, options).runAsync(ctx);
|
||||
return await new EndRollout(channelObject, this.options).runAsync(ctx);
|
||||
case ManageRolloutActions.VIEW:
|
||||
return this.viewRollout(channelObject);
|
||||
}
|
||||
}
|
||||
|
||||
viewRollout(channelObject: UpdateChannelObject): UpdateChannelObject {
|
||||
if (!this.options.json) {
|
||||
printRollout(channelObject);
|
||||
Log.warn('For formatted output, add the --json flag to your command.');
|
||||
}
|
||||
return channelObject;
|
||||
}
|
||||
|
||||
async getJsonAsync({
|
||||
originalChannelObject,
|
||||
updatedChannelObject,
|
||||
}: {
|
||||
originalChannelObject: UpdateChannelObject;
|
||||
updatedChannelObject: UpdateChannelObject;
|
||||
}): Promise<JSONOutput> {
|
||||
return {
|
||||
hasRollout: isRollout(updatedChannelObject),
|
||||
...(isRollout(originalChannelObject)
|
||||
? { originalRolloutInfo: await this.getRolloutJsonAsync(originalChannelObject) }
|
||||
: {}),
|
||||
...(isRollout(updatedChannelObject)
|
||||
? { currentRolloutInfo: await this.getRolloutJsonAsync(updatedChannelObject) }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
async getRolloutJsonAsync(channelObject: UpdateChannelObject): Promise<JSONRolloutOutput> {
|
||||
const rollout = getRollout(channelObject);
|
||||
return {
|
||||
defaultBranch: rollout.defaultBranch,
|
||||
rolledOutBranch: rollout.rolledOutBranch,
|
||||
percentRolledOut: rollout.percentRolledOut,
|
||||
runtimeVersion: this.getRuntimeVersion(channelObject) ?? undefined,
|
||||
updatedAt: channelObject.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
getRuntimeVersion(channelInfo: UpdateChannelBasicInfoFragment): string | undefined {
|
||||
if (isRollout(channelInfo)) {
|
||||
const updatedRolloutInfo = getRolloutInfo(channelInfo);
|
||||
if (isConstrainedRolloutInfo(updatedRolloutInfo)) {
|
||||
return updatedRolloutInfo.runtimeVersion;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async getChannelObjectAsync(
|
||||
ctx: EASUpdateContext,
|
||||
{ channelName, runtimeVersion }: { channelName: string; runtimeVersion?: string }
|
||||
): Promise<UpdateChannelObject> {
|
||||
const { graphqlClient, app } = ctx;
|
||||
const { projectId } = app;
|
||||
return await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName,
|
||||
...(runtimeVersion ? { filter: { runtimeVersions: [runtimeVersion] } } : {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,8 @@ export enum MainMenuActions {
|
||||
export type RolloutActions =
|
||||
| MainMenuActions.CREATE_NEW
|
||||
| ManageRolloutActions.EDIT
|
||||
| ManageRolloutActions.END;
|
||||
| ManageRolloutActions.END
|
||||
| ManageRolloutActions.VIEW;
|
||||
|
||||
/**
|
||||
* Manage a rollout for the project.
|
||||
@@ -126,7 +127,11 @@ export class RolloutMainMenu implements EASUpdateAction<void> {
|
||||
toMainMenuAction(action: RolloutActions): MainMenuActions {
|
||||
if (action === MainMenuActions.CREATE_NEW) {
|
||||
return MainMenuActions.CREATE_NEW;
|
||||
} else if (action === ManageRolloutActions.EDIT || action === ManageRolloutActions.END) {
|
||||
} else if (
|
||||
action === ManageRolloutActions.EDIT ||
|
||||
action === ManageRolloutActions.END ||
|
||||
action === ManageRolloutActions.VIEW
|
||||
) {
|
||||
return MainMenuActions.MANAGE_EXISTING;
|
||||
} else {
|
||||
throw new Error(`Action not supported yet: ` + action);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { testBasicChannelInfo, testChannelObject } from '../../../channel/__tests__/fixtures';
|
||||
import { getAlwaysTrueBranchMapping, getBranchMapping } from '../../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../../commands/channel/edit';
|
||||
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
|
||||
import { confirmAsync } from '../../../prompts';
|
||||
import { RolloutBranchMapping, editRolloutBranchMapping } from '../../branch-mapping';
|
||||
import { EditRollout } from '../EditRollout';
|
||||
|
||||
jest.mock('../../../prompts');
|
||||
jest.mock('../../../graphql/queries/ChannelQuery');
|
||||
jest.mock('../../../commands/channel/edit');
|
||||
describe(EditRollout, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('throws if the channel doesnt have an active rollout', async () => {
|
||||
const ctx = createCtxMock();
|
||||
const editRollout = new EditRollout({
|
||||
...testBasicChannelInfo,
|
||||
branchMapping: JSON.stringify(getAlwaysTrueBranchMapping('test-branch')),
|
||||
});
|
||||
await expect(editRollout.runAsync(ctx)).rejects.toThrowError('is not a rollout');
|
||||
});
|
||||
it('edits the channel with an active rollout', async () => {
|
||||
const ctx = createCtxMock();
|
||||
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockResolvedValue(testChannelObject);
|
||||
jest.mocked(updateChannelBranchMappingAsync).mockResolvedValue(testChannelObject);
|
||||
jest.mocked(confirmAsync).mockImplementation(async () => true);
|
||||
const editRollout = new EditRollout(testChannelObject, { percent: 50 });
|
||||
await editRollout.runAsync(ctx);
|
||||
expect(updateChannelBranchMappingAsync).toBeCalledWith(
|
||||
expect.any(Function),
|
||||
expect.objectContaining({
|
||||
branchMapping: JSON.stringify(
|
||||
editRolloutBranchMapping(
|
||||
getBranchMapping(testChannelObject.branchMapping) as RolloutBranchMapping,
|
||||
50
|
||||
)
|
||||
),
|
||||
})
|
||||
);
|
||||
});
|
||||
it('works in Non-Interactive Mode', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
});
|
||||
jest.mocked(ChannelQuery.viewUpdateChannelAsync).mockResolvedValue(testChannelObject);
|
||||
jest.mocked(updateChannelBranchMappingAsync).mockResolvedValue(testChannelObject);
|
||||
jest.mocked(confirmAsync).mockImplementation(async () => true);
|
||||
const editRollout = new EditRollout(testChannelObject, { percent: 50 });
|
||||
await editRollout.runAsync(ctx);
|
||||
expect(updateChannelBranchMappingAsync).toBeCalledWith(
|
||||
expect.any(Function),
|
||||
expect.objectContaining({
|
||||
branchMapping: JSON.stringify(
|
||||
editRolloutBranchMapping(
|
||||
getBranchMapping(testChannelObject.branchMapping) as RolloutBranchMapping,
|
||||
50
|
||||
)
|
||||
),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
import { testUpdateBranch1, testUpdateBranch2 } from '../../../channel/__tests__/fixtures';
|
||||
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
|
||||
import { NonInteractiveError } from '../../../eas-update/utils';
|
||||
import { RuntimeQuery } from '../../../graphql/queries/RuntimeQuery';
|
||||
import { UpdateQuery } from '../../../graphql/queries/UpdateQuery';
|
||||
import { confirmAsync, promptAsync } from '../../../prompts';
|
||||
import { SelectRuntime } from '../SelectRuntime';
|
||||
|
||||
jest.mock('../../../prompts');
|
||||
jest.mock('../../../graphql/queries/RuntimeQuery');
|
||||
jest.mock('../../../graphql/queries/UpdateQuery');
|
||||
const runtimeFragment1 = { id: 'test-runtime-1-id', version: '1.0.0' };
|
||||
const runtimeFragment2 = { id: 'test-runtime-2-id', version: '2.0.0' };
|
||||
describe(SelectRuntime, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('returns null if project has no runtimes', async () => {
|
||||
const ctx = createCtxMock();
|
||||
jest.mocked(RuntimeQuery.getRuntimesOnBranchAsync).mockResolvedValue({
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: false,
|
||||
},
|
||||
});
|
||||
const selectRuntime = new SelectRuntime(testUpdateBranch1);
|
||||
const runtime = await selectRuntime.runAsync(ctx);
|
||||
expect(runtime).toBeNull();
|
||||
});
|
||||
it('still prompts if project has only one runtime', async () => {
|
||||
const ctx = createCtxMock();
|
||||
jest.mocked(RuntimeQuery.getRuntimesOnBranchAsync).mockResolvedValue({
|
||||
edges: [{ node: runtimeFragment1, cursor: 'cursor' }],
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: false,
|
||||
},
|
||||
});
|
||||
jest.mocked(UpdateQuery.viewUpdateGroupsOnBranchAsync).mockResolvedValue([]);
|
||||
jest.mocked(confirmAsync).mockImplementation(async () => true);
|
||||
const selectRuntime = new SelectRuntime(testUpdateBranch1);
|
||||
const runtime = await selectRuntime.runAsync(ctx);
|
||||
expect(runtime).toBe('1.0.0');
|
||||
expect(confirmAsync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('returns the selected runtime', async () => {
|
||||
jest.mocked(RuntimeQuery.getRuntimesOnBranchAsync).mockResolvedValue({
|
||||
edges: [
|
||||
{ node: runtimeFragment1, cursor: 'cursor' },
|
||||
{ node: runtimeFragment2, cursor: 'cursor' },
|
||||
],
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: false,
|
||||
},
|
||||
});
|
||||
jest.mocked(promptAsync).mockImplementation(async () => ({
|
||||
item: runtimeFragment2,
|
||||
}));
|
||||
const ctx = createCtxMock();
|
||||
const selectRuntime = new SelectRuntime(testUpdateBranch1);
|
||||
const runtime = await selectRuntime.runAsync(ctx);
|
||||
expect(runtime).toBe('2.0.0');
|
||||
});
|
||||
it('filters the runtime by intersecting branch id', async () => {
|
||||
const ctx = createCtxMock();
|
||||
jest.mocked(RuntimeQuery.getRuntimesOnBranchAsync).mockResolvedValue({
|
||||
edges: [],
|
||||
pageInfo: {
|
||||
hasPreviousPage: false,
|
||||
hasNextPage: false,
|
||||
},
|
||||
});
|
||||
const selectRuntime = new SelectRuntime(testUpdateBranch1, {
|
||||
anotherBranchToIntersectRuntimesBy: testUpdateBranch2,
|
||||
});
|
||||
const runtime = await selectRuntime.runAsync(ctx);
|
||||
expect(runtime).toBeNull();
|
||||
expect(RuntimeQuery.getRuntimesOnBranchAsync).toBeCalledWith(
|
||||
expect.any(Function),
|
||||
expect.objectContaining({
|
||||
filter: { branchId: testUpdateBranch2.id },
|
||||
})
|
||||
);
|
||||
});
|
||||
it('throws an error in Non-Interactive Mode', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
});
|
||||
const selectRuntime = new SelectRuntime(testUpdateBranch1);
|
||||
|
||||
// dont fail if users are running in non-interactive mode
|
||||
await expect(selectRuntime.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
|
||||
});
|
||||
});
|
||||
@@ -257,6 +257,17 @@ export function isRollout(channelInfo: ChannelBasicInfo): boolean {
|
||||
return isRolloutBranchMapping(branchMapping);
|
||||
}
|
||||
|
||||
export function doesTargetRollout(
|
||||
branchMapping: RolloutBranchMapping,
|
||||
runtimeVersion: string
|
||||
): boolean {
|
||||
const rolloutInfo = getRolloutInfoFromBranchMapping(branchMapping);
|
||||
if (!isConstrainedRolloutInfo(rolloutInfo)) {
|
||||
return true;
|
||||
}
|
||||
return rolloutInfo.runtimeVersion === runtimeVersion;
|
||||
}
|
||||
|
||||
export function createRolloutBranchMapping({
|
||||
defaultBranchId,
|
||||
rolloutBranchId,
|
||||
|
||||
@@ -10,9 +10,13 @@ import Log from '../log';
|
||||
import { promptAsync } from '../prompts';
|
||||
import { FormattedUpdateGroupDescription, getUpdateGroupDescriptions } from '../update/utils';
|
||||
import formatFields from '../utils/formatFields';
|
||||
import { Rollout, getRollout, isConstrainedRollout } from './branch-mapping';
|
||||
import { Rollout, getRollout, isConstrainedRollout, isRollout } from './branch-mapping';
|
||||
|
||||
export function printRollout(channel: UpdateChannelObject): void {
|
||||
if (!isRollout(channel)) {
|
||||
Log.log(`Channel ${chalk.bold(channel.name)} doesn't have a rollout.`);
|
||||
return;
|
||||
}
|
||||
const rollout = getRollout(channel);
|
||||
displayRolloutDetails(channel.name, rollout);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { App, Runtime, UpdateBranch, UpdateChannel } from '../../graphql/generated';
|
||||
import { App, Runtime, UpdateBranch, UpdateChannel, UpdateInsights } from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import { getBranchNameFromChannelNameAsync } from '../getBranchNameFromChannelNameAsync';
|
||||
|
||||
@@ -125,6 +125,7 @@ function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
|
||||
createdAt: '2022-12-07T02:24:43.487Z',
|
||||
runtimeVersion: '1.0.0',
|
||||
runtime: {} as Runtime, // Temporary fix to resolve type errors
|
||||
insights: {} as UpdateInsights, // Temporary fix to resolve type errors
|
||||
platform: 'ios',
|
||||
manifestFragment: '...',
|
||||
isRollBackToEmbedded: false,
|
||||
|
||||
@@ -5,14 +5,16 @@ import nullthrows from 'nullthrows';
|
||||
import { getUpdateGroupUrl } from '../build/utils/url';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import fetch from '../fetch';
|
||||
import { Update, UpdateInfoGroup } from '../graphql/generated';
|
||||
import { UpdateFragment, UpdateInfoGroup } from '../graphql/generated';
|
||||
import { PublishMutation } from '../graphql/mutations/PublishMutation';
|
||||
import Log, { link } from '../log';
|
||||
import { ora } from '../ora';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||
import {
|
||||
CodeSigningInfo,
|
||||
checkDirectiveBodyAgainstUpdateInfoGroup,
|
||||
checkManifestBodyAgainstUpdateInfoGroup,
|
||||
getDirectiveBodyAsync,
|
||||
getManifestBodyAsync,
|
||||
signBody,
|
||||
} from '../utils/code-signing';
|
||||
@@ -23,15 +25,7 @@ export type UpdateToRepublish = {
|
||||
groupId: string;
|
||||
branchId: string;
|
||||
branchName: string;
|
||||
} & Pick<
|
||||
Update,
|
||||
| 'message'
|
||||
| 'runtimeVersion'
|
||||
| 'manifestFragment'
|
||||
| 'platform'
|
||||
| 'gitCommitHash'
|
||||
| 'codeSigningInfo'
|
||||
>;
|
||||
} & UpdateFragment;
|
||||
|
||||
/**
|
||||
* @param updatesToPublish The update group to republish
|
||||
@@ -65,6 +59,13 @@ export async function republishAsync({
|
||||
update.branchName === arbitraryUpdate.branchName &&
|
||||
update.runtimeVersion === arbitraryUpdate.runtimeVersion;
|
||||
assert(updatesToPublish.every(isSameGroup), 'All updates must belong to the same update group');
|
||||
|
||||
assert(
|
||||
updatesToPublish.every(u => u.isRollBackToEmbedded) ||
|
||||
updatesToPublish.every(u => !u.isRollBackToEmbedded),
|
||||
'All updates must either be roll back to embedded updates or not'
|
||||
);
|
||||
|
||||
const { runtimeVersion } = arbitraryUpdate;
|
||||
|
||||
// If codesigning was created for the original update, we need to add it to the republish.
|
||||
@@ -96,16 +97,25 @@ export async function republishAsync({
|
||||
let updatesRepublished: Awaited<ReturnType<typeof PublishMutation.publishUpdateGroupAsync>>;
|
||||
|
||||
try {
|
||||
const updateInfoGroup = Object.fromEntries(
|
||||
updatesToPublish.map(update => [update.platform, JSON.parse(update.manifestFragment)])
|
||||
);
|
||||
const arbitraryUpdate = updatesToPublish[0];
|
||||
const objectToMergeIn = arbitraryUpdate.isRollBackToEmbedded
|
||||
? {
|
||||
rollBackToEmbeddedInfoGroup: Object.fromEntries(
|
||||
updatesToPublish.map(update => [update.platform, true])
|
||||
),
|
||||
}
|
||||
: {
|
||||
updateInfoGroup: Object.fromEntries(
|
||||
updatesToPublish.map(update => [update.platform, JSON.parse(update.manifestFragment)])
|
||||
),
|
||||
};
|
||||
|
||||
updatesRepublished = await PublishMutation.publishUpdateGroupAsync(graphqlClient, [
|
||||
{
|
||||
branchId: targetBranchId,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
updateInfoGroup,
|
||||
...objectToMergeIn,
|
||||
gitCommitHash: updatesToPublish[0].gitCommitHash,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
},
|
||||
@@ -120,19 +130,31 @@ export async function republishAsync({
|
||||
method: 'GET',
|
||||
headers: { accept: 'multipart/mixed' },
|
||||
});
|
||||
const manifestBody = nullthrows(await getManifestBodyAsync(response));
|
||||
|
||||
checkManifestBodyAgainstUpdateInfoGroup(
|
||||
manifestBody,
|
||||
nullthrows(nullthrows(updateInfoGroup)[newUpdate.platform as keyof UpdateInfoGroup])
|
||||
);
|
||||
let signature;
|
||||
if (newUpdate.isRollBackToEmbedded) {
|
||||
const directiveBody = nullthrows(await getDirectiveBodyAsync(response));
|
||||
|
||||
const manifestSignature = signBody(manifestBody, codeSigningInfo);
|
||||
checkDirectiveBodyAgainstUpdateInfoGroup(directiveBody);
|
||||
signature = signBody(directiveBody, codeSigningInfo);
|
||||
} else {
|
||||
const manifestBody = nullthrows(await getManifestBodyAsync(response));
|
||||
|
||||
checkManifestBodyAgainstUpdateInfoGroup(
|
||||
manifestBody,
|
||||
nullthrows(
|
||||
nullthrows(objectToMergeIn.updateInfoGroup)[
|
||||
newUpdate.platform as keyof UpdateInfoGroup
|
||||
]
|
||||
)
|
||||
);
|
||||
signature = signBody(manifestBody, codeSigningInfo);
|
||||
}
|
||||
|
||||
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
||||
alg: codeSigningInfo.codeSigningMetadata.alg,
|
||||
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
||||
sig: manifestSignature,
|
||||
sig: signature,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonAccessor, EasJsonUtils, errors } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
|
||||
import Log from '../../log';
|
||||
import { selectAsync } from '../../prompts';
|
||||
@@ -9,6 +12,7 @@ import {
|
||||
maybePrintBuildProfileDeprecationWarningsAsync,
|
||||
} from '../profiles';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../prompts');
|
||||
jest.mock('@expo/eas-json', () => {
|
||||
const actual = jest.requireActual('@expo/eas-json');
|
||||
@@ -32,6 +36,7 @@ const getBuildProfileDeprecationWarningsAsync = jest.spyOn(
|
||||
);
|
||||
const newLineSpy = jest.spyOn(Log, 'newLine');
|
||||
const warnSpy = jest.spyOn(Log, 'warn');
|
||||
const projectDir = '/app';
|
||||
|
||||
describe(getProfilesAsync, () => {
|
||||
afterEach(() => {
|
||||
@@ -51,6 +56,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profileName).toBe('production');
|
||||
@@ -71,6 +77,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
})
|
||||
).rejects.toThrowError(errors.MissingProfileError);
|
||||
});
|
||||
@@ -86,6 +93,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profileName).toBe('custom-profile');
|
||||
@@ -98,6 +106,52 @@ describe(getProfilesAsync, () => {
|
||||
expect(getBuildProfileAsync).toBeCalledWith(easJsonAccessor, Platform.IOS, 'custom-profile');
|
||||
});
|
||||
|
||||
describe('node version', () => {
|
||||
const nodeVersion = '14.17.1';
|
||||
|
||||
it('is read from profile', async () => {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
getBuildProfileAsync.mockImplementation(async () => {
|
||||
return { node: nodeVersion } as BuildProfile<Platform.ANDROID>;
|
||||
});
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profile.node).toBe(nodeVersion);
|
||||
});
|
||||
|
||||
describe('with .nvmrc', () => {
|
||||
beforeEach(async () => {
|
||||
vol.reset();
|
||||
await fs.mkdirp(os.tmpdir());
|
||||
|
||||
vol.fromJSON({ '.nvmrc': nodeVersion }, projectDir);
|
||||
});
|
||||
|
||||
it('is read from .nvmrc', async () => {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
getBuildProfileAsync.mockImplementation(async () => {
|
||||
return {} as BuildProfile<Platform.ANDROID>;
|
||||
});
|
||||
|
||||
const result = await getProfilesAsync({
|
||||
easJsonAccessor,
|
||||
platforms: [Platform.ANDROID],
|
||||
profileName: 'custom-profile',
|
||||
type: 'build',
|
||||
projectDir,
|
||||
});
|
||||
|
||||
expect(result[0].profile.node).toBe(nodeVersion);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('throws validation error if eas.json is invalid', async () => {
|
||||
getBuildProfileAsync.mockImplementation(() => {
|
||||
throw new errors.InvalidEasJsonError('eas.json is not valid');
|
||||
@@ -109,6 +163,7 @@ describe(getProfilesAsync, () => {
|
||||
platforms: [Platform.ANDROID, Platform.IOS],
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
projectDir,
|
||||
})
|
||||
).rejects.toThrowError(/eas.json is not valid/);
|
||||
});
|
||||
|
||||
@@ -56,7 +56,26 @@ export function shouldUseVersionedExpoCLIExpensive(
|
||||
return !!resolveFrom.silent(projectDir, '@expo/cli');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we can and should use `expo export` with multiple `--platform` flags.
|
||||
* This is an issue related to `expo export --all` causing issues when users have Metro web configured.
|
||||
* See: https://github.com/expo/expo/pull/23621
|
||||
*/
|
||||
export function shouldUseVersionedExpoCLIWithExplicitPlatformsExpensive(
|
||||
projectDir: string
|
||||
): boolean {
|
||||
const expoCliPath = resolveFrom.silent(projectDir, '@expo/cli/package.json');
|
||||
if (!expoCliPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return gteSdkVersion(require(expoCliPath).version, '0.10.11');
|
||||
}
|
||||
|
||||
export const shouldUseVersionedExpoCLI = memoize(shouldUseVersionedExpoCLIExpensive);
|
||||
export const shouldUseVersionedExpoCLIWithExplicitPlatforms = memoize(
|
||||
shouldUseVersionedExpoCLIWithExplicitPlatformsExpensive
|
||||
);
|
||||
|
||||
export async function expoCommandAsync(
|
||||
projectDir: string,
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
ProfileType,
|
||||
SubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import Log, { learnMore } from '../log';
|
||||
|
||||
@@ -24,19 +26,23 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
platforms,
|
||||
profileName,
|
||||
type,
|
||||
projectDir,
|
||||
}: {
|
||||
easJsonAccessor: EasJsonAccessor;
|
||||
platforms: Platform[];
|
||||
profileName?: string;
|
||||
projectDir: string;
|
||||
type: T;
|
||||
}): Promise<ProfileData<T>[]> {
|
||||
const results = platforms.map(async function (platform) {
|
||||
const profile = await readProfileAsync({
|
||||
const profile = await readProfileWithOverridesAsync({
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
type,
|
||||
profileName,
|
||||
projectDir,
|
||||
});
|
||||
|
||||
return {
|
||||
profile,
|
||||
profileName: profileName ?? 'production',
|
||||
@@ -47,16 +53,50 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
return await Promise.all(results);
|
||||
}
|
||||
|
||||
async function readProfileAsync<T extends ProfileType>({
|
||||
async function maybeSetNodeVersionFromFileAsync(
|
||||
projectDir: string,
|
||||
profile: BuildProfile<Platform>
|
||||
): Promise<void> {
|
||||
if (profile?.node) {
|
||||
return;
|
||||
}
|
||||
const nodeVersion = await getNodeVersionFromFileAsync(projectDir);
|
||||
if (nodeVersion) {
|
||||
Log.log(
|
||||
`The EAS build profile does not specify a Node.js version. Using the version specified in .nvmrc: ${nodeVersion} `
|
||||
);
|
||||
|
||||
profile.node = nodeVersion;
|
||||
}
|
||||
}
|
||||
|
||||
async function getNodeVersionFromFileAsync(projectDir: string): Promise<string | undefined> {
|
||||
const nvmrcPath = path.join(projectDir, '.nvmrc');
|
||||
if (!(await fs.pathExists(nvmrcPath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodeVersion: string;
|
||||
try {
|
||||
nodeVersion = (await fs.readFile(nvmrcPath, 'utf8')).toString().trim();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
return nodeVersion;
|
||||
}
|
||||
|
||||
async function readProfileWithOverridesAsync<T extends ProfileType>({
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
type,
|
||||
profileName,
|
||||
projectDir,
|
||||
}: {
|
||||
easJsonAccessor: EasJsonAccessor;
|
||||
platform: Platform;
|
||||
type: T;
|
||||
profileName?: string;
|
||||
projectDir: string;
|
||||
}): Promise<EasProfile<T>> {
|
||||
if (type === 'build') {
|
||||
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
@@ -66,6 +106,7 @@ async function readProfileAsync<T extends ProfileType>({
|
||||
);
|
||||
|
||||
await maybePrintBuildProfileDeprecationWarningsAsync(easJsonAccessor, platform, profileName);
|
||||
await maybeSetNodeVersionFromFileAsync(projectDir, buildProfile);
|
||||
|
||||
return buildProfile as EasProfile<T>;
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,8 @@ const humanReadableServiceName: Record<StatuspageServiceName, string> = {
|
||||
[StatuspageServiceName.EasBuild]: 'EAS Build',
|
||||
[StatuspageServiceName.EasSubmit]: 'EAS Submit',
|
||||
[StatuspageServiceName.EasUpdate]: 'EAS Update',
|
||||
[StatuspageServiceName.GithubApiRequests]: 'GitHub API Requests',
|
||||
[StatuspageServiceName.GithubWebhooks]: 'Github Webhooks',
|
||||
};
|
||||
|
||||
function warnAboutServiceOutage(service: StatuspageServiceFragment): void {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "4.1.0",
|
||||
"version": "5.3.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.18.6",
|
||||
"@expo/eas-build-job": "1.0.30",
|
||||
"@expo/eas-build-job": "1.0.39",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "10.1.0",
|
||||
"golden-fleece": "1.0.9",
|
||||
"joi": "17.7.0",
|
||||
"log-symbols": "4.1.0",
|
||||
"semver": "7.3.8",
|
||||
"semver": "7.5.2",
|
||||
"terminal-link": "2.1.1",
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
|
||||
@@ -153,6 +153,16 @@
|
||||
"description": "The version of Yarn to use for the build. See: https://www.npmjs.com/package/yarn",
|
||||
"markdownDescription": "The exact version of [Yarn](https://www.npmjs.com/package/yarn) to use for the build."
|
||||
},
|
||||
"pnpm": {
|
||||
"type": "string",
|
||||
"description": "The version of pnpm to use for the build. See: https://www.npmjs.com/package/pnpm",
|
||||
"markdownDescription": "The exact version of [pnpm](https://www.npmjs.com/package/pnpm) to use for the build."
|
||||
},
|
||||
"bun": {
|
||||
"type": "string",
|
||||
"description": "The version of Bun to use for the build. See: https://bun.sh/",
|
||||
"markdownDescription": "The exact version of [Bun](https://bun.sh/) to use for the build."
|
||||
},
|
||||
"expoCli": {
|
||||
"type": "string",
|
||||
"description": "The version of `expo-cli` used to `prebuild` your app.",
|
||||
@@ -346,26 +356,32 @@
|
||||
"enum": [
|
||||
"default",
|
||||
"latest",
|
||||
"macos-ventura-13.6-xcode-15.0",
|
||||
"macos-ventura-13.4-xcode-14.3.1",
|
||||
"macos-ventura-13.3-xcode-14.3",
|
||||
"macos-monterey-12.6-xcode-14.2",
|
||||
"macos-monterey-12.6-xcode-14.1",
|
||||
"macos-monterey-12.6-xcode-14.0",
|
||||
"macos-monterey-12.4-xcode-13.4",
|
||||
"macos-monterey-12.3-xcode-13.3",
|
||||
"macos-monterey-12.1-xcode-13.2",
|
||||
"macos-big-sur-11.4-xcode-13.0",
|
||||
"macos-big-sur-11.4-xcode-12.5"
|
||||
"macos-monterey-12.6-xcode-14.0"
|
||||
],
|
||||
"markdownEnumDescriptions": [
|
||||
"`macos-ventura-13.3-xcode-14.3` for Apple silicon builders, `macos-monterey-12.6-xcode-14.1` for Intel builders",
|
||||
"`macos-ventura-13.4-xcode-14.3.1` for Apple silicon builders, `macos-monterey-12.6-xcode-14.2` for Intel builders",
|
||||
"`macos-ventura-13.4-xcode-14.3.1`",
|
||||
"`macos-ventura-13.6-xcode-15.0`",
|
||||
"only available for Apple silicon builders",
|
||||
"only available for Apple silicon builders"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
"deprecated": true,
|
||||
"enum": [
|
||||
"macos-monterey-12.4-xcode-13.4",
|
||||
"macos-monterey-12.3-xcode-13.3",
|
||||
"macos-monterey-12.1-xcode-13.2"
|
||||
],
|
||||
"markdownEnumDescriptions": [
|
||||
"This image is deprecated, please use one of the more recent images instead.",
|
||||
"This image is deprecated, please use one of the more recent images instead.",
|
||||
"This image is deprecated, please use one of the more recent images instead."
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -39,6 +39,8 @@ const CommonBuildProfileSchema = Joi.object({
|
||||
// build environment
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
|
||||
node: Joi.string().empty(null).custom(semverCheck),
|
||||
pnpm: Joi.string().empty(null).custom(semverCheck),
|
||||
bun: Joi.string().empty(null).custom(semverCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverCheck),
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ export interface CommonBuildProfile {
|
||||
// build environment
|
||||
env?: Record<string, string>;
|
||||
node?: string;
|
||||
pnpm?: string;
|
||||
bun?: string;
|
||||
yarn?: string;
|
||||
expoCli?: string;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"fs-extra": "10.1.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"marked": "3.0.4",
|
||||
"semver": "7.3.8",
|
||||
"semver": "7.5.2",
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Generated
+4
-4
@@ -166,10 +166,10 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-3.0.4.tgz#b8a1539e5e05c6ea9e93f15c0bad1d54ce890406"
|
||||
integrity sha512-jBo8AOayNaEcvBhNobg6/BLhdsK3NvnKWJg33MAAPbvTWiG4QBn9gpW1+7RssrKu4K1dKlN+0goVQwV41xEfOA==
|
||||
|
||||
semver@7.3.8:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
semver@7.5.2:
|
||||
version "7.5.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb"
|
||||
integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
|
||||
@@ -1329,10 +1329,10 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@expo/[email protected].1":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.3.1.tgz#99a2627c167af138722af9806a09a35f87115a39"
|
||||
integrity sha512-ADPHXIpTWlt9tIg9yhwyuxQ6UMICWMfHgoCE1v8zzyYsa3BcKvawgkAGApYNxp2IYUKiNMqRYzViEKgq+Svk3w==
|
||||
"@expo/[email protected].2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.3.2.tgz#c2c80e03bb4c310e183b109ea37bbc88cef59313"
|
||||
integrity sha512-8utf2r+ka9uI1qhazBEbLzjPX0CIBvvpBHy0o4XFoLUiZDvBqGBEctduvJc49hvu/16hxVtNqGXs1U97OVKe4g==
|
||||
|
||||
"@expo/bunyan@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1395,10 +1395,18 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
version "1.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.30.tgz#f8c57a673a2cc328f4041346996c480c3d910e92"
|
||||
integrity sha512-KQGn9POn5p3guMNQJ5ykG3wffRlSEpjTCLO87d5lJll9E4u1grpqHJH2chOh5ICxg7tiWcO4iXt5S8tU7Ogpig==
|
||||
"@expo/[email protected]9":
|
||||
version "1.0.39"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.39.tgz#d3bc6cd50c499f07255bceeb9460e092a19505f7"
|
||||
integrity sha512-OqCCxLx9HRMFQDiZvfpOfNmGhsTrV15IUOhmbp9iIa+uO/VyPpBvXqiA4ENCN9Jmf6yXtirIranCeJcm+jAuSA==
|
||||
dependencies:
|
||||
joi "^17.9.2"
|
||||
semver "^7.5.4"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.43.tgz#e331b5039372c31b15aedbadc229efee3dcaefba"
|
||||
integrity sha512-LMOoDIEax31uGBGux6/ocbCjUbWK3cUHuCxrsYlU+bvba4pGifegYWt2YQ/HC2477paq+K69IGjOUCHvNWG6Yg==
|
||||
dependencies:
|
||||
joi "^17.9.2"
|
||||
semver "^7.5.4"
|
||||
@@ -12084,10 +12092,10 @@ [email protected], semver@^7.3.2:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@7.3.8, semver@^7.0.0, semver@^7.3.8:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
semver@7.5.2:
|
||||
version "7.5.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb"
|
||||
integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
@@ -12110,6 +12118,13 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.3.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.0.0, semver@^7.3.8:
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
|
||||
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^7.3.7:
|
||||
version "7.3.7"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
|
||||
@@ -13651,9 +13666,9 @@ wonka@^6.3.2:
|
||||
integrity sha512-2xXbQ1LnwNS7egVm1HPhW2FyKrekolzhpM3mCwXdQr55gO+tAiY76rhb32OL9kKsW8taj++iP7C6hxlVzbnvrw==
|
||||
|
||||
word-wrap@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
|
||||
|
||||
wordwrap@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
Reference in new issue
Block a user