mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
69
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
365a7465f4 | ||
|
|
85a66629c8 | ||
|
|
8a2d68fcb5 | ||
|
|
62819e0956 | ||
|
|
5c6880f4ce | ||
|
|
d5f562edf8 | ||
|
|
077d8b3d86 | ||
|
|
21c8131bcf | ||
|
|
18bf41782f | ||
|
|
0513fc51a6 | ||
|
|
528988dc0f | ||
|
|
7f46baeb21 | ||
|
|
608d61ed69 | ||
|
|
2b2b20a0f9 | ||
|
|
77423d1179 | ||
|
|
ebc277dfe4 | ||
|
|
427c8f152d | ||
|
|
1c245d23b4 | ||
|
|
29c4695351 | ||
|
|
7b968f6e0d | ||
|
|
8a38cc24e1 | ||
|
|
a85b9e2d80 | ||
|
|
e0f69327da | ||
|
|
76d4e65d92 | ||
|
|
58917ecbb4 | ||
|
|
e75a102361 | ||
|
|
061f626b2c | ||
|
|
3159550468 | ||
|
|
a20bd0051e | ||
|
|
0b64d1c8df | ||
|
|
fe37600323 | ||
|
|
ab507d0bdd | ||
|
|
7505b8d40a | ||
|
|
b31ddee6e7 | ||
|
|
01ce802b73 | ||
|
|
f75ef22154 | ||
|
|
7d3f4e8fd4 | ||
|
|
004caf4bb7 | ||
|
|
5a16f3d6d8 | ||
|
|
d2e4cb5fc6 | ||
|
|
2c1c9b91cb | ||
|
|
b447194c85 | ||
|
|
9ab095597a | ||
|
|
b1edff7242 | ||
|
|
4d9e37269c | ||
|
|
e7b48949db | ||
|
|
1c3edbe492 | ||
|
|
deaa3a9105 | ||
|
|
cbfcdaec5d | ||
|
|
37a32c93ee | ||
|
|
6461a78fa2 | ||
|
|
86c8ac093f | ||
|
|
608cdff981 | ||
|
|
4dc4ef85fa | ||
|
|
eaf15acb76 | ||
|
|
ae4ca91658 | ||
|
|
23ebb77952 | ||
|
|
ed2352f289 | ||
|
|
38ae8b68f1 | ||
|
|
5e02807ac9 | ||
|
|
f05d6ca3a2 | ||
|
|
b57082647d | ||
|
|
9d4fdc3c75 | ||
|
|
d1af9e385f | ||
|
|
59bbe19dde | ||
|
|
5e5b55f9e4 | ||
|
|
a1d086ffc6 | ||
|
|
2a96d04be3 | ||
|
|
d126ee988a |
No files matched your search
@@ -226,16 +226,3 @@ jobs:
|
||||
SLACK_USERNAME: 'EAS CLI CI'
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_FOOTER: ''
|
||||
open-pr-in-universe:
|
||||
name: Open a PR with `@expo/eas-json` bump in universe
|
||||
needs: publish-to-npm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Open a PR with `@expo/eas-json` version bump in universe
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
repo: expo/universe
|
||||
ref: main
|
||||
workflow: update-eas-json.yml
|
||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
+110
-1
@@ -10,15 +10,124 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Show `eas deploy` upload error messages. ([#2771](https://github.com/expo/eas-cli/pull/2771) by [@kadikraman](https://github.com/kadikraman))
|
||||
### 🧹 Chores
|
||||
|
||||
## [15.0.1](https://github.com/expo/eas-cli/releases/tag/v15.0.1) - 2025-02-04
|
||||
|
||||
## [15.0.0](https://github.com/expo/eas-cli/releases/tag/v15.0.0) - 2025-02-04
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Use Git to archive projects containing a Git repository. (Previously, Git would only be used if `requireCommit` flag in `eas.json` was set to `true`.) ([#2841](https://github.com/expo/eas-cli/pull/2841) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Print warning for `NoVcsClient` only once. ([#2863](https://github.com/expo/eas-cli/pull/2863) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add update support for fingerprint:compare. ([#2850](https://github.com/expo/eas-cli/pull/2850) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add update group id support for fingerprint:compare. ([#2851](https://github.com/expo/eas-cli/pull/2851) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add better interactive support for fingerprint:compare. ([#2854](https://github.com/expo/eas-cli/pull/2854) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [14.7.1](https://github.com/expo/eas-cli/releases/tag/v14.7.1) - 2025-01-31
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Account for `ios.config.usesNonExemptEncryption` in non-exempt encryption status prompt. ([#2852](https://github.com/expo/eas-cli/pull/2852) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
|
||||
## [14.7.0](https://github.com/expo/eas-cli/releases/tag/v14.7.0) - 2025-01-30
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `--submit` and `-s` as aliases for `--auto-submit` flag. ([#2846](https://github.com/expo/eas-cli/pull/2846) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Skip non-exempt check in non-interactive mode. ([#2849](https://github.com/expo/eas-cli/pull/2849) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
|
||||
## [14.6.0](https://github.com/expo/eas-cli/releases/tag/v14.6.0) - 2025-01-30
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Prompt to set non-exempt encryption status for the iOS app to support faster store submissions. ([#2843](https://github.com/expo/eas-cli/pull/2843) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Automatically create internal TestFlight group in EAS Submit command. ([#2839](https://github.com/expo/eas-cli/pull/2839) by [@evanbacon](https://github.com/evanbacon))
|
||||
- Sanitize and generate names for EAS Submit to prevent failures due to invalid characters or taken names. ([#2842](https://github.com/expo/eas-cli/pull/2842) by [@evanbacon](https://github.com/evanbacon))
|
||||
- Release `eas fingerprint:compare`. ([#2821](https://github.com/expo/eas-cli/pull/2821) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Make new autoIncremented builds start at nr 1 by default ([#2828](https://github.com/expo/eas-cli/pull/2828) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
## [14.5.0](https://github.com/expo/eas-cli/releases/tag/v14.5.0) - 2025-01-22
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Allow filtering by `--fingerprint_hash` in `eas build:list` command. ([#2818](https://github.com/expo/eas-cli/pull/2818) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Ensure that the AASA file is served with content type application/json ([#2829](https://github.com/expo/eas-cli/pull/2829) by [@kadikraman](https://github.com/kadikraman))
|
||||
- Ensure that the AppleID provided in prompt or saved to cache does not contain invalid unprintable characters ([#2830](https://github.com/expo/eas-cli/pull/2830) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Fix logs typos in the `eas deploy` command. ([#2822](https://github.com/expo/eas-cli/pull/2822) by [@kadikraman](https://github.com/kadikraman))
|
||||
- Make `deploy` the top level command for hosting. ([#2824](https://github.com/expo/eas-cli/pull/2824) by [@kadikraman](https://github.com/kadikraman))
|
||||
- Allow longer submit profile extension chain (up to 5, same as build profile) ([#2831](https://github.com/expo/eas-cli/pull/2831) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Make variable naming more explicit, remove deprecated runtimeFingerprintSource uses. ([#2816](https://github.com/expo/eas-cli/pull/2816) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
## [14.4.1](https://github.com/expo/eas-cli/releases/tag/v14.4.1) - 2025-01-15
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Enable shell execution for env:exec commands. ([#2788](https://github.com/expo/eas-cli/pull/2788) by [@tharakadesilva](https://github.com/tharakadesilva))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Make automatic env resolution message shorter. ([#2806](https://github.com/expo/eas-cli/pull/2806) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Make "No remote versions are configured" message green instead of yellow. ([#2805](https://github.com/expo/eas-cli/pull/2805) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Upload local fingerprint on `eas fingerprint:compare`. ([#2808](https://github.com/expo/eas-cli/pull/2808) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Upgrade `eas-cli-local-build-plugin` to `1.0.163` to support Bun's new text-based lock file in local builds. ([#2817](https://github.com/expo/eas-cli/pull/2817) by [@shiroyasha9](https://github.com/shiroyasha9))
|
||||
|
||||
## [14.4.0](https://github.com/expo/eas-cli/releases/tag/v14.4.0) - 2025-01-09
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Load `.env` variables even when `--environment` is specified for `deploy` command. Conflicts will be highlighted by a warning message. ([#2783](https://github.com/expo/eas-cli/pull/2783) by [@kitten](https://github.com/kitten))
|
||||
- Silence all non-command output in non-interactive mode of eas env:exec. ([#2800](https://github.com/expo/eas-cli/pull/2800) by [@wschurman](https://github.com/wschurman))
|
||||
- Unhide `deploy` and `deploy:alias` commands ([#2807](https://github.com/expo/eas-cli/pull/2807) by [@kitten](https://github.com/kitten))
|
||||
|
||||
## [14.3.1](https://github.com/expo/eas-cli/releases/tag/v14.3.1) - 2025-01-08
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Bump `@expo/[email protected]` to support Bun text-based lock files. ([#2801](https://github.com/expo/eas-cli/pull/2801) by [@kudo](https://github.com/kudo))
|
||||
|
||||
## [14.3.0](https://github.com/expo/eas-cli/releases/tag/v14.3.0) - 2025-01-07
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Upload `package.json` when uploading workflow sources. ([#2786](https://github.com/expo/eas-cli/pull/2786) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Show `eas deploy` upload error messages. ([#2771](https://github.com/expo/eas-cli/pull/2771) by [@kadikraman](https://github.com/kadikraman))
|
||||
- Prevent EAS CLI dependencies check from running repeatedly. ([#2781](https://github.com/expo/eas-cli/pull/2781) by [@kitten](https://github.com/kitten))
|
||||
- Prevent optimistic request body parsing for `eas deploy`. ([#2784](https://github.com/expo/eas-cli/pull/2784) by [@kadikraman](https://github.com/kadikraman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update log output for `worker` deploy and alias commands. ([#2780](https://github.com/expo/eas-cli/pull/2780) by [@kitten](https://github.com/kitten))
|
||||
- Update various messages wording. ([#2790](https://github.com/expo/eas-cli/pull/2790) by [@simek](https://github.com/simek))
|
||||
- Use node18 as tsconfig base. ([#2739](https://github.com/expo/eas-cli/pull/2739) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [14.2.0](https://github.com/expo/eas-cli/releases/tag/v14.2.0) - 2024-12-13
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `eas deploy --dry-run` flag to output tarball. ([#2761](https://github.com/expo/eas-cli/pull/2761) by [@kitten](https://github.com/kitten))
|
||||
- Allow specifying credentials for android builds. ([#2775](https://github.com/expo/eas-cli/pull/2775) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "14.2.0",
|
||||
"version": "15.0.2",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+269
-76
@@ -69,6 +69,7 @@ eas --help COMMAND
|
||||
* [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
||||
* [`eas build:configure`](#eas-buildconfigure)
|
||||
* [`eas build:delete [BUILD_ID]`](#eas-builddelete-build_id)
|
||||
* [`eas build:dev`](#eas-builddev)
|
||||
* [`eas build:inspect`](#eas-buildinspect)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:resign`](#eas-buildresign)
|
||||
@@ -88,6 +89,9 @@ eas --help COMMAND
|
||||
* [`eas config`](#eas-config)
|
||||
* [`eas credentials`](#eas-credentials)
|
||||
* [`eas credentials:configure-build`](#eas-credentialsconfigure-build)
|
||||
* [`eas deploy [2m[options][22m`](#eas-deploy-2moptions22m)
|
||||
* [`eas deploy:alias`](#eas-deployalias)
|
||||
* [`eas deploy:promote`](#eas-deploypromote)
|
||||
* [`eas device:create`](#eas-devicecreate)
|
||||
* [`eas device:delete`](#eas-devicedelete)
|
||||
* [`eas device:list`](#eas-devicelist)
|
||||
@@ -102,6 +106,7 @@ eas --help COMMAND
|
||||
* [`eas env:pull [ENVIRONMENT]`](#eas-envpull-environment)
|
||||
* [`eas env:push [ENVIRONMENT]`](#eas-envpush-environment)
|
||||
* [`eas env:update [ENVIRONMENT]`](#eas-envupdate-environment)
|
||||
* [`eas fingerprint:compare [HASH1] [HASH2]`](#eas-fingerprintcompare-hash1-hash2)
|
||||
* [`eas help [COMMAND]`](#eas-help-command)
|
||||
* [`eas init`](#eas-init)
|
||||
* [`eas init:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-initonboarding-target_project_directory)
|
||||
@@ -131,6 +136,8 @@ eas --help COMMAND
|
||||
* [`eas webhook:update`](#eas-webhookupdate)
|
||||
* [`eas webhook:view ID`](#eas-webhookview-id)
|
||||
* [`eas whoami`](#eas-whoami)
|
||||
* [`eas worker:alias`](#eas-workeralias)
|
||||
* [`eas deploy [2m[options][22m`](#eas-deploy-2moptions22m-1)
|
||||
* [`eas workflow:create [NAME]`](#eas-workflowcreate-name)
|
||||
* [`eas workflow:run [FILE]`](#eas-workflowrun-file)
|
||||
* [`eas workflow:validate PATH`](#eas-workflowvalidate-path)
|
||||
@@ -153,7 +160,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -170,7 +177,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -187,7 +194,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -201,7 +208,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -253,7 +260,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -274,7 +281,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -294,7 +301,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -314,7 +321,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -337,7 +344,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -345,16 +352,16 @@ start a build
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build [-p android|ios|all] [-e <value>] [--local] [--output <value>] [--wait] [--clear-cache]
|
||||
[--auto-submit | --auto-submit-with-profile <value>] [-m <value>] [--build-logger-level
|
||||
trace|debug|info|warn|error|fatal] [--freeze-credentials] [--json --non-interactive]
|
||||
$ eas build [-p android|ios|all] [-e <value>] [--local] [--output <value>] [--wait] [--clear-cache] [-s |
|
||||
--auto-submit-with-profile <value>] [-m <value>] [--build-logger-level trace|debug|info|warn|error|fatal]
|
||||
[--freeze-credentials] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to
|
||||
"production" if defined in eas.json.
|
||||
-m, --message=<value> A short message describing the build
|
||||
-p, --platform=(android|ios|all)
|
||||
--auto-submit Submit on build complete using the submit profile with the
|
||||
-s, --auto-submit Submit on build complete using the submit profile with the
|
||||
same name as the build profile
|
||||
--auto-submit-with-profile=PROFILE_NAME Submit on build complete using the submit profile with
|
||||
provided name
|
||||
@@ -374,7 +381,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -393,7 +400,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -410,7 +417,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:delete [BUILD_ID]`
|
||||
|
||||
@@ -429,7 +436,27 @@ DESCRIPTION
|
||||
delete a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/delete.ts)_
|
||||
|
||||
## `eas build:dev`
|
||||
|
||||
run dev client simulator/emulator build with matching fingerprint or create a new one
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:dev [-p ios|android] [-e <value>]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. It must be a profile allowing to create
|
||||
emulator/simulator internal distribution dev client builds. The "development-simulator"
|
||||
build profile will be selected by default.
|
||||
-p, --platform=(ios|android)
|
||||
|
||||
DESCRIPTION
|
||||
run dev client simulator/emulator build with matching fingerprint or create a new one
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/dev.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/dev.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -464,7 +491,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -475,8 +502,8 @@ USAGE
|
||||
$ eas build:list [-p android|ios|all] [--status
|
||||
new|in-queue|in-progress|pending-cancel|errored|finished|canceled] [--distribution store|internal|simulator]
|
||||
[--channel <value>] [--app-version <value>] [--app-build-version <value>] [--sdk-version <value>] [--runtime-version
|
||||
<value>] [--app-identifier <value>] [-e <value>] [--git-commit-hash <value>] [--offset <value>] [--limit <value>]
|
||||
[--json --non-interactive] [--simulator]
|
||||
<value>] [--app-identifier <value>] [-e <value>] [--git-commit-hash <value>] [--fingerprint-hash <value>] [--offset
|
||||
<value>] [--limit <value>] [--json --non-interactive] [--simulator]
|
||||
|
||||
FLAGS
|
||||
-e, --build-profile=<value> Filter only builds created with the
|
||||
@@ -491,6 +518,8 @@ FLAGS
|
||||
--channel=<value>
|
||||
--distribution=(store|internal|simulator) Filter only builds with the specified
|
||||
distribution type
|
||||
--fingerprint-hash=<value> Filter only builds with the specified
|
||||
fingerprint hash
|
||||
--git-commit-hash=<value> Filter only builds created with the
|
||||
specified git commit hash
|
||||
--json Enable JSON output, non-JSON messages
|
||||
@@ -514,7 +543,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -543,7 +572,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -569,7 +598,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -619,7 +648,7 @@ DESCRIPTION
|
||||
get the latest version from EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
@@ -638,7 +667,7 @@ DESCRIPTION
|
||||
update version of an app
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -657,7 +686,7 @@ DESCRIPTION
|
||||
update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -674,7 +703,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -695,7 +724,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -717,7 +746,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -737,7 +766,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:pause [NAME]`
|
||||
|
||||
@@ -759,7 +788,7 @@ DESCRIPTION
|
||||
pause a channel to stop it from sending updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/pause.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/pause.ts)_
|
||||
|
||||
## `eas channel:resume [NAME]`
|
||||
|
||||
@@ -781,7 +810,7 @@ DESCRIPTION
|
||||
resume a channel to start sending updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/resume.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/resume.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
@@ -814,7 +843,7 @@ DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -837,7 +866,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -858,7 +887,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -875,7 +904,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/credentials/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/credentials/index.ts)_
|
||||
|
||||
## `eas credentials:configure-build`
|
||||
|
||||
@@ -893,7 +922,83 @@ DESCRIPTION
|
||||
Set up credentials for building your project.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/credentials/configure-build.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
|
||||
|
||||
## `eas deploy [2m[options][22m`
|
||||
|
||||
Deploy your Expo Router web build and API Routes.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas deploy [options]
|
||||
$ eas deploy --prod
|
||||
|
||||
FLAGS
|
||||
--alias=name Custom alias to assign to the new deployment.
|
||||
--dry-run Outputs a tarball of the new deployment instead of uploading it.
|
||||
--environment=(development|preview|production) Environment variable's environment
|
||||
--export-dir=dir [default: dist] Directory where the Expo project was exported.
|
||||
--id=xyz123 Custom unique identifier for the new deployment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--prod Create a new production deployment.
|
||||
|
||||
DESCRIPTION
|
||||
Deploy your Expo Router web build and API Routes.
|
||||
|
||||
ALIASES
|
||||
$ eas worker:deploy
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/deploy/index.ts)_
|
||||
|
||||
## `eas deploy:alias`
|
||||
|
||||
Assign deployment aliases.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas deploy:alias [--prod] [--alias <value>] [--id <value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
--alias=name Custom alias to assign to the existing deployment.
|
||||
--id=xyz123 Unique identifier of an existing deployment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--prod Promote an existing deployment to production.
|
||||
|
||||
DESCRIPTION
|
||||
Assign deployment aliases.
|
||||
|
||||
ALIASES
|
||||
$ eas worker:alias
|
||||
$ eas deploy:promote
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/deploy/alias.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/deploy/alias.ts)_
|
||||
|
||||
## `eas deploy:promote`
|
||||
|
||||
Assign deployment aliases.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas deploy:promote [--prod] [--alias <value>] [--id <value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
--alias=name Custom alias to assign to the existing deployment.
|
||||
--id=xyz123 Unique identifier of an existing deployment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--prod Promote an existing deployment to production.
|
||||
|
||||
DESCRIPTION
|
||||
Assign deployment aliases.
|
||||
|
||||
ALIASES
|
||||
$ eas worker:alias
|
||||
$ eas deploy:promote
|
||||
```
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -907,7 +1012,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -927,7 +1032,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -948,7 +1053,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:rename`
|
||||
|
||||
@@ -969,7 +1074,7 @@ DESCRIPTION
|
||||
rename a registered device
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -983,7 +1088,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -997,7 +1102,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas env:create [ENVIRONMENT]`
|
||||
|
||||
@@ -1026,7 +1131,7 @@ DESCRIPTION
|
||||
create an environment variable for the current project or account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/create.ts)_
|
||||
|
||||
## `eas env:delete [ENVIRONMENT]`
|
||||
|
||||
@@ -1050,7 +1155,7 @@ DESCRIPTION
|
||||
delete an environment variable for the current project or account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/delete.ts)_
|
||||
|
||||
## `eas env:exec ENVIRONMENT BASH_COMMAND`
|
||||
|
||||
@@ -1071,7 +1176,7 @@ DESCRIPTION
|
||||
execute a command with environment variables from the selected environment
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/exec.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/exec.ts)_
|
||||
|
||||
## `eas env:get [ENVIRONMENT]`
|
||||
|
||||
@@ -1096,7 +1201,7 @@ DESCRIPTION
|
||||
view an environment variable for the current project or account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/get.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/get.ts)_
|
||||
|
||||
## `eas env:list [ENVIRONMENT]`
|
||||
|
||||
@@ -1121,7 +1226,7 @@ DESCRIPTION
|
||||
list environment variables for the current project or account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/list.ts)_
|
||||
|
||||
## `eas env:pull [ENVIRONMENT]`
|
||||
|
||||
@@ -1144,7 +1249,7 @@ DESCRIPTION
|
||||
pull environment variables for the selected environment to .env file
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/pull.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/pull.ts)_
|
||||
|
||||
## `eas env:push [ENVIRONMENT]`
|
||||
|
||||
@@ -1165,7 +1270,7 @@ DESCRIPTION
|
||||
push environment variables from .env file to the selected environment
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/push.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/push.ts)_
|
||||
|
||||
## `eas env:update [ENVIRONMENT]`
|
||||
|
||||
@@ -1196,7 +1301,46 @@ DESCRIPTION
|
||||
update an environment variable on the current project or account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/env/update.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/env/update.ts)_
|
||||
|
||||
## `eas fingerprint:compare [HASH1] [HASH2]`
|
||||
|
||||
compare fingerprints of the current project, builds and updates
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas fingerprint:compare [HASH1] [HASH2] [--build-id <value>] [--update-id <value>] [--json --non-interactive]
|
||||
|
||||
ARGUMENTS
|
||||
HASH1 If provided alone, HASH1 is compared against the current project's fingerprint.
|
||||
HASH2 If two hashes are provided, HASH1 is compared against HASH2.
|
||||
|
||||
FLAGS
|
||||
--build-id=<value>... Compare the fingerprint with the build with the specified ID
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--update-id=<value>... Compare the fingerprint with the update with the specified ID
|
||||
|
||||
DESCRIPTION
|
||||
compare fingerprints of the current project, builds and updates
|
||||
|
||||
EXAMPLES
|
||||
$ eas fingerprint:compare # Compare fingerprints in interactive mode
|
||||
|
||||
$ eas fingerprint:compare <FINGERPRINT-HASH> # Compare fingerprint against local directory
|
||||
|
||||
$ eas fingerprint:compare <FINGERPRINT-HASH-1> <FINGERPRINT-HASH-2> # Compare provided fingerprints
|
||||
|
||||
$ eas fingerprint:compare --build-id <BUILD-ID> # Compare fingerprint from build against local directory
|
||||
|
||||
$ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2> # Compare fingerprint from a build against another build
|
||||
|
||||
$ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID> # Compare fingerprint from build against fingerprint from update
|
||||
|
||||
$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> # Compare fingerprint from update against provided fingerprint
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/fingerprint/compare.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/fingerprint/compare.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -1304,7 +1448,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -1321,7 +1465,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -1338,7 +1482,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1368,7 +1512,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1382,7 +1526,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1405,7 +1549,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1423,7 +1567,7 @@ ALIASES
|
||||
$ eas onboarding
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1454,7 +1598,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1496,7 +1640,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1516,7 +1660,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1537,7 +1681,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:edit [GROUPID]`
|
||||
|
||||
@@ -1561,7 +1705,7 @@ DESCRIPTION
|
||||
edit all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/edit.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/edit.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1583,7 +1727,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1615,7 +1759,7 @@ DESCRIPTION
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:roll-back-to-embedded`
|
||||
|
||||
@@ -1643,7 +1787,7 @@ DESCRIPTION
|
||||
roll back to the embedded update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
|
||||
|
||||
## `eas update:rollback`
|
||||
|
||||
@@ -1663,7 +1807,7 @@ DESCRIPTION
|
||||
roll back to an embedded update or an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/rollback.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/rollback.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1683,7 +1827,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1704,7 +1848,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1724,7 +1868,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1742,7 +1886,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1764,7 +1908,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1781,7 +1925,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
@@ -1798,6 +1942,55 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
## `eas worker:alias`
|
||||
|
||||
Assign deployment aliases.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas worker:alias [--prod] [--alias <value>] [--id <value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
--alias=name Custom alias to assign to the existing deployment.
|
||||
--id=xyz123 Unique identifier of an existing deployment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--prod Promote an existing deployment to production.
|
||||
|
||||
DESCRIPTION
|
||||
Assign deployment aliases.
|
||||
|
||||
ALIASES
|
||||
$ eas worker:alias
|
||||
$ eas deploy:promote
|
||||
```
|
||||
|
||||
## `eas deploy [2m[options][22m`
|
||||
|
||||
Deploy your Expo Router web build and API Routes.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas deploy [options]
|
||||
$ eas deploy --prod
|
||||
|
||||
FLAGS
|
||||
--alias=name Custom alias to assign to the new deployment.
|
||||
--dry-run Outputs a tarball of the new deployment instead of uploading it.
|
||||
--environment=(development|preview|production) Environment variable's environment
|
||||
--export-dir=dir [default: dist] Directory where the Expo project was exported.
|
||||
--id=xyz123 Custom unique identifier for the new deployment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--prod Create a new production deployment.
|
||||
|
||||
DESCRIPTION
|
||||
Deploy your Expo Router web build and API Routes.
|
||||
|
||||
ALIASES
|
||||
$ eas worker:deploy
|
||||
```
|
||||
|
||||
## `eas workflow:create [NAME]`
|
||||
|
||||
create a new workflow configuration YAML file
|
||||
@@ -1816,7 +2009,7 @@ DESCRIPTION
|
||||
create a new workflow configuration YAML file
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/workflow/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/workflow/create.ts)_
|
||||
|
||||
## `eas workflow:run [FILE]`
|
||||
|
||||
@@ -1836,7 +2029,7 @@ DESCRIPTION
|
||||
Run an EAS workflow
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/workflow/run.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/workflow/run.ts)_
|
||||
|
||||
## `eas workflow:validate PATH`
|
||||
|
||||
@@ -1856,5 +2049,5 @@ DESCRIPTION
|
||||
validate a workflow configuration yaml file
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v14.2.0/packages/eas-cli/src/commands/workflow/validate.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v15.0.2/packages/eas-cli/src/commands/workflow/validate.ts)_
|
||||
<!-- commandsstop -->
|
||||
@@ -12,3 +12,6 @@ if (process.env.TMPDIR) {
|
||||
(fs.realpath as any).native = jest.fn();
|
||||
|
||||
module.exports = fs;
|
||||
|
||||
// Ensure requiring node:fs returns the mock too. This is needed for expo/config and expo/json-file.
|
||||
jest.mock('node:fs', () => fs);
|
||||
Generated
+2583
-86
File diff suppressed because it is too large.
Load diff
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "14.2.0",
|
||||
"version": "15.0.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "2.1.5",
|
||||
"@expo/apple-utils": "2.1.6",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "10.0.6",
|
||||
"@expo/config-plugins": "9.0.12",
|
||||
"@expo/eas-build-job": "1.0.156",
|
||||
"@expo/eas-json": "14.2.0",
|
||||
"@expo/env": "^0.4.0",
|
||||
"@expo/eas-build-job": "1.0.165",
|
||||
"@expo/eas-json": "15.0.0",
|
||||
"@expo/env": "^1.0.0",
|
||||
"@expo/json-file": "8.3.3",
|
||||
"@expo/logger": "1.0.117",
|
||||
"@expo/multipart-body-parser": "2.0.0",
|
||||
"@expo/osascript": "2.1.4",
|
||||
"@expo/package-manager": "1.6.1",
|
||||
"@expo/package-manager": "1.7.0",
|
||||
"@expo/pkcs12": "0.1.3",
|
||||
"@expo/plist": "0.2.0",
|
||||
"@expo/plugin-help": "5.1.23",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "1.0.156",
|
||||
"@expo/steps": "1.0.166",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "^1.26.2",
|
||||
"@oclif/plugin-autocomplete": "^2.3.10",
|
||||
@@ -95,6 +95,7 @@
|
||||
"@graphql-codegen/introspection": "4.0.0",
|
||||
"@graphql-codegen/typescript": "4.0.1",
|
||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||
"@tsconfig/node18": "18.2.4",
|
||||
"@types/cli-progress": "3.11.5",
|
||||
"@types/dateformat": "3.0.1",
|
||||
"@types/diff": "6.0.0",
|
||||
|
||||
@@ -38,7 +38,7 @@ describe(ensureProjectConfiguredAsync, () => {
|
||||
}),
|
||||
});
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeTruthy();
|
||||
const vcsClientMock = jest.mocked(new GitClient());
|
||||
const vcsClientMock = jest.mocked(new GitClient({ requireCommit: false }));
|
||||
vcsClientMock.showChangedFilesAsync.mockImplementation(async () => {});
|
||||
vcsClientMock.isCommitRequiredAsync.mockImplementation(async () => false);
|
||||
vcsClientMock.trackFileAsync.mockImplementation(async () => {});
|
||||
@@ -60,7 +60,7 @@ describe(ensureProjectConfiguredAsync, () => {
|
||||
});
|
||||
});
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeFalsy();
|
||||
const vcsClientMock = jest.mocked(new GitClient());
|
||||
const vcsClientMock = jest.mocked(new GitClient({ requireCommit: false }));
|
||||
vcsClientMock.showChangedFilesAsync.mockImplementation(async () => {});
|
||||
vcsClientMock.isCommitRequiredAsync.mockImplementation(async () => false);
|
||||
vcsClientMock.trackFileAsync.mockImplementation(async () => {});
|
||||
|
||||
@@ -1,23 +1,43 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import { AndroidBuildProfile } from '@expo/eas-json/build/build/types';
|
||||
import assert from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import { Client } from '../../../vcs/vcs';
|
||||
import {
|
||||
BumpStrategy,
|
||||
bumpVersionAsync,
|
||||
bumpVersionInAppJsonAsync,
|
||||
maybeResolveVersionsAsync,
|
||||
resolveRemoteVersionCodeAsync,
|
||||
} from '../version';
|
||||
|
||||
const fsReal = jest.requireActual('fs').promises as typeof fs;
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../commandUtils/context/contextUtils/createGraphqlClient');
|
||||
jest.mock('../../../vcs/vcs');
|
||||
jest.mock('../../../graphql/queries/AppVersionQuery');
|
||||
jest.mock('../../../graphql/mutations/AppVersionMutation');
|
||||
jest.mock('../../../ora', () => ({
|
||||
ora: jest.fn().mockImplementation(() => ({
|
||||
start: jest.fn().mockImplementation(() => ({
|
||||
succeed: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
fail: jest.fn(),
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
|
||||
const vcsClient = resolveVcsClient();
|
||||
|
||||
@@ -216,12 +236,160 @@ describe(maybeResolveVersionsAsync, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(resolveRemoteVersionCodeAsync, () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('uses current remote versionCode when remote version set, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue({
|
||||
buildVersion: '11',
|
||||
storeVersion: '1.2.3',
|
||||
});
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject();
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: {} as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('11');
|
||||
expect(createAppVersionAsyncSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('initializes versionCode from local files when remote version not set, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ versionCode: 22, versionName: '2.3.4' });
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: {} as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('22');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('2.3.4');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('22');
|
||||
});
|
||||
|
||||
it('initializes versionCode starting with 1 when remote version not set and not set in local files, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ versionCode: null, versionName: null });
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: {} as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('1');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('1.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('1');
|
||||
});
|
||||
|
||||
it('increments current remote versionCode when remote version set, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue({
|
||||
buildVersion: '11',
|
||||
storeVersion: '1.2.3',
|
||||
});
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject();
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: { autoIncrement: true } as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('12');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('3.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('12');
|
||||
});
|
||||
|
||||
it('increments versionCode from local files when remote version not set, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ versionCode: 22, versionName: '2.3.4' });
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: { autoIncrement: true } as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('23');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('2.3.4');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('23');
|
||||
});
|
||||
|
||||
it('initializes versionCode starting with 1 when remote version not set and not set in local files, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ versionCode: null, versionName: null });
|
||||
|
||||
const result = await resolveRemoteVersionCodeAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationId: 'fakeApplicationId',
|
||||
buildProfile: { autoIncrement: true } as AndroidBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('1');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('1.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
function initBareWorkflowProject({
|
||||
versionCode = 123,
|
||||
versionName = '3.0.0',
|
||||
}: {
|
||||
versionCode?: number;
|
||||
versionName?: string;
|
||||
versionCode?: number | null;
|
||||
versionName?: string | null;
|
||||
} = {}): ExpoConfig {
|
||||
const fakeExp: ExpoConfig = {
|
||||
name: 'myproject',
|
||||
@@ -239,8 +407,8 @@ function initBareWorkflowProject({
|
||||
'./android/app/build.gradle': `android {
|
||||
defaultConfig {
|
||||
applicationId "com.expo.testapp"
|
||||
versionCode ${versionCode}
|
||||
versionName "${versionName}"
|
||||
${versionCode ? `versionCode "${versionCode}"` : ''}
|
||||
${versionName ? `versionName "${versionName}"` : ''}
|
||||
}
|
||||
}`,
|
||||
'./android/app/src/main/AndroidManifest.xml': 'fake',
|
||||
|
||||
@@ -151,6 +151,7 @@ async function ensureAndroidCredentialsAsync(
|
||||
ctx.android.gradleContext
|
||||
);
|
||||
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
|
||||
name: ctx.buildProfile.keystoreName,
|
||||
app: {
|
||||
account: nullthrows(
|
||||
ctx.user.accounts.find(a => a.name === ctx.accountName),
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
transformProjectArchive,
|
||||
transformWorkflow,
|
||||
} from '../graphql';
|
||||
import { buildProfileEnvironmentToEnvironment } from '../utils/environment';
|
||||
|
||||
export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
return {
|
||||
@@ -29,6 +30,7 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
environment: buildProfileEnvironmentToEnvironment(job.environment),
|
||||
loggerLevel: job.loggerLevel
|
||||
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||
: undefined,
|
||||
|
||||
@@ -92,6 +92,7 @@ export async function prepareJobAsync(
|
||||
gradleCommand: buildProfile.gradleCommand,
|
||||
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
|
||||
buildArtifactPaths: buildProfile.buildArtifactPaths,
|
||||
environment: ctx.buildProfile.environment,
|
||||
buildType,
|
||||
username,
|
||||
...(ctx.android.versionCodeOverride && {
|
||||
|
||||
@@ -99,7 +99,7 @@ export async function maybeResolveVersionsAsync(
|
||||
exp: ExpoConfig,
|
||||
buildProfile: BuildProfile<Platform.ANDROID>,
|
||||
vcsClient: Client
|
||||
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
||||
): Promise<{ appVersion?: string; appBuildVersion?: string; isVersionInitialized?: boolean }> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
const buildGradle = await getAppBuildGradleAsync(projectDir);
|
||||
@@ -113,6 +113,11 @@ export async function maybeResolveVersionsAsync(
|
||||
resolveConfigValue(buildGradle, 'versionName', parsedGradleCommand?.flavor) ?? '1.0.0',
|
||||
appBuildVersion:
|
||||
resolveConfigValue(buildGradle, 'versionCode', parsedGradleCommand?.flavor) ?? '1',
|
||||
isVersionInitialized: !resolveConfigValue(
|
||||
buildGradle,
|
||||
'versionCode',
|
||||
parsedGradleCommand?.flavor
|
||||
),
|
||||
};
|
||||
} catch {
|
||||
return {};
|
||||
@@ -204,14 +209,18 @@ export async function resolveRemoteVersionCodeAsync(
|
||||
|
||||
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile, vcsClient);
|
||||
let currentBuildVersion: string;
|
||||
let shouldInitializeVersionCode = false;
|
||||
if (remoteVersions?.buildVersion) {
|
||||
currentBuildVersion = remoteVersions.buildVersion;
|
||||
} else {
|
||||
if (localVersions.appBuildVersion) {
|
||||
Log.warn(
|
||||
'No remote versions are configured for this project, versionCode will be initialized based on the value from the local project.'
|
||||
Log.log(
|
||||
chalk.green(
|
||||
'No remote versions are configured for this project, versionCode will be initialized based on the value from the local project.'
|
||||
)
|
||||
);
|
||||
currentBuildVersion = localVersions.appBuildVersion;
|
||||
shouldInitializeVersionCode = localVersions.isVersionInitialized ?? false;
|
||||
} else {
|
||||
Log.error(
|
||||
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
|
||||
@@ -221,7 +230,10 @@ export async function resolveRemoteVersionCodeAsync(
|
||||
}
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
} else if (
|
||||
(!buildProfile.autoIncrement && !remoteVersions?.buildVersion) ||
|
||||
shouldInitializeVersionCode
|
||||
) {
|
||||
const spinner = ora(
|
||||
`Initializing versionCode with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
|
||||
@@ -709,7 +709,10 @@ async function computeAndMaybeUploadFingerprintFromExpoUpdatesAsync<T extends Pl
|
||||
* It's ok for fingerprintSources or runtimeVersion to be empty
|
||||
* fingerprintSources only exist if the project is using runtimeVersion.policy: fingerprint
|
||||
*/
|
||||
if (!resolvedRuntimeVersion.fingerprint || !resolvedRuntimeVersion.runtimeVersion) {
|
||||
if (
|
||||
!resolvedRuntimeVersion.expoUpdatesRuntimeFingerprint ||
|
||||
!resolvedRuntimeVersion.runtimeVersion
|
||||
) {
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion.runtimeVersion ?? undefined,
|
||||
};
|
||||
@@ -717,14 +720,14 @@ async function computeAndMaybeUploadFingerprintFromExpoUpdatesAsync<T extends Pl
|
||||
|
||||
const uploadedFingerprint = await maybeUploadFingerprintAsync({
|
||||
hash: resolvedRuntimeVersion.runtimeVersion,
|
||||
fingerprint: resolvedRuntimeVersion.fingerprint,
|
||||
fingerprint: resolvedRuntimeVersion.expoUpdatesRuntimeFingerprint,
|
||||
graphqlClient: ctx.graphqlClient,
|
||||
localBuildMode: ctx.localBuildOptions.localBuildMode,
|
||||
});
|
||||
return {
|
||||
runtimeVersion: uploadedFingerprint.hash,
|
||||
fingerprintSource: uploadedFingerprint.fingerprintSource,
|
||||
fingerprintHash: resolvedRuntimeVersion.fingerprintHash ?? undefined,
|
||||
fingerprintHash: resolvedRuntimeVersion.expoUpdatesRuntimeFingerprintHash ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import fs from 'fs-extra';
|
||||
|
||||
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
||||
import Log, { learnMore } from '../log';
|
||||
import { ora } from '../ora';
|
||||
import { easCliVersion } from '../utils/easCli';
|
||||
import { Client } from '../vcs/vcs';
|
||||
|
||||
@@ -52,6 +53,78 @@ async function configureAsync({
|
||||
}
|
||||
}
|
||||
|
||||
export async function doesBuildProfileExistAsync({
|
||||
projectDir,
|
||||
profileName,
|
||||
}: {
|
||||
projectDir: string;
|
||||
profileName: string;
|
||||
}): Promise<boolean> {
|
||||
try {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const easJson = await easJsonAccessor.readRawJsonAsync();
|
||||
if (!easJson.build?.[profileName]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
Log.error(`We were unable to read ${chalk.bold('eas.json')} contents. Error: ${error}.`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createBuildProfileAsync({
|
||||
projectDir,
|
||||
profileName,
|
||||
profileContents,
|
||||
vcsClient,
|
||||
nonInteractive,
|
||||
}: {
|
||||
projectDir: string;
|
||||
profileName: string;
|
||||
profileContents: Record<string, any>;
|
||||
vcsClient: Client;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<void> {
|
||||
const spinner = ora(`Adding "${profileName}" build profile to ${chalk.bold('eas.json')}`).start();
|
||||
try {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
return {
|
||||
...easJsonRawObject,
|
||||
build: {
|
||||
...easJsonRawObject.build,
|
||||
[profileName]: profileContents,
|
||||
},
|
||||
};
|
||||
});
|
||||
await easJsonAccessor.writeAsync();
|
||||
spinner.succeed(
|
||||
`Successfully added "${profileName}" build profile to ${chalk.bold('eas.json')}.`
|
||||
);
|
||||
|
||||
if (await vcsClient.isCommitRequiredAsync()) {
|
||||
Log.newLine();
|
||||
await reviewAndCommitChangesAsync(
|
||||
vcsClient,
|
||||
`Add "${profileName}" build profile to eas.json`,
|
||||
{
|
||||
nonInteractive,
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
spinner.fail(
|
||||
`We were not able to configure "${profileName}" build profile inside of ${chalk.bold(
|
||||
'eas.json'
|
||||
)}. Error: ${error}.`
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const EAS_JSON_DEFAULT: EasJson = {
|
||||
cli: {
|
||||
version: `>= ${easCliVersion}`,
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { isEnvironment } from './utils/environment';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EnvironmentVariableEnvironment } from '../graphql/generated';
|
||||
import { EnvironmentVariablesQuery } from '../graphql/queries/EnvironmentVariablesQuery';
|
||||
import Log, { learnMore } from '../log';
|
||||
|
||||
function isEnvironment(env: string): env is EnvironmentVariableEnvironment {
|
||||
return Object.values(EnvironmentVariableEnvironment).includes(
|
||||
env as EnvironmentVariableEnvironment
|
||||
);
|
||||
}
|
||||
|
||||
export async function evaluateConfigWithEnvVarsAsync<Config extends { projectId: string }, Opts>({
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
@@ -84,13 +79,13 @@ async function resolveEnvVarsAsync({
|
||||
|
||||
if (Object.keys(serverEnvVars).length > 0) {
|
||||
Log.log(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS servers: ${Object.keys(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS: ${Object.keys(
|
||||
serverEnvVars
|
||||
).join(', ')}.`
|
||||
);
|
||||
} else {
|
||||
Log.log(
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS servers.`
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,7 +107,7 @@ async function resolveEnvVarsAsync({
|
||||
);
|
||||
if (overlappingKeys.length > 0) {
|
||||
Log.warn(
|
||||
`The following environment variables are defined in both the "${buildProfileName}" build profile "env" configuration and the "${environment.toLowerCase()}" environment on EAS servers: ${overlappingKeys.join(
|
||||
`The following environment variables are defined in both the "${buildProfileName}" build profile "env" configuration and the "${environment.toLowerCase()}" environment on EAS: ${overlappingKeys.join(
|
||||
', '
|
||||
)}. The values from the build profile configuration will be used.`
|
||||
);
|
||||
@@ -135,23 +130,18 @@ async function resolveEnvVarsAsync({
|
||||
function resolveSuggestedEnvironmentForBuildProfileConfiguration(
|
||||
buildProfile: BuildProfile
|
||||
): EnvironmentVariableEnvironment {
|
||||
const setEnvironmentMessage =
|
||||
'Set the "environment" field in the build profile if you want to specify the environment manually.';
|
||||
if (buildProfile.distribution === 'store') {
|
||||
Log.log(
|
||||
`We detected that you are building for the "store" distribution. Resolving the environment for environment variables used during the build to "production". ${setEnvironmentMessage}`
|
||||
);
|
||||
return EnvironmentVariableEnvironment.Production;
|
||||
} else {
|
||||
if (buildProfile.developmentClient) {
|
||||
Log.log(
|
||||
`We detected that you are building the development client. Resolving the environment for environment variables used during the build to "development". ${setEnvironmentMessage}`
|
||||
);
|
||||
return EnvironmentVariableEnvironment.Development;
|
||||
}
|
||||
Log.log(
|
||||
`We detected that you are building for the "internal" distribution. Resolving the environment for environment variables used during the build to "preview". ${setEnvironmentMessage}`
|
||||
);
|
||||
return EnvironmentVariableEnvironment.Preview;
|
||||
}
|
||||
const environment =
|
||||
buildProfile.distribution === 'store'
|
||||
? EnvironmentVariableEnvironment.Production
|
||||
: buildProfile.developmentClient
|
||||
? EnvironmentVariableEnvironment.Development
|
||||
: EnvironmentVariableEnvironment.Preview;
|
||||
|
||||
Log.log(
|
||||
`Resolved "${environment.toLowerCase()}" environment for the build. ${learnMore(
|
||||
'https://docs.expo.dev/eas/environment-variables/#setting-the-environment-for-your-builds'
|
||||
)}`
|
||||
);
|
||||
|
||||
return environment;
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { IosBuildProfile } from '@expo/eas-json/build/build/types';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { Target } from '../../../credentials/ios/types';
|
||||
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { readPlistAsync } from '../../../utils/plist';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import { Client } from '../../../vcs/vcs';
|
||||
import {
|
||||
BumpStrategy,
|
||||
bumpVersionAsync,
|
||||
@@ -15,9 +22,23 @@ import {
|
||||
getInfoPlistPath,
|
||||
readBuildNumberAsync,
|
||||
readShortVersionAsync,
|
||||
resolveRemoteBuildNumberAsync,
|
||||
} from '../version';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../commandUtils/context/contextUtils/createGraphqlClient');
|
||||
jest.mock('../../../vcs/vcs');
|
||||
jest.mock('../../../graphql/queries/AppVersionQuery');
|
||||
jest.mock('../../../graphql/mutations/AppVersionMutation');
|
||||
jest.mock('../../../ora', () => ({
|
||||
ora: jest.fn().mockImplementation(() => ({
|
||||
start: jest.fn().mockImplementation(() => ({
|
||||
succeed: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
fail: jest.fn(),
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
|
||||
const vcsClient = resolveVcsClient();
|
||||
|
||||
@@ -359,6 +380,154 @@ describe(getInfoPlistPath, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(resolveRemoteBuildNumberAsync, () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('uses current remote buildNumber when remote version set, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue({
|
||||
buildVersion: '11',
|
||||
storeVersion: '1.2.3',
|
||||
});
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject();
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: {} as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('11');
|
||||
expect(createAppVersionAsyncSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('initializes buildNumber from local files when remote version not set, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ buildNumber: '22', appVersion: '2.3.4' });
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: {} as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('22');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('2.3.4');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('22');
|
||||
});
|
||||
|
||||
it('initializes buildNumber starting with 1 when remote version not set and local version is 1, autoIncrement=false', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ buildNumber: '1', appVersion: '1.0.0' });
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: {} as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('1');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('1.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('1');
|
||||
});
|
||||
|
||||
it('increments current remote buildNumber when remote version set, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue({
|
||||
buildVersion: '11',
|
||||
storeVersion: '1.2.3',
|
||||
});
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject();
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: { autoIncrement: true } as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('12');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('1.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('12');
|
||||
});
|
||||
|
||||
it('increments buildNumber from local files when remote version not set, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ buildNumber: '22', appVersion: '2.3.4' });
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: { autoIncrement: true } as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('23');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('2.3.4');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('23');
|
||||
});
|
||||
|
||||
it('initializes buildNumber starting with 1 when remote version not set and local version is 1, autoIncrement=true', async () => {
|
||||
const graphQLClientMock = instance(mock<ExpoGraphqlClient>());
|
||||
const vcsClientMock = instance(mock<Client>());
|
||||
vcsClientMock.getRootPathAsync = async () => '/app';
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockResolvedValue(null);
|
||||
const createAppVersionAsyncSpy = jest.spyOn(AppVersionMutation, 'createAppVersionAsync');
|
||||
const exp = initBareWorkflowProject({ buildNumber: '1', appVersion: '1.0.0' });
|
||||
|
||||
const result = await resolveRemoteBuildNumberAsync(graphQLClientMock, {
|
||||
projectDir: '/app',
|
||||
projectId: 'fakeProjectId',
|
||||
exp,
|
||||
applicationTarget: {} as Target,
|
||||
buildProfile: { autoIncrement: true } as IosBuildProfile,
|
||||
vcsClient: vcsClientMock,
|
||||
});
|
||||
|
||||
expect(result).toBe('1');
|
||||
expect(createAppVersionAsyncSpy).toHaveBeenCalled();
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].storeVersion).toBe('1.0.0');
|
||||
expect(createAppVersionAsyncSpy.mock.calls[0][1].buildVersion).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
function initBareWorkflowProject({
|
||||
appVersion = '1.0.0',
|
||||
buildNumber = '1',
|
||||
|
||||
@@ -10,6 +10,7 @@ import { IosCredentials } from '../../credentials/ios/types';
|
||||
import { BuildParamsInput } from '../../graphql/generated';
|
||||
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
|
||||
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier';
|
||||
import { ensureNonExemptEncryptionIsDefinedForManagedProjectAsync } from '../../project/ios/exemptEncryption';
|
||||
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
|
||||
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
|
||||
@@ -28,6 +29,7 @@ export async function createIosContextAsync(
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx);
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync(ctx);
|
||||
}
|
||||
|
||||
checkNodeEnvVariable(ctx);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
transformProjectArchive,
|
||||
transformWorkflow,
|
||||
} from '../graphql';
|
||||
import { buildProfileEnvironmentToEnvironment } from '../utils/environment';
|
||||
|
||||
export function transformJob(job: Ios.Job): IosJobInput {
|
||||
return {
|
||||
@@ -31,6 +32,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
environment: buildProfileEnvironmentToEnvironment(job.environment),
|
||||
loggerLevel: job.loggerLevel
|
||||
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||
: undefined,
|
||||
|
||||
@@ -88,6 +88,7 @@ export async function prepareJobAsync(
|
||||
buildConfiguration: buildProfile.buildConfiguration,
|
||||
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
|
||||
buildArtifactPaths: buildProfile.buildArtifactPaths,
|
||||
environment: ctx.buildProfile.environment,
|
||||
username,
|
||||
...(ctx.ios.buildNumberOverride && {
|
||||
version: {
|
||||
|
||||
@@ -330,14 +330,18 @@ export async function resolveRemoteBuildNumberAsync(
|
||||
vcsClient
|
||||
);
|
||||
let currentBuildVersion: string;
|
||||
let shouldInitializeBuildNumber = false;
|
||||
if (remoteVersions?.buildVersion) {
|
||||
currentBuildVersion = remoteVersions.buildVersion;
|
||||
} else {
|
||||
if (localBuildNumber) {
|
||||
Log.warn(
|
||||
'No remote versions are configured for this project, buildNumber will be initialized based on the value from the local project.'
|
||||
Log.log(
|
||||
chalk.green(
|
||||
'No remote versions are configured for this project, buildNumber will be initialized based on the value from the local project.'
|
||||
)
|
||||
);
|
||||
currentBuildVersion = localBuildNumber;
|
||||
shouldInitializeBuildNumber = localBuildNumber === '1';
|
||||
} else {
|
||||
Log.error(
|
||||
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
|
||||
@@ -347,7 +351,10 @@ export async function resolveRemoteBuildNumberAsync(
|
||||
}
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
} else if (
|
||||
(!buildProfile.autoIncrement && !remoteVersions?.buildVersion) ||
|
||||
shouldInitializeBuildNumber
|
||||
) {
|
||||
const spinner = ora(
|
||||
`Initializing buildNumber with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
|
||||
@@ -7,7 +7,7 @@ import Log from '../log';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.152';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.168';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,6 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
|
||||
selectedImage: ctx.buildProfile.image,
|
||||
customNodeVersion: ctx.buildProfile.node,
|
||||
environment: ctx.buildProfile.environment,
|
||||
simulator: 'simulator' in ctx.buildProfile && ctx.buildProfile.simulator,
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { prepareAndroidBuildAsync } from './android/build';
|
||||
@@ -62,7 +63,7 @@ import {
|
||||
validateBuildProfileVersionSettingsAsync,
|
||||
} from '../project/remoteVersionSource';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { runAsync } from '../run/run';
|
||||
import { getEasBuildRunCachedAppPath, runAsync } from '../run/run';
|
||||
import { isRunnableOnSimulatorOrEmulator } from '../run/utils';
|
||||
import { createSubmissionContextAsync } from '../submit/context';
|
||||
import {
|
||||
@@ -99,15 +100,27 @@ export interface BuildFlags {
|
||||
repack: boolean;
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
analytics: Analytics,
|
||||
vcsClient: Client,
|
||||
projectDir: string,
|
||||
flags: BuildFlags,
|
||||
actor: Actor,
|
||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
|
||||
): Promise<{
|
||||
export async function runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
flags,
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
downloadSimBuildAutoConfirm,
|
||||
envOverride,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
analytics: Analytics;
|
||||
vcsClient: Client;
|
||||
projectDir: string;
|
||||
flags: BuildFlags;
|
||||
actor: Actor;
|
||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||
downloadSimBuildAutoConfirm?: boolean;
|
||||
envOverride?: Env;
|
||||
}): Promise<{
|
||||
buildIds: string[];
|
||||
}> {
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
@@ -192,13 +205,15 @@ export async function runBuildAndSubmitAsync(
|
||||
for (const buildProfile of buildProfiles) {
|
||||
const platform = toAppPlatform(buildProfile.platform);
|
||||
|
||||
const { env } = await evaluateConfigWithEnvVarsAsync({
|
||||
buildProfile: buildProfile.profile,
|
||||
buildProfileName: buildProfile.profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: buildProfile.profile.env },
|
||||
});
|
||||
const { env } = !envOverride
|
||||
? await evaluateConfigWithEnvVarsAsync({
|
||||
buildProfile: buildProfile.profile,
|
||||
buildProfileName: buildProfile.profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: buildProfile.profile.env },
|
||||
})
|
||||
: { env: envOverride };
|
||||
|
||||
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
|
||||
projectDir,
|
||||
@@ -308,7 +323,7 @@ export async function runBuildAndSubmitAsync(
|
||||
[BuildStatus.Errored, BuildStatus.Canceled, BuildStatus.PendingCancel].includes(build?.status)
|
||||
);
|
||||
|
||||
await maybeDownloadAndRunSimulatorBuildsAsync(builds, flags);
|
||||
await maybeDownloadAndRunSimulatorBuildsAsync(builds, flags, downloadSimBuildAutoConfirm);
|
||||
|
||||
if (haveAllBuildsFailedOrCanceled || !flags.autoSubmit) {
|
||||
if (flags.json) {
|
||||
@@ -531,18 +546,29 @@ function exitWithNonZeroCodeIfSomeBuildsFailed(maybeBuilds: (BuildFragment | nul
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadAndRunAsync(build: BuildFragment): Promise<void> {
|
||||
export async function downloadAndRunAsync(build: BuildFragment): Promise<void> {
|
||||
assert(build.artifacts?.applicationArchiveUrl);
|
||||
const cachedAppPath = getEasBuildRunCachedAppPath(build.project.id, build.id, build.platform);
|
||||
|
||||
if (await pathExists(cachedAppPath)) {
|
||||
Log.newLine();
|
||||
Log.log(`Using cached app...`);
|
||||
await runAsync(cachedAppPath, build.platform);
|
||||
return;
|
||||
}
|
||||
|
||||
const buildPath = await downloadAndMaybeExtractAppAsync(
|
||||
build.artifacts.applicationArchiveUrl,
|
||||
build.platform
|
||||
build.platform,
|
||||
cachedAppPath
|
||||
);
|
||||
await runAsync(buildPath, build.platform);
|
||||
}
|
||||
|
||||
async function maybeDownloadAndRunSimulatorBuildsAsync(
|
||||
builds: MaybeBuildFragment[],
|
||||
flags: BuildFlags
|
||||
flags: BuildFlags,
|
||||
autoConfirm?: boolean
|
||||
): Promise<void> {
|
||||
const simBuilds = builds.filter(truthy).filter(isRunnableOnSimulatorOrEmulator);
|
||||
|
||||
@@ -550,11 +576,15 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
|
||||
for (const simBuild of simBuilds) {
|
||||
if (simBuild.platform === AppPlatform.Android || process.platform === 'darwin') {
|
||||
Log.newLine();
|
||||
const confirm = await confirmAsync({
|
||||
message: `Install and run the ${
|
||||
simBuild.platform === AppPlatform.Android ? 'Android' : 'iOS'
|
||||
} build on ${simBuild.platform === AppPlatform.Android ? 'an emulator' : 'a simulator'}?`,
|
||||
});
|
||||
const confirm =
|
||||
autoConfirm ??
|
||||
(await confirmAsync({
|
||||
message: `Install and run the ${
|
||||
simBuild.platform === AppPlatform.Android ? 'Android' : 'iOS'
|
||||
} build on ${
|
||||
simBuild.platform === AppPlatform.Android ? 'an emulator' : 'a simulator'
|
||||
}?`,
|
||||
}));
|
||||
if (confirm) {
|
||||
await downloadAndRunAsync(simBuild);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { EnvironmentVariableEnvironment } from '../../graphql/generated';
|
||||
|
||||
type Environment = NonNullable<BuildProfile['environment']>;
|
||||
|
||||
const BuildProfileEnvironmentToEnvironment: Record<Environment, EnvironmentVariableEnvironment> = {
|
||||
production: EnvironmentVariableEnvironment.Production,
|
||||
preview: EnvironmentVariableEnvironment.Preview,
|
||||
development: EnvironmentVariableEnvironment.Development,
|
||||
};
|
||||
|
||||
export function isEnvironment(env: string): env is EnvironmentVariableEnvironment {
|
||||
return Object.values(EnvironmentVariableEnvironment).includes(
|
||||
env as EnvironmentVariableEnvironment
|
||||
);
|
||||
}
|
||||
|
||||
export function buildProfileEnvironmentToEnvironment(
|
||||
environment: BuildProfile['environment']
|
||||
): EnvironmentVariableEnvironment | null {
|
||||
if (!environment) {
|
||||
return null;
|
||||
}
|
||||
return BuildProfileEnvironmentToEnvironment[environment];
|
||||
}
|
||||
+6
-1
@@ -103,6 +103,8 @@ export async function findProjectRootAsync({
|
||||
}
|
||||
}
|
||||
|
||||
let ranEnsureEasCliIsNotInDependencies = false;
|
||||
|
||||
/**
|
||||
* Determine the project root directory and ensure some constraints about the project setup
|
||||
* like CLI version and dependencies.
|
||||
@@ -113,6 +115,9 @@ export async function findProjectRootAsync({
|
||||
export async function findProjectDirAndVerifyProjectSetupAsync(): Promise<string> {
|
||||
const projectDir = await findProjectRootAsync();
|
||||
await applyCliConfigAsync(projectDir);
|
||||
await ensureEasCliIsNotInDependenciesAsync(projectDir);
|
||||
if (!ranEnsureEasCliIsNotInDependencies) {
|
||||
ranEnsureEasCliIsNotInDependencies = true;
|
||||
await ensureEasCliIsNotInDependenciesAsync(projectDir);
|
||||
}
|
||||
return projectDir;
|
||||
}
|
||||
+2
-2
@@ -42,13 +42,13 @@ export async function loadServerSideEnvironmentVariablesAsync({
|
||||
|
||||
if (Object.keys(serverEnvVars).length > 0) {
|
||||
Log.log(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS servers: ${Object.keys(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS: ${Object.keys(
|
||||
serverEnvVars
|
||||
).join(', ')}.`
|
||||
);
|
||||
} else {
|
||||
Log.log(
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS servers.`
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS.`
|
||||
);
|
||||
}
|
||||
Log.newLine();
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonAccessor } from '@expo/eas-json';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {
|
||||
createBuildProfileAsync,
|
||||
doesBuildProfileExistAsync,
|
||||
ensureProjectConfiguredAsync,
|
||||
} from '../../build/configure';
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../../build/evaluateConfigWithEnvVarsAsync';
|
||||
import { downloadAndRunAsync, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { BuildFragment, BuildStatus, DistributionType } from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { toAppPlatform } from '../../graphql/types/AppPlatform';
|
||||
import Log from '../../log';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { confirmAsync, promptAsync } from '../../prompts';
|
||||
import { expoCommandAsync } from '../../utils/expoCli';
|
||||
import { createFingerprintAsync } from '../../utils/fingerprintCli';
|
||||
import { ProfileData, getProfilesAsync } from '../../utils/profiles';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
const DEFAULT_EAS_BUILD_RUN_PROFILE_NAME = 'development-simulator';
|
||||
|
||||
export default class BuildDev extends EasCommand {
|
||||
static override hidden: true;
|
||||
|
||||
static override description =
|
||||
'run dev client simulator/emulator build with matching fingerprint or create a new one';
|
||||
|
||||
static override flags = {
|
||||
platform: Flags.enum<Platform.IOS | Platform.ANDROID>({
|
||||
char: 'p',
|
||||
options: [Platform.IOS, Platform.ANDROID],
|
||||
}),
|
||||
profile: Flags.string({
|
||||
char: 'e',
|
||||
description: `Name of the build profile from eas.json. It must be a profile allowing to create emulator/simulator internal distribution dev client builds. The "${DEFAULT_EAS_BUILD_RUN_PROFILE_NAME}" build profile will be selected by default.`,
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.ProjectDir,
|
||||
...this.ContextOptions.Vcs,
|
||||
...this.ContextOptions.Analytics,
|
||||
...this.ContextOptions.ProjectId,
|
||||
};
|
||||
|
||||
protected override async runAsync(): Promise<any> {
|
||||
const { flags } = await this.parse(BuildDev);
|
||||
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
projectDir,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectId,
|
||||
} = await this.getContextAsync(BuildDev, {
|
||||
nonInteractive: false,
|
||||
withServerSideEnvironment: null,
|
||||
});
|
||||
|
||||
const platform = await this.selectPlatformAsync(flags.platform);
|
||||
if (process.platform !== 'darwin' && platform === Platform.IOS) {
|
||||
Errors.error('Running iOS builds in simulator is only supported on macOS.', { exit: 1 });
|
||||
}
|
||||
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
|
||||
await ensureProjectConfiguredAsync({
|
||||
projectDir,
|
||||
nonInteractive: false,
|
||||
vcsClient,
|
||||
});
|
||||
|
||||
const buildProfile = await this.ensureValidBuildRunProfileExistsAsync({
|
||||
projectDir,
|
||||
platform,
|
||||
selectedBuildProfileName: flags.profile,
|
||||
vcsClient,
|
||||
});
|
||||
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform, vcsClient);
|
||||
|
||||
const { env } = await evaluateConfigWithEnvVarsAsync({
|
||||
buildProfile: buildProfile.profile,
|
||||
buildProfileName: buildProfile.profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: buildProfile.profile.env },
|
||||
});
|
||||
|
||||
const fingerprint = await createFingerprintAsync(projectDir, {
|
||||
env,
|
||||
workflow,
|
||||
platforms: [platform],
|
||||
});
|
||||
if (!fingerprint) {
|
||||
Errors.error('Failed to calculate fingerprint', { exit: 1 });
|
||||
}
|
||||
Log.log(`✨ Calculated fingerprint hash: ${fingerprint.hash}`);
|
||||
Log.newLine();
|
||||
|
||||
const builds = await this.getBuildsAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
platform,
|
||||
fingerprint,
|
||||
});
|
||||
if (builds.length !== 0) {
|
||||
const build = builds[0];
|
||||
Log.succeed(
|
||||
`🎯 Found successful build with matching fingerprint on EAS servers. Running it...`
|
||||
);
|
||||
|
||||
if (build.artifacts?.applicationArchiveUrl) {
|
||||
await downloadAndRunAsync(build);
|
||||
await this.startDevServerAsync({ projectDir, platform });
|
||||
return;
|
||||
} else {
|
||||
Log.warn('Artifacts for this build expired. New build will be started.');
|
||||
}
|
||||
}
|
||||
|
||||
Log.log('🚀 No successful build with matching fingerprint found. Starting a new build...');
|
||||
|
||||
const previousBuildsForSelectedProfile = await this.getBuildsAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
platform,
|
||||
});
|
||||
if (
|
||||
previousBuildsForSelectedProfile.length > 0 &&
|
||||
previousBuildsForSelectedProfile[0].metrics?.buildDuration
|
||||
) {
|
||||
Log.log(
|
||||
`🕒 Previous build for "${buildProfile.profileName}" profile completed in ${Math.floor(
|
||||
previousBuildsForSelectedProfile[0].metrics.buildDuration / 60000
|
||||
)} minutes.`
|
||||
);
|
||||
}
|
||||
|
||||
await runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
flags: {
|
||||
requestedPlatform:
|
||||
platform === Platform.ANDROID ? RequestedPlatform.Android : RequestedPlatform.Ios,
|
||||
nonInteractive: false,
|
||||
freezeCredentials: false,
|
||||
wait: true,
|
||||
clearCache: false,
|
||||
json: false,
|
||||
autoSubmit: false,
|
||||
localBuildOptions: {},
|
||||
repack: false,
|
||||
profile: flags.profile ?? DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
},
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
downloadSimBuildAutoConfirm: true,
|
||||
envOverride: env,
|
||||
});
|
||||
await this.startDevServerAsync({ projectDir, platform });
|
||||
}
|
||||
|
||||
private async selectPlatformAsync(platform?: Platform): Promise<Platform> {
|
||||
if (platform) {
|
||||
return platform;
|
||||
}
|
||||
const { resolvedPlatform } = await promptAsync({
|
||||
type: 'select',
|
||||
message: 'Select platform',
|
||||
name: 'resolvedPlatform',
|
||||
choices: [
|
||||
{ title: 'Android', value: Platform.ANDROID },
|
||||
{ title: 'iOS', value: Platform.IOS },
|
||||
],
|
||||
});
|
||||
return resolvedPlatform;
|
||||
}
|
||||
|
||||
private async validateBuildRunProfileAsync({
|
||||
platform,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
}: {
|
||||
platform: Platform;
|
||||
buildProfile: BuildProfile;
|
||||
buildProfileName: string;
|
||||
}): Promise<void> {
|
||||
if (buildProfile.developmentClient !== true) {
|
||||
Errors.error(
|
||||
`Profile "${buildProfileName}" must specify "developmentClient: true" to create a dev client build. Select a different profile or update the profile in eas.json.`,
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
if (buildProfile.distribution !== 'internal') {
|
||||
Errors.error(
|
||||
`Profile "${buildProfileName}" must specify "distribution: internal" in order to work with eas build:dev command. Select a different profile or update the profile in eas.json.`,
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
|
||||
if (platform === Platform.IOS) {
|
||||
const iosProfile = buildProfile as BuildProfile<Platform.IOS>;
|
||||
if (iosProfile.simulator !== true && iosProfile.withoutCredentials !== true) {
|
||||
Errors.error(
|
||||
`Profile "${buildProfileName}" must specify "ios.simulator: true" or "withoutCredentials: true" to create an iOS simulator build. Select a different profile or update the profile in eas.json.`,
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const androidProfile = buildProfile as BuildProfile<Platform.ANDROID>;
|
||||
if (
|
||||
androidProfile.distribution !== 'internal' &&
|
||||
androidProfile.withoutCredentials !== true
|
||||
) {
|
||||
Errors.error(
|
||||
`Profile "${buildProfileName}" must specify "distribution: internal" or "withoutCredentials: true" to create an Android emulator build. Select a different profile or update the profile in eas.json.`,
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureValidBuildRunProfileExistsAsync({
|
||||
projectDir,
|
||||
platform,
|
||||
selectedBuildProfileName,
|
||||
vcsClient,
|
||||
}: {
|
||||
projectDir: string;
|
||||
platform: Platform;
|
||||
selectedBuildProfileName?: string;
|
||||
vcsClient: Client;
|
||||
}): Promise<ProfileData<'build'>> {
|
||||
if (
|
||||
!!selectedBuildProfileName ||
|
||||
(await doesBuildProfileExistAsync({
|
||||
projectDir,
|
||||
profileName: DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
}))
|
||||
) {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const [buildProfile] = await getProfilesAsync({
|
||||
type: 'build',
|
||||
easJsonAccessor,
|
||||
platforms: [platform],
|
||||
profileName: selectedBuildProfileName ?? DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
projectDir,
|
||||
});
|
||||
await this.validateBuildRunProfileAsync({
|
||||
buildProfileName: selectedBuildProfileName ?? DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
platform,
|
||||
buildProfile: buildProfile.profile,
|
||||
});
|
||||
} else {
|
||||
const createBuildProfile = await confirmAsync({
|
||||
message: `We want to go ahead and generate "${DEFAULT_EAS_BUILD_RUN_PROFILE_NAME}" build profile for you, that matches eas build:dev criteria. Do you want to proceed?`,
|
||||
});
|
||||
if (!createBuildProfile) {
|
||||
Errors.error(
|
||||
'Come back later or specify different build compliant with eas build:dev requirements by using "--profile" flag.',
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
await createBuildProfileAsync({
|
||||
projectDir,
|
||||
profileName: DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
profileContents: {
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
ios: {
|
||||
simulator: true,
|
||||
},
|
||||
environment: 'development',
|
||||
},
|
||||
nonInteractive: false,
|
||||
vcsClient,
|
||||
});
|
||||
}
|
||||
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const [buildProfile] = await getProfilesAsync({
|
||||
type: 'build',
|
||||
easJsonAccessor,
|
||||
platforms: [platform],
|
||||
profileName: selectedBuildProfileName ?? DEFAULT_EAS_BUILD_RUN_PROFILE_NAME,
|
||||
projectDir,
|
||||
});
|
||||
return buildProfile;
|
||||
}
|
||||
|
||||
private async getBuildsAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
platform,
|
||||
fingerprint,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
platform: Platform;
|
||||
fingerprint?: { hash: string };
|
||||
}): Promise<BuildFragment[]> {
|
||||
return await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
filter: {
|
||||
platform: toAppPlatform(platform),
|
||||
fingerprintHash: fingerprint?.hash,
|
||||
status: BuildStatus.Finished,
|
||||
simulator: platform === Platform.IOS ? true : undefined,
|
||||
distribution: platform === Platform.ANDROID ? DistributionType.Internal : undefined,
|
||||
developmentClient: true,
|
||||
},
|
||||
offset: 0,
|
||||
limit: 1,
|
||||
});
|
||||
}
|
||||
|
||||
private async startDevServerAsync({
|
||||
projectDir,
|
||||
platform,
|
||||
}: {
|
||||
projectDir: string;
|
||||
platform: Platform;
|
||||
}): Promise<void> {
|
||||
Log.newLine();
|
||||
Log.log(
|
||||
`Starting development server: ${chalk.dim(
|
||||
`npx expo start --dev-client ${platform === Platform.IOS ? '--ios' : '--android'}`
|
||||
)}`
|
||||
);
|
||||
await expoCommandAsync(projectDir, [
|
||||
'start',
|
||||
'--dev-client',
|
||||
platform === Platform.IOS ? '--ios' : '--android',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -78,10 +78,12 @@ export default class Build extends EasCommand {
|
||||
description: 'Clear cache before the build',
|
||||
}),
|
||||
'auto-submit': Flags.boolean({
|
||||
char: 's',
|
||||
default: false,
|
||||
description:
|
||||
'Submit on build complete using the submit profile with the same name as the build profile',
|
||||
exclusive: ['auto-submit-with-profile'],
|
||||
aliases: ['submit'],
|
||||
}),
|
||||
'auto-submit-with-profile': Flags.string({
|
||||
description: 'Submit on build complete using the submit profile with provided name',
|
||||
@@ -158,15 +160,15 @@ export default class Build extends EasCommand {
|
||||
|
||||
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
await runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
flagsWithPlatform,
|
||||
flags: flagsWithPlatform,
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync
|
||||
);
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
});
|
||||
}
|
||||
|
||||
private sanitizeFlags(
|
||||
|
||||
@@ -99,12 +99,12 @@ export default class BuildInspect extends EasCommand {
|
||||
await this.copyToOutputDirAsync(tmpWorkingdir, outputDirectory);
|
||||
} else {
|
||||
try {
|
||||
await runBuildAndSubmitAsync(
|
||||
await runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
{
|
||||
flags: {
|
||||
nonInteractive: false,
|
||||
freezeCredentials: false,
|
||||
wait: true,
|
||||
@@ -124,8 +124,8 @@ export default class BuildInspect extends EasCommand {
|
||||
repack: false,
|
||||
},
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync
|
||||
);
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
});
|
||||
if (!flags.verbose) {
|
||||
Log.log(chalk.green('Build successful'));
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import GitNoCommitClient from '../../vcs/clients/gitNoCommit';
|
||||
import NoVcsClient from '../../vcs/clients/noVcs';
|
||||
import GitClient from '../../vcs/clients/git';
|
||||
|
||||
/**
|
||||
* This command will be run on the EAS Build workers, when building
|
||||
@@ -64,18 +63,24 @@ export default class BuildInternal extends EasCommand {
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(BuildInternal, {
|
||||
nonInteractive: true,
|
||||
vcsClientOverride: process.env.EAS_NO_VCS ? new NoVcsClient() : new GitNoCommitClient(),
|
||||
withServerSideEnvironment: null,
|
||||
});
|
||||
|
||||
if (vcsClient instanceof GitClient) {
|
||||
// `build:internal` is run on EAS workers and the repo may have been changed
|
||||
// by pre-install hooks or other scripts. We don't want to require committing changes
|
||||
// to continue the build.
|
||||
vcsClient.requireCommit = false;
|
||||
}
|
||||
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
await runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
{
|
||||
flags: {
|
||||
requestedPlatform: flags.platform as RequestedPlatform,
|
||||
profile: flags.profile,
|
||||
nonInteractive: true,
|
||||
@@ -91,7 +96,7 @@ export default class BuildInternal extends EasCommand {
|
||||
repack: false,
|
||||
},
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync
|
||||
);
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,9 @@ export default class BuildList extends EasCommand {
|
||||
aliases: ['gitCommitHash'],
|
||||
description: 'Filter only builds created with the specified git commit hash',
|
||||
}),
|
||||
'fingerprint-hash': Flags.string({
|
||||
description: 'Filter only builds with the specified fingerprint hash',
|
||||
}),
|
||||
...EasPaginatedQueryFlags,
|
||||
limit: getLimitFlagWithCustomValues({ defaultTo: 10, limit: BUILDS_LIMIT }),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
@@ -130,6 +133,7 @@ export default class BuildList extends EasCommand {
|
||||
buildProfile: flags['build-profile'],
|
||||
gitCommitHash: flags['git-commit-hash'],
|
||||
simulator: flags.simulator,
|
||||
fingerprintHash: flags['fingerprint-hash'],
|
||||
},
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import assert from 'assert';
|
||||
import { pathExists } from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { getLatestBuildAsync, listAndSelectBuildOnAppAsync } from '../../build/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
@@ -17,13 +16,12 @@ import Log from '../../log';
|
||||
import { appPlatformDisplayNames } from '../../platform';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { RunArchiveFlags, runAsync } from '../../run/run';
|
||||
import { RunArchiveFlags, getEasBuildRunCachedAppPath, runAsync } from '../../run/run';
|
||||
import { isRunnableOnSimulatorOrEmulator } from '../../run/utils';
|
||||
import {
|
||||
downloadAndMaybeExtractAppAsync,
|
||||
extractAppFromLocalArchiveAsync,
|
||||
} from '../../utils/download';
|
||||
import { getEasBuildRunCacheDirectoryPath } from '../../utils/paths';
|
||||
|
||||
interface RawRunFlags {
|
||||
latest?: boolean;
|
||||
@@ -245,17 +243,6 @@ async function maybeGetBuildAsync(
|
||||
}
|
||||
}
|
||||
|
||||
function getEasBuildRunCachedAppPath(
|
||||
projectId: string,
|
||||
buildId: string,
|
||||
platform: AppPlatform
|
||||
): string {
|
||||
return path.join(
|
||||
getEasBuildRunCacheDirectoryPath(),
|
||||
`${projectId}_${buildId}.${platform === AppPlatform.Ios ? 'app' : 'apk'}`
|
||||
);
|
||||
}
|
||||
|
||||
async function getPathToSimulatorBuildAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
|
||||
@@ -120,7 +120,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform, vcsClient);
|
||||
if (!remoteVersions?.buildVersion) {
|
||||
Log.warn(
|
||||
`Skipping versions sync for ${platformDisplayName}. There are no versions configured on Expo servers, use "eas build:version:set" or run a build to initialize it.`
|
||||
`Skipping versions sync for ${platformDisplayName}. There are no versions configured on EAS, use "eas build:version:set" or run a build to initialize it.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
+4
-7
@@ -33,12 +33,9 @@ interface RawDeployAliasFlags {
|
||||
}
|
||||
|
||||
export default class WorkerAlias extends EasCommand {
|
||||
static override description = 'Assign deployment aliases';
|
||||
static override aliases = ['deploy:alias', 'deploy:promote'];
|
||||
|
||||
// TODO(@kitten): Keep command hidden until worker deployments are live
|
||||
static override hidden = true;
|
||||
static override state = 'beta';
|
||||
static override description = 'Assign deployment aliases.';
|
||||
static override aliases = ['worker:alias', 'deploy:promote'];
|
||||
static override state = 'preview';
|
||||
|
||||
static override flags = {
|
||||
prod: Flags.boolean({
|
||||
@@ -73,7 +70,7 @@ export default class WorkerAlias extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
Log.warn('EAS Worker Deployments are in beta and subject to breaking changes.');
|
||||
Log.warn('EAS Hosting is still in preview and subject to changes.');
|
||||
|
||||
const {
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
+17
-12
@@ -65,13 +65,10 @@ interface DeployInProgressParams {
|
||||
}
|
||||
|
||||
export default class WorkerDeploy extends EasCommand {
|
||||
static override description = 'Deploy your Expo web build';
|
||||
static override aliases = ['deploy'];
|
||||
static override description = 'Deploy your Expo Router web build and API Routes.';
|
||||
static override aliases = ['worker:deploy'];
|
||||
static override usage = [chalk`deploy {dim [options]}`, `deploy --prod`];
|
||||
|
||||
// TODO(@kitten): Keep command hidden until worker deployments are live
|
||||
static override hidden = true;
|
||||
static override state = 'beta';
|
||||
static override state = 'preview';
|
||||
|
||||
static override flags = {
|
||||
prod: Flags.boolean({
|
||||
@@ -114,7 +111,7 @@ export default class WorkerDeploy extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
Log.warn('EAS Worker Deployments are in beta and subject to breaking changes.');
|
||||
Log.warn('EAS Hosting is still in preview and subject to changes.');
|
||||
|
||||
const {
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
@@ -253,7 +250,7 @@ export default class WorkerDeploy extends EasCommand {
|
||||
let progress = ora('Preparing project').start();
|
||||
|
||||
try {
|
||||
const manifest = await WorkerAssets.createManifestAsync(
|
||||
const manifestResult = await WorkerAssets.createManifestAsync(
|
||||
{
|
||||
environment: flags.environment,
|
||||
projectDir,
|
||||
@@ -261,13 +258,20 @@ export default class WorkerDeploy extends EasCommand {
|
||||
},
|
||||
graphqlClient
|
||||
);
|
||||
if (manifestResult.conflictingVariableNames?.length) {
|
||||
Log.warn(
|
||||
'> The following environment variables were present in local .env files as well as EAS environment variables. ' +
|
||||
'In case of conflict, the EAS environment variable values will be used: ' +
|
||||
manifestResult.conflictingVariableNames.join(' ')
|
||||
);
|
||||
}
|
||||
assetMap = await WorkerAssets.createAssetMapAsync(
|
||||
projectDist.type === 'server' ? projectDist.clientPath : projectDist.path
|
||||
);
|
||||
tarPath = await WorkerAssets.packFilesIterableAsync(
|
||||
emitWorkerTarballAsync({
|
||||
assetMap,
|
||||
manifest,
|
||||
manifest: manifestResult.manifest,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -426,7 +430,7 @@ async function resolveExportedProjectAsync(
|
||||
|
||||
if (!exportDirStat?.isDirectory()) {
|
||||
throw new Error(
|
||||
`No "${flags.exportDir}/" folder found. Prepare your project for deployment with "npx expo export --platform web"`
|
||||
`No "${flags.exportDir}/" folder found. Export your app with "npx expo export --platform web"`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -451,7 +455,8 @@ function logExportedProjectInfo(
|
||||
// Only show the timestamp for exports older than 1 minute
|
||||
if (project.modifiedAt && Date.now() - project.modifiedAt.getTime() > 60_000) {
|
||||
modifiedAgo = ` - exported ${formatTimeAgo(project.modifiedAt)}`;
|
||||
Log.warn(`> Project export: ${project.type}${modifiedAgo}`);
|
||||
} else {
|
||||
Log.log(chalk`{dim > Project export: ${project.type}}`);
|
||||
}
|
||||
|
||||
Log.log(chalk`{dim > Project export: ${project.type}${modifiedAgo}}`);
|
||||
}
|
||||
@@ -147,10 +147,10 @@ export default class DeviceDelete extends EasCommand {
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
chosenDevice: AppleDevice | AppleDeviceQueryResult
|
||||
): Promise<void> {
|
||||
const removalSpinner = ora(`Removing Apple device on Expo`).start();
|
||||
const removalSpinner = ora(`Removing Apple device on EAS`).start();
|
||||
try {
|
||||
await AppleDeviceMutation.deleteAppleDeviceAsync(graphqlClient, chosenDevice.id);
|
||||
removalSpinner.succeed('Removed Apple device from Expo');
|
||||
removalSpinner.succeed('Removed Apple device from EAS');
|
||||
} catch (err) {
|
||||
removalSpinner.fail();
|
||||
throw err;
|
||||
|
||||
@@ -105,12 +105,12 @@ export default class DeviceRename extends EasCommand {
|
||||
chosenDevice: AppleDevice | AppleDeviceQueryResult,
|
||||
newDeviceName: string
|
||||
): Promise<void> {
|
||||
const removalSpinner = ora(`Renaming Apple device on Expo`).start();
|
||||
const removalSpinner = ora(`Renaming Apple device on EAS`).start();
|
||||
try {
|
||||
await AppleDeviceMutation.updateAppleDeviceAsync(graphqlClient, chosenDevice.id, {
|
||||
name: newDeviceName,
|
||||
});
|
||||
removalSpinner.succeed('Renamed Apple device on Expo');
|
||||
removalSpinner.succeed('Renamed Apple device on EAS');
|
||||
} catch (err) {
|
||||
removalSpinner.fail();
|
||||
throw err;
|
||||
|
||||
+69
-18
@@ -53,9 +53,10 @@ export default class EnvExec extends EasCommand {
|
||||
},
|
||||
];
|
||||
|
||||
private isNonInteractive: boolean = false;
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags, args } = await this.parse(EnvExec);
|
||||
|
||||
const parsedFlags = this.sanitizeFlagsAndArgs(flags, args);
|
||||
|
||||
const {
|
||||
@@ -65,6 +66,8 @@ export default class EnvExec extends EasCommand {
|
||||
nonInteractive: parsedFlags.nonInteractive,
|
||||
});
|
||||
|
||||
this.isNonInteractive = parsedFlags.nonInteractive;
|
||||
|
||||
const environment =
|
||||
parsedFlags.environment ??
|
||||
(await promptVariableEnvironmentAsync({ nonInteractive: parsedFlags.nonInteractive }));
|
||||
@@ -72,12 +75,20 @@ export default class EnvExec extends EasCommand {
|
||||
graphqlClient,
|
||||
projectId,
|
||||
environment,
|
||||
nonInteractive: parsedFlags.nonInteractive,
|
||||
});
|
||||
|
||||
await this.runCommandWithEnvVarsAsync({
|
||||
command: parsedFlags.command,
|
||||
environmentVariables,
|
||||
});
|
||||
if (parsedFlags.nonInteractive) {
|
||||
await this.runCommandNonInteractiveWithEnvVarsAsync({
|
||||
command: parsedFlags.command,
|
||||
environmentVariables,
|
||||
});
|
||||
} else {
|
||||
await this.runCommandWithEnvVarsAsync({
|
||||
command: parsedFlags.command,
|
||||
environmentVariables,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private sanitizeFlagsAndArgs(
|
||||
@@ -96,13 +107,48 @@ export default class EnvExec extends EasCommand {
|
||||
throw new Error("Invalid environment. Use one of 'production', 'preview', or 'development'.");
|
||||
}
|
||||
|
||||
const firstChar = bash_command[0];
|
||||
const lastChar = bash_command[bash_command.length - 1];
|
||||
const cleanCommand =
|
||||
(firstChar === '"' && lastChar === '"') || (firstChar === "'" && lastChar === "'")
|
||||
? bash_command.slice(1, -1)
|
||||
: bash_command;
|
||||
|
||||
return {
|
||||
nonInteractive: rawFlags['non-interactive'],
|
||||
environment,
|
||||
command: bash_command,
|
||||
command: cleanCommand,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
protected override async catch(err: Error): Promise<any> {
|
||||
// when in non-interactive, make the behavior of this command a pure passthrough, outputting only the subprocess' stdout and stderr and exiting with the
|
||||
// sub-command's exit status
|
||||
if (this.isNonInteractive) {
|
||||
process.exitCode = process.exitCode ?? (err as any).status ?? 1;
|
||||
} else {
|
||||
await super.catch(err);
|
||||
}
|
||||
}
|
||||
|
||||
private async runCommandNonInteractiveWithEnvVarsAsync({
|
||||
command,
|
||||
environmentVariables,
|
||||
}: {
|
||||
command: string;
|
||||
environmentVariables: Record<string, string>;
|
||||
}): Promise<void> {
|
||||
await spawnAsync(command, [], {
|
||||
shell: true,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
...environmentVariables,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async runCommandWithEnvVarsAsync({
|
||||
command,
|
||||
environmentVariables,
|
||||
@@ -111,7 +157,8 @@ export default class EnvExec extends EasCommand {
|
||||
environmentVariables: Record<string, string>;
|
||||
}): Promise<void> {
|
||||
Log.log(`Running command: ${chalk.bold(command)}`);
|
||||
const spawnPromise = spawnAsync('bash', ['-c', command], {
|
||||
const spawnPromise = spawnAsync(command, [], {
|
||||
shell: true,
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -147,10 +194,12 @@ export default class EnvExec extends EasCommand {
|
||||
graphqlClient,
|
||||
projectId,
|
||||
environment,
|
||||
nonInteractive,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
environment: EnvironmentVariableEnvironment;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<Record<string, string>> {
|
||||
const environmentVariablesQueryResult =
|
||||
await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
|
||||
@@ -162,18 +211,20 @@ export default class EnvExec extends EasCommand {
|
||||
({ value }) => !!value
|
||||
);
|
||||
|
||||
if (nonSecretEnvironmentVariables.length > 0) {
|
||||
Log.log(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS: ${nonSecretEnvironmentVariables
|
||||
.map(e => e.name)
|
||||
.join(', ')}.`
|
||||
);
|
||||
} else {
|
||||
Log.log(
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS servers.`
|
||||
);
|
||||
if (!nonInteractive) {
|
||||
if (nonSecretEnvironmentVariables.length > 0) {
|
||||
Log.log(
|
||||
`Environment variables with visibility "Plain text" and "Sensitive" loaded from the "${environment.toLowerCase()}" environment on EAS: ${nonSecretEnvironmentVariables
|
||||
.map(e => e.name)
|
||||
.join(', ')}.`
|
||||
);
|
||||
} else {
|
||||
Log.log(
|
||||
`No environment variables with visibility "Plain text" and "Sensitive" found for the "${environment.toLowerCase()}" environment on EAS.`
|
||||
);
|
||||
}
|
||||
Log.newLine();
|
||||
}
|
||||
Log.newLine();
|
||||
|
||||
const environmentVariables: Record<string, string> = {};
|
||||
for (const { name, value } of nonSecretEnvironmentVariables) {
|
||||
|
||||
@@ -1,33 +1,94 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { getExpoWebsiteBaseUrl } from '../../api';
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { fetchBuildsAsync, formatBuild } from '../../commandUtils/builds';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { AppPlatform, BuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildStatus,
|
||||
FingerprintFragment,
|
||||
UpdateFragment,
|
||||
} from '../../graphql/generated';
|
||||
import { FingerprintMutation } from '../../graphql/mutations/FingerprintMutation';
|
||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { FingerprintQuery } from '../../graphql/queries/FingerprintQuery';
|
||||
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
|
||||
import Log from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync';
|
||||
import {
|
||||
getDisplayNameForProjectIdAsync,
|
||||
getOwnerAccountForProjectIdAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
|
||||
import { selectAsync } from '../../prompts';
|
||||
import { promptAsync, selectAsync } from '../../prompts';
|
||||
import { selectUpdateGroupOnBranchAsync } from '../../update/queries';
|
||||
import { Fingerprint, FingerprintDiffItem } from '../../utils/fingerprint';
|
||||
import { createFingerprintAsync, diffFingerprint } from '../../utils/fingerprintCli';
|
||||
import { abridgedDiff } from '../../utils/fingerprintDiff';
|
||||
import formatFields, { FormatFieldsItem } from '../../utils/formatFields';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
enum FingerprintOriginType {
|
||||
Build = 'build',
|
||||
Update = 'update',
|
||||
Hash = 'hash',
|
||||
Project = 'project',
|
||||
}
|
||||
|
||||
type FingerprintOrigin = {
|
||||
type: FingerprintOriginType;
|
||||
build?: BuildFragment;
|
||||
update?: UpdateFragment;
|
||||
};
|
||||
|
||||
export default class FingerprintCompare extends EasCommand {
|
||||
static override description = 'compare fingerprints of the current project, builds and updates';
|
||||
static override hidden = true;
|
||||
static override strict = false;
|
||||
|
||||
static override examples = [
|
||||
'$ eas fingerprint:compare \t # Compare fingerprints in interactive mode',
|
||||
'$ eas fingerprint:compare <FINGERPRINT-HASH> \t # Compare fingerprint against local directory',
|
||||
'$ eas fingerprint:compare <FINGERPRINT-HASH-1> <FINGERPRINT-HASH-2> \t # Compare provided fingerprints',
|
||||
'$ eas fingerprint:compare --build-id <BUILD-ID> \t # Compare fingerprint from build against local directory',
|
||||
'$ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2>\t # Compare fingerprint from a build against another build',
|
||||
'$ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID>\t # Compare fingerprint from build against fingerprint from update',
|
||||
'$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> \t # Compare fingerprint from update against provided fingerprint',
|
||||
];
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'hash1',
|
||||
description:
|
||||
"If provided alone, HASH1 is compared against the current project's fingerprint.",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'hash2',
|
||||
description: 'If two hashes are provided, HASH1 is compared against HASH2.',
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
'build-id': Flags.string({
|
||||
aliases: ['buildId'],
|
||||
description: 'Compare the fingerprint with the build with the specified ID',
|
||||
multiple: true,
|
||||
}),
|
||||
'update-id': Flags.string({
|
||||
aliases: ['updateId'],
|
||||
description: 'Compare the fingerprint with the update with the specified ID',
|
||||
multiple: true,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
@@ -40,8 +101,16 @@ export default class FingerprintCompare extends EasCommand {
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(FingerprintCompare);
|
||||
const { json: jsonFlag, 'non-interactive': nonInteractive, buildId: buildIdFromArg } = flags;
|
||||
const { args, flags } = await this.parse(FingerprintCompare);
|
||||
const { hash1, hash2 } = args;
|
||||
const {
|
||||
json,
|
||||
'non-interactive': nonInteractive,
|
||||
'build-id': buildIds,
|
||||
'update-id': updateIds,
|
||||
} = flags;
|
||||
const [buildId1, buildId2] = buildIds ?? [];
|
||||
const [updateId1, updateId2] = updateIds ?? [];
|
||||
|
||||
const {
|
||||
projectId,
|
||||
@@ -52,56 +121,65 @@ export default class FingerprintCompare extends EasCommand {
|
||||
nonInteractive,
|
||||
withServerSideEnvironment: null,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
if (json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
let buildId: string | null = buildIdFromArg;
|
||||
if (!buildId) {
|
||||
if (nonInteractive) {
|
||||
throw new Error('Build ID must be provided in non-interactive mode');
|
||||
const firstFingerprintInfo = await getFingerprintInfoAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
{
|
||||
nonInteractive,
|
||||
buildId: buildId1,
|
||||
updateId: updateId1,
|
||||
hash: hash1,
|
||||
}
|
||||
);
|
||||
const { fingerprint: firstFingerprint, origin: firstFingerprintOrigin } = firstFingerprintInfo;
|
||||
|
||||
buildId = await selectBuildToCompareAsync(graphqlClient, projectId, displayName, {
|
||||
filters: { hasFingerprint: true },
|
||||
});
|
||||
if (!buildId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const isFirstFingerprintSpecifiedByFlagOrArg = hash1 || buildId1 || updateId1;
|
||||
const isSecondFingerprintSpecifiedByFlagOrArg = hash2 || buildId2 || updateId2;
|
||||
const secondFingerprintInfo = await getFingerprintInfoAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
{
|
||||
nonInteractive,
|
||||
buildId: buildId2,
|
||||
updateId: updateId2,
|
||||
hash: hash2,
|
||||
useProjectFingerprint:
|
||||
isFirstFingerprintSpecifiedByFlagOrArg && !isSecondFingerprintSpecifiedByFlagOrArg,
|
||||
},
|
||||
firstFingerprintInfo
|
||||
);
|
||||
const { fingerprint: secondFingerprint, origin: secondFingerprintOrigin } =
|
||||
secondFingerprintInfo;
|
||||
|
||||
Log.log(`Comparing fingerprints of the current project and build ${buildId}…`);
|
||||
const buildWithFingerprint = await BuildQuery.withFingerprintByIdAsync(graphqlClient, buildId);
|
||||
const fingerprintDebugUrl = buildWithFingerprint.fingerprint?.debugInfoUrl;
|
||||
if (!fingerprintDebugUrl) {
|
||||
Log.error('A fingerprint for the build could not be found.');
|
||||
if (json) {
|
||||
printJsonOnlyOutput({ fingerprint1: firstFingerprint, fingerprint2: secondFingerprint });
|
||||
return;
|
||||
}
|
||||
const fingerprintResponse = await fetch(fingerprintDebugUrl);
|
||||
const fingerprint = (await fingerprintResponse.json()) as Fingerprint;
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const buildPlatform = buildWithFingerprint.platform;
|
||||
const workflow = workflows[appPlatformToPlatform(buildPlatform)];
|
||||
|
||||
const projectFingerprint = await createFingerprintAsync(projectDir, {
|
||||
workflow,
|
||||
platforms: [appPlatformToString(buildPlatform)],
|
||||
debug: true,
|
||||
env: undefined,
|
||||
});
|
||||
if (!projectFingerprint) {
|
||||
Log.error('Project fingerprints can only be computed for projects with SDK 52 or higher');
|
||||
return;
|
||||
}
|
||||
if (fingerprint.hash === projectFingerprint.hash) {
|
||||
Log.log(`✅ Project fingerprint matches build`);
|
||||
if (firstFingerprint.hash === secondFingerprint.hash) {
|
||||
Log.log(
|
||||
`✅ ${capitalizeFirstLetter(
|
||||
prettyPrintFingerprint(firstFingerprint, firstFingerprintOrigin)
|
||||
)} matches ${prettyPrintFingerprint(secondFingerprint, secondFingerprintOrigin)}`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
Log.log(`🔄 Project fingerprint differs from build`);
|
||||
Log.log(
|
||||
`🔄 ${capitalizeFirstLetter(
|
||||
prettyPrintFingerprint(firstFingerprint, firstFingerprintOrigin)
|
||||
)} differs from ${prettyPrintFingerprint(secondFingerprint, secondFingerprintOrigin)}`
|
||||
);
|
||||
}
|
||||
|
||||
const fingerprintDiffs = diffFingerprint(projectDir, fingerprint, projectFingerprint);
|
||||
const fingerprintDiffs = diffFingerprint(projectDir, firstFingerprint, secondFingerprint);
|
||||
if (!fingerprintDiffs) {
|
||||
Log.error('Fingerprint diffs can only be computed for projects with SDK 52 or higher');
|
||||
return;
|
||||
@@ -154,6 +232,386 @@ export default class FingerprintCompare extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
async function getFingerprintInfoAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
{
|
||||
buildId,
|
||||
updateId,
|
||||
hash,
|
||||
useProjectFingerprint,
|
||||
nonInteractive,
|
||||
}: {
|
||||
buildId?: string;
|
||||
updateId?: string;
|
||||
hash: string;
|
||||
useProjectFingerprint?: boolean;
|
||||
nonInteractive: boolean;
|
||||
},
|
||||
firstFingerprintInfo?: {
|
||||
fingerprint: Fingerprint;
|
||||
platforms?: AppPlatform[];
|
||||
origin: FingerprintOrigin;
|
||||
}
|
||||
): Promise<{ fingerprint: Fingerprint; origin: FingerprintOrigin }> {
|
||||
if (hash) {
|
||||
return await getFingerprintInfoFromHashAsync(graphqlClient, projectId, hash);
|
||||
} else if (updateId) {
|
||||
return await getFingerprintInfoFromUpdateGroupIdOrUpdateIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
nonInteractive,
|
||||
updateId
|
||||
);
|
||||
} else if (buildId) {
|
||||
return await getFingerprintInfoFromBuildIdAsync(graphqlClient, buildId);
|
||||
} else if (useProjectFingerprint) {
|
||||
if (!firstFingerprintInfo) {
|
||||
throw new Error(
|
||||
'First fingerprint must be provided in order to compare against the project.'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoFromLocalProjectAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
}
|
||||
|
||||
if (nonInteractive) {
|
||||
throw new Error(
|
||||
'Insufficent arguments provided for fingerprint comparison in non-interactive mode'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoInteractiveAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
}
|
||||
|
||||
async function getFingerprintInfoInteractiveAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
firstFingerprintInfo?: {
|
||||
fingerprint: Fingerprint;
|
||||
platforms?: AppPlatform[];
|
||||
origin: FingerprintOrigin;
|
||||
}
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const prompt = firstFingerprintInfo
|
||||
? 'Select the second fingerprint to compare against'
|
||||
: 'Select a reference fingerprint for comparison';
|
||||
const originType = await selectAsync<FingerprintOriginType>(prompt, [
|
||||
...(firstFingerprintInfo
|
||||
? [{ title: 'Current project fingerprint', value: FingerprintOriginType.Project }]
|
||||
: []),
|
||||
{ title: 'Build fingerprint', value: FingerprintOriginType.Build },
|
||||
{ title: 'Update fingerprint', value: FingerprintOriginType.Update },
|
||||
{ title: 'Enter a fingerprint hash manually', value: FingerprintOriginType.Hash },
|
||||
]);
|
||||
if (originType === FingerprintOriginType.Project) {
|
||||
if (!firstFingerprintInfo) {
|
||||
throw new Error(
|
||||
'First fingerprint must be provided in order to compare against the project.'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoFromLocalProjectAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
} else if (originType === FingerprintOriginType.Build) {
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
const buildId = await selectBuildToCompareAsync(graphqlClient, projectId, displayName, {
|
||||
filters: { hasFingerprint: true },
|
||||
});
|
||||
if (!buildId) {
|
||||
throw new Error('Must select build with fingerprint for comparison.');
|
||||
}
|
||||
return await getFingerprintInfoFromBuildIdAsync(graphqlClient, buildId);
|
||||
} else if (originType === FingerprintOriginType.Update) {
|
||||
const selectedBranch = await selectBranchOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
promptTitle: 'On which branch would you like search for an update?',
|
||||
displayTextForListItem: updateBranch => ({
|
||||
title: updateBranch.name,
|
||||
}),
|
||||
paginatedQueryOptions: {
|
||||
json: false,
|
||||
nonInteractive: false,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const selectedUpdateGroup = await selectUpdateGroupOnBranchAsync(graphqlClient, {
|
||||
projectId,
|
||||
branchName: selectedBranch.name,
|
||||
paginatedQueryOptions: {
|
||||
json: false,
|
||||
nonInteractive: false,
|
||||
offset: 0,
|
||||
},
|
||||
});
|
||||
const updateGroupId = selectedUpdateGroup[0].group;
|
||||
return await getFingerprintInfoFromUpdateGroupIdOrUpdateIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
false,
|
||||
updateGroupId
|
||||
);
|
||||
} else if (originType === FingerprintOriginType.Hash) {
|
||||
const { hash } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'hash',
|
||||
message: 'Provide the fingerprint hash',
|
||||
validate: (value: string) => !!value.trim(),
|
||||
hint: '0000000000000000000000000000000000000000',
|
||||
});
|
||||
return await getFingerprintInfoFromHashAsync(graphqlClient, projectId, hash);
|
||||
} else {
|
||||
throw new Error(`Unsupported fingerprint origin type: ${originType}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function getFingerprintInfoFromLocalProjectAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
firstFingerprintInfo: {
|
||||
fingerprint: Fingerprint;
|
||||
platforms?: AppPlatform[];
|
||||
origin: FingerprintOrigin;
|
||||
}
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const firstFingerprintPlatforms = firstFingerprintInfo.platforms;
|
||||
if (!firstFingerprintPlatforms) {
|
||||
throw new Error(
|
||||
`Cannot compare the local directory against the provided fingerprint hash "${firstFingerprintInfo.fingerprint.hash}" because the associated platform could not be determined. Ensure the fingerprint is linked to a build or update to identify the platform.`
|
||||
);
|
||||
}
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const optionsFromWorkflow = getFingerprintOptionsFromWorkflow(
|
||||
firstFingerprintPlatforms,
|
||||
workflows
|
||||
);
|
||||
|
||||
const projectFingerprint = await createFingerprintAsync(projectDir, {
|
||||
...optionsFromWorkflow,
|
||||
platforms: firstFingerprintPlatforms.map(appPlatformToString),
|
||||
debug: true,
|
||||
env: undefined,
|
||||
});
|
||||
if (!projectFingerprint) {
|
||||
throw new Error('Project fingerprints can only be computed for projects with SDK 52 or higher');
|
||||
}
|
||||
|
||||
const uploadedFingerprint = await maybeUploadFingerprintAsync({
|
||||
hash: projectFingerprint.hash,
|
||||
fingerprint: {
|
||||
fingerprintSources: projectFingerprint.sources,
|
||||
isDebugFingerprintSource: Log.isDebug,
|
||||
},
|
||||
graphqlClient,
|
||||
});
|
||||
await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, {
|
||||
hash: uploadedFingerprint.hash,
|
||||
source: uploadedFingerprint.fingerprintSource,
|
||||
});
|
||||
|
||||
return { fingerprint: projectFingerprint, origin: { type: FingerprintOriginType.Project } };
|
||||
}
|
||||
|
||||
async function getFingerprintFromUpdateFragmentAsync(
|
||||
updateWithFingerprint: UpdateFragment
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
if (!updateWithFingerprint.fingerprint) {
|
||||
throw new Error(`Fingerprint for update ${updateWithFingerprint.id} was not computed.`);
|
||||
} else if (!updateWithFingerprint.fingerprint.debugInfoUrl) {
|
||||
throw new Error(`Fingerprint source for update ${updateWithFingerprint.id} was not computed.`);
|
||||
}
|
||||
|
||||
return {
|
||||
fingerprint: await getFingerprintFromFingerprintFragmentAsync(
|
||||
updateWithFingerprint.fingerprint
|
||||
),
|
||||
platforms: [stringToAppPlatform(updateWithFingerprint.platform)],
|
||||
origin: {
|
||||
type: FingerprintOriginType.Update,
|
||||
update: updateWithFingerprint,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function getFingerprintInfoFromHashAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
hash: string
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const fingerprintFragment = await getFingerprintFragmentFromHashAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
hash
|
||||
);
|
||||
const fingerprint = await getFingerprintFromFingerprintFragmentAsync(fingerprintFragment);
|
||||
let platforms;
|
||||
const fingerprintBuilds = fingerprintFragment.builds?.edges.map(edge => edge.node) ?? [];
|
||||
const fingerprintUpdates = fingerprintFragment.updates?.edges.map(edge => edge.node) ?? [];
|
||||
if (fingerprintBuilds.length > 0) {
|
||||
platforms = [fingerprintBuilds[0].platform];
|
||||
} else if (fingerprintUpdates.length > 0) {
|
||||
platforms = [stringToAppPlatform(fingerprintUpdates[0].platform)];
|
||||
}
|
||||
return {
|
||||
fingerprint,
|
||||
platforms,
|
||||
origin: {
|
||||
type: FingerprintOriginType.Hash,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function getFingerprintInfoFromUpdateGroupIdOrUpdateIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
nonInteractive: boolean,
|
||||
updateGroupIdOrUpdateId: string
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
// Some people may pass in update group id instead of update id, so add interactive support for that
|
||||
try {
|
||||
const maybeUpdateGroupId = updateGroupIdOrUpdateId;
|
||||
const updateGroup = await UpdateQuery.viewUpdateGroupAsync(graphqlClient, {
|
||||
groupId: maybeUpdateGroupId,
|
||||
});
|
||||
if (updateGroup.length === 1) {
|
||||
const update = updateGroup[0];
|
||||
return await getFingerprintFromUpdateFragmentAsync(update);
|
||||
}
|
||||
if (nonInteractive) {
|
||||
const [accountName, project] = await Promise.all([
|
||||
(await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name,
|
||||
AppQuery.byIdAsync(graphqlClient, projectId),
|
||||
]);
|
||||
const updateUrl =
|
||||
getExpoWebsiteBaseUrl() +
|
||||
`/accounts/${accountName}/projects/${project.name}/updates/${maybeUpdateGroupId}`;
|
||||
throw new Error(
|
||||
`Please pass in your update ID from ${updateUrl} or use interactive mode to select the update ID.`
|
||||
);
|
||||
}
|
||||
const update = await selectAsync<UpdateFragment>(
|
||||
'Select a platform to compute the fingerprint from',
|
||||
updateGroup.map(update => ({
|
||||
title: update.platform,
|
||||
value: update,
|
||||
}))
|
||||
);
|
||||
return await getFingerprintFromUpdateFragmentAsync(update);
|
||||
} catch (error: any) {
|
||||
if (!error?.message.includes('Could not find any updates with group ID')) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const updateId = updateGroupIdOrUpdateId;
|
||||
const updateWithFingerprint = await UpdateQuery.viewByUpdateAsync(graphqlClient, {
|
||||
updateId,
|
||||
});
|
||||
return await getFingerprintFromUpdateFragmentAsync(updateWithFingerprint);
|
||||
}
|
||||
|
||||
async function getFingerprintInfoFromBuildIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
buildId: string
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const buildWithFingerprint = await BuildQuery.withFingerprintByIdAsync(graphqlClient, buildId);
|
||||
if (!buildWithFingerprint.fingerprint) {
|
||||
throw new Error(`Fingerprint for build ${buildId} was not computed.`);
|
||||
} else if (!buildWithFingerprint.fingerprint.debugInfoUrl) {
|
||||
throw new Error(`Fingerprint source for build ${buildId} was not computed.`);
|
||||
}
|
||||
return {
|
||||
fingerprint: await getFingerprintFromFingerprintFragmentAsync(buildWithFingerprint.fingerprint),
|
||||
platforms: [buildWithFingerprint.platform],
|
||||
origin: {
|
||||
type: FingerprintOriginType.Build,
|
||||
build: buildWithFingerprint,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function getFingerprintFragmentFromHashAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
hash: string
|
||||
): Promise<FingerprintFragment> {
|
||||
const fingerprint = await FingerprintQuery.byHashAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
hash,
|
||||
});
|
||||
if (!fingerprint) {
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
throw new Error(`Fingerprint with hash ${hash} was not uploaded for ${displayName}.`);
|
||||
}
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
async function getFingerprintFromFingerprintFragmentAsync(
|
||||
fingerprintFragment: Pick<FingerprintFragment, 'debugInfoUrl' | 'hash'>
|
||||
): Promise<Fingerprint> {
|
||||
const fingerprintDebugUrl = fingerprintFragment.debugInfoUrl;
|
||||
if (!fingerprintDebugUrl) {
|
||||
throw new Error(
|
||||
`The source for fingerprint hash ${fingerprintFragment.hash} was not computed.`
|
||||
);
|
||||
}
|
||||
const fingerprintResponse = await fetch(fingerprintDebugUrl);
|
||||
return (await fingerprintResponse.json()) as Fingerprint;
|
||||
}
|
||||
|
||||
function getFingerprintOptionsFromWorkflow(
|
||||
platforms: AppPlatform[],
|
||||
workflowsByPlatform: Record<Platform, Workflow>
|
||||
): { workflow?: Workflow; ignorePaths?: string[] } {
|
||||
if (platforms.length === 0) {
|
||||
throw new Error('Could not determine platform from fingerprint sources');
|
||||
}
|
||||
|
||||
// Single platform case
|
||||
if (platforms.length === 1) {
|
||||
const platform = platforms[0];
|
||||
return { workflow: workflowsByPlatform[appPlatformToPlatform(platform)] };
|
||||
}
|
||||
|
||||
// Multiple platforms case
|
||||
const workflows = platforms.map(platform => workflowsByPlatform[appPlatformToPlatform(platform)]);
|
||||
|
||||
// If all workflows are the same, return the common workflow
|
||||
const [firstWorkflow, ...restWorkflows] = workflows;
|
||||
if (restWorkflows.every(workflow => workflow === firstWorkflow)) {
|
||||
return { workflow: firstWorkflow };
|
||||
}
|
||||
|
||||
// Generate ignorePaths for mixed workflows
|
||||
const ignorePaths = platforms
|
||||
.filter(platform => workflowsByPlatform[appPlatformToPlatform(platform)] === Workflow.MANAGED)
|
||||
.map(platform => `${appPlatformToString(platform)}/**/*`);
|
||||
|
||||
return { ignorePaths };
|
||||
}
|
||||
|
||||
function printContentDiff(diff: FingerprintDiffItem): void {
|
||||
if (diff.op === 'added') {
|
||||
const sourceType = diff.addedSource.type;
|
||||
@@ -303,6 +761,23 @@ function printContentsDiff(contents1: string | Buffer, contents2: string | Buffe
|
||||
abridgedDiff(prettifiedContents1, prettifiedContents2, 0);
|
||||
}
|
||||
|
||||
function prettyPrintFingerprint(fingerprint: Fingerprint, origin: FingerprintOrigin): string {
|
||||
if (origin.type === FingerprintOriginType.Project) {
|
||||
return `fingerprint ${fingerprint.hash} from local directory`;
|
||||
} else if (origin.type === FingerprintOriginType.Update) {
|
||||
return `fingerprint ${fingerprint.hash} from ${
|
||||
origin.update?.platform ? stringToAppPlatform(origin.update?.platform) : ''
|
||||
} ${origin.type}`;
|
||||
} else if (origin.type === FingerprintOriginType.Build) {
|
||||
return `fingerprint ${fingerprint.hash} from ${origin.build?.platform} ${origin.type}`;
|
||||
}
|
||||
return `fingerprint ${fingerprint.hash}`;
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(string: string): string {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
function isJSON(str: string): boolean {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
@@ -334,6 +809,17 @@ function appPlatformToString(platform: AppPlatform): string {
|
||||
}
|
||||
}
|
||||
|
||||
function stringToAppPlatform(platform: string): AppPlatform {
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
return AppPlatform.Android;
|
||||
case 'ios':
|
||||
return AppPlatform.Ios;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function selectBuildToCompareAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
|
||||
@@ -162,7 +162,10 @@ export default class Onboarding extends EasCommand {
|
||||
cloneMethod,
|
||||
});
|
||||
|
||||
const vcsClient = new GitClient(finalTargetProjectDirectory);
|
||||
const vcsClient = new GitClient({
|
||||
maybeCwdOverride: finalTargetProjectDirectory,
|
||||
requireCommit: false,
|
||||
});
|
||||
if (!app.githubRepository) {
|
||||
await fs.remove(path.join(finalTargetProjectDirectory, '.git'));
|
||||
await runCommandAsync({
|
||||
@@ -285,12 +288,12 @@ export default class Onboarding extends EasCommand {
|
||||
} else {
|
||||
Log.log('🚀 Now we are going to trigger your first build');
|
||||
Log.log();
|
||||
const { buildIds } = await runBuildAndSubmitAsync(
|
||||
const { buildIds } = await runBuildAndSubmitAsync({
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
finalTargetProjectDirectory,
|
||||
{
|
||||
projectDir: finalTargetProjectDirectory,
|
||||
flags: {
|
||||
nonInteractive: true,
|
||||
requestedPlatform:
|
||||
platform === Platform.ANDROID ? RequestedPlatform.Android : RequestedPlatform.Ios,
|
||||
@@ -307,8 +310,9 @@ export default class Onboarding extends EasCommand {
|
||||
repack: true,
|
||||
},
|
||||
actor,
|
||||
getDynamicProjectConfigFn
|
||||
);
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
getDynamicPrivateProjectConfigAsync: getDynamicProjectConfigFn,
|
||||
});
|
||||
const buildId = buildIds[0];
|
||||
Log.log();
|
||||
Log.log('🚀 You can now go back to the website to continue:');
|
||||
|
||||
@@ -18,8 +18,7 @@ import AndroidSubmitCommand from '../../submit/android/AndroidSubmitCommand';
|
||||
import { SubmissionContext, createSubmissionContextAsync } from '../../submit/context';
|
||||
import IosSubmitCommand from '../../submit/ios/IosSubmitCommand';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import GitNoCommitClient from '../../vcs/clients/gitNoCommit';
|
||||
import NoVcsClient from '../../vcs/clients/noVcs';
|
||||
import GitClient from '../../vcs/clients/git';
|
||||
|
||||
/**
|
||||
* This command will be run on the EAS workers.
|
||||
@@ -65,10 +64,16 @@ export default class SubmitInternal extends EasCommand {
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(SubmitInternal, {
|
||||
nonInteractive: true,
|
||||
vcsClientOverride: process.env.EAS_NO_VCS ? new NoVcsClient() : new GitNoCommitClient(),
|
||||
withServerSideEnvironment: null,
|
||||
});
|
||||
|
||||
if (vcsClient instanceof GitClient) {
|
||||
// `build:internal` is run on EAS workers and the repo may have been changed
|
||||
// by pre-install hooks or other scripts. We don't want to require committing changes
|
||||
// to continue the build.
|
||||
vcsClient.requireCommit = false;
|
||||
}
|
||||
|
||||
const submissionProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
EasJsonAccessor.fromProjectPath(projectDir),
|
||||
flags.platform,
|
||||
|
||||
@@ -410,11 +410,11 @@ export default class UpdatePublish extends EasCommand {
|
||||
runtimeToPlatformsAndFingerprintInfoMapping.map(async info => {
|
||||
return {
|
||||
...info,
|
||||
fingerprintSource: info.fingerprint
|
||||
expoUpdatesRuntimeFingerprintSource: info.expoUpdatesRuntimeFingerprint
|
||||
? (
|
||||
await maybeUploadFingerprintAsync({
|
||||
hash: info.runtimeVersion,
|
||||
fingerprint: info.fingerprint,
|
||||
fingerprint: info.expoUpdatesRuntimeFingerprint,
|
||||
graphqlClient,
|
||||
})
|
||||
).fingerprintSource ?? null
|
||||
@@ -449,7 +449,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
// Sort the updates into different groups based on their platform specific runtime versions
|
||||
const updateGroups: PublishUpdateGroupInput[] =
|
||||
runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping.map(
|
||||
({ runtimeVersion, platforms, fingerprintSource, fingerprintInfoGroup }) => {
|
||||
({ runtimeVersion, platforms, fingerprintInfoGroup }) => {
|
||||
const localUpdateInfoGroup = Object.fromEntries(
|
||||
platforms.map(platform => [
|
||||
platform,
|
||||
@@ -485,9 +485,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
branchId: branch.id,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
rolloutInfoGroup: localRolloutInfoGroup,
|
||||
runtimeFingerprintSource: fingerprintSource
|
||||
? transformFingerprintSource(fingerprintSource)
|
||||
: null,
|
||||
fingerprintInfoGroup: transformedFingerprintInfoGroup,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
|
||||
@@ -208,8 +208,8 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
platform,
|
||||
runtimeVersionInfo: {
|
||||
runtimeVersion: selectedRuntime,
|
||||
fingerprint: null,
|
||||
fingerprintHash: null,
|
||||
expoUpdatesRuntimeFingerprint: null,
|
||||
expoUpdatesRuntimeFingerprintHash: null,
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { WorkflowRevisionMutation } from '../../graphql/mutations/WorkflowRevisi
|
||||
import { WorkflowRunMutation } from '../../graphql/mutations/WorkflowRunMutation';
|
||||
import Log, { link } from '../../log';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { uploadAccountScopedEasJsonAsync } from '../../project/uploadAccountScopedEasJsonAsync';
|
||||
import { uploadAccountScopedFileAsync } from '../../project/uploadAccountScopedFileAsync';
|
||||
import { uploadAccountScopedProjectSourceAsync } from '../../project/uploadAccountScopedProjectSourceAsync';
|
||||
import { WorkflowFile } from '../../utils/workflowFile';
|
||||
|
||||
@@ -81,6 +81,7 @@ export default class WorkflowRun extends EasCommand {
|
||||
|
||||
let projectArchiveBucketKey: string;
|
||||
let easJsonBucketKey: string;
|
||||
let packageJsonBucketKey: string;
|
||||
|
||||
try {
|
||||
({ projectArchiveBucketKey } = await uploadAccountScopedProjectSourceAsync({
|
||||
@@ -88,10 +89,17 @@ export default class WorkflowRun extends EasCommand {
|
||||
vcsClient,
|
||||
accountId: account.id,
|
||||
}));
|
||||
({ easJsonBucketKey } = await uploadAccountScopedEasJsonAsync({
|
||||
({ fileBucketKey: easJsonBucketKey } = await uploadAccountScopedFileAsync({
|
||||
graphqlClient,
|
||||
accountId: account.id,
|
||||
projectDir,
|
||||
filePath: path.join(projectDir, 'eas.json'),
|
||||
maxSizeBytes: 1024 * 1024,
|
||||
}));
|
||||
({ fileBucketKey: packageJsonBucketKey } = await uploadAccountScopedFileAsync({
|
||||
graphqlClient,
|
||||
accountId: account.id,
|
||||
filePath: path.join(projectDir, 'package.json'),
|
||||
maxSizeBytes: 1024 * 1024,
|
||||
}));
|
||||
} catch (err) {
|
||||
Log.error('Failed to upload project sources.');
|
||||
@@ -113,6 +121,7 @@ export default class WorkflowRun extends EasCommand {
|
||||
type: WorkflowProjectSourceType.Gcs,
|
||||
projectArchiveBucketKey,
|
||||
easJsonBucketKey,
|
||||
packageJsonBucketKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface AndroidCredentials {
|
||||
|
||||
interface Options {
|
||||
app: AppLookupParams;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export default class AndroidCredentialsProvider {
|
||||
@@ -37,7 +38,7 @@ export default class AndroidCredentialsProvider {
|
||||
}
|
||||
|
||||
private async getRemoteAsync(): Promise<AndroidCredentials> {
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials({ app: this.options.app });
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials(this.options);
|
||||
const buildCredentials = await setupBuildCredentialsAction.runAsync(this.ctx);
|
||||
return this.toAndroidCredentials(buildCredentials);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { Session } from '@expo/apple-utils';
|
||||
import nock from 'nock';
|
||||
|
||||
import { createAppAsync } from '../ensureAppExists';
|
||||
|
||||
const FIXTURE_SUCCESS = {
|
||||
data: {
|
||||
type: 'apps',
|
||||
id: '6741087677',
|
||||
attributes: {
|
||||
name: 'expo (xxx)',
|
||||
bundleId: 'com.bacon.jan27.x',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const FIXTURE_INVALID_NAME = {
|
||||
errors: [
|
||||
{
|
||||
id: 'b3e7ca18-e4ce-4e55-83ce-8fff35dbaeca',
|
||||
status: '409',
|
||||
code: 'ENTITY_ERROR.ATTRIBUTE.INVALID.INVALID_CHARACTERS',
|
||||
title: 'An attribute value has invalid characters.',
|
||||
detail:
|
||||
'App Name contains certain Unicode symbols, emoticons, diacritics, special characters, or private use characters that are not permitted.',
|
||||
source: {
|
||||
pointer: '/included/1/name',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const FIXTURE_ALREADY_USED_ON_ACCOUNT = {
|
||||
errors: [
|
||||
{
|
||||
id: 'b91aefc5-0e94-48d9-8613-5b1a464a20f0',
|
||||
status: '409',
|
||||
code: 'ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE.SAME_ACCOUNT',
|
||||
title:
|
||||
'The provided entity includes an attribute with a value that has already been used on this account.',
|
||||
detail:
|
||||
'The app name you entered is already being used for another app in your account. If you would like to use the name for this app you will need to submit an update to your other app to change the name, or remove it from App Store Connect.',
|
||||
source: {
|
||||
pointer: '/included/1/name',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const FIXTURE_ALREADY_USED_ON_ANOTHER_ACCOUNT = {
|
||||
errors: [
|
||||
{
|
||||
id: '72b960f2-9e51-4f19-8d83-7cc08d42fec4',
|
||||
status: '409',
|
||||
code: 'ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE.DIFFERENT_ACCOUNT',
|
||||
title:
|
||||
'The provided entity includes an attribute with a value that has already been used on a different account.',
|
||||
detail:
|
||||
'The App Name you entered is already being used. If you have trademark rights to this name and would like it released for your use, submit a claim.',
|
||||
source: {
|
||||
pointer: '/included/1/name',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const MOCK_CONTEXT = {
|
||||
providerId: 1337,
|
||||
teamId: 'test-team-id',
|
||||
token: 'test-token',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
// Mock setup cookies API calls.
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.get(`/olympus/v1/session`)
|
||||
.reply(200, {
|
||||
provider: {
|
||||
providerId: 1337,
|
||||
publicProviderId: 'xxx-xxx-xxx-xxx-xxx',
|
||||
name: 'Evan Bacon',
|
||||
contentTypes: ['SOFTWARE'],
|
||||
subType: 'INDIVIDUAL',
|
||||
},
|
||||
});
|
||||
|
||||
await Session.fetchCurrentSessionInfoAsync();
|
||||
});
|
||||
|
||||
function getNameFromBody(body: any): any {
|
||||
return body.included.find((item: any) => item.id === '${new-appInfoLocalization-id}')?.attributes
|
||||
?.name;
|
||||
}
|
||||
|
||||
it('asserts invalid name cases', async () => {
|
||||
const scope = nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`, body => {
|
||||
expect(getNameFromBody(body)).toBe('Expo 🚀');
|
||||
|
||||
return true;
|
||||
})
|
||||
.reply(409, FIXTURE_INVALID_NAME);
|
||||
|
||||
// Already used on same account
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`, body => {
|
||||
expect(getNameFromBody(body)).toBe('Expo -');
|
||||
return true;
|
||||
})
|
||||
.reply(409, FIXTURE_ALREADY_USED_ON_ACCOUNT);
|
||||
|
||||
// Already used on different account
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`, body => {
|
||||
expect(getNameFromBody(body)).toMatch(/Expo - \([\w\d]+\)/);
|
||||
return true;
|
||||
})
|
||||
.reply(409, FIXTURE_ALREADY_USED_ON_ANOTHER_ACCOUNT);
|
||||
|
||||
// Success
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`, body => {
|
||||
expect(getNameFromBody(body)).toMatch(/Expo - \([\w\d]+\)/);
|
||||
return true;
|
||||
})
|
||||
.reply(200, FIXTURE_SUCCESS);
|
||||
|
||||
await createAppAsync(MOCK_CONTEXT, {
|
||||
bundleId: 'com.bacon.jan27.x',
|
||||
name: 'Expo 🚀',
|
||||
companyName: 'expo',
|
||||
});
|
||||
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('works on first try', async () => {
|
||||
nock('https://appstoreconnect.apple.com').post(`/iris/v1/apps`).reply(200, FIXTURE_SUCCESS);
|
||||
|
||||
await createAppAsync(MOCK_CONTEXT, {
|
||||
bundleId: 'com.bacon.jan27.x',
|
||||
name: 'Expo',
|
||||
companyName: 'expo',
|
||||
});
|
||||
});
|
||||
|
||||
it('doubles up entropy', async () => {
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`)
|
||||
.reply(409, FIXTURE_ALREADY_USED_ON_ANOTHER_ACCOUNT);
|
||||
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`)
|
||||
.reply(409, FIXTURE_ALREADY_USED_ON_ANOTHER_ACCOUNT);
|
||||
|
||||
nock('https://appstoreconnect.apple.com')
|
||||
.post(`/iris/v1/apps`, body => {
|
||||
expect(getNameFromBody(body)).toMatch(/Expo \([\w\d]+\) \([\w\d]+\)/);
|
||||
return true;
|
||||
})
|
||||
.reply(200, FIXTURE_SUCCESS);
|
||||
|
||||
await createAppAsync(MOCK_CONTEXT, {
|
||||
bundleId: 'com.bacon.jan27.x',
|
||||
name: 'Expo',
|
||||
companyName: 'expo',
|
||||
});
|
||||
});
|
||||
+152
-1
@@ -1,11 +1,26 @@
|
||||
import { JsonFileCache } from '@expo/apple-utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import { promptAsync } from '../../../../prompts';
|
||||
import { AppleTeamType } from '../authenticateTypes';
|
||||
import { resolveAppleTeamAsync, resolveAscApiKeyAsync } from '../resolveCredentials';
|
||||
import * as Keychain from '../keychain';
|
||||
import {
|
||||
resolveAppleTeamAsync,
|
||||
resolveAscApiKeyAsync,
|
||||
resolveUserCredentialsAsync,
|
||||
} from '../resolveCredentials';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('fs');
|
||||
jest.mock('@expo/apple-utils', () => {
|
||||
return {
|
||||
getCacheAsync: jest.fn(),
|
||||
usernameCachePath: jest.fn(),
|
||||
...jest.requireActual('@expo/apple-utils'),
|
||||
};
|
||||
});
|
||||
jest.mock('../keychain');
|
||||
|
||||
const testAscApiKey = {
|
||||
keyP8: 'test-key-p8',
|
||||
@@ -89,3 +104,139 @@ describe(resolveAppleTeamAsync, () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(resolveUserCredentialsAsync, () => {
|
||||
let KeychainMock: any;
|
||||
beforeAll(() => {
|
||||
JsonFileCache.getCacheAsync = jest.fn().mockImplementation(async (filePath: string) => {
|
||||
const content = await fs.readFile(filePath, 'utf-8');
|
||||
return content ? JSON.parse(content) : null;
|
||||
});
|
||||
KeychainMock = jest.mocked(Keychain);
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
KeychainMock.EXPO_NO_KEYCHAIN = '';
|
||||
});
|
||||
|
||||
it('uses credentials resolved from options', async () => {
|
||||
const result = await resolveUserCredentialsAsync({
|
||||
username: 'fakeUsername',
|
||||
password: 'fakePassword',
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
username: 'fakeUsername',
|
||||
password: 'fakePassword',
|
||||
});
|
||||
});
|
||||
|
||||
it('uses credentials resolved from environment', async () => {
|
||||
process.env.EXPO_APPLE_ID = 'fakeUsername';
|
||||
process.env.EXPO_APPLE_PASSWORD = 'fakePassword';
|
||||
|
||||
const result = await resolveUserCredentialsAsync({});
|
||||
expect(result).toMatchObject({
|
||||
username: 'fakeUsername',
|
||||
password: 'fakePassword',
|
||||
});
|
||||
});
|
||||
|
||||
it('uses credentials from prompt, does not update cache if they match', async () => {
|
||||
vol.fromJSON({
|
||||
'./.app-store/auth/username.json': `
|
||||
{
|
||||
"username": "fakeUsername"
|
||||
}
|
||||
`,
|
||||
});
|
||||
jest.mocked(promptAsync).mockResolvedValueOnce({ username: 'fakeUsername' });
|
||||
JsonFileCache.usernameCachePath = jest.fn().mockReturnValue('./.app-store/auth/username.json');
|
||||
const cacheAsyncSpy = jest.spyOn(JsonFileCache, 'cacheAsync');
|
||||
|
||||
const result = await resolveUserCredentialsAsync({});
|
||||
expect(result).toMatchObject({
|
||||
username: 'fakeUsername',
|
||||
});
|
||||
expect(cacheAsyncSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses credentials from prompt, updates cache if they don't match", async () => {
|
||||
vol.fromJSON({
|
||||
'./.app-store/auth/username.json': `
|
||||
{
|
||||
"username": "fakeUsername"
|
||||
}
|
||||
`,
|
||||
});
|
||||
jest.mocked(promptAsync).mockResolvedValueOnce({ username: 'newFakeUsername' });
|
||||
JsonFileCache.usernameCachePath = jest.fn().mockReturnValue('./.app-store/auth/username.json');
|
||||
const cacheAsyncSpy = jest.spyOn(JsonFileCache, 'cacheAsync');
|
||||
|
||||
expect(JSON.parse(await fs.readFile('./.app-store/auth/username.json', 'utf-8'))).toMatchObject(
|
||||
{ username: 'fakeUsername' }
|
||||
);
|
||||
const result = await resolveUserCredentialsAsync({});
|
||||
expect(result).toMatchObject({
|
||||
username: 'newFakeUsername',
|
||||
});
|
||||
expect(cacheAsyncSpy).toHaveBeenCalledWith('./.app-store/auth/username.json', {
|
||||
username: 'newFakeUsername',
|
||||
});
|
||||
expect(JSON.parse(await fs.readFile('./.app-store/auth/username.json', 'utf-8'))).toMatchObject(
|
||||
{ username: 'newFakeUsername' }
|
||||
);
|
||||
});
|
||||
|
||||
it("uses credentials from prompt, doesn't read or update cache if EXPO_NO_KEYCHAIN is set", async () => {
|
||||
vol.fromJSON({
|
||||
'./.app-store/auth/username.json': `
|
||||
{
|
||||
"username": "fakeUsername"
|
||||
}
|
||||
`,
|
||||
});
|
||||
jest.mocked(promptAsync).mockResolvedValueOnce({ username: 'newFakeUsername' });
|
||||
JsonFileCache.usernameCachePath = jest.fn().mockReturnValue('./.app-store/auth/username.json');
|
||||
KeychainMock.EXPO_NO_KEYCHAIN = 'true';
|
||||
const getCacheAsyncSpy = jest.spyOn(JsonFileCache, 'getCacheAsync');
|
||||
const cacheAsyncSpy = jest.spyOn(JsonFileCache, 'cacheAsync');
|
||||
|
||||
expect(JSON.parse(await fs.readFile('./.app-store/auth/username.json', 'utf-8'))).toMatchObject(
|
||||
{ username: 'fakeUsername' }
|
||||
);
|
||||
const result = await resolveUserCredentialsAsync({});
|
||||
expect(result).toMatchObject({
|
||||
username: 'newFakeUsername',
|
||||
});
|
||||
expect(getCacheAsyncSpy).not.toHaveBeenCalled();
|
||||
expect(cacheAsyncSpy).not.toHaveBeenCalled();
|
||||
await expect(fs.access('./.app-store/auth/username.json')).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('clears credentials from prompt if malformed before using and updating cache', async () => {
|
||||
vol.fromJSON({
|
||||
'./.app-store/auth/username.json': `
|
||||
{
|
||||
"username": "fakeUsername"
|
||||
}
|
||||
`,
|
||||
});
|
||||
jest.mocked(promptAsync).mockResolvedValueOnce({ username: '\x17newFake\x01Username\x1F' });
|
||||
JsonFileCache.usernameCachePath = jest.fn().mockReturnValue('./.app-store/auth/username.json');
|
||||
const cacheAsyncSpy = jest.spyOn(JsonFileCache, 'cacheAsync');
|
||||
|
||||
expect(JSON.parse(await fs.readFile('./.app-store/auth/username.json', 'utf-8'))).toMatchObject(
|
||||
{ username: 'fakeUsername' }
|
||||
);
|
||||
const result = await resolveUserCredentialsAsync({});
|
||||
expect(result).toMatchObject({
|
||||
username: 'newFakeUsername',
|
||||
});
|
||||
expect(cacheAsyncSpy).toHaveBeenCalledWith('./.app-store/auth/username.json', {
|
||||
username: 'newFakeUsername',
|
||||
});
|
||||
expect(JSON.parse(await fs.readFile('./.app-store/auth/username.json', 'utf-8'))).toMatchObject(
|
||||
{ username: 'newFakeUsername' }
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { App, BundleId } from '@expo/apple-utils';
|
||||
import { App, BundleId, RequestContext } from '@expo/apple-utils';
|
||||
import { JSONObject } from '@expo/json-file';
|
||||
import chalk from 'chalk';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
import { getRequestContext, isUserAuthCtx } from './authenticate';
|
||||
import { AuthCtx, UserAuthCtx } from './authenticateTypes';
|
||||
@@ -188,7 +189,7 @@ export async function ensureAppExistsAsync(
|
||||
/**
|
||||
* **Does not support App Store Connect API (CI).**
|
||||
*/
|
||||
app = await App.createAsync(context, {
|
||||
app = await createAppAsync(context, {
|
||||
bundleId: bundleIdentifier,
|
||||
name,
|
||||
primaryLocale: language,
|
||||
@@ -215,3 +216,120 @@ export async function ensureAppExistsAsync(
|
||||
);
|
||||
return app;
|
||||
}
|
||||
|
||||
function sanitizeName(name: string): string {
|
||||
return (
|
||||
name
|
||||
// Replace emojis with a `-`
|
||||
.replace(/[\p{Emoji}]/gu, '-')
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.trim()
|
||||
);
|
||||
}
|
||||
|
||||
export async function createAppAsync(
|
||||
context: RequestContext,
|
||||
props: {
|
||||
bundleId: string;
|
||||
name: string;
|
||||
primaryLocale?: string;
|
||||
companyName?: string;
|
||||
sku?: string;
|
||||
},
|
||||
retryCount = 0
|
||||
): Promise<App> {
|
||||
try {
|
||||
/**
|
||||
* **Does not support App Store Connect API (CI).**
|
||||
*/
|
||||
return await App.createAsync(context, props);
|
||||
} catch (error) {
|
||||
if (retryCount >= 5) {
|
||||
throw error;
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
const handleDuplicateNameErrorAsync = async (): Promise<App> => {
|
||||
const generatedName = props.name + ` (${randomBytes(3).toString('hex')})`;
|
||||
Log.warn(
|
||||
`App name "${props.name}" is already taken. Using generated name "${generatedName}" which can be changed later from https://appstoreconnect.apple.com.`
|
||||
);
|
||||
// Sanitize the name and try again.
|
||||
return await createAppAsync(
|
||||
context,
|
||||
{
|
||||
...props,
|
||||
name: generatedName,
|
||||
},
|
||||
retryCount + 1
|
||||
);
|
||||
};
|
||||
|
||||
if (isAppleError(error)) {
|
||||
// New error class that is thrown when the name is already taken but belongs to you.
|
||||
if (
|
||||
error.data.errors.some(
|
||||
e =>
|
||||
e.code === 'ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE.SAME_ACCOUNT' ||
|
||||
e.code === 'ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE.DIFFERENT_ACCOUNT'
|
||||
)
|
||||
) {
|
||||
return await handleDuplicateNameErrorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if ('code' in error && typeof error.code === 'string') {
|
||||
if (
|
||||
// Name is invalid
|
||||
error.code === 'APP_CREATE_NAME_INVALID'
|
||||
// UnexpectedAppleResponse: An attribute value has invalid characters. - App Name contains certain Unicode symbols, emoticons, diacritics, special characters, or private use characters that are not permitted.
|
||||
// Name is taken
|
||||
) {
|
||||
const sanitizedName = sanitizeName(props.name);
|
||||
if (sanitizedName === props.name) {
|
||||
throw error;
|
||||
}
|
||||
Log.warn(
|
||||
`App name "${props.name}" contains invalid characters. Using sanitized name "${sanitizedName}" which can be changed later from https://appstoreconnect.apple.com.`
|
||||
);
|
||||
// Sanitize the name and try again.
|
||||
return await createAppAsync(
|
||||
context,
|
||||
{
|
||||
...props,
|
||||
name: sanitizedName,
|
||||
},
|
||||
retryCount + 1
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
// UnexpectedAppleResponse: The provided entity includes an attribute with a value that has already been used on a different account. - The App Name you entered is already being used. If you have trademark rights to
|
||||
// this name and would like it released for your use, submit a claim.
|
||||
error.code === 'APP_CREATE_NAME_UNAVAILABLE'
|
||||
) {
|
||||
return await handleDuplicateNameErrorAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function isAppleError(error: any): error is {
|
||||
data: {
|
||||
errors: {
|
||||
id: string;
|
||||
status: string;
|
||||
/** 'ENTITY_ERROR.ATTRIBUTE.INVALID.INVALID_CHARACTERS' */
|
||||
code: string;
|
||||
/** 'An attribute value has invalid characters.' */
|
||||
title: string;
|
||||
/** 'App Name contains certain Unicode symbols, emoticons, diacritics, special characters, or private use characters that are not permitted.' */
|
||||
detail: string;
|
||||
}[];
|
||||
};
|
||||
} {
|
||||
return 'data' in error && 'errors' in error.data && Array.isArray(error.data.errors);
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { App, BetaGroup, Session, User, UserRole } from '@expo/apple-utils';
|
||||
|
||||
import { isAppleError } from './ensureAppExists';
|
||||
import Log from '../../../log';
|
||||
import { ora } from '../../../ora';
|
||||
import { confirmAsync } from '../../../prompts';
|
||||
|
||||
// The name of the internal TestFlight group, this should probably never change.
|
||||
const AUTO_GROUP_NAME = 'Team (Expo)';
|
||||
|
||||
/**
|
||||
* Ensure a TestFlight internal group with access to all builds exists for the app and has all admin users invited to it.
|
||||
* This allows users to instantly access their builds from TestFlight after it finishes processing.
|
||||
*/
|
||||
export async function ensureTestFlightGroupExistsAsync(app: App): Promise<void> {
|
||||
const group = await ensureInternalGroupAsync(app);
|
||||
const users = await User.getAsync(app.context);
|
||||
const admins = users.filter(user => user.attributes.roles?.includes(UserRole.ADMIN));
|
||||
|
||||
await addAllUsersToInternalGroupAsync(group, admins);
|
||||
}
|
||||
|
||||
async function ensureInternalGroupAsync(app: App): Promise<BetaGroup> {
|
||||
const groups = await app.getBetaGroupsAsync({
|
||||
query: {
|
||||
includes: ['betaTesters'],
|
||||
},
|
||||
});
|
||||
|
||||
let betaGroup = groups.find(group => group.attributes.name === AUTO_GROUP_NAME);
|
||||
if (!betaGroup) {
|
||||
const spinner = ora().start('Creating TestFlight group...');
|
||||
|
||||
try {
|
||||
// Apple throw an error if you create the group too quickly after creating the app. We'll retry a few times.
|
||||
await pollRetryAsync(
|
||||
async () => {
|
||||
betaGroup = await app.createBetaGroupAsync({
|
||||
name: AUTO_GROUP_NAME,
|
||||
publicLinkEnabled: false,
|
||||
publicLinkLimitEnabled: false,
|
||||
isInternalGroup: true,
|
||||
// Automatically add latest builds to the group without needing to run the command.
|
||||
hasAccessToAllBuilds: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
shouldRetry(error) {
|
||||
if (isAppleError(error)) {
|
||||
spinner.text = `TestFlight not ready, retrying in 25 seconds...`;
|
||||
|
||||
return error.data.errors.some(
|
||||
error => error.code === 'ENTITY_ERROR.RELATIONSHIP.INVALID'
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
}
|
||||
);
|
||||
spinner.succeed(`TestFlight group created: ${AUTO_GROUP_NAME}`);
|
||||
} catch (error: any) {
|
||||
spinner.fail('Failed to create TestFlight group...');
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (!betaGroup) {
|
||||
throw new Error('Failed to create internal TestFlight group');
|
||||
}
|
||||
|
||||
// `hasAccessToAllBuilds` is a newer feature that allows the group to automatically have access to all builds. This cannot be patched so we need to recreate the group.
|
||||
if (!betaGroup.attributes.hasAccessToAllBuilds) {
|
||||
if (
|
||||
await confirmAsync({
|
||||
message: 'Regenerate internal TestFlight group to allow automatic access to all builds?',
|
||||
})
|
||||
) {
|
||||
await BetaGroup.deleteAsync(app.context, { id: betaGroup.id });
|
||||
return await ensureInternalGroupAsync(app);
|
||||
}
|
||||
}
|
||||
|
||||
return betaGroup;
|
||||
}
|
||||
|
||||
async function addAllUsersToInternalGroupAsync(group: BetaGroup, users: User[]): Promise<void> {
|
||||
let emails = users
|
||||
.filter(user => user.attributes.email)
|
||||
.map(user => ({
|
||||
email: user.attributes.email!,
|
||||
firstName: user.attributes.firstName ?? '',
|
||||
lastName: user.attributes.lastName ?? '',
|
||||
}));
|
||||
|
||||
const { betaTesters } = group.attributes;
|
||||
const existingEmails = betaTesters?.map(tester => tester.attributes.email).filter(Boolean) ?? [];
|
||||
// Filter out existing beta testers.
|
||||
if (betaTesters) {
|
||||
emails = emails.filter(
|
||||
user => !existingEmails.find(existingEmail => existingEmail === user.email)
|
||||
);
|
||||
}
|
||||
|
||||
// No new users to add to the internal group.
|
||||
if (!emails.length) {
|
||||
// No need to log which users are here on subsequent runs as devs already know the drill at this point.
|
||||
Log.debug(`All current admins are already added to the group: ${group.attributes.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug(`Adding ${emails.length} users to internal group: ${group.attributes.name}`);
|
||||
Log.debug(`Users: ${emails.map(user => user.email).join(', ')}`);
|
||||
|
||||
const data = await group.createBulkBetaTesterAssignmentsAsync(emails);
|
||||
|
||||
const success = data.attributes.betaTesters.every(tester => {
|
||||
if (tester.assignmentResult === 'FAILED') {
|
||||
if (tester.errors && Array.isArray(tester.errors) && tester.errors.length) {
|
||||
if (
|
||||
tester.errors.length === 1 &&
|
||||
tester.errors[0].key === 'Halliday.tester.already.exists'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
for (const error of tester.errors) {
|
||||
Log.error(
|
||||
`Error adding user ${tester.email} to TestFlight group "${group.attributes.name}": ${error.key}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (tester.assignmentResult === 'NOT_QUALIFIED_FOR_INTERNAL_GROUP') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
const groupUrl = await getTestFlightGroupUrlAsync(group);
|
||||
|
||||
Log.error(
|
||||
`Unable to add all admins to TestFlight group "${
|
||||
group.attributes.name
|
||||
}". You can add them manually in App Store Connect. ${groupUrl ?? ''}`
|
||||
);
|
||||
} else {
|
||||
Log.log(
|
||||
`TestFlight access enabled for: ` +
|
||||
data.attributes.betaTesters
|
||||
.map(tester => tester.email)
|
||||
.filter(Boolean)
|
||||
.join(', ')
|
||||
);
|
||||
// TODO: When we have more TestFlight functionality, we can link to it from here.
|
||||
}
|
||||
}
|
||||
|
||||
async function getTestFlightGroupUrlAsync(group: BetaGroup): Promise<string | null> {
|
||||
if (group.context.providerId) {
|
||||
try {
|
||||
const session = await Session.getSessionForProviderIdAsync(group.context.providerId);
|
||||
|
||||
return `https://appstoreconnect.apple.com/teams/${session.provider.publicProviderId}/apps/6741088859/testflight/groups/${group.id}`;
|
||||
} catch (error) {
|
||||
// Avoid crashing if we can't get the session.
|
||||
Log.debug('Failed to get session for provider ID', error);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function pollRetryAsync<T>(
|
||||
fn: () => Promise<T>,
|
||||
{
|
||||
shouldRetry,
|
||||
retries = 10,
|
||||
// 25 seconds was the minium interval I calculated when measuring against 5 second intervals.
|
||||
interval = 25000,
|
||||
}: { shouldRetry?: (error: Error) => boolean; retries?: number; interval?: number } = {}
|
||||
): Promise<T> {
|
||||
let lastError: Error | null = null;
|
||||
for (let i = 0; i < retries; i++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error: any) {
|
||||
if (shouldRetry && !shouldRetry(error)) {
|
||||
throw error;
|
||||
}
|
||||
lastError = error;
|
||||
await new Promise(resolve => setTimeout(resolve, interval));
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw lastError;
|
||||
}
|
||||
@@ -29,6 +29,7 @@ function resolveProfileType(
|
||||
return resolveProfileTypeIos(profileClass, isEnterprise);
|
||||
case ApplePlatform.TV_OS:
|
||||
return resolveProfileTypeAppleTv(profileClass, isEnterprise);
|
||||
case ApplePlatform.VISION_OS:
|
||||
case ApplePlatform.MAC_OS:
|
||||
throw new Error(`${applePlatform} profiles are not supported`);
|
||||
}
|
||||
|
||||
@@ -208,13 +208,18 @@ async function promptUsernameAsync(): Promise<string> {
|
||||
// the default value for quicker authentication.
|
||||
const lastAppleId = await getCachedUsernameAsync();
|
||||
|
||||
const { username } = await promptAsync({
|
||||
let { username } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'username',
|
||||
message: `Apple ID:`,
|
||||
validate: (val: string) => val !== '',
|
||||
initial: lastAppleId ?? undefined,
|
||||
});
|
||||
// https://github.com/expo/eas-cli/issues/2254
|
||||
// user got an invalid unprintable character in their username, which was saved to cache and used to prefill the prompt
|
||||
// which made them accept the prefilled value not knowing it contains the character and fail to log in
|
||||
// this replace makes sure no such characters are used or cached
|
||||
username = username.replace(/[\x00-\x1F]/gi, '');
|
||||
|
||||
if (username && username !== lastAppleId) {
|
||||
await cacheUsernameAsync(username);
|
||||
|
||||
@@ -48,7 +48,7 @@ async function collectDataAndRegisterDeviceAsync(
|
||||
}
|
||||
|
||||
const { udid, deviceClass, name } = deviceData;
|
||||
const spinner = ora(`Registering Apple device on Expo`).start();
|
||||
const spinner = ora(`Registering Apple device on EAS`).start();
|
||||
try {
|
||||
await AppleDeviceMutation.createAppleDeviceAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -45,7 +45,7 @@ async function collectDataAndRegisterDeviceAsync(
|
||||
): Promise<void> {
|
||||
const { udid, deviceClass, name } = await collectDeviceDataAsync(appleTeam);
|
||||
|
||||
const spinner = ora(`Registering Apple device on Expo`).start();
|
||||
const spinner = ora(`Registering Apple device on EAS`).start();
|
||||
try {
|
||||
await AppleDeviceMutation.createAppleDeviceAsync(
|
||||
graphqlClient,
|
||||
|
||||
+380
-39
@@ -24,6 +24,7 @@ export type Scalars = {
|
||||
JSON: { input: any; output: any; }
|
||||
JSONObject: { input: any; output: any; }
|
||||
WorkerDeploymentIdentifier: { input: any; output: any; }
|
||||
WorkerDeploymentRequestID: { input: any; output: any; }
|
||||
};
|
||||
|
||||
export type AcceptUserInvitationResult = {
|
||||
@@ -133,9 +134,12 @@ export type Account = {
|
||||
id: Scalars['ID']['output'];
|
||||
isCurrent: Scalars['Boolean']['output'];
|
||||
isDisabled: Scalars['Boolean']['output'];
|
||||
/** Whether an Account plan falls into AppDevDomainName's free or paid tier */
|
||||
isFreeAppDevDomainTier: Scalars['Boolean']['output'];
|
||||
/** Whether this account has SSO enabled. Can be queried by all members. */
|
||||
isSSOEnabled: Scalars['Boolean']['output'];
|
||||
lastDeletionAttemptTime?: Maybe<Scalars['DateTime']['output']>;
|
||||
logRocketOrganization?: Maybe<LogRocketOrganization>;
|
||||
name: Scalars['String']['output'];
|
||||
/** Offers set on this account */
|
||||
offers?: Maybe<Array<Offer>>;
|
||||
@@ -143,6 +147,7 @@ export type Account = {
|
||||
owner?: Maybe<User>;
|
||||
/** Owning UserActor of this account if personal account */
|
||||
ownerUserActor?: Maybe<UserActor>;
|
||||
profileImageUrl: Scalars['String']['output'];
|
||||
pushSecurityEnabled: Scalars['Boolean']['output'];
|
||||
/** @deprecated Legacy access tokens are deprecated */
|
||||
requiresAccessTokenForPushSecurity: Scalars['Boolean']['output'];
|
||||
@@ -722,12 +727,7 @@ export type AccountUsageEasBuildMetadata = {
|
||||
waiverType?: Maybe<EasBuildWaiverType>;
|
||||
};
|
||||
|
||||
export type AccountUsageEasJobsMetadata = {
|
||||
__typename?: 'AccountUsageEASJobsMetadata';
|
||||
resourceClassDisplayName: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type AccountUsageMetadata = AccountUsageEasBuildMetadata | AccountUsageEasJobsMetadata;
|
||||
export type AccountUsageMetadata = AccountUsageEasBuildMetadata;
|
||||
|
||||
export type AccountUsageMetric = {
|
||||
__typename?: 'AccountUsageMetric';
|
||||
@@ -1090,6 +1090,7 @@ export type AndroidJobInput = {
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
customBuildConfig?: InputMaybe<CustomBuildConfigInput>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
experimental?: InputMaybe<Scalars['JSONObject']['input']>;
|
||||
gradleCommand?: InputMaybe<Scalars['String']['input']>;
|
||||
loggerLevel?: InputMaybe<WorkerLoggerLevel>;
|
||||
@@ -1251,6 +1252,7 @@ export type App = Project & {
|
||||
environmentVariables: Array<EnvironmentVariable>;
|
||||
/** Environment variables for an app with decrypted secret values */
|
||||
environmentVariablesIncludingSensitive: Array<EnvironmentVariableWithSecret>;
|
||||
fingerprintsPaginated: AppFingerprintsConnection;
|
||||
fullName: Scalars['String']['output'];
|
||||
githubBuildTriggers: Array<GitHubBuildTrigger>;
|
||||
githubJobRunTriggers: Array<GitHubJobRunTrigger>;
|
||||
@@ -1300,6 +1302,7 @@ export type App = Project & {
|
||||
likeCount: Scalars['Int']['output'];
|
||||
/** @deprecated 'likes' have been deprecated. */
|
||||
likedBy: Array<Maybe<User>>;
|
||||
logRocketProject?: Maybe<LogRocketProject>;
|
||||
name: Scalars['String']['output'];
|
||||
ownerAccount: Account;
|
||||
/** @deprecated No longer supported */
|
||||
@@ -1366,6 +1369,8 @@ export type App = Project & {
|
||||
/** EAS updates owned by an app */
|
||||
updates: Array<Update>;
|
||||
updatesPaginated: AppUpdatesConnection;
|
||||
/** Project query object for querying EAS usage metrics */
|
||||
usageMetrics: AppUsageMetrics;
|
||||
/** @deprecated Use ownerAccount.name instead */
|
||||
username: Scalars['String']['output'];
|
||||
/** @deprecated No longer supported */
|
||||
@@ -1479,6 +1484,16 @@ export type AppEnvironmentVariablesIncludingSensitiveArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppFingerprintsPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<FingerprintFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppIosAppCredentialsArgs = {
|
||||
filter?: InputMaybe<IosAppCredentialsFilter>;
|
||||
@@ -1587,6 +1602,7 @@ export type AppUpdatesArgs = {
|
||||
export type AppUpdatesPaginatedArgs = {
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<UpdateFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
@@ -1732,6 +1748,18 @@ export type AppDevDomainNameMutationChangeDevDomainNameArgs = {
|
||||
name: Scalars['DevDomainName']['input'];
|
||||
};
|
||||
|
||||
export type AppFingerprintEdge = {
|
||||
__typename?: 'AppFingerprintEdge';
|
||||
cursor: Scalars['String']['output'];
|
||||
node: Fingerprint;
|
||||
};
|
||||
|
||||
export type AppFingerprintsConnection = {
|
||||
__typename?: 'AppFingerprintsConnection';
|
||||
edges: Array<AppFingerprintEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type AppIcon = {
|
||||
__typename?: 'AppIcon';
|
||||
/** @deprecated No longer supported */
|
||||
@@ -2053,6 +2081,44 @@ export enum AppUploadSessionType {
|
||||
ProfileImageUpload = 'PROFILE_IMAGE_UPLOAD'
|
||||
}
|
||||
|
||||
export type AppUsageMetric = {
|
||||
__typename?: 'AppUsageMetric';
|
||||
id: Scalars['ID']['output'];
|
||||
metricType: UsageMetricType;
|
||||
serviceMetric: EasServiceMetric;
|
||||
timestamp: Scalars['DateTime']['output'];
|
||||
value: Scalars['Float']['output'];
|
||||
};
|
||||
|
||||
export type AppUsageMetricTotal = {
|
||||
__typename?: 'AppUsageMetricTotal';
|
||||
billingPeriod: BillingPeriod;
|
||||
id: Scalars['ID']['output'];
|
||||
planMetrics: Array<EstimatedUsage>;
|
||||
/** Total cost of overages, in cents */
|
||||
totalCost: Scalars['Float']['output'];
|
||||
};
|
||||
|
||||
export type AppUsageMetrics = {
|
||||
__typename?: 'AppUsageMetrics';
|
||||
byBillingPeriod: AppUsageMetricTotal;
|
||||
metricsForServiceMetric: Array<AppUsageMetric>;
|
||||
};
|
||||
|
||||
|
||||
export type AppUsageMetricsByBillingPeriodArgs = {
|
||||
date: Scalars['DateTime']['input'];
|
||||
service?: InputMaybe<EasService>;
|
||||
};
|
||||
|
||||
|
||||
export type AppUsageMetricsMetricsForServiceMetricArgs = {
|
||||
filterParams?: InputMaybe<Scalars['JSONObject']['input']>;
|
||||
granularity: UsageMetricsGranularity;
|
||||
serviceMetric: EasServiceMetric;
|
||||
timespan: UsageMetricsTimespan;
|
||||
};
|
||||
|
||||
/** Represents Play Store/App Store version of an application */
|
||||
export type AppVersion = {
|
||||
__typename?: 'AppVersion';
|
||||
@@ -2838,7 +2904,7 @@ export type BuildArtifacts = {
|
||||
applicationArchiveUrl?: Maybe<Scalars['String']['output']>;
|
||||
buildArtifactsUrl?: Maybe<Scalars['String']['output']>;
|
||||
buildUrl?: Maybe<Scalars['String']['output']>;
|
||||
/** @deprecated Use 'runtime.fingerprintDebugInfoUrl' instead. */
|
||||
/** @deprecated Use 'runtime.fingerprint.debugInfoUrl' instead. */
|
||||
fingerprintUrl?: Maybe<Scalars['String']['output']>;
|
||||
xcodeBuildLogsUrl?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
@@ -2869,7 +2935,9 @@ export type BuildFilter = {
|
||||
appVersion?: InputMaybe<Scalars['String']['input']>;
|
||||
buildProfile?: InputMaybe<Scalars['String']['input']>;
|
||||
channel?: InputMaybe<Scalars['String']['input']>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
fingerprintHash?: InputMaybe<Scalars['String']['input']>;
|
||||
gitCommitHash?: InputMaybe<Scalars['String']['input']>;
|
||||
hasFingerprint?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
platform?: InputMaybe<AppPlatform>;
|
||||
@@ -2883,6 +2951,7 @@ export type BuildFilterInput = {
|
||||
channel?: InputMaybe<Scalars['String']['input']>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
distributions?: InputMaybe<Array<DistributionType>>;
|
||||
fingerprintHash?: InputMaybe<Scalars['String']['input']>;
|
||||
hasFingerprint?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
platforms?: InputMaybe<Array<AppPlatform>>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -3294,6 +3363,7 @@ export enum CrashSampleFor {
|
||||
}
|
||||
|
||||
export type CrashesFilters = {
|
||||
crashKind?: InputMaybe<Array<WorkerDeploymentCrashKind>>;
|
||||
name?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
@@ -3346,6 +3416,11 @@ export type CreateEnvironmentVariableInput = {
|
||||
visibility: EnvironmentVariableVisibility;
|
||||
};
|
||||
|
||||
export type CreateFingerprintInput = {
|
||||
hash: Scalars['String']['input'];
|
||||
source?: InputMaybe<FingerprintSourceInput>;
|
||||
};
|
||||
|
||||
export type CreateGitHubAppInstallationInput = {
|
||||
accountId: Scalars['ID']['input'];
|
||||
installationIdentifier: Scalars['Int']['input'];
|
||||
@@ -3596,6 +3671,16 @@ export type DeleteIosAppCredentialsResult = {
|
||||
id: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type DeleteLogRocketOrganizationResult = {
|
||||
__typename?: 'DeleteLogRocketOrganizationResult';
|
||||
accountId: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type DeleteLogRocketProjectResult = {
|
||||
__typename?: 'DeleteLogRocketProjectResult';
|
||||
id: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type DeleteRobotResult = {
|
||||
__typename?: 'DeleteRobotResult';
|
||||
id: Scalars['ID']['output'];
|
||||
@@ -3918,6 +4003,9 @@ export enum EntityTypeName {
|
||||
IosAppCredentialsEntity = 'IosAppCredentialsEntity',
|
||||
UserInvitationEntity = 'UserInvitationEntity',
|
||||
UserPermissionEntity = 'UserPermissionEntity',
|
||||
WorkerCustomDomainEntity = 'WorkerCustomDomainEntity',
|
||||
WorkerDeploymentAliasEntity = 'WorkerDeploymentAliasEntity',
|
||||
WorkerEntity = 'WorkerEntity',
|
||||
WorkflowEntity = 'WorkflowEntity',
|
||||
WorkflowRevisionEntity = 'WorkflowRevisionEntity'
|
||||
}
|
||||
@@ -4007,6 +4095,8 @@ export type EnvironmentVariableMutation = {
|
||||
createEnvironmentVariableForAccount: EnvironmentVariable;
|
||||
/** Create an environment variable for an App */
|
||||
createEnvironmentVariableForApp: EnvironmentVariable;
|
||||
/** Bulk delete environment variables */
|
||||
deleteBulkEnvironmentVariables: Array<DeleteEnvironmentVariableResult>;
|
||||
/** Delete an environment variable */
|
||||
deleteEnvironmentVariable: DeleteEnvironmentVariableResult;
|
||||
/** Bulk link shared environment variables */
|
||||
@@ -4015,6 +4105,8 @@ export type EnvironmentVariableMutation = {
|
||||
linkSharedEnvironmentVariable: EnvironmentVariable;
|
||||
/** Unlink shared environment variable */
|
||||
unlinkSharedEnvironmentVariable: EnvironmentVariable;
|
||||
/** Bulk update environment variables */
|
||||
updateBulkEnvironmentVariables: Array<EnvironmentVariable>;
|
||||
/** Update an environment variable */
|
||||
updateEnvironmentVariable: EnvironmentVariable;
|
||||
};
|
||||
@@ -4044,6 +4136,11 @@ export type EnvironmentVariableMutationCreateEnvironmentVariableForAppArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type EnvironmentVariableMutationDeleteBulkEnvironmentVariablesArgs = {
|
||||
ids: Array<Scalars['ID']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type EnvironmentVariableMutationDeleteEnvironmentVariableArgs = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
@@ -4068,6 +4165,11 @@ export type EnvironmentVariableMutationUnlinkSharedEnvironmentVariableArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type EnvironmentVariableMutationUpdateBulkEnvironmentVariablesArgs = {
|
||||
environmentVariablesData: Array<UpdateEnvironmentVariableInput>;
|
||||
};
|
||||
|
||||
|
||||
export type EnvironmentVariableMutationUpdateEnvironmentVariableArgs = {
|
||||
environmentVariableData: UpdateEnvironmentVariableInput;
|
||||
};
|
||||
@@ -4180,12 +4282,44 @@ export enum Feature {
|
||||
export type Fingerprint = {
|
||||
__typename?: 'Fingerprint';
|
||||
app: App;
|
||||
builds: AppBuildsConnection;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
debugInfoUrl?: Maybe<Scalars['String']['output']>;
|
||||
hash: Scalars['String']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
source?: Maybe<FingerprintSource>;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
updates: AppUpdatesConnection;
|
||||
};
|
||||
|
||||
|
||||
export type FingerprintBuildsArgs = {
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<FingerprintBuildsFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type FingerprintUpdatesArgs = {
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type FingerprintBuildsFilterInput = {
|
||||
channel?: InputMaybe<Scalars['String']['input']>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
distributions?: InputMaybe<Array<DistributionType>>;
|
||||
platforms?: InputMaybe<Array<AppPlatform>>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']['input']>;
|
||||
simulator?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
};
|
||||
|
||||
export type FingerprintFilterInput = {
|
||||
hashes?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
export type FingerprintInfo = {
|
||||
@@ -4199,6 +4333,18 @@ export type FingerprintInfoGroup = {
|
||||
web?: InputMaybe<FingerprintInfo>;
|
||||
};
|
||||
|
||||
export type FingerprintMutation = {
|
||||
__typename?: 'FingerprintMutation';
|
||||
/** Create or get an existing fingerprint for an App */
|
||||
createOrGetExistingFingerprint: Fingerprint;
|
||||
};
|
||||
|
||||
|
||||
export type FingerprintMutationCreateOrGetExistingFingerprintArgs = {
|
||||
appId: Scalars['ID']['input'];
|
||||
fingerprintData: CreateFingerprintInput;
|
||||
};
|
||||
|
||||
export type FingerprintSource = {
|
||||
__typename?: 'FingerprintSource';
|
||||
bucketKey: Scalars['String']['output'];
|
||||
@@ -4226,6 +4372,22 @@ export type FutureSubscription = {
|
||||
startDate: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type GenerateLogRocketOrganizationLinkResult = {
|
||||
__typename?: 'GenerateLogRocketOrganizationLinkResult';
|
||||
url: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GenerateLogRocketOrganizationLinkingUrlInput = {
|
||||
accountId: Scalars['ID']['input'];
|
||||
callbackUrl: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type GenerateLogRocketReplayTokenResult = {
|
||||
__typename?: 'GenerateLogRocketReplayTokenResult';
|
||||
orgSlug: Scalars['String']['output'];
|
||||
replayToken: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GetSignedAssetUploadSpecificationsResult = {
|
||||
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
||||
specifications: Array<Scalars['String']['output']>;
|
||||
@@ -4239,6 +4401,7 @@ export enum GitHubAppEnvironment {
|
||||
|
||||
export type GitHubAppInstallation = {
|
||||
__typename?: 'GitHubAppInstallation';
|
||||
/** The Expo account that owns the installation entity. */
|
||||
account: Account;
|
||||
actor?: Maybe<Actor>;
|
||||
id: Scalars['ID']['output'];
|
||||
@@ -4258,10 +4421,17 @@ export type GitHubAppInstallationAccessibleRepository = {
|
||||
url: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export enum GitHubAppInstallationAccountType {
|
||||
Organization = 'ORGANIZATION',
|
||||
User = 'USER'
|
||||
}
|
||||
|
||||
export type GitHubAppInstallationMetadata = {
|
||||
__typename?: 'GitHubAppInstallationMetadata';
|
||||
githubAccountAvatarUrl?: Maybe<Scalars['String']['output']>;
|
||||
/** The login of the GitHub account that owns the installation. Not the display name. */
|
||||
githubAccountName?: Maybe<Scalars['String']['output']>;
|
||||
githubAccountType?: Maybe<GitHubAppInstallationAccountType>;
|
||||
installationStatus: GitHubAppInstallationStatus;
|
||||
};
|
||||
|
||||
@@ -4905,6 +5075,7 @@ export type IosJobInput = {
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
/** @deprecated */
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
experimental?: InputMaybe<Scalars['JSONObject']['input']>;
|
||||
loggerLevel?: InputMaybe<WorkerLoggerLevel>;
|
||||
mode?: InputMaybe<BuildMode>;
|
||||
@@ -4997,6 +5168,7 @@ export type IosSubmissionConfigInput = {
|
||||
export type JobRun = {
|
||||
__typename?: 'JobRun';
|
||||
app: App;
|
||||
artifacts: Array<WorkflowArtifact>;
|
||||
/** @deprecated No longer supported */
|
||||
childJobRun?: Maybe<JobRun>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
@@ -5106,6 +5278,15 @@ export type LineDataset = {
|
||||
label: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type LinkLogRocketOrganizationToExpoAccountInput = {
|
||||
accountId: Scalars['ID']['input'];
|
||||
client_id: Scalars['String']['input'];
|
||||
client_secret: Scalars['String']['input'];
|
||||
orgName: Scalars['String']['input'];
|
||||
orgSlug: Scalars['String']['input'];
|
||||
state: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type LinkSharedEnvironmentVariableInput = {
|
||||
appId: Scalars['ID']['input'];
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
@@ -5118,6 +5299,76 @@ export type LogNameTypeMapping = {
|
||||
typeName: EntityTypeName;
|
||||
};
|
||||
|
||||
export type LogRocketOrganization = {
|
||||
__typename?: 'LogRocketOrganization';
|
||||
account: Account;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
orgName: Scalars['String']['output'];
|
||||
orgSlug: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type LogRocketOrganizationMutation = {
|
||||
__typename?: 'LogRocketOrganizationMutation';
|
||||
/** Delete a LogRocket organization by ID */
|
||||
deleteLogRocketOrganization: DeleteLogRocketOrganizationResult;
|
||||
/** Generate a LogRocket linking URL */
|
||||
generateLogRocketOrganizationLinkingURL: GenerateLogRocketOrganizationLinkResult;
|
||||
/** Generate a LogRocket replay token for an organization */
|
||||
generateLogRocketReplayToken: GenerateLogRocketReplayTokenResult;
|
||||
/** Link a LogRocket organization to an Expo account */
|
||||
linkLogRocketOrganizationToExpoAccount: LogRocketOrganization;
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketOrganizationMutationDeleteLogRocketOrganizationArgs = {
|
||||
accountId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketOrganizationMutationGenerateLogRocketOrganizationLinkingUrlArgs = {
|
||||
input: GenerateLogRocketOrganizationLinkingUrlInput;
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketOrganizationMutationGenerateLogRocketReplayTokenArgs = {
|
||||
accountId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketOrganizationMutationLinkLogRocketOrganizationToExpoAccountArgs = {
|
||||
input: LinkLogRocketOrganizationToExpoAccountInput;
|
||||
};
|
||||
|
||||
export type LogRocketProject = {
|
||||
__typename?: 'LogRocketProject';
|
||||
app: App;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
logRocketOrgId: Scalars['ID']['output'];
|
||||
logRocketProjectSlug: Scalars['String']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type LogRocketProjectMutation = {
|
||||
__typename?: 'LogRocketProjectMutation';
|
||||
/** Create a LogRocket project */
|
||||
createLogRocketProject: LogRocketProject;
|
||||
/** Delete a LogRocket project by ID */
|
||||
deleteLogRocketProject: DeleteLogRocketProjectResult;
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketProjectMutationCreateLogRocketProjectArgs = {
|
||||
appId: Scalars['ID']['input'];
|
||||
logRocketProjectSlug: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type LogRocketProjectMutationDeleteLogRocketProjectArgs = {
|
||||
logRocketProjectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type LogsTimespan = {
|
||||
end: Scalars['DateTime']['input'];
|
||||
start?: InputMaybe<Scalars['DateTime']['input']>;
|
||||
@@ -5512,6 +5763,7 @@ export type PublicArtifacts = {
|
||||
export type PublishUpdateGroupInput = {
|
||||
awaitingCodeSigningInfo?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
branchId: Scalars['String']['input'];
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
excludedAssets?: InputMaybe<Array<PartialManifestAsset>>;
|
||||
fingerprintInfoGroup?: InputMaybe<FingerprintInfoGroup>;
|
||||
gitCommitHash?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -5519,7 +5771,6 @@ export type PublishUpdateGroupInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
rollBackToEmbeddedInfoGroup?: InputMaybe<UpdateRollBackToEmbeddedGroup>;
|
||||
rolloutInfoGroup?: InputMaybe<UpdateRolloutInfoGroup>;
|
||||
runtimeFingerprintSource?: InputMaybe<FingerprintSourceInput>;
|
||||
runtimeVersion: Scalars['String']['input'];
|
||||
turtleJobRunId?: InputMaybe<Scalars['String']['input']>;
|
||||
updateInfoGroup?: InputMaybe<UpdateInfoGroup>;
|
||||
@@ -5541,10 +5792,12 @@ export type RequestsFilters = {
|
||||
hasCustomDomainOrigin?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
isAsset?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
isCrash?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
isLimitExceeded?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
isVerifiedBot?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
method?: InputMaybe<Array<RequestMethod>>;
|
||||
os?: InputMaybe<Array<UserAgentOs>>;
|
||||
pathname?: InputMaybe<Scalars['String']['input']>;
|
||||
requestId?: InputMaybe<Array<Scalars['WorkerDeploymentRequestID']['input']>>;
|
||||
responseType?: InputMaybe<Array<ResponseType>>;
|
||||
status?: InputMaybe<Array<Scalars['Int']['input']>>;
|
||||
statusType?: InputMaybe<Array<ResponseStatusType>>;
|
||||
@@ -5617,8 +5870,11 @@ export type Robot = Actor & {
|
||||
*/
|
||||
featureGates: Scalars['JSONObject']['output'];
|
||||
firstName?: Maybe<Scalars['String']['output']>;
|
||||
/** GitHub App Installations that manage this actor */
|
||||
githubAppInstallations: Array<GitHubAppInstallation>;
|
||||
id: Scalars['ID']['output'];
|
||||
isExpoAdmin: Scalars['Boolean']['output'];
|
||||
isManagedByGitHubApp: Scalars['Boolean']['output'];
|
||||
lastDeletionAttemptTime?: Maybe<Scalars['DateTime']['output']>;
|
||||
};
|
||||
|
||||
@@ -5731,6 +5987,8 @@ export type RootMutation = {
|
||||
environmentSecret: EnvironmentSecretMutation;
|
||||
/** Mutations that create and delete EnvironmentVariables */
|
||||
environmentVariable: EnvironmentVariableMutation;
|
||||
/** Mutations that modify App fingerprints */
|
||||
fingerprint: FingerprintMutation;
|
||||
/** Mutations that utilize services facilitated by the GitHub App */
|
||||
githubApp: GitHubAppMutation;
|
||||
/** Mutations for GitHub App installations */
|
||||
@@ -5753,6 +6011,10 @@ export type RootMutation = {
|
||||
/** Mutations that modify an EAS Build */
|
||||
jobRun: JobRunMutation;
|
||||
keystoreGenerationUrl: KeystoreGenerationUrlMutation;
|
||||
/** Mutations for LogRocket organizations */
|
||||
logRocketOrganization: LogRocketOrganizationMutation;
|
||||
/** Mutations for LogRocket projects */
|
||||
logRocketProject: LogRocketProjectMutation;
|
||||
/** Mutations that modify the currently authenticated User */
|
||||
me: MeMutation;
|
||||
/** Mutations that modify a NotificationSubscription */
|
||||
@@ -5946,7 +6208,7 @@ export type Runtime = {
|
||||
builds: AppBuildsConnection;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
deployments: DeploymentsConnection;
|
||||
fingerprintDebugInfoUrl?: Maybe<Scalars['String']['output']>;
|
||||
fingerprint?: Maybe<Fingerprint>;
|
||||
firstBuildCreatedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
@@ -6383,7 +6645,7 @@ export enum SubmissionAndroidTrack {
|
||||
}
|
||||
|
||||
export type SubmissionArchiveSourceInput = {
|
||||
/** Required if the archive source type is GCS_BUILD_APPLICATION_ARCHIVE or GCS_SUBMIT_ARCHIVE */
|
||||
/** Required if the archive source type is GCS_BUILD_APPLICATION_ARCHIVE, GCS_BUILD_APPLICATION_ARCHIVE_ORCHESTRATOR or GCS_SUBMIT_ARCHIVE */
|
||||
bucketKey?: InputMaybe<Scalars['String']['input']>;
|
||||
type: SubmissionArchiveSourceType;
|
||||
/** Required if the archive source type is URL */
|
||||
@@ -6392,6 +6654,7 @@ export type SubmissionArchiveSourceInput = {
|
||||
|
||||
export enum SubmissionArchiveSourceType {
|
||||
GcsBuildApplicationArchive = 'GCS_BUILD_APPLICATION_ARCHIVE',
|
||||
GcsBuildApplicationArchiveOrchestrator = 'GCS_BUILD_APPLICATION_ARCHIVE_ORCHESTRATOR',
|
||||
GcsSubmitArchive = 'GCS_SUBMIT_ARCHIVE',
|
||||
Url = 'URL'
|
||||
}
|
||||
@@ -6549,6 +6812,7 @@ export type Update = ActivityTimelineProjectActivity & {
|
||||
codeSigningInfo?: Maybe<CodeSigningInfo>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
deployments: DeploymentResult;
|
||||
environment?: Maybe<EnvironmentVariableEnvironment>;
|
||||
expoGoSDKVersion?: Maybe<Scalars['String']['output']>;
|
||||
fingerprint?: Maybe<Fingerprint>;
|
||||
gitCommitHash?: Maybe<Scalars['String']['output']>;
|
||||
@@ -6746,6 +7010,12 @@ export type UpdateEnvironmentVariableInput = {
|
||||
visibility?: InputMaybe<EnvironmentVariableVisibility>;
|
||||
};
|
||||
|
||||
export type UpdateFilterInput = {
|
||||
fingerprintHash?: InputMaybe<Scalars['String']['input']>;
|
||||
hasFingerprint?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
runtimeVersion?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type UpdateGitHubBuildTriggerInput = {
|
||||
autoSubmit: Scalars['Boolean']['input'];
|
||||
buildProfile: Scalars['String']['input'];
|
||||
@@ -7641,6 +7911,7 @@ export type WorkerDeployment = ActivityTimelineProjectActivity & {
|
||||
activityTimestamp: Scalars['DateTime']['output'];
|
||||
actor?: Maybe<Actor>;
|
||||
aliases?: Maybe<Array<WorkerDeploymentAlias>>;
|
||||
app: App;
|
||||
crashes?: Maybe<WorkerDeploymentCrashes>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
deploymentDomain: Scalars['String']['output'];
|
||||
@@ -7651,7 +7922,6 @@ export type WorkerDeployment = ActivityTimelineProjectActivity & {
|
||||
logs?: Maybe<WorkerDeploymentLogs>;
|
||||
requests?: Maybe<WorkerDeploymentRequests>;
|
||||
subdomain: Scalars['String']['output'];
|
||||
tier?: Maybe<WorkerDeploymentTier>;
|
||||
url: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
@@ -7705,9 +7975,19 @@ export type WorkerDeploymentCrashEdge = {
|
||||
request?: Maybe<WorkerDeploymentRequestNode>;
|
||||
};
|
||||
|
||||
export enum WorkerDeploymentCrashKind {
|
||||
ExceededCpu = 'EXCEEDED_CPU',
|
||||
ExceededMemory = 'EXCEEDED_MEMORY',
|
||||
ExceededSubrequests = 'EXCEEDED_SUBREQUESTS',
|
||||
Generic = 'GENERIC',
|
||||
Internal = 'INTERNAL',
|
||||
ResponseStreamDisconnected = 'RESPONSE_STREAM_DISCONNECTED'
|
||||
}
|
||||
|
||||
export type WorkerDeploymentCrashNode = {
|
||||
__typename?: 'WorkerDeploymentCrashNode';
|
||||
crashHash: Scalars['ID']['output'];
|
||||
crashKind: WorkerDeploymentCrashKind;
|
||||
crashTimestamp: Scalars['DateTime']['output'];
|
||||
deploymentIdentifier: Scalars['String']['output'];
|
||||
firstStackLine?: Maybe<Scalars['String']['output']>;
|
||||
@@ -7819,6 +8099,7 @@ export type WorkerDeploymentRequestNode = {
|
||||
hasCustomDomainOrigin: Scalars['Boolean']['output'];
|
||||
isAsset: Scalars['Boolean']['output'];
|
||||
isCrash: Scalars['Boolean']['output'];
|
||||
isLimitExceeded: Scalars['Boolean']['output'];
|
||||
isRejected: Scalars['Boolean']['output'];
|
||||
isStaleIfError: Scalars['Boolean']['output'];
|
||||
isStaleWhileRevalidate: Scalars['Boolean']['output'];
|
||||
@@ -7828,6 +8109,7 @@ export type WorkerDeploymentRequestNode = {
|
||||
os?: Maybe<UserAgentOs>;
|
||||
pathname: Scalars['String']['output'];
|
||||
region?: Maybe<Scalars['String']['output']>;
|
||||
requestId: Scalars['WorkerDeploymentRequestID']['output'];
|
||||
requestTimestamp: Scalars['DateTime']['output'];
|
||||
responseType: ResponseType;
|
||||
scriptName: Scalars['String']['output'];
|
||||
@@ -7929,6 +8211,8 @@ export type WorkerDeploymentRequestsAggregationNode = {
|
||||
durationP50: Scalars['Float']['output'];
|
||||
durationP90: Scalars['Float']['output'];
|
||||
durationP99: Scalars['Float']['output'];
|
||||
limitExceededPerMs?: Maybe<Scalars['Float']['output']>;
|
||||
limitExceededSum: Scalars['Int']['output'];
|
||||
requestsPerMs?: Maybe<Scalars['Float']['output']>;
|
||||
requestsSum: Scalars['Int']['output'];
|
||||
sampleRate?: Maybe<Scalars['Float']['output']>;
|
||||
@@ -8004,11 +8288,6 @@ export type WorkerDeploymentRequestsTimeseriesEdge = {
|
||||
timestamp: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export enum WorkerDeploymentTier {
|
||||
Free = 'FREE',
|
||||
Paid = 'PAID'
|
||||
}
|
||||
|
||||
export type WorkerDeploymentsConnection = {
|
||||
__typename?: 'WorkerDeploymentsConnection';
|
||||
edges: Array<WorkerDeploymentEdge>;
|
||||
@@ -8052,6 +8331,19 @@ export type WorkflowRunsPaginatedArgs = {
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type WorkflowArtifact = {
|
||||
__typename?: 'WorkflowArtifact';
|
||||
contentType?: Maybe<Scalars['String']['output']>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
downloadUrl?: Maybe<Scalars['String']['output']>;
|
||||
fileSizeBytes?: Maybe<Scalars['Int']['output']>;
|
||||
filename: Scalars['String']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
jobRun: JobRun;
|
||||
name: Scalars['String']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
};
|
||||
|
||||
export type WorkflowJob = {
|
||||
__typename?: 'WorkflowJob';
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
@@ -8112,6 +8404,7 @@ export enum WorkflowJobType {
|
||||
AppleDeviceRegistrationRequest = 'APPLE_DEVICE_REGISTRATION_REQUEST',
|
||||
Build = 'BUILD',
|
||||
Custom = 'CUSTOM',
|
||||
Deploy = 'DEPLOY',
|
||||
GetBuild = 'GET_BUILD',
|
||||
MaestroTest = 'MAESTRO_TEST',
|
||||
RequireApproval = 'REQUIRE_APPROVAL',
|
||||
@@ -8121,6 +8414,7 @@ export enum WorkflowJobType {
|
||||
|
||||
export type WorkflowProjectSourceInput = {
|
||||
easJsonBucketKey: Scalars['String']['input'];
|
||||
packageJsonBucketKey?: InputMaybe<Scalars['String']['input']>;
|
||||
projectArchiveBucketKey: Scalars['String']['input'];
|
||||
type: WorkflowProjectSourceType;
|
||||
};
|
||||
@@ -8204,7 +8498,11 @@ export type WorkflowRun = ActivityTimelineProjectActivity & {
|
||||
name: Scalars['String']['output'];
|
||||
pullRequestNumber?: Maybe<Scalars['Int']['output']>;
|
||||
requestedGitRef?: Maybe<Scalars['String']['output']>;
|
||||
retriedWorkflowRun?: Maybe<WorkflowRun>;
|
||||
retries: Array<WorkflowRun>;
|
||||
sourceExpiresAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
status: WorkflowRunStatus;
|
||||
triggerEventType: WorkflowRunTriggerEventType;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
workflow: Workflow;
|
||||
workflowRevision?: Maybe<WorkflowRevision>;
|
||||
@@ -8230,6 +8528,7 @@ export type WorkflowRunMutation = {
|
||||
__typename?: 'WorkflowRunMutation';
|
||||
cancelWorkflowRun: WorkflowRun;
|
||||
createWorkflowRun: WorkflowRun;
|
||||
retryWorkflowRun: WorkflowRun;
|
||||
};
|
||||
|
||||
|
||||
@@ -8244,6 +8543,12 @@ export type WorkflowRunMutationCreateWorkflowRunArgs = {
|
||||
workflowRunInput: WorkflowRunInput;
|
||||
};
|
||||
|
||||
|
||||
export type WorkflowRunMutationRetryWorkflowRunArgs = {
|
||||
fromFailedJobs?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
workflowRunId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type WorkflowRunQuery = {
|
||||
__typename?: 'WorkflowRunQuery';
|
||||
byId: WorkflowRun;
|
||||
@@ -8264,6 +8569,15 @@ export enum WorkflowRunStatus {
|
||||
Success = 'SUCCESS'
|
||||
}
|
||||
|
||||
export enum WorkflowRunTriggerEventType {
|
||||
Github = 'GITHUB',
|
||||
GithubPullRequestOpened = 'GITHUB_PULL_REQUEST_OPENED',
|
||||
GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED',
|
||||
GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE',
|
||||
GithubPush = 'GITHUB_PUSH',
|
||||
Manual = 'MANUAL'
|
||||
}
|
||||
|
||||
export type WorkflowRunsConnection = {
|
||||
__typename?: 'WorkflowRunsConnection';
|
||||
edges: Array<WorkflowRunEdge>;
|
||||
@@ -8819,7 +9133,7 @@ export type CreateAndroidBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
|
||||
export type CreateIosBuildMutationVariables = Exact<{
|
||||
appId: Scalars['ID']['input'];
|
||||
@@ -8829,7 +9143,7 @@ export type CreateIosBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
|
||||
export type UpdateBuildMetadataMutationVariables = Exact<{
|
||||
buildId: Scalars['ID']['input'];
|
||||
@@ -8837,7 +9151,7 @@ export type UpdateBuildMetadataMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateBuildMetadataMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', updateBuildMetadata: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type UpdateBuildMetadataMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', updateBuildMetadata: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } };
|
||||
|
||||
export type RetryIosBuildMutationVariables = Exact<{
|
||||
buildId: Scalars['ID']['input'];
|
||||
@@ -8845,7 +9159,7 @@ export type RetryIosBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } };
|
||||
|
||||
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
|
||||
input: CreateEnvironmentSecretInput;
|
||||
@@ -8926,6 +9240,14 @@ export type CreateBulkEnvironmentVariablesForAppMutationVariables = Exact<{
|
||||
|
||||
export type CreateBulkEnvironmentVariablesForAppMutation = { __typename?: 'RootMutation', environmentVariable: { __typename?: 'EnvironmentVariableMutation', createBulkEnvironmentVariablesForApp: Array<{ __typename?: 'EnvironmentVariable', id: string }> } };
|
||||
|
||||
export type CreateFingeprintMutationVariables = Exact<{
|
||||
fingerprintData: CreateFingerprintInput;
|
||||
appId: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateFingeprintMutation = { __typename?: 'RootMutation', fingerprint: { __typename?: 'FingerprintMutation', createOrGetExistingFingerprint: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, builds: { __typename?: 'AppBuildsConnection', edges: Array<{ __typename?: 'AppBuildEdge', node: { __typename?: 'Build', platform: AppPlatform, id: string } }> }, updates: { __typename?: 'AppUpdatesConnection', edges: Array<{ __typename?: 'AppUpdateEdge', node: { __typename?: 'Update', id: string, platform: string } }> } } } };
|
||||
|
||||
export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -8943,7 +9265,7 @@ export type UpdatePublishMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdatePublishMutation = { __typename?: 'RootMutation', updateBranch: { __typename?: 'UpdateBranchMutation', publishUpdateGroups: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> } };
|
||||
export type UpdatePublishMutation = { __typename?: 'RootMutation', updateBranch: { __typename?: 'UpdateBranchMutation', publishUpdateGroups: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> } };
|
||||
|
||||
export type SetCodeSigningInfoMutationVariables = Exact<{
|
||||
updateId: Scalars['ID']['input'];
|
||||
@@ -8959,7 +9281,7 @@ export type SetRolloutPercentageMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type SetRolloutPercentageMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', setRolloutPercentage: { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null } } };
|
||||
export type SetRolloutPercentageMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', setRolloutPercentage: { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null } } };
|
||||
|
||||
export type CreateAndroidSubmissionMutationVariables = Exact<{
|
||||
appId: Scalars['ID']['input'];
|
||||
@@ -9098,7 +9420,7 @@ export type BranchesByAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> }> } } };
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> }> } } };
|
||||
|
||||
export type BranchesBasicPaginatedOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9119,28 +9441,28 @@ export type ViewBranchesOnUpdateChannelQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewBranchesOnUpdateChannelQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> } | null } } };
|
||||
export type ViewBranchesOnUpdateChannelQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> } | null } } };
|
||||
|
||||
export type BuildsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } };
|
||||
|
||||
export type BuildsWithSubmissionsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } };
|
||||
|
||||
export type BuildsWithFingerprintByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsWithFingerprintByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null } | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type BuildsWithFingerprintByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, builds: { __typename?: 'AppBuildsConnection', edges: Array<{ __typename?: 'AppBuildEdge', node: { __typename?: 'Build', platform: AppPlatform, id: string } }> }, updates: { __typename?: 'AppUpdatesConnection', edges: Array<{ __typename?: 'AppUpdateEdge', node: { __typename?: 'Update', id: string, platform: string } }> } } | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } };
|
||||
|
||||
export type ViewBuildsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9150,7 +9472,7 @@ export type ViewBuildsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
|
||||
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null }> } } };
|
||||
|
||||
export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9159,7 +9481,7 @@ export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateChannelOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, isPaused: boolean, 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, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> } | null } } };
|
||||
export type ViewUpdateChannelOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannelByName?: { __typename?: 'UpdateChannel', id: string, isPaused: boolean, 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, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> } | null } } };
|
||||
|
||||
export type ViewUpdateChannelsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9168,7 +9490,7 @@ export type ViewUpdateChannelsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateChannelsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannels: Array<{ __typename?: 'UpdateChannel', id: string, isPaused: boolean, 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, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> }> } } };
|
||||
export type ViewUpdateChannelsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateChannels: Array<{ __typename?: 'UpdateChannel', id: string, isPaused: boolean, 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, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> }> }> } } };
|
||||
|
||||
export type ViewUpdateChannelsPaginatedOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9228,6 +9550,18 @@ export type EnvironmentVariablesSharedWithSensitiveQueryVariables = Exact<{
|
||||
|
||||
export type EnvironmentVariablesSharedWithSensitiveQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, ownerAccount: { __typename?: 'Account', id: string, environmentVariablesIncludingSensitive: Array<{ __typename?: 'EnvironmentVariableWithSecret', id: string, name: string, value?: string | null, environments?: Array<EnvironmentVariableEnvironment> | null, createdAt: any, updatedAt: any, scope: EnvironmentVariableScope, visibility: EnvironmentVariableVisibility, type: EnvironmentSecretType, valueWithFileContent?: string | null }> } } } };
|
||||
|
||||
export type FingerprintsByAppIdQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
fingerprintFilter?: InputMaybe<FingerprintFilterInput>;
|
||||
}>;
|
||||
|
||||
|
||||
export type FingerprintsByAppIdQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, fingerprintsPaginated: { __typename?: 'AppFingerprintsConnection', edges: Array<{ __typename?: 'AppFingerprintEdge', node: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, builds: { __typename?: 'AppBuildsConnection', edges: Array<{ __typename?: 'AppBuildEdge', node: { __typename?: 'Build', platform: AppPlatform, id: string } }> }, updates: { __typename?: 'AppUpdatesConnection', edges: Array<{ __typename?: 'AppUpdateEdge', node: { __typename?: 'Update', id: string, platform: string } }> } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null } } } } };
|
||||
|
||||
export type GoogleServiceAccountKeyByIdQueryVariables = Exact<{
|
||||
ascApiKeyId: Scalars['ID']['input'];
|
||||
}>;
|
||||
@@ -9292,7 +9626,7 @@ export type ViewUpdatesByGroupQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdatesByGroupQuery = { __typename?: 'RootQuery', updatesByGroup: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> };
|
||||
export type ViewUpdatesByGroupQuery = { __typename?: 'RootQuery', updatesByGroup: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> };
|
||||
|
||||
export type ViewUpdateGroupsOnBranchQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9303,7 +9637,7 @@ export type ViewUpdateGroupsOnBranchQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateGroupsOnBranchQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> } | null } } };
|
||||
export type ViewUpdateGroupsOnBranchQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> } | null } } };
|
||||
|
||||
export type ViewUpdateGroupsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
@@ -9313,7 +9647,14 @@ export type ViewUpdateGroupsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewUpdateGroupsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> } } };
|
||||
export type ViewUpdateGroupsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateGroups: Array<Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }>> } } };
|
||||
|
||||
export type UpdateByIdQueryVariables = Exact<{
|
||||
updateId: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateByIdQuery = { __typename?: 'RootQuery', updates: { __typename?: 'UpdateQuery', byId: { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null } } };
|
||||
|
||||
export type CurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -9339,11 +9680,11 @@ export type AccountFragment = { __typename?: 'Account', id: string, name: string
|
||||
|
||||
export type AppFragment = { __typename?: 'App', id: string, name: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> }, githubRepository?: { __typename?: 'GitHubRepository', id: string, metadata: { __typename?: 'GitHubRepositoryMetadata', githubRepoOwnerName: string, githubRepoName: string } } | null };
|
||||
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null };
|
||||
|
||||
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null };
|
||||
|
||||
export type BuildWithFingerprintFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null } | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
export type BuildWithFingerprintFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, builds: { __typename?: 'AppBuildsConnection', edges: Array<{ __typename?: 'AppBuildEdge', node: { __typename?: 'Build', platform: AppPlatform, id: string } }> }, updates: { __typename?: 'AppUpdatesConnection', edges: Array<{ __typename?: 'AppUpdateEdge', node: { __typename?: 'Update', id: string, platform: string } }> } } | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null };
|
||||
|
||||
export type EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, type: EnvironmentSecretType, createdAt: any };
|
||||
|
||||
@@ -9351,7 +9692,7 @@ export type EnvironmentVariableFragment = { __typename?: 'EnvironmentVariable',
|
||||
|
||||
export type EnvironmentVariableWithSecretFragment = { __typename?: 'EnvironmentVariableWithSecret', id: string, name: string, value?: string | null, environments?: Array<EnvironmentVariableEnvironment> | null, createdAt: any, updatedAt: any, scope: EnvironmentVariableScope, visibility: EnvironmentVariableVisibility, type: EnvironmentSecretType };
|
||||
|
||||
export type FingerprintFragment = { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null };
|
||||
export type FingerprintFragment = { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, builds: { __typename?: 'AppBuildsConnection', edges: Array<{ __typename?: 'AppBuildEdge', node: { __typename?: 'Build', platform: AppPlatform, id: string } }> }, updates: { __typename?: 'AppUpdatesConnection', edges: Array<{ __typename?: 'AppUpdateEdge', node: { __typename?: 'Update', id: string, platform: string } }> } };
|
||||
|
||||
export type RuntimeFragment = { __typename?: 'Runtime', id: string, version: string };
|
||||
|
||||
@@ -9359,9 +9700,9 @@ export type StatuspageServiceFragment = { __typename?: 'StatuspageService', id:
|
||||
|
||||
export type SubmissionFragment = { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null };
|
||||
|
||||
export type UpdateFragment = { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null };
|
||||
export type UpdateFragment = { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null };
|
||||
|
||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> };
|
||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | 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, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string, debugInfoUrl?: string | null, source?: { __typename?: 'FingerprintSource', type: FingerprintSourceType, bucketKey: string, isDebugFingerprint?: boolean | null } | null } | null }> };
|
||||
|
||||
export type UpdateBranchBasicInfoFragment = { __typename?: 'UpdateBranch', id: string, name: string };
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { FingerprintSource } from '@expo/eas-build-job';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import { CreateFingeprintMutation, FingerprintFragment } from '../generated';
|
||||
import { FingerprintFragmentNode } from '../types/Fingerprint';
|
||||
|
||||
export const FingerprintMutation = {
|
||||
async createFingerprintAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
appId: string,
|
||||
fingerprintData: { hash: string; source?: FingerprintSource }
|
||||
): Promise<FingerprintFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateFingeprintMutation>(
|
||||
gql`
|
||||
mutation CreateFingeprintMutation(
|
||||
$fingerprintData: CreateFingerprintInput!
|
||||
$appId: ID!
|
||||
) {
|
||||
fingerprint {
|
||||
createOrGetExistingFingerprint(fingerprintData: $fingerprintData, appId: $appId) {
|
||||
id
|
||||
...FingerprintFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(FingerprintFragmentNode)}
|
||||
`,
|
||||
{ appId, fingerprintData }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return data.fingerprint.createOrGetExistingFingerprint;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
FingerprintFilterInput,
|
||||
FingerprintFragment,
|
||||
FingerprintsByAppIdQuery,
|
||||
} from '../generated';
|
||||
import { FingerprintFragmentNode } from '../types/Fingerprint';
|
||||
|
||||
export const FingerprintQuery = {
|
||||
async byHashAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
hash,
|
||||
}: {
|
||||
appId: string;
|
||||
hash: string;
|
||||
}
|
||||
): Promise<FingerprintFragment | null> {
|
||||
const fingerprintConnection = await FingerprintQuery.getFingerprintsAsync(graphqlClient, {
|
||||
appId,
|
||||
fingerprintFilter: { hashes: [hash] },
|
||||
first: 1,
|
||||
});
|
||||
const fingerprints = fingerprintConnection.edges.map(edge => edge.node);
|
||||
return fingerprints[0] ?? null;
|
||||
},
|
||||
async getFingerprintsAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
first,
|
||||
after,
|
||||
last,
|
||||
before,
|
||||
fingerprintFilter,
|
||||
}: {
|
||||
appId: string;
|
||||
first?: number;
|
||||
after?: string;
|
||||
last?: number;
|
||||
before?: string;
|
||||
fingerprintFilter?: FingerprintFilterInput;
|
||||
}
|
||||
): Promise<FingerprintsByAppIdQuery['app']['byId']['fingerprintsPaginated']> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<FingerprintsByAppIdQuery>(
|
||||
gql`
|
||||
query FingerprintsByAppId(
|
||||
$appId: String!
|
||||
$after: String
|
||||
$first: Int
|
||||
$before: String
|
||||
$last: Int
|
||||
$fingerprintFilter: FingerprintFilterInput
|
||||
) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
fingerprintsPaginated(
|
||||
after: $after
|
||||
first: $first
|
||||
before: $before
|
||||
last: $last
|
||||
filter: $fingerprintFilter
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...FingerprintFragment
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(FingerprintFragmentNode)}
|
||||
`,
|
||||
{ appId, after, first, before, last, fingerprintFilter },
|
||||
{ additionalTypenames: ['Fingerprint'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app?.byId.fingerprintsPaginated;
|
||||
},
|
||||
};
|
||||
@@ -4,6 +4,8 @@ import gql from 'graphql-tag';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
UpdateByIdQuery,
|
||||
UpdateByIdQueryVariables,
|
||||
UpdateFragment,
|
||||
ViewUpdateGroupsOnAppQuery,
|
||||
ViewUpdateGroupsOnAppQueryVariables,
|
||||
@@ -137,4 +139,30 @@ export const UpdateQuery = {
|
||||
|
||||
return response.app.byId.updateGroups;
|
||||
},
|
||||
async viewByUpdateAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ updateId }: UpdateByIdQueryVariables
|
||||
): Promise<UpdateFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<UpdateByIdQuery, UpdateByIdQueryVariables>(
|
||||
gql`
|
||||
query UpdateByIdQuery($updateId: ID!) {
|
||||
updates {
|
||||
byId(updateId: $updateId) {
|
||||
id
|
||||
...UpdateFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateFragmentNode)}
|
||||
`,
|
||||
{ updateId },
|
||||
{ additionalTypenames: ['Update'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.updates.byId;
|
||||
},
|
||||
};
|
||||
@@ -57,6 +57,11 @@ export const BuildFragmentNode = gql`
|
||||
completedAt
|
||||
expirationDate
|
||||
isForIosSimulator
|
||||
metrics {
|
||||
buildWaitTime
|
||||
buildQueueTime
|
||||
buildDuration
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -5,5 +5,23 @@ export const FingerprintFragmentNode = gql`
|
||||
id
|
||||
hash
|
||||
debugInfoUrl
|
||||
builds(first: 1) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
... on Build {
|
||||
platform
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updates(first: 1) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
platform
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -38,6 +38,7 @@ export const UpdateFragmentNode = gql`
|
||||
fingerprint {
|
||||
id
|
||||
hash
|
||||
debugInfoUrl
|
||||
source {
|
||||
type
|
||||
bucketKey
|
||||
|
||||
+2
-2
@@ -84,13 +84,13 @@ export async function fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsyn
|
||||
const projectDashboardUrl = getProjectDashboardUrl(accountName, projectName);
|
||||
const projectLink = link(projectDashboardUrl, { text: projectFullName });
|
||||
|
||||
const spinner = ora(`Creating ${chalk.bold(projectFullName)} on Expo`).start();
|
||||
const spinner = ora(`Creating ${chalk.bold(projectFullName)} on EAS`).start();
|
||||
try {
|
||||
const id = await AppMutation.createAppAsync(graphqlClient, {
|
||||
accountId: account.id,
|
||||
projectName,
|
||||
});
|
||||
spinner.succeed(`Created ${chalk.bold(projectLink)} on Expo`);
|
||||
spinner.succeed(`Created ${chalk.bold(projectLink)} on EAS`);
|
||||
return id;
|
||||
} catch (err) {
|
||||
spinner.fail();
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
import { getConfig } from '@expo/config';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import * as prompts from '../../../prompts';
|
||||
import { ensureNonExemptEncryptionIsDefinedForManagedProjectAsync } from '../exemptEncryption';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../prompts');
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetAllMocks();
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
test('prompts non-exempt encryption is not used', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.json': JSON.stringify({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
jest.mocked(prompts.confirmAsync).mockReset().mockResolvedValueOnce(true);
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({
|
||||
expo: {
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
},
|
||||
},
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('does not prompt if the value is already set', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.json': JSON.stringify({
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
// Reset but no mock values.
|
||||
jest.mocked(prompts.confirmAsync).mockReset();
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
expect(prompts.confirmAsync).toHaveBeenCalledTimes(0);
|
||||
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
},
|
||||
},
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
});
|
||||
});
|
||||
|
||||
test('prompts non-exempt encryption in CI', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.json': JSON.stringify({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
// Reset but no mock values.
|
||||
jest.mocked(prompts.confirmAsync).mockReset();
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({
|
||||
expo: {
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
},
|
||||
},
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('prompts non-exempt encryption is not used but app.config.js', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.config.js':
|
||||
`module.exports = ` +
|
||||
JSON.stringify({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
jest.mocked(prompts.confirmAsync).mockReset().mockResolvedValueOnce(true);
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
// Not set.
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({});
|
||||
});
|
||||
|
||||
test('prompts non-exempt encryption is used', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.json': JSON.stringify({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
jest
|
||||
.mocked(prompts.confirmAsync)
|
||||
.mockReset()
|
||||
.mockResolvedValueOnce(false)
|
||||
// Are you sure?
|
||||
.mockResolvedValueOnce(true);
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
expect(prompts.confirmAsync).toHaveBeenCalledTimes(2);
|
||||
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('prompts non-exempt encryption is used but backed out', async () => {
|
||||
const projectRoot = '/project';
|
||||
vol.fromJSON(
|
||||
{
|
||||
'package.json': JSON.stringify({}),
|
||||
'app.json': JSON.stringify({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
},
|
||||
}),
|
||||
},
|
||||
projectRoot
|
||||
);
|
||||
|
||||
jest
|
||||
.mocked(prompts.confirmAsync)
|
||||
.mockReset()
|
||||
.mockResolvedValueOnce(false)
|
||||
// Are you sure?
|
||||
.mockResolvedValueOnce(false);
|
||||
|
||||
await ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir: projectRoot,
|
||||
exp: getConfig(projectRoot, { skipSDKVersionRequirement: true }).exp,
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
expect(getConfig(projectRoot, { skipSDKVersionRequirement: true }).rootConfig).toEqual({
|
||||
expo: {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -29,7 +29,7 @@ describe(getApplePlatformFromSdkRoot, () => {
|
||||
// Update `getApplePlatformFromSdkRoot` to be compatible with new Apple Platforms
|
||||
test('all enumerations work with the function', () => {
|
||||
expect(Object.values(ApplePlatform).sort()).toEqual(
|
||||
[ApplePlatform.IOS, ApplePlatform.TV_OS, ApplePlatform.MAC_OS].sort()
|
||||
[ApplePlatform.IOS, ApplePlatform.TV_OS, ApplePlatform.MAC_OS, ApplePlatform.VISION_OS].sort()
|
||||
);
|
||||
});
|
||||
test('existing SDKs work with the function', () => {
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { ExpoConfig, getProjectConfigDescription, modifyConfigAsync } from '@expo/config';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
|
||||
/** Non-exempt encryption must be set on every build in App Store Connect, we move it to before the build process to attempt only setting it once for the entire life-cycle of the project. */
|
||||
export async function ensureNonExemptEncryptionIsDefinedForManagedProjectAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
nonInteractive,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<void> {
|
||||
// TODO: We could add bare workflow support in the future.
|
||||
// TODO: We could add wizard support for non-exempt encryption in the future.
|
||||
|
||||
const ITSAppUsesNonExemptEncryption =
|
||||
exp.ios?.infoPlist?.ITSAppUsesNonExemptEncryption ?? exp.ios?.config?.usesNonExemptEncryption;
|
||||
|
||||
if (ITSAppUsesNonExemptEncryption == null) {
|
||||
await configureNonExemptEncryptionAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
nonInteractive,
|
||||
});
|
||||
} else {
|
||||
Log.debug(`ITSAppUsesNonExemptEncryption is defined in the app config.`);
|
||||
}
|
||||
}
|
||||
|
||||
async function configureNonExemptEncryptionAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
nonInteractive,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<void> {
|
||||
const description = getProjectConfigDescription(projectDir);
|
||||
if (nonInteractive) {
|
||||
Log.warn(
|
||||
chalk`${description} is missing {bold ios.infoPlist.ITSAppUsesNonExemptEncryption} boolean. Manual configuration is required in App Store Connect before the app can be tested.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let onlyExemptEncryption = await confirmAsync({
|
||||
message: `iOS app only uses standard/exempt encryption? ${chalk.dim(
|
||||
learnMore(
|
||||
'https://developer.apple.com/documentation/Security/complying-with-encryption-export-regulations'
|
||||
)
|
||||
)}`,
|
||||
initial: true,
|
||||
});
|
||||
|
||||
if (!onlyExemptEncryption) {
|
||||
const confirm = await confirmAsync({
|
||||
message: `Are you sure your app uses non-exempt encryption? Selecting 'Yes' will require annual self-classification reports for the US government.`,
|
||||
initial: true,
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
Log.warn(
|
||||
chalk`Set {bold ios.infoPlist.ITSAppUsesNonExemptEncryption} in ${description} to release Apple builds faster.`
|
||||
);
|
||||
onlyExemptEncryption = true;
|
||||
}
|
||||
}
|
||||
|
||||
const ITSAppUsesNonExemptEncryption = !onlyExemptEncryption;
|
||||
|
||||
// Only set this value if the answer is no, this enables developers to see the more in-depth prompt in App Store Connect. They can set the value manually in the app.json to avoid the EAS prompt in subsequent builds.
|
||||
if (ITSAppUsesNonExemptEncryption) {
|
||||
Log.warn(
|
||||
`You'll need to manually configure the encryption status in App Store Connect before your build can be tested.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
// NOTE: Is is it possible to assert that the config needs to be modifiable before building the app?
|
||||
const modification = await modifyConfigAsync(
|
||||
projectDir,
|
||||
{
|
||||
ios: {
|
||||
...(exp.ios ?? {}),
|
||||
infoPlist: {
|
||||
...(exp.ios?.infoPlist ?? {}),
|
||||
ITSAppUsesNonExemptEncryption,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
skipSDKVersionRequirement: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (modification.type !== 'success') {
|
||||
Log.log();
|
||||
if (modification.type === 'warn') {
|
||||
// The project is using a dynamic config, give the user a helpful log and bail out.
|
||||
Log.log(chalk.yellow(modification.message));
|
||||
}
|
||||
|
||||
const edits = {
|
||||
ios: {
|
||||
infoPlist: {
|
||||
ITSAppUsesNonExemptEncryption,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Log.log(chalk.cyan(`Add the following to ${description}:`));
|
||||
Log.log();
|
||||
Log.log(JSON.stringify(edits, null, 2));
|
||||
Log.log();
|
||||
}
|
||||
}
|
||||
@@ -733,11 +733,11 @@ export const defaultPublishPlatforms: UpdatePublishPlatform[] = ['android', 'ios
|
||||
|
||||
export type RuntimeVersionInfo = {
|
||||
runtimeVersion: string;
|
||||
fingerprint: {
|
||||
expoUpdatesRuntimeFingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
fingerprintHash: string | null;
|
||||
expoUpdatesRuntimeFingerprintHash: string | null;
|
||||
};
|
||||
|
||||
type FingerprintInfoGroup = {
|
||||
@@ -797,11 +797,11 @@ async function getRuntimeVersionInfoForPlatformAsync({
|
||||
env: Env | undefined;
|
||||
}): Promise<{
|
||||
runtimeVersion: string;
|
||||
fingerprint: {
|
||||
expoUpdatesRuntimeFingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
fingerprintHash: string | null;
|
||||
expoUpdatesRuntimeFingerprintHash: string | null;
|
||||
}> {
|
||||
if (await isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync(projectDir)) {
|
||||
try {
|
||||
@@ -852,8 +852,8 @@ async function getRuntimeVersionInfoForPlatformAsync({
|
||||
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion,
|
||||
fingerprint: null,
|
||||
fingerprintHash: null,
|
||||
expoUpdatesRuntimeFingerprint: null,
|
||||
expoUpdatesRuntimeFingerprintHash: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -875,13 +875,15 @@ export function getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersion
|
||||
platforms: runtimeVersionInfoObjects.map(
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.platform
|
||||
),
|
||||
fingerprint:
|
||||
expoUpdatesRuntimeFingerprint:
|
||||
runtimeVersionInfoObjects.map(
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.runtimeVersionInfo.fingerprint
|
||||
runtimeVersionInfoObject =>
|
||||
runtimeVersionInfoObject.runtimeVersionInfo.expoUpdatesRuntimeFingerprint
|
||||
)[0] ?? null,
|
||||
fingerprintHash:
|
||||
expoUpdatesRuntimeFingerprintHash:
|
||||
runtimeVersionInfoObjects.map(
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.runtimeVersionInfo.fingerprintHash
|
||||
runtimeVersionInfoObject =>
|
||||
runtimeVersionInfoObject.runtimeVersionInfo.expoUpdatesRuntimeFingerprintHash
|
||||
)[0] ?? null,
|
||||
};
|
||||
}
|
||||
@@ -899,20 +901,20 @@ export async function maybeCalculateFingerprintForRuntimeVersionInfoObjectsWitho
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping: (RuntimeVersionInfo & {
|
||||
platforms: UpdatePublishPlatform[];
|
||||
fingerprintSource: FingerprintSource | null;
|
||||
expoUpdatesRuntimeFingerprintSource: FingerprintSource | null;
|
||||
})[];
|
||||
workflowsByPlatform: Record<Platform, Workflow>;
|
||||
env: Env | undefined;
|
||||
}): Promise<
|
||||
(RuntimeVersionInfo & {
|
||||
platforms: UpdatePublishPlatform[];
|
||||
fingerprintSource: FingerprintSource | null;
|
||||
expoUpdatesRuntimeFingerprintSource: FingerprintSource | null;
|
||||
fingerprintInfoGroup: FingerprintInfoGroup;
|
||||
})[]
|
||||
> {
|
||||
const runtimesToComputeFingerprintsFor =
|
||||
runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping.filter(
|
||||
infoGroup => !infoGroup.fingerprintHash
|
||||
infoGroup => !infoGroup.expoUpdatesRuntimeFingerprintHash
|
||||
);
|
||||
const fingerprintOptionsByRuntimeAndPlatform = new Map<string, FingerprintOptions>();
|
||||
for (const infoGroup of runtimesToComputeFingerprintsFor) {
|
||||
@@ -975,9 +977,11 @@ export async function maybeCalculateFingerprintForRuntimeVersionInfoObjectsWitho
|
||||
infoGroup
|
||||
): infoGroup is RuntimeVersionInfo & {
|
||||
platforms: UpdatePublishPlatform[];
|
||||
fingerprintSource: FingerprintSource;
|
||||
fingerprintHash: string;
|
||||
} => !!infoGroup.fingerprintHash && !!infoGroup.fingerprintSource
|
||||
expoUpdatesRuntimeFingerprintSource: FingerprintSource;
|
||||
expoUpdatesRuntimeFingerprintHash: string;
|
||||
} =>
|
||||
!!infoGroup.expoUpdatesRuntimeFingerprintHash &&
|
||||
!!infoGroup.expoUpdatesRuntimeFingerprintSource
|
||||
)
|
||||
.map(infoGroup => {
|
||||
const platform = infoGroup.platforms[0];
|
||||
@@ -985,8 +989,8 @@ export async function maybeCalculateFingerprintForRuntimeVersionInfoObjectsWitho
|
||||
...infoGroup,
|
||||
fingerprintInfoGroup: {
|
||||
[platform]: {
|
||||
fingerprintHash: infoGroup.fingerprintHash,
|
||||
fingerprintSource: infoGroup.fingerprintSource,
|
||||
fingerprintHash: infoGroup.expoUpdatesRuntimeFingerprintHash,
|
||||
fingerprintSource: infoGroup.expoUpdatesRuntimeFingerprintSource,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -23,11 +23,11 @@ export async function resolveRuntimeVersionUsingCLIAsync({
|
||||
cwd?: string;
|
||||
}): Promise<{
|
||||
runtimeVersion: string | null;
|
||||
fingerprint: {
|
||||
expoUpdatesRuntimeFingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
fingerprintHash: string | null;
|
||||
expoUpdatesRuntimeFingerprintHash: string | null;
|
||||
}> {
|
||||
Log.debug('Using expo-updates runtimeversion:resolve CLI for runtime version resolution');
|
||||
|
||||
@@ -47,13 +47,13 @@ export async function resolveRuntimeVersionUsingCLIAsync({
|
||||
|
||||
return {
|
||||
runtimeVersion: runtimeVersionResult.runtimeVersion ?? null,
|
||||
fingerprint: runtimeVersionResult.fingerprintSources
|
||||
expoUpdatesRuntimeFingerprint: runtimeVersionResult.fingerprintSources
|
||||
? {
|
||||
fingerprintSources: runtimeVersionResult.fingerprintSources,
|
||||
isDebugFingerprintSource: useDebugFingerprintSource,
|
||||
}
|
||||
: null,
|
||||
fingerprintHash: runtimeVersionResult.fingerprintSources
|
||||
expoUpdatesRuntimeFingerprintHash: runtimeVersionResult.fingerprintSources
|
||||
? runtimeVersionResult.runtimeVersion
|
||||
: null,
|
||||
};
|
||||
@@ -75,19 +75,19 @@ export async function resolveRuntimeVersionAsync({
|
||||
cwd?: string;
|
||||
}): Promise<{
|
||||
runtimeVersion: string | null;
|
||||
fingerprint: {
|
||||
expoUpdatesRuntimeFingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
fingerprintHash: string | null;
|
||||
expoUpdatesRuntimeFingerprintHash: string | null;
|
||||
} | null> {
|
||||
if (!(await isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync(projectDir))) {
|
||||
// fall back to the previous behavior (using the @expo/config-plugins eas-cli dependency rather
|
||||
// than the versioned @expo/config-plugins dependency in the project)
|
||||
return {
|
||||
runtimeVersion: await Updates.getRuntimeVersionNullableAsync(projectDir, exp, platform),
|
||||
fingerprint: null,
|
||||
fingerprintHash: null,
|
||||
expoUpdatesRuntimeFingerprint: null,
|
||||
expoUpdatesRuntimeFingerprintHash: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { AccountUploadSessionType } from '../graphql/generated';
|
||||
import { uploadAccountScopedFileAtPathToGCSAsync } from '../uploads';
|
||||
import { formatBytes } from '../utils/files';
|
||||
import { createProgressTracker } from '../utils/progress';
|
||||
|
||||
/**
|
||||
* Uploads the `eas.json` file to GCS as account-scoped object.
|
||||
* Used in workflows. Takes care of logging progress.
|
||||
*/
|
||||
export async function uploadAccountScopedEasJsonAsync({
|
||||
graphqlClient,
|
||||
accountId,
|
||||
projectDir,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
accountId: string;
|
||||
projectDir: string;
|
||||
}): Promise<{ easJsonBucketKey: string }> {
|
||||
const easJsonFilePath = path.join(projectDir, 'eas.json');
|
||||
|
||||
const easJsonFileStat = await fs.promises.stat(easJsonFilePath);
|
||||
|
||||
if (easJsonFileStat.size > 1024 * 1024) {
|
||||
throw new Error('eas.json is too big. Maximum allowed size is 1MB.');
|
||||
}
|
||||
|
||||
const easJsonBucketKey = await uploadAccountScopedFileAtPathToGCSAsync(graphqlClient, {
|
||||
accountId,
|
||||
type: AccountUploadSessionType.WorkflowsProjectSources,
|
||||
path: easJsonFilePath,
|
||||
handleProgressEvent: createProgressTracker({
|
||||
total: easJsonFileStat.size,
|
||||
message: ratio =>
|
||||
`Uploading eas.json to EAS (${formatBytes(easJsonFileStat.size * ratio)} / ${formatBytes(
|
||||
easJsonFileStat.size
|
||||
)})`,
|
||||
completedMessage: (duration: string) => `Uploaded eas.json to EAS ${chalk.dim(duration)}`,
|
||||
}),
|
||||
});
|
||||
|
||||
return { easJsonBucketKey };
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { AccountUploadSessionType } from '../graphql/generated';
|
||||
import { uploadAccountScopedFileAtPathToGCSAsync } from '../uploads';
|
||||
import { formatBytes } from '../utils/files';
|
||||
import { createProgressTracker } from '../utils/progress';
|
||||
|
||||
/**
|
||||
* Uploads a file to GCS as account-scoped object.
|
||||
* Used in workflows. Takes care of logging progress.
|
||||
* (Uses file name when mentioning file in logs.)
|
||||
*/
|
||||
export async function uploadAccountScopedFileAsync({
|
||||
graphqlClient,
|
||||
accountId,
|
||||
filePath,
|
||||
maxSizeBytes,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
accountId: string;
|
||||
filePath: string;
|
||||
maxSizeBytes: number;
|
||||
}): Promise<{ fileBucketKey: string }> {
|
||||
const fileName = path.basename(filePath);
|
||||
const fileStat = await fs.promises.stat(filePath);
|
||||
|
||||
if (fileStat.size > maxSizeBytes) {
|
||||
throw new Error(`File is too big. Maximum allowed size is ${formatBytes(maxSizeBytes)}.`);
|
||||
}
|
||||
|
||||
const fileBucketKey = await uploadAccountScopedFileAtPathToGCSAsync(graphqlClient, {
|
||||
accountId,
|
||||
type: AccountUploadSessionType.WorkflowsProjectSources,
|
||||
path: filePath,
|
||||
handleProgressEvent: createProgressTracker({
|
||||
total: fileStat.size,
|
||||
message: ratio =>
|
||||
`Uploading ${fileName} to EAS (${formatBytes(fileStat.size * ratio)} / ${formatBytes(
|
||||
fileStat.size
|
||||
)})`,
|
||||
completedMessage: (duration: string) => `Uploaded ${fileName} to EAS ${chalk.dim(duration)}`,
|
||||
}),
|
||||
});
|
||||
|
||||
return { fileBucketKey };
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import path from 'path';
|
||||
|
||||
import { runAppOnAndroidEmulatorAsync } from './android/run';
|
||||
import { runAppOnIosSimulatorAsync } from './ios/run';
|
||||
import { AppPlatform } from '../graphql/generated';
|
||||
import { getEasBuildRunCacheDirectoryPath } from '../utils/paths';
|
||||
|
||||
export interface RunArchiveFlags {
|
||||
latest?: boolean;
|
||||
@@ -19,3 +22,14 @@ export async function runAsync(
|
||||
await runAppOnAndroidEmulatorAsync(simulatorBuildPath);
|
||||
}
|
||||
}
|
||||
|
||||
export function getEasBuildRunCachedAppPath(
|
||||
projectId: string,
|
||||
buildId: string,
|
||||
platform: AppPlatform
|
||||
): string {
|
||||
return path.join(
|
||||
getEasBuildRunCacheDirectoryPath(),
|
||||
`${projectId}_${buildId}.${platform === AppPlatform.Ios ? 'app' : 'apk'}`
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { App, RequestContext, Session, User } from '@expo/apple-utils';
|
||||
import { RequestContext, Session, User } from '@expo/apple-utils';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { sanitizeLanguage } from './utils/language';
|
||||
import { getRequestContext } from '../../credentials/ios/appstore/authenticate';
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
ensureAppExistsAsync,
|
||||
ensureBundleIdExistsWithNameAsync,
|
||||
} from '../../credentials/ios/appstore/ensureAppExists';
|
||||
import { ensureTestFlightGroupExistsAsync } from '../../credentials/ios/appstore/ensureTestFlightGroup';
|
||||
import Log from '../../log';
|
||||
import { getBundleIdentifierAsync } from '../../project/ios/bundleIdentifier';
|
||||
import { promptAsync } from '../../prompts';
|
||||
@@ -85,37 +85,22 @@ async function createAppStoreConnectAppAsync(
|
||||
);
|
||||
}
|
||||
|
||||
let app: App | null = null;
|
||||
const app = await ensureAppExistsAsync(userAuthCtx, {
|
||||
name: appName,
|
||||
language,
|
||||
companyName,
|
||||
bundleIdentifier: bundleId,
|
||||
sku,
|
||||
});
|
||||
|
||||
try {
|
||||
app = await ensureAppExistsAsync(userAuthCtx, {
|
||||
name: appName,
|
||||
language,
|
||||
companyName,
|
||||
bundleIdentifier: bundleId,
|
||||
sku,
|
||||
});
|
||||
await ensureTestFlightGroupExistsAsync(app);
|
||||
} catch (error: any) {
|
||||
if (
|
||||
// Name is invalid
|
||||
error.message.match(
|
||||
/App Name contains certain Unicode(.*)characters that are not permitted/
|
||||
) ||
|
||||
// UnexpectedAppleResponse: An attribute value has invalid characters. - App Name contains certain Unicode symbols, emoticons, diacritics, special characters, or private use characters that are not permitted.
|
||||
// Name is taken
|
||||
error.message.match(/The App Name you entered is already being used/)
|
||||
// UnexpectedAppleResponse: The provided entity includes an attribute with a value that has already been used on a different account. - The App Name you entered is already being used. If you have trademark rights to
|
||||
// this name and would like it released for your use, submit a claim.
|
||||
) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.warn(
|
||||
`Change the name in your app config, or use a custom name with the ${chalk.bold(
|
||||
'--app-name'
|
||||
)} flag`
|
||||
);
|
||||
Log.newLine();
|
||||
}
|
||||
throw error;
|
||||
// This process is not critical to the app submission so we shouldn't let it fail the entire process.
|
||||
Log.error(
|
||||
'Failed to create an internal TestFlight group. This can be done manually in the App Store Connect website.'
|
||||
);
|
||||
Log.error(error);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -7,11 +7,12 @@ import Log from '../log';
|
||||
import { ora } from '../ora';
|
||||
|
||||
export type FingerprintOptions = {
|
||||
workflow: Workflow;
|
||||
workflow?: Workflow;
|
||||
platforms: string[];
|
||||
debug?: boolean;
|
||||
env: Env | undefined;
|
||||
cwd?: string;
|
||||
ignorePaths?: string[];
|
||||
};
|
||||
|
||||
export function diffFingerprint(
|
||||
@@ -85,12 +86,20 @@ async function createFingerprintWithoutLoggingAsync(
|
||||
> {
|
||||
const Fingerprint = require(fingerprintPath);
|
||||
const fingerprintOptions: Record<string, any> = {};
|
||||
const ignorePaths = [];
|
||||
if (options.workflow === Workflow.MANAGED) {
|
||||
ignorePaths.push('android/**/*');
|
||||
ignorePaths.push('ios/**/*');
|
||||
}
|
||||
if (options.ignorePaths) {
|
||||
ignorePaths.push(...options.ignorePaths);
|
||||
}
|
||||
if (ignorePaths.length > 0) {
|
||||
fingerprintOptions.ignorePaths = ignorePaths;
|
||||
}
|
||||
if (options.platforms) {
|
||||
fingerprintOptions.platforms = [...options.platforms];
|
||||
}
|
||||
if (options.workflow === Workflow.MANAGED) {
|
||||
fingerprintOptions.ignorePaths = ['android/**/*', 'ios/**/*'];
|
||||
}
|
||||
if (options.debug) {
|
||||
fingerprintOptions.debug = true;
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ function warnAboutServiceOutage(service: StatuspageServiceFragment): void {
|
||||
|
||||
if (service.incidents.length > 0) {
|
||||
const [currentIncident] = service.incidents;
|
||||
Log.warn(`Reason: ${currentIncident.name}`);
|
||||
Log.warn(`Reason: ${currentIncident.name}.`);
|
||||
}
|
||||
|
||||
Log.warn(
|
||||
`All information on service status and incidents available at ${link(
|
||||
'https://status.expo.dev/'
|
||||
)}.`
|
||||
)}`
|
||||
);
|
||||
Log.newLine();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as PackageManagerUtils from '@expo/package-manager';
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import { Errors } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
@@ -14,11 +15,17 @@ import {
|
||||
gitStatusAsync,
|
||||
isGitInstalledAsync,
|
||||
} from '../git';
|
||||
import { EASIGNORE_FILENAME, Ignore, makeShallowCopyAsync } from '../local';
|
||||
import { Client } from '../vcs';
|
||||
|
||||
export default class GitClient extends Client {
|
||||
constructor(private readonly maybeCwdOverride?: string) {
|
||||
private readonly maybeCwdOverride?: string;
|
||||
public requireCommit: boolean;
|
||||
|
||||
constructor(options: { maybeCwdOverride?: string; requireCommit: boolean }) {
|
||||
super();
|
||||
this.maybeCwdOverride = options.maybeCwdOverride;
|
||||
this.requireCommit = options.requireCommit;
|
||||
}
|
||||
|
||||
public override async ensureRepoExistsAsync(): Promise<void> {
|
||||
@@ -119,10 +126,6 @@ export default class GitClient extends Client {
|
||||
}
|
||||
}
|
||||
|
||||
public override async isCommitRequiredAsync(): Promise<boolean> {
|
||||
return await this.hasUncommittedChangesAsync();
|
||||
}
|
||||
|
||||
public override async showChangedFilesAsync(): Promise<void> {
|
||||
const gitStatusOutput = await gitStatusAsync({
|
||||
showUntracked: true,
|
||||
@@ -144,50 +147,80 @@ export default class GitClient extends Client {
|
||||
).stdout.trim();
|
||||
}
|
||||
|
||||
public override async isCommitRequiredAsync(): Promise<boolean> {
|
||||
if (!this.requireCommit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return await this.hasUncommittedChangesAsync();
|
||||
}
|
||||
|
||||
public async makeShallowCopyAsync(destinationPath: string): Promise<void> {
|
||||
if (await this.hasUncommittedChangesAsync()) {
|
||||
if (await this.isCommitRequiredAsync()) {
|
||||
// it should already be checked before this function is called, but in case it wasn't
|
||||
// we want to ensure that any changes were introduced by call to `setGitCaseSensitivityAsync`
|
||||
throw new Error('You have some uncommitted changes in your repository.');
|
||||
}
|
||||
|
||||
const rootPath = await this.getRootPathAsync();
|
||||
|
||||
let gitRepoUri;
|
||||
if (process.platform === 'win32') {
|
||||
// getRootDirectoryAsync() will return C:/path/to/repo on Windows and path
|
||||
// prefix should be file:///
|
||||
gitRepoUri = `file:///${await this.getRootPathAsync()}`;
|
||||
gitRepoUri = `file:///${rootPath}`;
|
||||
} else {
|
||||
// getRootDirectoryAsync() will /path/to/repo, and path prefix should be
|
||||
// file:/// so only file:// needs to be prepended
|
||||
gitRepoUri = `file://${await this.getRootPathAsync()}`;
|
||||
gitRepoUri = `file://${rootPath}`;
|
||||
}
|
||||
const isCaseSensitive = await isGitCaseSensitiveAsync(this.maybeCwdOverride);
|
||||
await setGitCaseSensitivityAsync(true, this.maybeCwdOverride);
|
||||
|
||||
await assertEnablingGitCaseSensitivityDoesNotCauseNewUncommittedChangesAsync(rootPath);
|
||||
|
||||
const isCaseSensitive = await isGitCaseSensitiveAsync(rootPath);
|
||||
try {
|
||||
if (await this.hasUncommittedChangesAsync()) {
|
||||
Log.error('Detected inconsistent filename casing between your local filesystem and git.');
|
||||
Log.error('This will likely cause your build to fail. Impacted files:');
|
||||
await spawnAsync('git', ['status', '--short'], {
|
||||
stdio: 'inherit',
|
||||
cwd: this.maybeCwdOverride,
|
||||
});
|
||||
Log.newLine();
|
||||
Log.error(
|
||||
`Error: Resolve filename casing inconsistencies before proceeding. ${learnMore(
|
||||
'https://expo.fyi/macos-ignorecase'
|
||||
)}`
|
||||
);
|
||||
throw new Error('You have some uncommitted changes in your repository.');
|
||||
}
|
||||
await setGitCaseSensitivityAsync(true, rootPath);
|
||||
await spawnAsync(
|
||||
'git',
|
||||
['clone', '--no-hardlinks', '--depth', '1', gitRepoUri, destinationPath],
|
||||
{
|
||||
cwd: this.maybeCwdOverride,
|
||||
}
|
||||
{ cwd: rootPath }
|
||||
);
|
||||
|
||||
const sourceEasignorePath = path.join(rootPath, EASIGNORE_FILENAME);
|
||||
if (await fs.exists(sourceEasignorePath)) {
|
||||
const cachedFilesWeShouldHaveIgnored = (
|
||||
await spawnAsync(
|
||||
'git',
|
||||
[
|
||||
'ls-files',
|
||||
'--exclude-from',
|
||||
sourceEasignorePath,
|
||||
// `--ignored --cached` makes git print files that should be
|
||||
// ignored by rules from `--exclude-from`, but instead are currently cached.
|
||||
'--ignored',
|
||||
'--cached',
|
||||
// separates file names with null characters
|
||||
'-z',
|
||||
],
|
||||
{ cwd: destinationPath }
|
||||
)
|
||||
).stdout
|
||||
.split('\0')
|
||||
// ls-files' output is terminated by a null character
|
||||
.filter(file => file !== '');
|
||||
|
||||
await Promise.all(
|
||||
cachedFilesWeShouldHaveIgnored.map(file => fs.rm(path.join(destinationPath, file)))
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await setGitCaseSensitivityAsync(isCaseSensitive, this.maybeCwdOverride);
|
||||
await setGitCaseSensitivityAsync(isCaseSensitive, rootPath);
|
||||
}
|
||||
|
||||
// After we create the shallow Git copy, we copy the files
|
||||
// again. This way we include the changed and untracked files
|
||||
// (`git clone` only copies the committed changes).
|
||||
await makeShallowCopyAsync(rootPath, destinationPath);
|
||||
}
|
||||
|
||||
public override async getCommitHashAsync(): Promise<string | undefined> {
|
||||
@@ -252,10 +285,24 @@ export default class GitClient extends Client {
|
||||
}
|
||||
|
||||
public override async isFileIgnoredAsync(filePath: string): Promise<boolean> {
|
||||
const rootPath = await this.getRootPathAsync();
|
||||
const easIgnorePath = path.join(rootPath, EASIGNORE_FILENAME);
|
||||
if (await fs.exists(easIgnorePath)) {
|
||||
const ignore = new Ignore(rootPath);
|
||||
const wouldNotBeCopiedToClone = ignore.ignores(filePath);
|
||||
const wouldBeDeletedFromClone =
|
||||
(
|
||||
await spawnAsync(
|
||||
'git',
|
||||
['ls-files', '--exclude-from', easIgnorePath, '--ignored', '--cached', filePath],
|
||||
{ cwd: rootPath }
|
||||
)
|
||||
).stdout.trim() !== '';
|
||||
return wouldNotBeCopiedToClone && wouldBeDeletedFromClone;
|
||||
}
|
||||
|
||||
try {
|
||||
await spawnAsync('git', ['check-ignore', '-q', filePath], {
|
||||
cwd: this.maybeCwdOverride ?? path.normalize(await this.getRootPathAsync()),
|
||||
});
|
||||
await spawnAsync('git', ['check-ignore', '-q', filePath], { cwd: rootPath });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -386,3 +433,51 @@ async function setGitCaseSensitivityAsync(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function assertEnablingGitCaseSensitivityDoesNotCauseNewUncommittedChangesAsync(
|
||||
cwd: string
|
||||
): Promise<void> {
|
||||
// Remember uncommited changes before case sensitivity change
|
||||
// for later comparison so we log to the user only the files
|
||||
// that were marked as changed after the case sensitivity change.
|
||||
const uncommittedChangesBeforeCaseSensitivityChange = await gitStatusAsync({
|
||||
showUntracked: true,
|
||||
cwd,
|
||||
});
|
||||
|
||||
const isCaseSensitive = await isGitCaseSensitiveAsync(cwd);
|
||||
await setGitCaseSensitivityAsync(true, cwd);
|
||||
try {
|
||||
const uncommitedChangesAfterCaseSensitivityChange = await gitStatusAsync({
|
||||
showUntracked: true,
|
||||
cwd,
|
||||
});
|
||||
|
||||
if (
|
||||
uncommitedChangesAfterCaseSensitivityChange !== uncommittedChangesBeforeCaseSensitivityChange
|
||||
) {
|
||||
const baseUncommitedChangesSet = new Set(
|
||||
uncommittedChangesBeforeCaseSensitivityChange.split('\n')
|
||||
);
|
||||
|
||||
const errorMessage = [
|
||||
'Detected inconsistent filename casing between your local filesystem and git.',
|
||||
'This will likely cause your job to fail. Impacted files:',
|
||||
...uncommitedChangesAfterCaseSensitivityChange.split('\n').flatMap(changedFile => {
|
||||
// This file was changed before the case sensitivity change too.
|
||||
if (baseUncommitedChangesSet.has(changedFile)) {
|
||||
return [];
|
||||
}
|
||||
return [changedFile];
|
||||
}),
|
||||
`Resolve filename casing inconsistencies before proceeding. ${learnMore(
|
||||
'https://expo.fyi/macos-ignorecase'
|
||||
)}`,
|
||||
];
|
||||
|
||||
throw new Error(errorMessage.join('\n'));
|
||||
}
|
||||
} finally {
|
||||
await setGitCaseSensitivityAsync(isCaseSensitive, cwd);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import chalk from 'chalk';
|
||||
import path from 'path';
|
||||
|
||||
import GitClient from './git';
|
||||
import Log from '../../log';
|
||||
import { Ignore, makeShallowCopyAsync } from '../local';
|
||||
|
||||
export default class GitNoCommitClient extends GitClient {
|
||||
public override async isCommitRequiredAsync(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
public override async getRootPathAsync(): Promise<string> {
|
||||
return (await spawnAsync('git', ['rev-parse', '--show-toplevel'])).stdout.trim();
|
||||
}
|
||||
|
||||
public override async makeShallowCopyAsync(destinationPath: string): Promise<void> {
|
||||
// normalize converts C:/some/path to C:\some\path on windows
|
||||
const srcPath = path.normalize(await this.getRootPathAsync());
|
||||
await makeShallowCopyAsync(srcPath, destinationPath);
|
||||
}
|
||||
|
||||
public override async isFileIgnoredAsync(filePath: string): Promise<boolean> {
|
||||
// normalize converts C:/some/path to C:\some\path on windows
|
||||
const rootPath = path.normalize(await this.getRootPathAsync());
|
||||
const ignore = new Ignore(rootPath);
|
||||
await ignore.initIgnoreAsync();
|
||||
return ignore.ignores(filePath);
|
||||
}
|
||||
|
||||
public override async trackFileAsync(file: string): Promise<void> {
|
||||
try {
|
||||
await super.trackFileAsync(file);
|
||||
} catch {
|
||||
// In the no commit workflow it doesn't matter if we fail to track changes,
|
||||
// so we can ignore if this throws an exception
|
||||
Log.warn(
|
||||
`Unable to track ${chalk.bold(path.basename(file))} in Git. Proceeding without tracking.`
|
||||
);
|
||||
Log.warn(` Reason: the command ${chalk.bold(`"git add ${file}"`)} exited with an error.`);
|
||||
Log.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import GitClient from './clients/git';
|
||||
import GitNoCommitClient from './clients/gitNoCommit';
|
||||
import NoVcsClient from './clients/noVcs';
|
||||
import { Client } from './vcs';
|
||||
|
||||
const NO_VCS_WARNING = `Using EAS CLI without version control system is not recommended, use this mode only if you know what you are doing.`;
|
||||
|
||||
let wasNoVcsWarningPrinted = false;
|
||||
|
||||
export function resolveVcsClient(requireCommit: boolean = false): Client {
|
||||
if (process.env.EAS_NO_VCS) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
// This log might be printed before cli arguments are evaluated,
|
||||
// so it needs to go to stderr in case command is run in JSON
|
||||
// only mode.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(chalk.yellow(NO_VCS_WARNING));
|
||||
if (!wasNoVcsWarningPrinted) {
|
||||
// This log might be printed before cli arguments are evaluated,
|
||||
// so it needs to go to stderr in case command is run in JSON
|
||||
// only mode.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(chalk.yellow(NO_VCS_WARNING));
|
||||
wasNoVcsWarningPrinted = true;
|
||||
}
|
||||
}
|
||||
return new NoVcsClient();
|
||||
}
|
||||
if (requireCommit) {
|
||||
return new GitClient();
|
||||
}
|
||||
return new GitNoCommitClient();
|
||||
return new GitClient({ requireCommit });
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
|
||||
import createIgnore, { Ignore as SingleFileIgnore } from 'ignore';
|
||||
import path from 'path';
|
||||
|
||||
const EASIGNORE_FILENAME = '.easignore';
|
||||
export const EASIGNORE_FILENAME = '.easignore';
|
||||
const GITIGNORE_FILENAME = '.gitignore';
|
||||
|
||||
const DEFAULT_IGNORE = `
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { get as getEnv } from '@expo/env';
|
||||
import { parseProjectEnv } from '@expo/env';
|
||||
import { Gzip, GzipOptions } from 'minizlib';
|
||||
import { HashOptions, createHash, randomBytes } from 'node:crypto';
|
||||
import fs, { createWriteStream } from 'node:fs';
|
||||
@@ -98,6 +98,11 @@ export interface Manifest {
|
||||
env: Record<string, string | undefined>;
|
||||
}
|
||||
|
||||
export interface CreateManifestResult {
|
||||
conflictingVariableNames: string[] | undefined;
|
||||
manifest: Manifest;
|
||||
}
|
||||
|
||||
interface CreateManifestParams {
|
||||
projectId: string;
|
||||
projectDir: string;
|
||||
@@ -108,23 +113,32 @@ interface CreateManifestParams {
|
||||
export async function createManifestAsync(
|
||||
params: CreateManifestParams,
|
||||
graphqlClient: ExpoGraphqlClient
|
||||
): Promise<Manifest> {
|
||||
let env: Record<string, string | undefined>;
|
||||
): Promise<CreateManifestResult> {
|
||||
// Resolve .env file variables
|
||||
const { env } = parseProjectEnv(params.projectDir, { mode: 'production' });
|
||||
// Maybe load EAS Environment Variables (based on `--environment` arg)
|
||||
let conflictingVariableNames: string[] | undefined;
|
||||
if (params.environment) {
|
||||
env = Object.fromEntries(
|
||||
(
|
||||
await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
|
||||
appId: params.projectId,
|
||||
environment: params.environment,
|
||||
})
|
||||
).map(variable => [variable.name, variable.value ?? undefined])
|
||||
const loadedVariables = await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
appId: params.projectId,
|
||||
environment: params.environment,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// NOTE: This is required for the .env resolution
|
||||
process.env.NODE_ENV = 'production';
|
||||
env = getEnv(params.projectDir).env;
|
||||
// Load EAS Env vars into `env` object, keeping track of conflicts
|
||||
conflictingVariableNames = [];
|
||||
for (const variable of loadedVariables) {
|
||||
if (variable.value != null) {
|
||||
if (env[variable.name] != null) {
|
||||
conflictingVariableNames.push(variable.name);
|
||||
}
|
||||
env[variable.name] = variable.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { env };
|
||||
const manifest: Manifest = { env };
|
||||
return { conflictingVariableNames, manifest };
|
||||
}
|
||||
|
||||
interface WorkerFileEntry {
|
||||
|
||||
@@ -4,6 +4,7 @@ import mime from 'mime';
|
||||
import { Gzip } from 'minizlib';
|
||||
import fetch, { Headers, HeadersInit, RequestInit, Response } from 'node-fetch';
|
||||
import fs, { createReadStream } from 'node:fs';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import promiseRetry from 'promise-retry';
|
||||
|
||||
@@ -28,6 +29,24 @@ const isCompressible = (contentType: string | null, size: number): boolean => {
|
||||
}
|
||||
};
|
||||
|
||||
const getContentTypeAsync = async (filePath: string): Promise<string | null> => {
|
||||
let contentType = mime.getType(path.basename(filePath));
|
||||
|
||||
if (!contentType) {
|
||||
const fileContent = await readFile(filePath, 'utf-8');
|
||||
try {
|
||||
// check if file is valid JSON without an extension, e.g. for the apple app site association file
|
||||
const parsedData = JSON.parse(fileContent);
|
||||
|
||||
if (parsedData) {
|
||||
contentType = 'application/json';
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return contentType;
|
||||
};
|
||||
|
||||
export interface UploadParams extends Omit<RequestInit, 'signal' | 'body'> {
|
||||
filePath: string;
|
||||
compress?: boolean;
|
||||
@@ -70,14 +89,15 @@ export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
|
||||
headers: headersInit,
|
||||
...requestInit
|
||||
} = params;
|
||||
const stat = await fs.promises.stat(params.filePath);
|
||||
const stat = await fs.promises.stat(filePath);
|
||||
if (stat.size > MAX_UPLOAD_SIZE) {
|
||||
throw new Error(
|
||||
`Upload of "${params.filePath}" aborted: File size is greater than the upload limit (>500MB)`
|
||||
`Upload of "${filePath}" aborted: File size is greater than the upload limit (>500MB)`
|
||||
);
|
||||
}
|
||||
|
||||
const contentType = mime.getType(path.basename(params.filePath));
|
||||
const contentType = await getContentTypeAsync(filePath);
|
||||
|
||||
return await promiseRetry(
|
||||
async retry => {
|
||||
const headers = new Headers(headersInit);
|
||||
@@ -109,8 +129,10 @@ export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
|
||||
return retry(error);
|
||||
}
|
||||
|
||||
const body = await response.json().catch(() => null);
|
||||
const errorMessage = body?.error ?? `Upload of "${filePath}" failed: ${response.statusText}`;
|
||||
const getErrorMessageAsync = async (): Promise<string> => {
|
||||
const body = await response.json().catch(() => null);
|
||||
return body?.error ?? `Upload of "${filePath}" failed: ${response.statusText}`;
|
||||
};
|
||||
|
||||
if (
|
||||
response.status === 408 ||
|
||||
@@ -118,12 +140,12 @@ export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
|
||||
response.status === 429 ||
|
||||
(response.status >= 500 && response.status <= 599)
|
||||
) {
|
||||
return retry(new Error(errorMessage));
|
||||
return retry(new Error(await getErrorMessageAsync()));
|
||||
} else if (response.status === 413) {
|
||||
const message = `Upload of "${filePath}" failed: File size exceeded the upload limit`;
|
||||
throw new Error(message);
|
||||
} else if (!response.ok) {
|
||||
throw new Error(errorMessage);
|
||||
throw new Error(await getErrorMessageAsync());
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"outDir": "build",
|
||||
"rootDir": "src",
|
||||
"typeRoots": ["../../ts-declarations", "../../node_modules/@types", "./node_modules/@types"]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "14.2.0",
|
||||
"version": "15.0.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.23.5",
|
||||
"@expo/eas-build-job": "1.0.156",
|
||||
"@expo/eas-build-job": "1.0.165",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "11.2.0",
|
||||
@@ -18,6 +18,7 @@
|
||||
"tslib": "2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node18": "18.2.4",
|
||||
"@types/babel__code-frame": "7.0.3",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"memfs": "3.4.13",
|
||||
|
||||
@@ -129,6 +129,33 @@ test('valid eas.json with specified environment', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('valid eas.json with specified keystoreName', async () => {
|
||||
const keystoreName = 'this-is-a-keystore-name';
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
development: {
|
||||
android: {
|
||||
credentialsSource: 'remote',
|
||||
keystoreName,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'development'
|
||||
);
|
||||
|
||||
expect(androidProfile).toEqual(
|
||||
expect.objectContaining({
|
||||
keystoreName,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('valid eas.json with top level withoutCredentials property', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
@@ -274,37 +301,124 @@ test('valid profile for internal distribution on Android', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile extending other profile', async () => {
|
||||
test('valid profile with extension chain not exceeding 5', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
base: {
|
||||
node: '12.0.0',
|
||||
},
|
||||
extension: {
|
||||
extension1: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
node: '13.0.0',
|
||||
},
|
||||
extension2: {
|
||||
extends: 'extension1',
|
||||
distribution: 'internal',
|
||||
node: '14.0.0',
|
||||
},
|
||||
extension3: {
|
||||
extends: 'extension2',
|
||||
distribution: 'internal',
|
||||
node: '15.0.0',
|
||||
},
|
||||
extension4: {
|
||||
extends: 'extension3',
|
||||
distribution: 'internal',
|
||||
node: '16.0.0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'base');
|
||||
const extendedProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
const extendedProfile1 = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'extension'
|
||||
'extension1'
|
||||
);
|
||||
const extendedProfile2 = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'extension2'
|
||||
);
|
||||
const extendedProfile3 = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'extension3'
|
||||
);
|
||||
const extendedProfile4 = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'extension4'
|
||||
);
|
||||
expect(baseProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
node: '12.0.0',
|
||||
});
|
||||
expect(extendedProfile).toEqual({
|
||||
expect(extendedProfile1).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '13.0.0',
|
||||
});
|
||||
expect(extendedProfile2).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '14.0.0',
|
||||
});
|
||||
expect(extendedProfile3).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '15.0.0',
|
||||
});
|
||||
expect(extendedProfile4).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '16.0.0',
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile with extension chain exceeding 5 - too long', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
base: {
|
||||
node: '12.0.0',
|
||||
},
|
||||
extension1: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
node: '13.0.0',
|
||||
},
|
||||
extension2: {
|
||||
extends: 'extension1',
|
||||
distribution: 'internal',
|
||||
node: '14.0.0',
|
||||
},
|
||||
extension3: {
|
||||
extends: 'extension2',
|
||||
distribution: 'internal',
|
||||
node: '15.0.0',
|
||||
},
|
||||
extension4: {
|
||||
extends: 'extension3',
|
||||
distribution: 'internal',
|
||||
node: '16.0.0',
|
||||
},
|
||||
extension5: {
|
||||
extends: 'extension4',
|
||||
distribution: 'internal',
|
||||
node: '17.0.0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
await expect(
|
||||
EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'extension5')
|
||||
).rejects.toThrow(
|
||||
'Too long chain of profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
});
|
||||
|
||||
test('valid profile extending other profile with platform specific envs', async () => {
|
||||
|
||||
@@ -169,7 +169,7 @@ test('ios config with ascApiKey fields set to env var', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('valid profile extending other profile', async () => {
|
||||
test('valid profile with extension chain not exceeding 5', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
submit: {
|
||||
base: {
|
||||
@@ -179,7 +179,7 @@ test('valid profile extending other profile', async () => {
|
||||
appleTeamId: 'AB32CZE81F',
|
||||
},
|
||||
},
|
||||
extension: {
|
||||
extension1: {
|
||||
extends: 'base',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81F',
|
||||
@@ -188,15 +188,57 @@ test('valid profile extending other profile', async () => {
|
||||
ascApiKeyId: 'AB32CZE81F',
|
||||
},
|
||||
},
|
||||
extension2: {
|
||||
extends: 'extension1',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81E',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81E',
|
||||
},
|
||||
},
|
||||
extension3: {
|
||||
extends: 'extension2',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81D',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81D',
|
||||
},
|
||||
},
|
||||
extension4: {
|
||||
extends: 'extension3',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81C',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81C',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
const baseProfile = await EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'base');
|
||||
const extendedProfile = await EasJsonUtils.getSubmitProfileAsync(
|
||||
const extendedProfile1 = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.IOS,
|
||||
'extension'
|
||||
'extension1'
|
||||
);
|
||||
const extendedProfile2 = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.IOS,
|
||||
'extension2'
|
||||
);
|
||||
const extendedProfile3 = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.IOS,
|
||||
'extension3'
|
||||
);
|
||||
const extendedProfile4 = await EasJsonUtils.getSubmitProfileAsync(
|
||||
accessor,
|
||||
Platform.IOS,
|
||||
'extension4'
|
||||
);
|
||||
expect(baseProfile).toEqual({
|
||||
language: 'en-US',
|
||||
@@ -204,7 +246,7 @@ test('valid profile extending other profile', async () => {
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'AB32CZE81F',
|
||||
});
|
||||
expect(extendedProfile).toEqual({
|
||||
expect(extendedProfile1).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
@@ -213,6 +255,99 @@ test('valid profile extending other profile', async () => {
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81F',
|
||||
});
|
||||
expect(extendedProfile2).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'AB32CZE81E',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81E',
|
||||
});
|
||||
expect(extendedProfile3).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'AB32CZE81D',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81D',
|
||||
});
|
||||
expect(extendedProfile4).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'AB32CZE81C',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81C',
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile with extension chain exceeding 5 - too long', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
submit: {
|
||||
base: {
|
||||
ios: {
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'AB32CZE81F',
|
||||
},
|
||||
},
|
||||
extension1: {
|
||||
extends: 'base',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81F',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81F',
|
||||
},
|
||||
},
|
||||
extension2: {
|
||||
extends: 'extension1',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81E',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81E',
|
||||
},
|
||||
},
|
||||
extension3: {
|
||||
extends: 'extension2',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81D',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81D',
|
||||
},
|
||||
},
|
||||
extension4: {
|
||||
extends: 'extension3',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81C',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81C',
|
||||
},
|
||||
},
|
||||
extension5: {
|
||||
extends: 'extension4',
|
||||
ios: {
|
||||
appleTeamId: 'AB32CZE81B',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: '2af70a7a-2ac5-44d4-924e-ae97a7ca9333',
|
||||
ascApiKeyId: 'AB32CZE81B',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath('/project');
|
||||
await expect(
|
||||
EasJsonUtils.getSubmitProfileAsync(accessor, Platform.IOS, 'extension5')
|
||||
).rejects.toThrow(
|
||||
'Too long chain of profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
});
|
||||
|
||||
test('ios config with with invalid appleId', async () => {
|
||||
|
||||
@@ -101,6 +101,14 @@ const AndroidBuildProfileSchema = PlatformBuildProfileSchema.concat(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('version', 'versionCode')
|
||||
),
|
||||
|
||||
keystoreName: Joi.when('credentialsSource', {
|
||||
is: 'remote',
|
||||
then: Joi.string(),
|
||||
otherwise: Joi.forbidden().messages({
|
||||
'any.unknown': 'keystoreName is not allowed when credentialsSource is not remote',
|
||||
}),
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -93,6 +93,8 @@ export interface AndroidBuildProfile extends PlatformBuildProfile {
|
||||
|
||||
// versions
|
||||
autoIncrement?: AndroidVersionAutoIncrement;
|
||||
|
||||
keystoreName?: string;
|
||||
}
|
||||
|
||||
export interface IosBuildProfile extends PlatformBuildProfile {
|
||||
|
||||
@@ -47,7 +47,7 @@ function resolveProfile<T extends Platform>({
|
||||
profileName: string;
|
||||
depth?: number;
|
||||
}): SubmitProfile<T> | undefined {
|
||||
if (depth >= 2) {
|
||||
if (depth >= 5) {
|
||||
throw new Error(
|
||||
'Too long chain of profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
{
|
||||
"extends": "@tsconfig/node18/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"outDir": "build",
|
||||
"rootDir": "src",
|
||||
"typeRoots": ["../../node_modules/@types", "../../ts-declarations", "./node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
}
|
||||
@@ -1359,10 +1359,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
|
||||
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
|
||||
|
||||
"@expo/[email protected].5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.5.tgz#a947697f10a7ab0f07f2d2fd075efdae9825e85e"
|
||||
integrity sha512-Yy4BWVoViYePwQk8rqvhosolS5MTTMgcsl9Aq3rzJrd1BH8C1V/h6N5xelqo+/V+ww2k8Bom7UTE1NlGcKHxRw==
|
||||
"@expo/[email protected].6":
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.6.tgz#293fafccffd9afa24c9992963d74052558d5c5a1"
|
||||
integrity sha512-6UkfLL/1AIRa082rlh26WG2K5Hzd67c8CIdNlHvrFmMEHlGl1PcVFZoUnNRFEROeTmrXW5/Sv4gJfmLXLTq3cQ==
|
||||
|
||||
"@expo/bunyan@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1465,20 +1465,20 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "3.35.0"
|
||||
|
||||
"@expo/[email protected]56":
|
||||
version "1.0.156"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.156.tgz#c8e1f34a480eba217e6aa37e5d178f440065a7e7"
|
||||
integrity sha512-pD3vWO7fIaQ4wmJy/x6L2dVK2rwf1qNzTE/4I5JI4r6vYSKrSCv8EIoE87xDBaRUBMk72ZdzyznJe516pMyTaw==
|
||||
"@expo/[email protected]5":
|
||||
version "1.0.165"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.165.tgz#71ec010330d23bb6f6c257ce1ca7c0258e8feaaa"
|
||||
integrity sha512-l4bPVRmc6SlrgiC7AOid6s+o3kyP47x17cGIPQ0d4FOaOxu6pP3bPZ3BuPjts1mfvrKg/c/Phe1fJ1nnNlo3Xw==
|
||||
dependencies:
|
||||
"@expo/logger" "1.0.117"
|
||||
joi "^17.13.1"
|
||||
semver "^7.6.2"
|
||||
zod "^3.23.8"
|
||||
|
||||
"@expo/env@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/env/-/env-0.4.0.tgz#1ff3a15084566d12ca92cb67e5b0a9796a9f0aa7"
|
||||
integrity sha512-g2JYFqck3xKIwJyK+8LxZ2ENZPWtRgjFWpeht9abnKgzXVXBeSNECFBkg+WQjQocSIdxXhEWM6hz4ZAe7Tc4ng==
|
||||
"@expo/env@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/env/-/env-1.0.0.tgz#8e22894fae04ba6dc41fe0f1351a4973d410273a"
|
||||
integrity sha512-43R5rxlaLJQOWunyfnQEpgHJCsSLCUWDoU/wJNABEYkHdt1Sjrlf+ieH3MG1f/RZYE+1Wss3sYiO9oPps1aMNg==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
debug "^4.3.4"
|
||||
@@ -1543,10 +1543,10 @@
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
exec-async "^2.2.0"
|
||||
|
||||
"@expo/package-manager@1.6.1":
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.6.1.tgz#ab845238dec10bb48bca2b90e060dfe8c1525602"
|
||||
integrity sha512-4rT46wP/94Ll+CWXtFKok1Lbo9XncSUtErFOo/9/3FVughGbIfdG4SKZOAWIpr9wxwEfkyhHfAP9q71ONlWODw==
|
||||
"@expo/package-manager@1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.7.0.tgz#6d98090fdfa9d31ab885acb012d6615741594fd1"
|
||||
integrity sha512-yWn5TIjd42wLHZjNtdZkvCkcxqUGxlI4YHb+bQmgm3tWZ8aBHnLhPb0rgU8+hVHCofmRvVUXfVZv8Uh+kkLXgw==
|
||||
dependencies:
|
||||
"@expo/json-file" "^9.0.0"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
@@ -1645,12 +1645,12 @@
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/[email protected]56":
|
||||
version "1.0.156"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.156.tgz#79a043a7dd4f5c098c954ed7a013c407631f515a"
|
||||
integrity sha512-fa2rMaWqF0DaqqyDXy2Dy5pBbXTPfnMD1ALQid3ApjokMV5Gq+CTyr7sYFGqr1vywK0sftpmKVAOMJzHV95dDg==
|
||||
"@expo/[email protected]66":
|
||||
version "1.0.166"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.166.tgz#b606ed72def361860ef5bfd3bd2df601112612af"
|
||||
integrity sha512-dzEFjbQr4ji65XeSrUgesE+lnnhJYMNdXMZV5PRb19mMGTYYbyeT20+RvSo1a+TOPdO6sPdSBZqfiQ7P4mGBmQ==
|
||||
dependencies:
|
||||
"@expo/eas-build-job" "1.0.156"
|
||||
"@expo/eas-build-job" "1.0.165"
|
||||
"@expo/logger" "1.0.117"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
arg "^5.0.2"
|
||||
@@ -3577,6 +3577,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
||||
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
||||
|
||||
"@tsconfig/[email protected]":
|
||||
version "18.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-18.2.4.tgz#094efbdd70f697d37c09f34067bf41bc4a828ae3"
|
||||
integrity sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==
|
||||
|
||||
"@tufjs/[email protected]":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31"
|
||||
@@ -13228,16 +13233,7 @@ string-length@^5.0.1:
|
||||
char-regex "^2.0.0"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -13393,7 +13389,7 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -13414,13 +13410,6 @@ strip-ansi@^6.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
||||
@@ -14627,16 +14616,7 @@ wordwrap@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
[email protected], wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", [email protected], wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
||||
Reference in new issue
Block a user