mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4b8ac9d3e | ||
|
|
88aa113961 | ||
|
|
e9461efe08 |
No files matched your search
@@ -1,2 +1 @@
|
||||
* text eol=lf
|
||||
**/yarn.lock linguist-generated
|
||||
@@ -0,0 +1,20 @@
|
||||
name: Installer tests
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Run the installer
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: bash bin/install.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: eas --version
|
||||
@@ -32,6 +32,7 @@ jobs:
|
||||
# Remove this whenever we solved those OOM errors.
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
- run: yarn lint --max-warnings=0
|
||||
- run: shellcheck bin/*.sh
|
||||
- uses: codecov/codecov-action@v1
|
||||
if: ${{ matrix.coverage }}
|
||||
with:
|
||||
|
||||
+1
-2
@@ -4,6 +4,5 @@
|
||||
"singleQuote": true,
|
||||
"bracketSameLine": true,
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "auto"
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
+3
-91
@@ -8,103 +8,15 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add 2022 Apple capabilities. ([#1307](https://github.com/expo/eas-cli/pull/1307) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Print warning when eas-cli is installed as project dependency. ([#1310](https://github.com/expo/eas-cli/pull/1310) by [@dsokal](https://github.com/dsokal))
|
||||
- Add eas.json to skip push notifications credentials setup. ([#1315](https://github.com/expo/eas-cli/pull/1315) by [@dsokal](https://github.com/dsokal))
|
||||
- Warn about EAS outages when running `build`, `submit`, and `update` commands. ([#1312](https://github.com/expo/eas-cli/pull/1312) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Replace promptToCreateProjectIfNotExistsAsync with getProjectIdAsync. ([#1303](https://github.com/expo/eas-cli/pull/1303) by [@wschurman](https://github.com/wschurman))
|
||||
- Use AppQuery instead of ProjectQuery. ([#1304](https://github.com/expo/eas-cli/pull/1304) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Improve instructions on setting `extra.eas.projectId` in app configuration. ([#1316](https://github.com/expo/eas-cli/pull/1316) by [@dsokal](https://github.com/dsokal))
|
||||
- Improve copy in EAS Submit - "e-mail" -> email, and make the link to app on App Store go directly to the TestFlight tab. ([#1318](https://github.com/expo/eas-cli/pull/1318) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
## [1.1.1](https://github.com/expo/eas-cli/releases/tag/v1.1.1) - 2022-08-23
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for `autoIncrement` option at the root of the build profile. ([#1298](https://github.com/expo/eas-cli/pull/1298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Handle trailing backslash in `.gitignore`. ([#1306](https://github.com/expo/eas-cli/pull/1306) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Retry ASC Api Key downloads if it has not fully propagated on Apple's infrastructure. ([#1302](https://github.com/expo/eas-cli/pull/1302) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [1.1.0](https://github.com/expo/eas-cli/releases/tag/v1.1.0) - 2022-08-23
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for passing platform flag to `credentials` command. ([#1277](https://github.com/expo/eas-cli/pull/1277) by [@Simek](https://github.com/Simek))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Prevent throwing dynamic app config write error when configuring project ID. ([#1301](https://github.com/expo/eas-cli/pull/1301) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove unused dependency and devDependency from the CLI. ([#1297](https://github.com/expo/eas-cli/pull/1297) by [@Simek](https://github.com/Simek))
|
||||
|
||||
## [1.0.0](https://github.com/expo/eas-cli/releases/tag/v1.0.0) - 2022-08-22
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Remove timeout when waiting for build / submission. ([#1289](https://github.com/expo/eas-cli/pull/1289) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add metadata support for dynamic store.config.js files. ([#1270](https://github.com/expo/eas-cli/pull/1270) by [@byCedric](https://github.com/byCedric))
|
||||
- Improve reliability of update asset presigned upload requests. ([#1278](https://github.com/expo/eas-cli/pull/1278) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Rebind `console.info` correctly after `ora` instance stops. ([#1113](https://github.com/expo/eas-cli/pull/1113) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Fix initializing git repository in monorepo. ([#1279](https://github.com/expo/eas-cli/pull/1279) by [@dsokal](https://github.com/dsokal))
|
||||
- Limit the number of SignedAssetUploadSpecifications fetched at a time. ([#1287](https://github.com/expo/eas-cli/pull/1287) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove unused install script. ([#1280](https://github.com/expo/eas-cli/pull/1280), [#1281](https://github.com/expo/eas-cli/pull/1281) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Remove "please" from output ([#1250](https://github.com/expo/eas-cli/pull/1250) by [@jonsamp](https://github.com/jonsamp))
|
||||
|
||||
## [0.60.0](https://github.com/expo/eas-cli/releases/tag/v0.60.0) - 2022-08-12
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `appVersion` runtime policy. ([#1267](https://github.com/expo/eas-cli/pull/1267) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add JSON Schema `metadataPath` to iOS submission profile. ([#1269](https://github.com/expo/eas-cli/pull/1269) by [@byCedric](https://github.com/byCedric))
|
||||
- Add warning when `appVersion` runtime policy is not supported. ([#1271](https://github.com/expo/eas-cli/pull/1271) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
## [0.59.0](https://github.com/expo/eas-cli/releases/tag/v0.59.0) - 2022-08-10
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix building Android projects locally that don't have execute permissions set for `gradlew`. ([82231](https://github.com/expo/eas-cli/commit/822313e90e94bd6ddd3872061a0c25a8aa4db7cd) by [@dsokal](https://github.com/dsokal))
|
||||
- Disable `nativeVersion` policy only for remote version source. ([#1261](https://github.com/expo/eas-cli/pull/1261) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Improve wording on version auto-increment. ([#1260](https://github.com/expo/eas-cli/pull/1260) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Upgrade dependencies. ([#1262](https://github.com/expo/eas-cli/pull/1262) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [0.58.0](https://github.com/expo/eas-cli/releases/tag/v0.58.0) - 2022-08-09
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Managed app versions support. ([#1209](https://github.com/expo/eas-cli/pull/1209), [#1219](https://github.com/expo/eas-cli/pull/1219), [#1232](https://github.com/expo/eas-cli/pull/1232) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Add a warning when publishing an update with too many assets. ([#1243](https://github.com/expo/eas-cli/pull/1243) by [@kgc00](https://github.com/kgc00))
|
||||
- Add submission info when building with `--json` option. ([#1246](https://github.com/expo/eas-cli/pull/1246) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Set all environment variables (documented in https://docs.expo.dev/build-reference/variables/) when running local build. ([#1256](https://github.com/expo/eas-cli/pull/1256) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Do not include sources in `eas-json` npm package. ([#1248](https://github.com/expo/eas-cli/pull/1248) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Add ts-node dev dependency to fix bin/run script. ([#1252](https://github.com/expo/eas-cli/pull/1252) by [@brentvatne](https://github.com/brentvatne))
|
||||
- Swallow error when unable to track file in no commit workflow. ([#1109](https://github.com/expo/eas-cli/pull/1109) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
## [0.57.0](https://github.com/expo/eas-cli/releases/tag/v0.57.0) - 2022-08-03
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
packages/eas-cli/src/build @dsokal @wkozyra95
|
||||
packages/eas-cli/src/commands/build @dsokal @wkozyra95
|
||||
|
||||
packages/eas-cli/src/submit @dsokal
|
||||
packages/eas-cli/src/commands/submit.ts @dsokal
|
||||
packages/eas-cli/src/submit @barthap @dsokal
|
||||
packages/eas-cli/src/commands/submit.ts @barthap @dsokal
|
||||
|
||||
packages/eas-cli/src/update @kgc00 @wschurman
|
||||
packages/eas-cli/src/commands/update @kgc00 @wschurman
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script downloads the latest release version of EAS CLI from GitHub and
|
||||
# installs it in /usr/local/bin
|
||||
|
||||
{
|
||||
# All code is inside a block to ensure the script executes only when
|
||||
# downloaded completely.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
prefix="${EAS_PREFIX:-/usr/local}"
|
||||
temp_dir="$(mktemp -d)"
|
||||
|
||||
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]
|
||||
then
|
||||
# Use colors when stdout is a terminal and NO_COLOR isn't set.
|
||||
text_bold=$'\033[1m'
|
||||
text_normal=$'\033[22m'
|
||||
text_red=$'\033[31m'
|
||||
text_default_color=$'\033[39m'
|
||||
else
|
||||
# Disable colors
|
||||
text_bold=""
|
||||
text_normal=""
|
||||
text_red=""
|
||||
text_default_color=""
|
||||
fi
|
||||
|
||||
abort() {
|
||||
echo -e "$text_bold$text_red$1$text_normal$text_default_color"
|
||||
exit 1
|
||||
}
|
||||
|
||||
install() {
|
||||
needs_sudo=""
|
||||
# Make sure path exists and can be written to by the current user.
|
||||
if ! mkdir -p "$prefix/lib" || [[ ! -w "$prefix/lib" ]]
|
||||
then
|
||||
needs_sudo=true
|
||||
fi
|
||||
if ! mkdir -p "$prefix/bin" || [[ ! -w "$prefix/bin" ]]
|
||||
then
|
||||
needs_sudo=true
|
||||
fi
|
||||
if ! rm -rf "$prefix/lib/eas"
|
||||
then
|
||||
needs_sudo=true
|
||||
fi
|
||||
# Run commands with sudo if necessary.
|
||||
if [ -n "$needs_sudo" ]
|
||||
then
|
||||
if [ "$EUID" -ne 0 ]
|
||||
then
|
||||
echo "Installing requires superuser access."
|
||||
echo "The sudo command will prompt for your password."
|
||||
fi
|
||||
sudo rm -rf "$prefix/lib/eas"
|
||||
sudo mv "$temp_dir/eas" "$prefix/lib/eas"
|
||||
sudo ln -fs "$prefix/lib/eas/bin/eas" "$prefix/bin/eas"
|
||||
else
|
||||
mv "$temp_dir/eas" "$prefix/lib/eas"
|
||||
ln -fs "$prefix/lib/eas/bin/eas" "$prefix/bin/eas"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! ":$PATH:" == *":$prefix/bin:"* ]]; then
|
||||
abort "Your path is missing $prefix/bin, you need to add this to \$PATH use this installer."
|
||||
fi
|
||||
|
||||
hardware="$(uname -m)"
|
||||
case "$hardware" in
|
||||
x86_64)
|
||||
arch="x64"
|
||||
;;
|
||||
arm*)
|
||||
arch="arm"
|
||||
;;
|
||||
*)
|
||||
abort "Unsupported CPU architecture $hardware."
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin*)
|
||||
platform="darwin"
|
||||
if [[ "$arch" != "x64" ]]; then
|
||||
abort "Unsupported CPU architecture $hardware on macOS."
|
||||
fi
|
||||
;;
|
||||
Linux*)
|
||||
platform="linux"
|
||||
;;
|
||||
*)
|
||||
abort "This installer is supported only on macOS, Linux, and Windows Subsystem for Linux."
|
||||
;;
|
||||
esac
|
||||
url=$(hub release -L 1 | xargs hub release show --show-downloads | grep "${platform}-${arch}.tar.gz")
|
||||
echo "Installing EAS CLI from $url"
|
||||
|
||||
mkdir -p "$temp_dir"
|
||||
cd "$temp_dir"
|
||||
|
||||
echo
|
||||
echo "Downloading..."
|
||||
if [ "$(command -v curl)" ]
|
||||
then
|
||||
curl --location --progress-bar "$url" | tar xz
|
||||
else
|
||||
wget -O- "$url" | tar xz
|
||||
fi
|
||||
|
||||
# Move the installation over and link the executable (may require sudo)
|
||||
install
|
||||
|
||||
# Test the bundled node binary and if it doesn't work remove it (fall back to
|
||||
# node installed in path).
|
||||
"$prefix/lib/eas/bin/node" -v > /dev/null || {
|
||||
rm "$prefix/lib/eas/bin/node"
|
||||
}
|
||||
|
||||
# Test the CLI
|
||||
executable=$(command -v eas) || abort "Could not run eas after installation."
|
||||
echo
|
||||
echo -e "EAS CLI is installed to ${executable}"
|
||||
eas --version
|
||||
|
||||
echo
|
||||
echo -e "Type ${text_bold}eas --help${text_normal} to get started."
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "1.2.0"
|
||||
"version": "0.57.0"
|
||||
}
|
||||
+11
-10
@@ -17,18 +17,19 @@
|
||||
"clean": "lerna run clean && rm -rf node_modules coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "28.1.6",
|
||||
"@types/node": "18.6.5",
|
||||
"eslint": "8.21.0",
|
||||
"eslint-config-universe": "11.1.0",
|
||||
"eslint-plugin-async-protect": "2.1.0",
|
||||
"@types/jest": "27.5.0",
|
||||
"@types/node": "17.0.32",
|
||||
"eslint": "8.15.0",
|
||||
"eslint-config-universe": "11.0.0",
|
||||
"eslint-plugin-async-protect": "1.1.0",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"jest": "28.1.3",
|
||||
"jest-watch-typeahead": "2.0.0",
|
||||
"jest": "28.1.0",
|
||||
"jest-watch-typeahead": "1.1.0",
|
||||
"lerna": "4.0.0",
|
||||
"oclif": "3.1.2",
|
||||
"prettier": "2.7.1",
|
||||
"ts-jest": "28.0.7",
|
||||
"oclif": "3.0.1",
|
||||
"prettier": "2.6.2",
|
||||
"ts-jest": "28.0.2",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"volta": {
|
||||
|
||||
+45
-110
@@ -19,28 +19,6 @@ npm install -g eas-cli
|
||||
yarn global add eas-cli
|
||||
```
|
||||
|
||||
## Enforcing eas-cli version for your project
|
||||
|
||||
If you want to enforce the `eas-cli` version for your project, use the `"cli.version"` field in **eas.json**. Installing `eas-cli` to your project dependencies is strongly discouraged because it may cause dependency conflicts that are difficult to debug.
|
||||
|
||||
An example of **eas.json** that enforces `eas-cli` in version `1.0.0` or newer:
|
||||
|
||||
```json
|
||||
{
|
||||
"cli": {
|
||||
"version": ">=1.0.0"
|
||||
},
|
||||
"build": {
|
||||
// build profiles
|
||||
}
|
||||
"submit": {
|
||||
// submit profiles
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Learn more: https://docs.expo.dev/build-reference/eas-json/
|
||||
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
@@ -71,8 +49,6 @@ eas --help COMMAND
|
||||
* [`eas build:inspect`](#eas-buildinspect)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:submit`](#eas-buildsubmit)
|
||||
* [`eas build:version:set`](#eas-buildversionset)
|
||||
* [`eas build:version:sync`](#eas-buildversionsync)
|
||||
* [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
||||
* [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
||||
* [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
||||
@@ -124,7 +100,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -141,7 +117,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -158,7 +134,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -172,7 +148,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -221,7 +197,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -241,7 +217,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -262,7 +238,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -281,7 +257,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -305,7 +281,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -335,7 +311,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -349,7 +325,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -366,7 +342,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -401,7 +377,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -434,7 +410,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -464,44 +440,6 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
Update version of an app.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:version:set [-p android|ios] [--profile <value>]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=(android|ios)
|
||||
--profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
|
||||
eas.json.
|
||||
|
||||
DESCRIPTION
|
||||
Update version of an app.
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
Update a version in native code with a value stored on EAS servers
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:version:sync [-p android|ios|all] [--profile <value>]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=(android|ios|all)
|
||||
--profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
|
||||
eas.json.
|
||||
|
||||
DESCRIPTION
|
||||
Update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
view a build for your project
|
||||
@@ -517,7 +455,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -537,7 +475,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -558,7 +496,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -575,7 +513,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -595,7 +533,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -613,7 +551,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -621,16 +559,13 @@ manage credentials
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas credentials [-p android|ios]
|
||||
|
||||
FLAGS
|
||||
-p, --platform=(android|ios)
|
||||
$ eas credentials
|
||||
|
||||
DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -644,7 +579,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -662,7 +597,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -679,7 +614,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -693,7 +628,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -707,7 +642,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -789,7 +724,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -806,7 +741,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -820,7 +755,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -837,7 +772,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -857,7 +792,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -874,7 +809,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -888,7 +823,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -918,7 +853,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -948,7 +883,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -965,7 +900,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -985,7 +920,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1004,7 +939,7 @@ DESCRIPTION
|
||||
view the recent updates for a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1024,7 +959,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1044,7 +979,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1061,7 +996,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1078,7 +1013,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1099,7 +1034,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1116,7 +1051,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v1.2.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
Regular → Executable
+6
-8
@@ -1,15 +1,13 @@
|
||||
const fs = require('fs/promises');
|
||||
#!/usr/bin/env sh
|
||||
|
||||
const path = './src/graphql/generated.ts';
|
||||
OUTFILE="src/graphql/generated.ts"
|
||||
|
||||
(async () => {
|
||||
const generatedCode = await fs.readFile(path, 'utf8');
|
||||
const comment = `/**
|
||||
content="/**
|
||||
* This file was generated using GraphQL Codegen
|
||||
* Command: yarn generate-graphql-code
|
||||
* Run this during development for automatic type generation when editing GraphQL documents
|
||||
* For more info and docs, visit https://graphql-code-generator.com/
|
||||
*/\n\n`;
|
||||
*/
|
||||
"
|
||||
|
||||
await fs.writeFile(path, comment + generatedCode);
|
||||
})();
|
||||
printf '%s\n%s\n' "$content" "$(cat $OUTFILE)" > $OUTFILE
|
||||
@@ -14,7 +14,7 @@ generates:
|
||||
dedupeOperationSuffix: true
|
||||
hooks:
|
||||
afterOneFileWrite:
|
||||
- 'node ./scripts/annotate-graphql-codegen.js'
|
||||
- ./annotate-graphql-codegen.sh
|
||||
./graphql.schema.json:
|
||||
plugins:
|
||||
- 'introspection'
|
||||
Generated
+1689
-3041
File diff suppressed because it is too large.
Load diff
@@ -1,39 +1,38 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "1.2.0",
|
||||
"version": "0.57.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "0.0.0-alpha.34",
|
||||
"@expo/apple-utils": "0.0.0-alpha.33",
|
||||
"@expo/code-signing-certificates": "0.0.2",
|
||||
"@expo/config": "7.0.1",
|
||||
"@expo/config-plugins": "5.0.1",
|
||||
"@expo/config-types": "46.0.2",
|
||||
"@expo/config": "6.0.23",
|
||||
"@expo/config-plugins": "4.1.4",
|
||||
"@expo/config-types": "45.0.0",
|
||||
"@expo/eas-build-job": "0.2.85",
|
||||
"@expo/eas-json": "1.2.0",
|
||||
"@expo/eas-json": "0.57.0",
|
||||
"@expo/json-file": "8.2.36",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/package-manager": "0.0.56",
|
||||
"@expo/pkcs12": "0.0.8",
|
||||
"@expo/plist": "0.0.18",
|
||||
"@expo/plugin-autocomplete": "1.4.0",
|
||||
"@expo/plugin-help": "5.2.0",
|
||||
"@expo/plugin-warn-if-update-available": "2.3.0",
|
||||
"@expo/prebuild-config": "5.0.3",
|
||||
"@expo/prebuild-config": "3.1.6",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.0",
|
||||
"@expo/spawn-async": "1.6.0",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "1.13.10",
|
||||
"@urql/core": "2.6.1",
|
||||
"@oclif/core": "1.9.0",
|
||||
"@urql/core": "2.4.4",
|
||||
"@urql/exchange-retry": "0.3.3",
|
||||
"ajv": "^6.12.6",
|
||||
"chalk": "4.1.2",
|
||||
"cli-progress": "3.11.2",
|
||||
"cli-progress": "3.11.0",
|
||||
"cli-table3": "0.6.2",
|
||||
"dateformat": "4.6.3",
|
||||
"env-paths": "2.2.0",
|
||||
@@ -55,7 +54,7 @@
|
||||
"keychain": "1.3.0",
|
||||
"log-symbols": "4.1.0",
|
||||
"mime": "3.0.0",
|
||||
"minimatch": "5.1.0",
|
||||
"minimatch": "5.0.1",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "2.6.7",
|
||||
"node-forge": "1.3.1",
|
||||
@@ -63,12 +62,12 @@
|
||||
"ora": "5.1.0",
|
||||
"pkg-dir": "4.2.0",
|
||||
"promise-limit": "2.7.0",
|
||||
"promise-retry": "2.0.1",
|
||||
"prompts": "2.4.2",
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.3.7",
|
||||
"slash": "3.0.0",
|
||||
"strip-ansi": "6.0.0",
|
||||
"tar": "6.1.11",
|
||||
"terminal-link": "2.1.1",
|
||||
"tslib": "2.4.0",
|
||||
@@ -78,34 +77,33 @@
|
||||
"wrap-ansi": "7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "2.11.5",
|
||||
"@graphql-codegen/introspection": "2.2.1",
|
||||
"@graphql-codegen/typescript": "2.7.3",
|
||||
"@graphql-codegen/typescript-operations": "2.5.3",
|
||||
"@types/cli-progress": "3.11.0",
|
||||
"@graphql-codegen/cli": "2.6.2",
|
||||
"@graphql-codegen/introspection": "2.1.1",
|
||||
"@graphql-codegen/typescript": "2.4.11",
|
||||
"@graphql-codegen/typescript-operations": "2.4.0",
|
||||
"@types/cli-progress": "3.9.2",
|
||||
"@types/dateformat": "3.0.1",
|
||||
"@types/envinfo": "7.8.1",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/getenv": "^1.0.0",
|
||||
"@types/mime": "2.0.3",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"@types/node-forge": "1.0.4",
|
||||
"@types/promise-retry": "1.1.3",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/node-forge": "1.0.2",
|
||||
"@types/prompts": "2.0.14",
|
||||
"@types/semver": "7.3.10",
|
||||
"@types/tar": "6.1.2",
|
||||
"@types/semver": "7.3.9",
|
||||
"@types/tar": "6.1.1",
|
||||
"@types/tough-cookie": "4.0.2",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"axios": "0.27.2",
|
||||
"eslint-plugin-graphql": "4.0.0",
|
||||
"memfs": "3.4.7",
|
||||
"form-data": "4.0.0",
|
||||
"memfs": "3.4.3",
|
||||
"mockdate": "3.0.5",
|
||||
"nock": "13.2.9",
|
||||
"rimraf": "3.0.2",
|
||||
"ts-deepmerge": "2.0.5",
|
||||
"nock": "13.2.4",
|
||||
"tempy": "0.7.0",
|
||||
"ts-deepmerge": "2.0.1",
|
||||
"ts-mockito": "2.6.1",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -173,7 +171,7 @@
|
||||
},
|
||||
"update": {
|
||||
"node": {
|
||||
"version": "18.6.0"
|
||||
"version": "12.13.0"
|
||||
},
|
||||
"s3": {
|
||||
"templates": {
|
||||
@@ -193,8 +191,8 @@
|
||||
},
|
||||
"repository": "expo/eas-cli",
|
||||
"scripts": {
|
||||
"postpack": "rimraf oclif.manifest.json",
|
||||
"prepack": "rimraf build && yarn build && node ./scripts/prepack.js",
|
||||
"postpack": "rm -f oclif.manifest.json",
|
||||
"prepack": "./scripts/prepack.sh",
|
||||
"pretarball-ci": "./scripts/pretarball-ci.sh",
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"watch": "yarn build --watch --preserveWatchOutput",
|
||||
@@ -202,7 +200,7 @@
|
||||
"test": "jest",
|
||||
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
|
||||
"generate-graphql-code": "graphql-codegen --config graphql-codegen.yml",
|
||||
"clean": "rimraf dist build tmp node_modules yarn-error.log"
|
||||
"clean": "rm -rf dist build tmp node_modules yarn-error.log"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.6.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const fs = require('fs/promises');
|
||||
const { promises: fs } = require('fs');
|
||||
|
||||
(async () => {
|
||||
// Patch `oclif readme` path and link generation
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const spawn = require('@expo/spawn-async');
|
||||
|
||||
(async () => {
|
||||
if (!process.env.CLI_SIZE_CHECK) {
|
||||
await spawn('yarn', ['oclif', 'manifest'], { stdio: 'inherit' });
|
||||
}
|
||||
})();
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
rm -rf build
|
||||
yarn build
|
||||
|
||||
if [ -z "$CLI_SIZE_CHECK" ]; then
|
||||
yarn oclif manifest
|
||||
fi
|
||||
@@ -15,9 +15,6 @@ export enum SubmissionEvent {
|
||||
SUBMIT_REQUEST_ATTEMPT = 'submit cli request attempt',
|
||||
SUBMIT_REQUEST_SUCCESS = 'submit cli request success',
|
||||
SUBMIT_REQUEST_FAIL = 'submit cli request fail',
|
||||
API_KEY_DOWNLOAD_FAIL = 'submit cli credentials api key download fail',
|
||||
API_KEY_DOWNLOAD_RETRY = 'submit cli credentials api key download fail temporary',
|
||||
API_KEY_DOWNLOAD_SUCCESS = 'submit cli credentials api key download succeed',
|
||||
}
|
||||
|
||||
export enum BuildEvent {
|
||||
|
||||
@@ -47,7 +47,7 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
Log.newLine();
|
||||
const confirmed = await toggleConfirmAsync({ message: 'Would you like to proceed?' });
|
||||
if (!confirmed) {
|
||||
Log.error('Update eas.json and come back again.');
|
||||
Log.error('Please update eas.json and come back again.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +213,7 @@ export async function resolveRemoteVersionCodeAsync({
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
const spinner = ora(
|
||||
`Initializing versionCode with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
const spinner = ora(`Initializing the versionCode with ${currentBuildVersion}.`).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
@@ -225,18 +223,16 @@ export async function resolveRemoteVersionCodeAsync({
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
|
||||
spinner.succeed(`Initialized the versionCode with ${currentBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(`Failed to initialize versionCode with ${chalk.bold(currentBuildVersion)}.`);
|
||||
spinner.fail(`Failed to initialize the versionCode with ${currentBuildVersion}.`);
|
||||
throw err;
|
||||
}
|
||||
return currentBuildVersion;
|
||||
} else {
|
||||
const nextBuildVersion = getNextVersionCode(currentBuildVersion);
|
||||
const spinner = ora(
|
||||
`Incrementing versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
`Incrementing the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
@@ -247,16 +243,10 @@ export async function resolveRemoteVersionCodeAsync({
|
||||
buildVersion: String(nextBuildVersion),
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(
|
||||
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
);
|
||||
spinner.succeed(`Incremented the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(
|
||||
`Failed to increment versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
`Failed to increment the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
|
||||
return async () => {
|
||||
if (ctx.localBuildOptions.enable) {
|
||||
await runLocalBuildAsync(job, metadata, ctx.localBuildOptions);
|
||||
await runLocalBuildAsync(job, ctx.localBuildOptions);
|
||||
return undefined;
|
||||
} else {
|
||||
try {
|
||||
@@ -134,18 +134,18 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
|
||||
function handleBuildRequestError(error: any, platform: Platform): never {
|
||||
if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'TURTLE_DEPRECATED_JOB_FORMAT') {
|
||||
Log.error('EAS Build API has changed. Upgrade to the latest eas-cli version.');
|
||||
Log.error('EAS Build API has changed, please upgrade to the latest eas-cli version.');
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE'
|
||||
) {
|
||||
Log.error(
|
||||
'EAS Build is down for maintenance. Try again later. Check https://status.expo.dev/ for updates.'
|
||||
'EAS Build is down for maintenance, please try again later. Check https://status.expo.dev/ for updates.'
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED') {
|
||||
Log.error(
|
||||
'EAS Build free tier is temporarily disabled. Try again later. Check https://status.expo.dev/ for updates.'
|
||||
'EAS Build free tier is temporarily disabled, please try again later. Check https://status.expo.dev/ for updates.'
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
@@ -157,7 +157,7 @@ function handleBuildRequestError(error: any, platform: Platform): never {
|
||||
throw new Error('Build request failed.');
|
||||
} else if (error?.graphQLErrors) {
|
||||
Log.error(
|
||||
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.'
|
||||
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, please report the issue.'
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
@@ -243,7 +243,11 @@ type MaybeBuildFragment = BuildFragment | null;
|
||||
|
||||
export async function waitForBuildEndAsync(
|
||||
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
|
||||
{ intervalSec = 10 } = {}
|
||||
{
|
||||
// 2 hours (max build time limit) + 10 minutes (possible queue time)
|
||||
timeoutSec = 2 * 60 * 60 + 10 * 60,
|
||||
intervalSec = 10,
|
||||
} = {}
|
||||
): Promise<MaybeBuildFragment[]> {
|
||||
let spinner;
|
||||
let originalSpinnerText;
|
||||
@@ -255,7 +259,8 @@ export async function waitForBuildEndAsync(
|
||||
originalSpinnerText = 'Waiting for builds to complete. You can press Ctrl+C to exit.';
|
||||
spinner = ora('Waiting for builds to complete. You can press Ctrl+C to exit.').start();
|
||||
}
|
||||
while (true) {
|
||||
const endTime = new Date().getTime() + timeoutSec * 1000;
|
||||
while (new Date().getTime() <= endTime) {
|
||||
const builds = await getBuildsSafelyAsync(buildIds);
|
||||
const { refetch } =
|
||||
builds.length === 1
|
||||
@@ -266,6 +271,10 @@ export async function waitForBuildEndAsync(
|
||||
}
|
||||
await sleepAsync(intervalSec * 1000);
|
||||
}
|
||||
spinner.fail('Timed out');
|
||||
throw new Error(
|
||||
'Timeout reached! It is taking longer than expected to finish the build, aborting...'
|
||||
);
|
||||
}
|
||||
|
||||
async function getBuildsSafelyAsync(buildIds: string[]): Promise<MaybeBuildFragment[]> {
|
||||
|
||||
@@ -60,7 +60,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
projectDir,
|
||||
user,
|
||||
env: buildProfile.env,
|
||||
easJsonCliConfig,
|
||||
});
|
||||
|
||||
const devClientProperties = getDevClientEventProperties({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Ios } from '@expo/eas-build-job';
|
||||
import { Env, Ios } from '@expo/eas-build-job';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
|
||||
@@ -27,6 +27,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
|
||||
function transformIosSecrets(secrets: {
|
||||
buildCredentials?: Ios.BuildCredentials;
|
||||
env?: Env;
|
||||
}): IosJobSecretsInput {
|
||||
const buildCredentials: IosJobSecretsInput['buildCredentials'] = [];
|
||||
|
||||
@@ -42,5 +43,6 @@ function transformIosSecrets(secrets: {
|
||||
|
||||
return {
|
||||
buildCredentials,
|
||||
environmentSecrets: secrets.env,
|
||||
};
|
||||
}
|
||||
@@ -303,9 +303,7 @@ export async function resolveRemoteBuildNumberAsync({
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
const spinner = ora(
|
||||
`Initializing buildNumber with ${chalk.bold(currentBuildVersion)}.`
|
||||
).start();
|
||||
const spinner = ora(`Initializing the buildNumber with ${currentBuildVersion}.`).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
@@ -315,18 +313,16 @@ export async function resolveRemoteBuildNumberAsync({
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
|
||||
spinner.succeed(`Initialized the buildNumber with ${currentBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(`Failed to initialize buildNumber with ${chalk.bold(currentBuildVersion)}.`);
|
||||
spinner.fail(`Failed to initialize the buildNumber with ${currentBuildVersion}.`);
|
||||
throw err;
|
||||
}
|
||||
return currentBuildVersion;
|
||||
} else {
|
||||
const nextBuildVersion = getNextBuildNumber(currentBuildVersion);
|
||||
const spinner = ora(
|
||||
`Incrementing buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
`Incrementing buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
@@ -337,16 +333,10 @@ export async function resolveRemoteBuildNumberAsync({
|
||||
buildVersion: nextBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(
|
||||
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
);
|
||||
spinner.succeed(`Incremented buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(
|
||||
`Failed to increment buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
nextBuildVersion
|
||||
)}.`
|
||||
`Failed to increment buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Job, Metadata } from '@expo/eas-build-job';
|
||||
import { Job } from '@expo/eas-build-job';
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import semver from 'semver';
|
||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.99';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.85';
|
||||
|
||||
export interface LocalBuildOptions {
|
||||
enable: boolean;
|
||||
@@ -18,12 +18,8 @@ export interface LocalBuildOptions {
|
||||
verbose?: boolean;
|
||||
}
|
||||
|
||||
export async function runLocalBuildAsync(
|
||||
job: Job,
|
||||
metadata: Metadata,
|
||||
options: LocalBuildOptions
|
||||
): Promise<void> {
|
||||
const { command, args } = await getCommandAndArgsAsync(job, metadata);
|
||||
export async function runLocalBuildAsync(job: Job, options: LocalBuildOptions): Promise<void> {
|
||||
const { command, args } = await getCommandAndArgsAsync(job);
|
||||
let spinner;
|
||||
if (!options.verbose) {
|
||||
spinner = ora().start(options.skipNativeBuild ? 'Preparing project' : 'Building project');
|
||||
@@ -57,18 +53,15 @@ export async function runLocalBuildAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function getCommandAndArgsAsync(
|
||||
job: Job,
|
||||
metadata: Metadata
|
||||
): Promise<{ command: string; args: string[] }> {
|
||||
const jobAndMetadataBase64 = Buffer.from(JSON.stringify({ job, metadata })).toString('base64');
|
||||
async function getCommandAndArgsAsync(job: Job): Promise<{ command: string; args: string[] }> {
|
||||
const jobBase64 = Buffer.from(JSON.stringify({ job })).toString('base64');
|
||||
if (process.env.EAS_LOCAL_BUILD_PLUGIN_PATH) {
|
||||
return {
|
||||
command: process.env.EAS_LOCAL_BUILD_PLUGIN_PATH,
|
||||
args: [jobAndMetadataBase64],
|
||||
args: [jobBase64],
|
||||
};
|
||||
} else {
|
||||
const args = [`${PLUGIN_PACKAGE_NAME}@${PLUGIN_PACKAGE_VERSION}`, jobAndMetadataBase64];
|
||||
const args = [`${PLUGIN_PACKAGE_NAME}@${PLUGIN_PACKAGE_VERSION}`, jobBase64];
|
||||
if (await isAtLeastNpm7Async()) {
|
||||
// npx shipped with npm >= 7.0.0 requires the "-y" flag to run commands without
|
||||
// prompting the user to install a package that is used for the first time
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
AppVersionSource,
|
||||
BuildProfile,
|
||||
EasJson,
|
||||
EasJsonReader,
|
||||
SubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
import { BuildProfile, EasJson, EasJsonReader, SubmitProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -14,10 +8,8 @@ import {
|
||||
BuildFragment,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
BuildWithSubmissionsFragment,
|
||||
SubmissionFragment,
|
||||
} from '../graphql/generated';
|
||||
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform';
|
||||
import Log from '../log';
|
||||
import {
|
||||
@@ -28,14 +20,12 @@ import {
|
||||
} from '../platform';
|
||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
|
||||
import {
|
||||
validateAppConfigForRemoteVersionSource,
|
||||
validateBuildProfileVersionSettings,
|
||||
} from '../project/remoteVersionSource';
|
||||
import { createSubmissionContextAsync } from '../submit/context';
|
||||
import {
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish,
|
||||
submitAsync,
|
||||
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
|
||||
} from '../submit/submit';
|
||||
@@ -116,7 +106,7 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
}
|
||||
|
||||
const startedBuilds: {
|
||||
build: BuildWithSubmissionsFragment | BuildFragment;
|
||||
build: BuildFragment;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
}[] = [];
|
||||
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
|
||||
@@ -170,7 +160,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
submitProfile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(startedBuild.build.id);
|
||||
submissions.push(submission);
|
||||
}
|
||||
|
||||
@@ -191,30 +180,16 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
accountName,
|
||||
});
|
||||
if (!flags.json) {
|
||||
printBuildResults(builds);
|
||||
}
|
||||
printBuildResults(builds, flags.json);
|
||||
|
||||
const haveAllBuildsFailedOrCanceled = builds.every(
|
||||
build => build?.status && [BuildStatus.Errored, BuildStatus.Canceled].includes(build?.status)
|
||||
);
|
||||
if (haveAllBuildsFailedOrCanceled || !flags.autoSubmit) {
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(builds);
|
||||
}
|
||||
exitWithNonZeroCodeIfSomeBuildsFailed(builds);
|
||||
} else {
|
||||
const completedSubmissions = await waitForSubmissionsToCompleteAsync(submissions);
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(
|
||||
await Promise.all(
|
||||
builds
|
||||
.filter((i): i is BuildWithSubmissionsFragment => !!i)
|
||||
.map(build => BuildQuery.withSubmissionsByIdAsync(build.id))
|
||||
)
|
||||
);
|
||||
}
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
||||
// the following function also exits with non zero code if any of the submissions failed
|
||||
await waitForSubmissionsToCompleteAsync(submissions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,10 +231,8 @@ async function prepareAndStartBuildAsync({
|
||||
)}`
|
||||
);
|
||||
}
|
||||
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
||||
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
||||
validateAppConfigForRemoteVersionSource(buildCtx.exp, buildProfile.platform);
|
||||
}
|
||||
|
||||
validateAppConfigForRemoteVersionSource(buildCtx.exp, buildProfile.platform);
|
||||
if (buildCtx.workflow === Workflow.MANAGED) {
|
||||
if (!sdkVersionChecked) {
|
||||
await checkExpoSdkIsSupportedAsync(buildCtx);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '../../graphql/generated';
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
|
||||
import { printJsonOnlyOutput } from '../../utils/json';
|
||||
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
|
||||
|
||||
export function printLogsUrls(builds: BuildFragment[]): void {
|
||||
@@ -31,14 +32,18 @@ export function printLogsUrls(builds: BuildFragment[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function printBuildResults(builds: (BuildFragment | null)[]): void {
|
||||
Log.newLine();
|
||||
if (builds.length === 1) {
|
||||
const [build] = builds;
|
||||
assert(build, 'Build should be defined');
|
||||
printBuildResult(build);
|
||||
export function printBuildResults(builds: (BuildFragment | null)[], json: boolean): void {
|
||||
if (json) {
|
||||
printJsonOnlyOutput(builds);
|
||||
} else {
|
||||
(builds.filter(i => i) as BuildFragment[]).forEach(build => printBuildResult(build));
|
||||
Log.newLine();
|
||||
if (builds.length === 1) {
|
||||
const [build] = builds;
|
||||
assert(build, 'Build should be defined');
|
||||
printBuildResult(build);
|
||||
} else {
|
||||
(builds.filter(i => i) as BuildFragment[]).forEach(build => printBuildResult(build));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,14 +99,14 @@ export function printDeprecationWarnings(deprecationInfo?: EasBuildDeprecationIn
|
||||
return;
|
||||
}
|
||||
if (deprecationInfo.type === EasBuildDeprecationInfoType.Internal) {
|
||||
Log.warn('This command is using API that soon will be deprecated. Upgrade EAS CLI.');
|
||||
Log.warn('This command is using API that soon will be deprecated, please update eas-cli.');
|
||||
Log.warn("Changes won't affect your project config.");
|
||||
Log.warn(deprecationInfo.message);
|
||||
} else if (deprecationInfo.type === EasBuildDeprecationInfoType.UserFacing) {
|
||||
Log.warn('This command is using API that soon will be deprecated.');
|
||||
Log.warn(deprecationInfo.message);
|
||||
} else {
|
||||
Log.warn('An unexpected warning was encountered. Report it as a bug:');
|
||||
Log.warn('An unexpected warning was encountered. Please report it as a bug:');
|
||||
Log.warn(deprecationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function maybeBailOnRepoStatusAsync(): Promise<void> {
|
||||
});
|
||||
|
||||
if (!answer) {
|
||||
throw new Error('Commit all changes. Aborting...');
|
||||
throw new Error('Please commit all changes. Aborting...');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<vo
|
||||
Log.addNewLineIfNone();
|
||||
Log.warn(`${chalk.bold('Warning!')} Your repository working tree is dirty.`);
|
||||
Log.log(
|
||||
`This operation needs to be run on a clean working tree. ${chalk.bold(
|
||||
'Commit all your changes before proceeding'
|
||||
`This operation needs to be run on a clean working tree, please ${chalk.bold(
|
||||
'commit all your changes before proceeding'
|
||||
)}.`
|
||||
);
|
||||
if (nonInteractive) {
|
||||
throw new Error('Commit all changes. Aborting...');
|
||||
throw new Error('Please commit all changes. Aborting...');
|
||||
}
|
||||
const answer = await confirmAsync({
|
||||
message: `Commit changes to git?`,
|
||||
@@ -52,7 +52,7 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<vo
|
||||
if (answer) {
|
||||
await commitPromptAsync({ commitAllFiles: true });
|
||||
} else {
|
||||
throw new Error('Commit all changes. Aborting...');
|
||||
throw new Error('Please commit all changes. Aborting...');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { EasJsonReader } from '@expo/eas-json';
|
||||
import * as PackageManagerUtils from '@expo/package-manager';
|
||||
import { Command } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import semver from 'semver';
|
||||
|
||||
import {
|
||||
@@ -12,7 +8,6 @@ import {
|
||||
initAsync as initAnalyticsAsync,
|
||||
logEvent,
|
||||
} from '../analytics/rudderstackClient';
|
||||
import { learnMore } from '../log';
|
||||
import { findProjectRootAsync } from '../project/projectUtils';
|
||||
import { getUserAsync } from '../user/User';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
@@ -33,11 +28,8 @@ export default abstract class EasCommand extends Command {
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
async run(): Promise<any> {
|
||||
await initAnalyticsAsync();
|
||||
|
||||
if (this.mustBeRunInsideProject) {
|
||||
const projectDir = await findProjectRootAsync();
|
||||
await this.applyCliConfigAsync(projectDir);
|
||||
await this.ensureEasCliIsNotInDependenciesAsync(projectDir);
|
||||
await this.applyCliConfigAsync();
|
||||
}
|
||||
|
||||
if (this.requiresAuthentication) {
|
||||
@@ -61,7 +53,8 @@ export default abstract class EasCommand extends Command {
|
||||
return super.finally(err);
|
||||
}
|
||||
|
||||
private async applyCliConfigAsync(projectDir: string): Promise<void> {
|
||||
private async applyCliConfigAsync(): Promise<void> {
|
||||
const projectDir = await findProjectRootAsync();
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
const config = await easJsonReader.getCliConfigAsync();
|
||||
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
|
||||
@@ -73,50 +66,4 @@ export default abstract class EasCommand extends Command {
|
||||
setVcsClient(new GitClient());
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureEasCliIsNotInDependenciesAsync(projectDir: string): Promise<void> {
|
||||
let printCliVersionWarning = false;
|
||||
|
||||
const consoleWarn = (msg?: string): void => {
|
||||
if (msg) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(chalk.yellow(msg));
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn();
|
||||
}
|
||||
};
|
||||
|
||||
if (await this.isEasCliInDependenciesAsync(projectDir)) {
|
||||
printCliVersionWarning = true;
|
||||
consoleWarn(`Found ${chalk.bold('eas-cli')} in your project dependencies.`);
|
||||
}
|
||||
|
||||
const maybeRepoRoot = PackageManagerUtils.findWorkspaceRoot(projectDir) ?? projectDir;
|
||||
if (maybeRepoRoot !== projectDir && (await this.isEasCliInDependenciesAsync(maybeRepoRoot))) {
|
||||
printCliVersionWarning = true;
|
||||
consoleWarn(`Found ${chalk.bold('eas-cli')} in your monorepo dependencies.`);
|
||||
}
|
||||
|
||||
if (printCliVersionWarning) {
|
||||
consoleWarn(
|
||||
`It's recommended to use the ${chalk.bold(
|
||||
'"cli.version"'
|
||||
)} field in eas.json to enforce the ${chalk.bold('eas-cli')} version for your project.`
|
||||
);
|
||||
consoleWarn(
|
||||
learnMore('https://github.com/expo/eas-cli#enforcing-eas-cli-version-for-your-project')
|
||||
);
|
||||
consoleWarn();
|
||||
}
|
||||
}
|
||||
|
||||
private async isEasCliInDependenciesAsync(dir: string): Promise<boolean> {
|
||||
const packageJsonPath = path.join(dir, 'package.json');
|
||||
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
||||
return (
|
||||
packageJson?.dependencies?.['eas-cli'] !== undefined ||
|
||||
packageJson?.devDependencies?.['eas-cli'] !== undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export default class BranchCreate extends EasCommand {
|
||||
({ name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide a branch name:',
|
||||
message: 'Please name the branch:',
|
||||
initial:
|
||||
(await getVcsClient().getBranchNameAsync()) ||
|
||||
`branch-${Math.random().toString(36).substr(2, 4)}`,
|
||||
|
||||
@@ -115,7 +115,7 @@ export default class BranchDelete extends EasCommand {
|
||||
({ name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide the name of the branch to delete:',
|
||||
message: 'Please enter the name of the branch to delete:',
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ export default class BranchRename extends EasCommand {
|
||||
({ currentName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'currentName',
|
||||
message: "Provide the name of the branch you'd like to rename:",
|
||||
message: 'Please enter the current name of the branch to rename:',
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export default class BranchRename extends EasCommand {
|
||||
({ newName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'newName',
|
||||
message: `Rename ${currentName}`,
|
||||
message: `Please rename ${currentName}`,
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ import path from 'path';
|
||||
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import { UserInputResourceClass } from '../../build/types';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { StatuspageServiceName } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
|
||||
import { findProjectRootAsync } from '../../project/projectUtils';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
|
||||
interface RawBuildFlags {
|
||||
platform?: string;
|
||||
@@ -97,30 +95,17 @@ export default class Build extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags: rawFlags } = await this.parse(Build);
|
||||
|
||||
if (rawFlags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
const flags = await this.sanitizeFlagsAsync(rawFlags);
|
||||
|
||||
const projectDir = await findProjectRootAsync();
|
||||
|
||||
if (!flags.localBuildOptions.enable) {
|
||||
await maybeWarnAboutEasOutagesAsync(
|
||||
flags.autoSubmit
|
||||
? [StatuspageServiceName.EasBuild, StatuspageServiceName.EasSubmit]
|
||||
: [StatuspageServiceName.EasBuild]
|
||||
);
|
||||
}
|
||||
|
||||
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
|
||||
|
||||
await runBuildAndSubmitAsync(projectDir, flagsWithPlatform);
|
||||
await runBuildAndSubmitAsync(projectDir, flags);
|
||||
}
|
||||
|
||||
private sanitizeFlags(
|
||||
flags: RawBuildFlags
|
||||
): Omit<BuildFlags, 'requestedPlatform'> & { requestedPlatform?: RequestedPlatform } {
|
||||
private async sanitizeFlagsAsync(flags: RawBuildFlags): Promise<BuildFlags> {
|
||||
const nonInteractive = flags['non-interactive'];
|
||||
if (!flags.local && flags.output) {
|
||||
Errors.error('--output is allowed only for local builds', { exit: 1 });
|
||||
@@ -132,11 +117,21 @@ export default class Build extends EasCommand {
|
||||
Errors.error('--json is allowed only when building in non-interactive mode', { exit: 1 });
|
||||
}
|
||||
|
||||
const requestedPlatform =
|
||||
flags.platform &&
|
||||
Object.values(RequestedPlatform).includes(flags.platform.toLowerCase() as RequestedPlatform)
|
||||
? (flags.platform.toLowerCase() as RequestedPlatform)
|
||||
: undefined;
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||
if (flags.local) {
|
||||
if (flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined) {
|
||||
// TODO: implement this
|
||||
Errors.error('Auto-submits are not yet supported when building locally', { exit: 1 });
|
||||
}
|
||||
|
||||
if (requestedPlatform === RequestedPlatform.All) {
|
||||
Errors.error('Builds for multiple platforms are not supported with flag --local', {
|
||||
exit: 1,
|
||||
});
|
||||
} else if (process.platform !== 'darwin' && requestedPlatform === RequestedPlatform.Ios) {
|
||||
Errors.error('Unsupported platform, macOS is required to build apps for iOS', { exit: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
if (flags['skip-credentials-check']) {
|
||||
Log.warnDeprecatedFlag(
|
||||
@@ -181,30 +176,4 @@ export default class Build extends EasCommand {
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
private async ensurePlatformSelectedAsync(
|
||||
flags: Omit<BuildFlags, 'requestedPlatform'> & { requestedPlatform?: RequestedPlatform }
|
||||
): Promise<BuildFlags> {
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.requestedPlatform);
|
||||
|
||||
if (flags.localBuildOptions.enable) {
|
||||
if (flags.autoSubmit) {
|
||||
// TODO: implement this
|
||||
Errors.error('Auto-submits are not yet supported when building locally', { exit: 1 });
|
||||
}
|
||||
|
||||
if (requestedPlatform === RequestedPlatform.All) {
|
||||
Errors.error('Builds for multiple platforms are not supported with flag --local', {
|
||||
exit: 1,
|
||||
});
|
||||
} else if (process.platform !== 'darwin' && requestedPlatform === RequestedPlatform.Ios) {
|
||||
Errors.error('Unsupported platform, macOS is required to build apps for iOS', { exit: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...flags,
|
||||
requestedPlatform,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export default class ChannelCreate extends EasCommand {
|
||||
({ name: channelName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide a channel name:',
|
||||
message: 'Please name the channel:',
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export default class ChannelDelete extends EasCommand {
|
||||
await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide the name of the channel to delete:',
|
||||
message: 'Please enter the name of the channel to delete:',
|
||||
validate: (value: any) => (value ? true : 'Channel name may not be empty.'),
|
||||
})
|
||||
).name;
|
||||
|
||||
@@ -127,7 +127,9 @@ export default class ChannelEdit extends EasCommand {
|
||||
|
||||
const existingChannel = await getChannelByNameForAppAsync({ appId: projectId, channelName });
|
||||
if (existingChannel.updateBranches.length > 1) {
|
||||
throw new Error('There is a rollout in progress. Manage it with "channel:rollout" instead.');
|
||||
throw new Error(
|
||||
'There is a rollout in progress. Please manage it with "channel:rollout" instead.'
|
||||
);
|
||||
}
|
||||
|
||||
const branchName = flags.branch ?? (await promptForBranchAsync());
|
||||
@@ -138,7 +140,7 @@ export default class ChannelEdit extends EasCommand {
|
||||
});
|
||||
if (!branch) {
|
||||
throw new Error(
|
||||
`Could not find a branch named "${branchName}". Check what branches exist on this project with ${chalk.bold(
|
||||
`Could not find a branch named "${branchName}". Please check what branches exist on this project with ${chalk.bold(
|
||||
'eas branch:list'
|
||||
)}.`
|
||||
);
|
||||
@@ -171,7 +173,7 @@ async function promptForChannelAsync(): Promise<string> {
|
||||
const { name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide the name of the channel to edit:',
|
||||
message: 'Please enter the name of the channel to edit:',
|
||||
validate: value => (value ? true : 'The channel name may not be empty.'),
|
||||
});
|
||||
return name;
|
||||
|
||||
@@ -92,7 +92,7 @@ async function startRolloutAsync({
|
||||
});
|
||||
if (!branch) {
|
||||
throw new Error(
|
||||
`Could not find a branch named "${branchName}". Check which branches exist on this project with ${chalk.bold(
|
||||
`Could not find a branch named "${branchName}". Please check what branches exist on this project with ${chalk.bold(
|
||||
'eas branch:list'
|
||||
)}.`
|
||||
);
|
||||
@@ -238,7 +238,7 @@ async function endRolloutAsync({
|
||||
});
|
||||
if (!branch) {
|
||||
throw new Error(
|
||||
`Could not find a branch named "${branchName}". Check which branches exist on this project with ${chalk.bold(
|
||||
`Could not find a branch named "${branchName}". Please check what branches exist on this project with ${chalk.bold(
|
||||
'eas branch:list'
|
||||
)}.`
|
||||
);
|
||||
@@ -354,7 +354,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
});
|
||||
if (!channel) {
|
||||
throw new Error(
|
||||
`Could not find a channel named "${channelName}". Check which channels exist on this project with ${chalk.bold(
|
||||
`Could not find a channel named "${channelName}". Please check what channels exist on this project with ${chalk.bold(
|
||||
'eas channel:list'
|
||||
)}.`
|
||||
);
|
||||
|
||||
@@ -176,7 +176,7 @@ export default class ChannelView extends EasCommand {
|
||||
({ name: channelName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide a channel name:',
|
||||
message: 'Please name the channel:',
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
|
||||
|
||||
export default class Credentials extends EasCommand {
|
||||
static description = 'manage credentials';
|
||||
|
||||
static flags = {
|
||||
platform: Flags.enum({ char: 'p', options: ['android', 'ios'] }),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(Credentials);
|
||||
await new SelectPlatform(flags.platform).runAsync();
|
||||
await new SelectPlatform().runAsync();
|
||||
}
|
||||
}
|
||||
@@ -51,11 +51,11 @@ export default class MetadataPull extends EasCommand {
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`🎉 Your store config is ready.
|
||||
Log.log(`🎉 Your store configuration is ready.
|
||||
|
||||
- Update the ${chalk.bold(relativePath)} file to prepare the app information.
|
||||
- Run ${chalk.bold('eas submit')} or manually upload a new app version to the app stores.
|
||||
- Once the app is uploaded, run ${chalk.bold('eas metadata:push')} to sync the store config.
|
||||
- Once the app is uploaded, run ${chalk.bold('eas metadata:push')} to sync the store configuration.
|
||||
- ${learnMore('https://docs.expo.dev/eas-metadata/introduction/')}`);
|
||||
} catch (error: any) {
|
||||
handleMetadataError(error);
|
||||
|
||||
@@ -3,11 +3,7 @@ import chalk from 'chalk';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import Log from '../../log';
|
||||
import { getExpoConfig } from '../../project/expoConfig';
|
||||
import {
|
||||
fetchProjectIdFromServerAsync,
|
||||
findProjectRootAsync,
|
||||
saveProjectIdToAppConfigAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { findProjectRootAsync, setProjectIdAsync } from '../../project/projectUtils';
|
||||
|
||||
export default class ProjectInit extends EasCommand {
|
||||
static description = 'create or link an EAS project';
|
||||
@@ -24,9 +20,6 @@ export default class ProjectInit extends EasCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
const projectId = await fetchProjectIdFromServerAsync(exp);
|
||||
await saveProjectIdToAppConfigAsync(projectDir, projectId);
|
||||
|
||||
Log.withTick(`Linked app.json to project with ID ${chalk.bold(projectId)}`);
|
||||
await setProjectIdAsync(projectDir);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export default class EnvironmentSecretList extends EasCommand {
|
||||
const projectAccountName = await getProjectAccountNameAsync(exp);
|
||||
|
||||
if (!projectDir) {
|
||||
throw new Error("Run this command inside your project's directory");
|
||||
throw new Error("Please run this command inside your project's directory");
|
||||
}
|
||||
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectId);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import { StatuspageServiceName, SubmissionFragment } from '../graphql/generated';
|
||||
import { SubmissionFragment } from '../graphql/generated';
|
||||
import { toAppPlatform } from '../graphql/types/AppPlatform';
|
||||
import Log from '../log';
|
||||
import {
|
||||
@@ -16,14 +16,9 @@ import {
|
||||
import { getExpoConfig } from '../project/expoConfig';
|
||||
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils';
|
||||
import { SubmitArchiveFlags, createSubmissionContextAsync } from '../submit/context';
|
||||
import {
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish,
|
||||
submitAsync,
|
||||
waitToCompleteAsync,
|
||||
} from '../submit/submit';
|
||||
import { submitAsync, waitToCompleteAsync } from '../submit/submit';
|
||||
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
|
||||
import { getProfilesAsync } from '../utils/profiles';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../utils/statuspageService';
|
||||
|
||||
interface RawCommandFlags {
|
||||
platform?: string;
|
||||
@@ -92,23 +87,18 @@ export default class Submit extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags: rawFlags } = await this.parse(Submit);
|
||||
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
const flags = await this.sanitizeFlagsAsync(rawFlags);
|
||||
|
||||
const projectDir = await findProjectRootAsync();
|
||||
const exp = getExpoConfig(projectDir);
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync([StatuspageServiceName.EasSubmit]);
|
||||
|
||||
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
|
||||
|
||||
const platforms = toPlatforms(flagsWithPlatform.requestedPlatform);
|
||||
const platforms = toPlatforms(flags.requestedPlatform);
|
||||
const submissionProfiles = await getProfilesAsync({
|
||||
type: 'submit',
|
||||
easJsonReader: new EasJsonReader(projectDir),
|
||||
platforms,
|
||||
profileName: flagsWithPlatform.profile,
|
||||
profileName: flags.profile,
|
||||
});
|
||||
|
||||
const submissions: SubmissionFragment[] = [];
|
||||
@@ -118,8 +108,8 @@ export default class Submit extends EasCommand {
|
||||
projectDir,
|
||||
projectId,
|
||||
profile: submissionProfile.profile,
|
||||
archiveFlags: flagsWithPlatform.archiveFlags,
|
||||
nonInteractive: flagsWithPlatform.nonInteractive,
|
||||
archiveFlags: flags.archiveFlags,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
|
||||
if (submissionProfiles.length > 1) {
|
||||
@@ -139,17 +129,12 @@ export default class Submit extends EasCommand {
|
||||
Log.newLine();
|
||||
printSubmissionDetailsUrls(submissions);
|
||||
|
||||
if (flagsWithPlatform.wait) {
|
||||
const completedSubmissions = await waitToCompleteAsync(submissions, {
|
||||
verbose: flagsWithPlatform.verbose,
|
||||
});
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
||||
if (flags.wait) {
|
||||
await waitToCompleteAsync(submissions, { verbose: flags.verbose });
|
||||
}
|
||||
}
|
||||
|
||||
private sanitizeFlags(
|
||||
flags: RawCommandFlags
|
||||
): Omit<CommandFlags, 'requestedPlatform'> & { requestedPlatform?: RequestedPlatform } {
|
||||
private async sanitizeFlagsAsync(flags: RawCommandFlags): Promise<CommandFlags> {
|
||||
const {
|
||||
platform,
|
||||
verbose,
|
||||
@@ -163,11 +148,15 @@ export default class Submit extends EasCommand {
|
||||
Errors.error('--platform is required when building in non-interactive mode', { exit: 1 });
|
||||
}
|
||||
|
||||
const requestedPlatform =
|
||||
flags.platform &&
|
||||
Object.values(RequestedPlatform).includes(flags.platform.toLowerCase() as RequestedPlatform)
|
||||
? (flags.platform.toLowerCase() as RequestedPlatform)
|
||||
: undefined;
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||
if (requestedPlatform === RequestedPlatform.All) {
|
||||
if (archiveFlags.id || archiveFlags.path || archiveFlags.url) {
|
||||
Errors.error(
|
||||
'--id, --path, and --url params are only supported when performing a single-platform submit',
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
archiveFlags,
|
||||
@@ -178,24 +167,4 @@ export default class Submit extends EasCommand {
|
||||
nonInteractive,
|
||||
};
|
||||
}
|
||||
|
||||
private async ensurePlatformSelectedAsync(
|
||||
flags: Omit<CommandFlags, 'requestedPlatform'> & { requestedPlatform?: RequestedPlatform }
|
||||
): Promise<CommandFlags> {
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.requestedPlatform);
|
||||
|
||||
if (requestedPlatform === RequestedPlatform.All) {
|
||||
if (flags.archiveFlags.id || flags.archiveFlags.path || flags.archiveFlags.url) {
|
||||
Errors.error(
|
||||
'--id, --path, and --url params are only supported when performing a single-platform submit',
|
||||
{ exit: 1 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...flags,
|
||||
requestedPlatform,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
PublishUpdateGroupInput,
|
||||
Robot,
|
||||
RootQueryUpdatesByGroupArgs,
|
||||
StatuspageServiceName,
|
||||
Update,
|
||||
UpdateInfoGroup,
|
||||
UpdatePublishMutation,
|
||||
@@ -60,7 +59,6 @@ import {
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
import { createUpdateBranchOnAppAsync } from '../branch/create';
|
||||
import { createUpdateChannelOnAppAsync } from '../channel/create';
|
||||
@@ -230,7 +228,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
'non-interactive': nonInteractive,
|
||||
},
|
||||
} = await this.parse(UpdatePublish);
|
||||
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
@@ -248,8 +245,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
isPublicConfig: false,
|
||||
});
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync([StatuspageServiceName.EasUpdate]);
|
||||
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
const hasExpoUpdates = isExpoUpdatesInstalledOrAvailable(projectDir, exp.sdkVersion);
|
||||
@@ -307,7 +302,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
({ name: branchName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'No branches found. Provide a branch name:',
|
||||
message: 'No branches found. Creating a new one. Please name the new branch:',
|
||||
initial:
|
||||
(await getVcsClient().getBranchNameAsync()) ||
|
||||
`branch-${Math.random().toString(36).substr(2, 4)}`,
|
||||
@@ -409,7 +404,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
({ publishMessage: message } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'publishMessage',
|
||||
message: `Provide an update message.`,
|
||||
message: `Please enter an update message.`,
|
||||
initial: `Republish "${oldMessage!}" - group: ${group}`,
|
||||
validate: (value: any) => (value ? true : validationMessage),
|
||||
}));
|
||||
@@ -431,7 +426,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
({ publishMessage: message } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'publishMessage',
|
||||
message: `Provide an update message.`,
|
||||
message: `Please enter an update message.`,
|
||||
initial: (await getVcsClient().getLastCommitMessageAsync())?.trim(),
|
||||
validate: (value: any) => (value ? true : validationMessage),
|
||||
}));
|
||||
@@ -762,9 +757,7 @@ async function checkEASUpdateURLIsSetAsync(exp: ExpoConfig): Promise<void> {
|
||||
|
||||
if (configuredURL !== expectedURL) {
|
||||
throw new Error(
|
||||
`The update URL is incorrectly configured for EAS Update. Set updates.url to ${expectedURL} in your ${chalk.bold(
|
||||
'app.json'
|
||||
)}.`
|
||||
`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class BranchView extends EasCommand {
|
||||
({ name: branchInteractive } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Provide the name of the branch whose updates you wish to view:',
|
||||
message: 'Please enter the name of the branch whose updates you wish to view:',
|
||||
initial: (await getVcsClient().getBranchNameAsync()) ?? undefined,
|
||||
validate: (value: any) => (value ? true : validationMessage),
|
||||
}));
|
||||
|
||||
@@ -18,7 +18,6 @@ export const jester2: Actor = {
|
||||
|
||||
export const testUsername = jester.username;
|
||||
export const testSlug = 'testApp';
|
||||
export const testProjectId = '7ef93448-3bc7-4b57-be32-99326dcf24f0';
|
||||
export const testBundleIdentifier = 'test.com.app';
|
||||
export const testPackageName = 'test.com.app';
|
||||
export const testExperienceName = `@${testUsername}/${testSlug}`;
|
||||
@@ -36,11 +35,6 @@ export const testAppJson = {
|
||||
sdkVersion: '38.0.0',
|
||||
ios: { bundleIdentifier: testBundleIdentifier },
|
||||
android: { package: testPackageName },
|
||||
extra: {
|
||||
eas: {
|
||||
projectId: testProjectId,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const testAppJsonWithDifferentOwner = {
|
||||
|
||||
@@ -54,7 +54,7 @@ export class RemoveKeystore {
|
||||
)
|
||||
);
|
||||
Log.warn(
|
||||
'Read https://docs.expo.dev/distribution/building-standalone-apps/#if-you-choose-to-build-for-android for more info before proceeding.'
|
||||
'Please read https://docs.expo.dev/distribution/building-standalone-apps/#if-you-choose-to-build-for-android for more info before proceeding.'
|
||||
);
|
||||
Log.newLine();
|
||||
Log.warn(
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
CommonAndroidAppCredentialsFragment,
|
||||
GoogleServiceAccountKeyFragment,
|
||||
} from '../../../graphql/generated';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { Account } from '../../../user/Account';
|
||||
import { AppQuery } from '../../ios/api/graphql/queries/AppQuery';
|
||||
import { GoogleServiceAccountKey, KeystoreWithType } from '../credentials';
|
||||
import { AndroidAppBuildCredentialsMutation } from './graphql/mutations/AndroidAppBuildCredentialsMutation';
|
||||
import { AndroidAppCredentialsMutation } from './graphql/mutations/AndroidAppCredentialsMutation';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
import { EasJson } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Log from '../log';
|
||||
@@ -20,7 +19,6 @@ export class CredentialsContext {
|
||||
public readonly nonInteractive: boolean;
|
||||
public readonly projectDir: string;
|
||||
public readonly user: Actor;
|
||||
public readonly easJsonCliConfig?: EasJson['cli'];
|
||||
|
||||
private shouldAskAuthenticateAppStore: boolean = true;
|
||||
private resolvedExp?: ExpoConfig;
|
||||
@@ -28,14 +26,12 @@ export class CredentialsContext {
|
||||
constructor(
|
||||
private options: {
|
||||
exp?: ExpoConfig;
|
||||
easJsonCliConfig?: EasJson['cli'];
|
||||
nonInteractive?: boolean;
|
||||
projectDir: string;
|
||||
user: Actor;
|
||||
env?: Env;
|
||||
}
|
||||
) {
|
||||
this.easJsonCliConfig = options.easJsonCliConfig;
|
||||
this.projectDir = options.projectDir;
|
||||
this.user = options.user;
|
||||
this.nonInteractive = options.nonInteractive ?? false;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export class MissingCredentialsNonInteractiveError extends Error {
|
||||
constructor(message?: string) {
|
||||
super(message ?? 'Credentials are not set up. Run this command again in interactive mode.');
|
||||
super(
|
||||
message ?? 'Credentials are not set up. Please run this command again in interactive mode.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import {
|
||||
CredentialsSource,
|
||||
DistributionType,
|
||||
EasJsonReader,
|
||||
IosEnterpriseProvisioning,
|
||||
} from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import { CredentialsSource, DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
|
||||
import { CommonIosAppCredentialsFragment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { findApplicationTarget } from '../../project/ios/target';
|
||||
import { selectAsync } from '../../prompts';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { CredentialsContext } from '../context';
|
||||
import * as credentialsJsonReader from '../credentialsJson/read';
|
||||
import { ensureAllTargetsAreConfigured } from '../credentialsJson/utils';
|
||||
@@ -27,12 +21,6 @@ interface Options {
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
}
|
||||
|
||||
enum PushNotificationSetupOption {
|
||||
YES,
|
||||
NO,
|
||||
NO_DONT_ASK_AGAIN,
|
||||
}
|
||||
|
||||
export default class IosCredentialsProvider {
|
||||
public readonly platform = Platform.IOS;
|
||||
|
||||
@@ -101,39 +89,13 @@ export default class IosCredentialsProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ctx.easJsonCliConfig?.promptToConfigurePushNotfications === false) {
|
||||
const confirmSetup = await confirmAsync({
|
||||
message: `Would you like to set up Push Notifications for your project?`,
|
||||
});
|
||||
if (!confirmSetup) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const setupOption = await selectAsync(
|
||||
`Would you like to set up Push Notifications for your project?`,
|
||||
[
|
||||
{ title: 'Yes', value: PushNotificationSetupOption.YES },
|
||||
{ title: 'No', value: PushNotificationSetupOption.NO },
|
||||
{
|
||||
title: `No, don't ask again (preference will be saved to eas.json)`,
|
||||
value: PushNotificationSetupOption.NO_DONT_ASK_AGAIN,
|
||||
},
|
||||
]
|
||||
);
|
||||
if (setupOption === PushNotificationSetupOption.YES) {
|
||||
return await setupPushKeyAction.runAsync(ctx);
|
||||
} else {
|
||||
if (setupOption === PushNotificationSetupOption.NO_DONT_ASK_AGAIN) {
|
||||
await this.disablePushNotificationsSetupInEasJsonAsync(ctx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async disablePushNotificationsSetupInEasJsonAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<void> {
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(ctx.projectDir);
|
||||
const easJson = await fs.readJSON(easJsonPath);
|
||||
easJson.cli = { ...easJson?.cli, promptToConfigurePushNotfications: false };
|
||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
||||
Log.withTick('Updated eas.json');
|
||||
return await setupPushKeyAction.runAsync(ctx);
|
||||
}
|
||||
|
||||
private assertProvisioningProfileType(provisioningProfile: string, targetName?: string): void {
|
||||
|
||||
@@ -147,8 +147,8 @@ const APPLE_DIST_CERTS_TOO_MANY_GENERATED_ERROR = `
|
||||
You can have only ${chalk.underline(
|
||||
'three'
|
||||
)} Apple Distribution Certificates generated on your Apple Developer account.
|
||||
Revoke the old ones or reuse existing from your other apps.
|
||||
Remember that Apple Distribution Certificates are not application specific!
|
||||
Please revoke the old ones or reuse existing from your other apps.
|
||||
Please remember that Apple Distribution Certificates are not application specific!
|
||||
`;
|
||||
|
||||
export async function provideOrGenerateDistributionCertificateAsync(
|
||||
|
||||
@@ -45,7 +45,7 @@ export class SetUpAdhocProvisioningProfile {
|
||||
return nullthrows(await getBuildCredentialsAsync(ctx, this.app, IosDistributionType.AdHoc));
|
||||
} else {
|
||||
throw new MissingCredentialsNonInteractiveError(
|
||||
'Provisioning profile is not configured correctly. Run this command again in interactive mode.'
|
||||
'Provisioning profile is not configured correctly. Please run this command again in interactive mode.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export class SetUpInternalProvisioningProfile {
|
||||
} else {
|
||||
if (adhocBuildCredentialsExist && enterpriseBuildCredentialsExist) {
|
||||
throw new Error(
|
||||
`You're in non-interactive mode. You have set up both adhoc and universal distribution credentials. Set the 'enterpriseProvisioning' property (to 'adhoc' or 'universal') in eas.json to choose the credentials to use.`
|
||||
`You're in non-interactive mode. You have set up both adhoc and universal distribution credentials. Please set the 'enterpriseProvisioning' property (to 'adhoc' or 'universal') in eas.json to choose the credentials to use.`
|
||||
);
|
||||
} else if (adhocBuildCredentialsExist) {
|
||||
return await this.setupAdhocProvisioningProfileAsync(ctx);
|
||||
@@ -78,7 +78,7 @@ export class SetUpInternalProvisioningProfile {
|
||||
return await this.setupUniversalProvisioningProfileAsync(ctx);
|
||||
} else {
|
||||
throw new Error(
|
||||
`You're in non-interactive mode. EAS CLI couldn't find any credentials suitable for internal distribution. Run this command again in interactive mode.`
|
||||
`You're in non-interactive mode. EAS CLI couldn't find any credentials suitable for internal distribution. Please run again in interactive mode.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export class SetUpProvisioningProfile {
|
||||
}
|
||||
if (ctx.nonInteractive) {
|
||||
throw new MissingCredentialsNonInteractiveError(
|
||||
'Provisioning profile is not configured correctly. Run this command again in interactive mode.'
|
||||
'Provisioning profile is not configured correctly. Please run this command again in interactive mode.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export class SetUpSubmissionCredentials {
|
||||
|
||||
async promptForAppSpecificPasswordAsync(): Promise<string> {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`Enter your Apple app-specific password.`);
|
||||
Log.log(`Please enter your Apple app-specific password.`);
|
||||
Log.log(learnMore('https://expo.fyi/apple-app-specific-password'));
|
||||
Log.warn(
|
||||
wrapAnsi(
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
IosAppBuildCredentialsFragment,
|
||||
IosDistributionType,
|
||||
} from '../../../graphql/generated';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { isWildcardBundleIdentifier } from '../../../project/ios/bundleIdentifier';
|
||||
import { Account } from '../../../user/Account';
|
||||
import { DistributionCertificate, PushKey } from '../appstore/Credentials.types';
|
||||
@@ -34,6 +33,7 @@ import { ApplePushKeyMutation } from './graphql/mutations/ApplePushKeyMutation';
|
||||
import { AppleTeamMutation } from './graphql/mutations/AppleTeamMutation';
|
||||
import { IosAppBuildCredentialsMutation } from './graphql/mutations/IosAppBuildCredentialsMutation';
|
||||
import { IosAppCredentialsMutation } from './graphql/mutations/IosAppCredentialsMutation';
|
||||
import { AppQuery } from './graphql/queries/AppQuery';
|
||||
import { AppStoreConnectApiKeyQuery } from './graphql/queries/AppStoreConnectApiKeyQuery';
|
||||
import { AppleAppIdentifierQuery } from './graphql/queries/AppleAppIdentifierQuery';
|
||||
import { AppleDeviceQuery } from './graphql/queries/AppleDeviceQuery';
|
||||
|
||||
+3
-3
@@ -2,9 +2,9 @@ import assert from 'assert';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import { AppByFullNameQuery, AppFragment } from '../generated';
|
||||
import { AppFragmentNode } from '../types/App';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../../../../graphql/client';
|
||||
import { AppByFullNameQuery, AppFragment } from '../../../../../graphql/generated';
|
||||
import { AppFragmentNode } from '../../../../../graphql/types/App';
|
||||
|
||||
export const AppQuery = {
|
||||
async byFullNameAsync(fullName: string): Promise<AppFragment> {
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ApiKey, ApiKeyType, UnexpectedResponse, UserRole } from '@expo/apple-utils';
|
||||
import { ApiKey, ApiKeyType, UserRole } from '@expo/apple-utils';
|
||||
|
||||
import {
|
||||
createAscApiKeyAsync,
|
||||
downloadWithRetryAsync,
|
||||
getAscApiKeyAsync,
|
||||
listAscApiKeysAsync,
|
||||
revokeAscApiKeyAsync,
|
||||
} from '../ascApiKey';
|
||||
import { getRequestContext } from '../authenticate';
|
||||
|
||||
jest.mock('@expo/apple-utils');
|
||||
jest.mock('../authenticate');
|
||||
jest.mock('../../../../ora');
|
||||
|
||||
@@ -96,38 +96,3 @@ test(`revokeAscApiKeyAsync`, async () => {
|
||||
expect(mockApiKey.revokeAsync).toBeCalled();
|
||||
expect(result).toEqual(mockAscApiKeyInfo);
|
||||
});
|
||||
|
||||
test(`downloadWithRetryAsync`, async () => {
|
||||
const cacheFailureMessage = `The specified resource does not exist - There is no resource of type 'apiKeys' with id 'TEST-ID'`;
|
||||
|
||||
// complete failure
|
||||
const mockApiKeyWithDownloadError = {
|
||||
...mockApiKey,
|
||||
downloadAsync: jest.fn(() => {
|
||||
throw new UnexpectedResponse(cacheFailureMessage);
|
||||
}),
|
||||
} as unknown as ApiKey;
|
||||
await expect(
|
||||
downloadWithRetryAsync(mockApiKeyWithDownloadError, { minTimeout: 1 }) // stay within jest timeout window
|
||||
).rejects.toThrowError(cacheFailureMessage);
|
||||
// expect to try once and retry 3 times = 4 total
|
||||
expect(mockApiKeyWithDownloadError.downloadAsync as jest.Mock).toBeCalledTimes(7);
|
||||
|
||||
// one time failure
|
||||
const mockApiKeyWithOneTimeDownloadError = mockApiKey as unknown as ApiKey;
|
||||
(mockApiKeyWithOneTimeDownloadError.downloadAsync as jest.Mock).mockClear();
|
||||
(mockApiKeyWithOneTimeDownloadError.downloadAsync as jest.Mock).mockImplementationOnce(() => {
|
||||
throw new UnexpectedResponse(cacheFailureMessage);
|
||||
});
|
||||
const keyP8AfterOneFailure = await downloadWithRetryAsync(mockApiKeyWithOneTimeDownloadError, {
|
||||
minTimeout: 1,
|
||||
});
|
||||
expect(keyP8AfterOneFailure).toBe('super secret');
|
||||
|
||||
// successful case
|
||||
(mockApiKey.downloadAsync as jest.Mock).mockClear();
|
||||
const keyP8NoFailure = await downloadWithRetryAsync(mockApiKeyWithOneTimeDownloadError, {
|
||||
minTimeout: 1,
|
||||
});
|
||||
expect(keyP8NoFailure).toBe('super secret');
|
||||
});
|
||||
+28
-169
@@ -94,30 +94,6 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
'com.apple.security.application-groups': [
|
||||
'group.CY-A5149AC2-49FC-11E7-B3F3-0335A16FFB8D.com.cydia.Extender',
|
||||
],
|
||||
|
||||
'com.apple.developer.fileprovider.testing-mode': true,
|
||||
'com.apple.developer.healthkit.recalibrate-estimates': true,
|
||||
'com.apple.developer.maps': true,
|
||||
'com.apple.developer.user-management': true,
|
||||
'com.apple.developer.networking.custom-protocol': true,
|
||||
'com.apple.developer.system-extension.install': true,
|
||||
'com.apple.developer.push-to-talk': true,
|
||||
'com.apple.developer.driverkit.transport.usb': true,
|
||||
'com.apple.developer.kernel.increased-memory-limit': true,
|
||||
'com.apple.developer.driverkit.communicates-with-drivers': true,
|
||||
'com.apple.developer.media-device-discovery-extension': true,
|
||||
'com.apple.developer.driverkit.allow-third-party-userclients': true,
|
||||
'com.apple.developer.weatherkit': true,
|
||||
'com.apple.developer.on-demand-install-capable': true,
|
||||
'com.apple.developer.driverkit.family.scsicontroller': true,
|
||||
'com.apple.developer.driverkit.family.serial': true,
|
||||
'com.apple.developer.driverkit.family.networking': true,
|
||||
'com.apple.developer.driverkit.family.hid.eventservice': true,
|
||||
'com.apple.developer.driverkit.family.hid.device': true,
|
||||
'com.apple.developer.driverkit': true,
|
||||
'com.apple.developer.driverkit.transport.hid': true,
|
||||
'com.apple.developer.driverkit.family.audio': true,
|
||||
'com.apple.developer.shared-with-you': true,
|
||||
});
|
||||
|
||||
expect(bundleId.updateBundleIdCapabilityAsync).toBeCalledWith([
|
||||
@@ -159,153 +135,36 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
},
|
||||
{ capabilityType: 'AUTOFILL_CREDENTIAL_PROVIDER', option: 'ON' },
|
||||
{ capabilityType: 'APP_GROUPS', option: 'ON' },
|
||||
{
|
||||
capabilityType: 'FILEPROVIDER_TESTINGMODE',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'HEALTHKIT_RECALIBRATE_ESTIMATES',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'MAPS',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'USER_MANAGEMENT',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'NETWORK_CUSTOM_PROTOCOL',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'SYSTEM_EXTENSION_INSTALL',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'PUSH_TO_TALK',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_USBTRANSPORT_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'INCREASED_MEMORY_LIMIT',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_COMMUNICATESWITHDRIVERS',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'MEDIA_DEVICE_DISCOVERY',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_ALLOWTHIRDPARTY_USERCLIENTS',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'WEATHERKIT',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'ONDEMANDINSTALL_EXTENSIONS',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_SCSICONTROLLER_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_SERIAL_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_NETWORKING_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_HIDEVENTSERVICE_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_HIDDEVICE_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_PUBLIC',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_TRANSPORT_HID_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'DRIVERKIT_FAMILY_AUDIO_PUB',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'SHARED_WITH_YOU',
|
||||
option: 'ON',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(results.enabled).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"HealthKit",
|
||||
"Apple Pay Payment Processing",
|
||||
"iCloud",
|
||||
"ClassKit",
|
||||
"Data Protection",
|
||||
"Network Extensions",
|
||||
"Personal VPN",
|
||||
"Hotspot",
|
||||
"Extended Virtual Address Space",
|
||||
"HomeKit",
|
||||
"Multipath",
|
||||
"Wireless Accessory Configuration",
|
||||
"Inter-App Audio",
|
||||
"Wallet",
|
||||
"Fonts",
|
||||
"App Attest",
|
||||
"NFC Tag Reading",
|
||||
"Sign In with Apple",
|
||||
"SiriKit",
|
||||
"Access WiFi Information",
|
||||
"Communication Notifications",
|
||||
"Time Sensitive Notifications",
|
||||
"Group Activities",
|
||||
"Family Controls",
|
||||
"AutoFill Credential Provider",
|
||||
"App Groups",
|
||||
"FileProvider TestingMode",
|
||||
"Recalibrate Estimates",
|
||||
"Maps",
|
||||
"TV Services",
|
||||
"Custom Network Protocol",
|
||||
"System Extension",
|
||||
"Push to Talk",
|
||||
"DriverKit USB Transport (development)",
|
||||
"Increased Memory Limit",
|
||||
"Communicates with Drivers",
|
||||
"Media Device Discovery",
|
||||
"DriverKit Allow Third Party UserClients",
|
||||
"WeatherKit",
|
||||
"On Demand Install Capable for App Clip Extensions",
|
||||
"DriverKit Family SCSIController (development)",
|
||||
"DriverKit Family Serial (development)",
|
||||
"DriverKit Family Networking (development)",
|
||||
"DriverKit Family HID EventService (development)",
|
||||
"DriverKit Family HID Device (development)",
|
||||
"DriverKit for Development",
|
||||
"DriverKit Transport HID (development)",
|
||||
"DriverKit Family Audio (development)",
|
||||
"Shared with You",
|
||||
]
|
||||
`);
|
||||
expect(results.enabled).toStrictEqual([
|
||||
'HealthKit',
|
||||
'Apple Pay Payment Processing',
|
||||
'iCloud',
|
||||
'ClassKit',
|
||||
'Data Protection',
|
||||
'Network Extensions',
|
||||
'Personal VPN',
|
||||
'Hotspot',
|
||||
'Extended Virtual Address Space',
|
||||
'HomeKit',
|
||||
'Multipath',
|
||||
'Wireless Accessory Configuration',
|
||||
'Inter-App Audio',
|
||||
'Wallet',
|
||||
'Fonts',
|
||||
'App Attest',
|
||||
'NFC Tag Reading',
|
||||
'Sign In with Apple',
|
||||
'SiriKit',
|
||||
'Access WiFi Information',
|
||||
'Communication Notifications',
|
||||
'Time Sensitive Notifications',
|
||||
'Group Activities',
|
||||
'Family Controls',
|
||||
'AutoFill Credential Provider',
|
||||
'App Groups',
|
||||
]);
|
||||
expect(results.disabled).toStrictEqual([]);
|
||||
});
|
||||
|
||||
|
||||
+2
-2
@@ -153,7 +153,7 @@ describe(syncCapabilityIdentifiersForEntitlementsAsync, () => {
|
||||
(_ctx: any, { identifier }: { identifier: string }): any => {
|
||||
// e2e test with: merchant.expodemo
|
||||
throw new Error(
|
||||
`There is a problem with the request entity - A Merchant ID with Identifier '${identifier}' is not available. Enter a different string.`
|
||||
`There is a problem with the request entity - A Merchant ID with Identifier '${identifier}' is not available. Please enter a different string.`
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -167,6 +167,6 @@ describe(syncCapabilityIdentifiersForEntitlementsAsync, () => {
|
||||
syncCapabilityIdentifiersForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.in-app-payments': ['merchant.bacon'],
|
||||
})
|
||||
).rejects.toThrow(/is not available. Enter a different string/);
|
||||
).rejects.toThrow(/is not available. Please enter a different string/);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
import { ApiKey, ApiKeyProps, ApiKeyType, UserRole } from '@expo/apple-utils';
|
||||
import promiseRetry from 'promise-retry';
|
||||
|
||||
import { Analytics, SubmissionEvent } from '../../../analytics/events';
|
||||
import Log, { learnMore } from '../../../log';
|
||||
import Log from '../../../log';
|
||||
import { ora } from '../../../ora';
|
||||
import { AscApiKey, AscApiKeyInfo } from './Credentials.types';
|
||||
import { getRequestContext } from './authenticate';
|
||||
@@ -51,73 +49,6 @@ export async function getAscApiKeyAsync(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a bug in Apple's infrastructure that does not propagate newly created objects for a
|
||||
* while. If the key has not propagated and you try to download it, Apple will error saying that
|
||||
* the resource does not exist. We retry with exponential backoff until the key propagates and
|
||||
* is available for download.
|
||||
* */
|
||||
export async function downloadWithRetryAsync(
|
||||
key: ApiKey,
|
||||
{
|
||||
minTimeout = 1000,
|
||||
retries = 6,
|
||||
factor = 2,
|
||||
}: { minTimeout?: number; retries?: number; factor?: number } = {}
|
||||
): Promise<string | null> {
|
||||
const RESOURCE_DOES_NOT_EXIST_MESSAGE =
|
||||
'The specified resource does not exist - There is no resource of type';
|
||||
try {
|
||||
const keyP8 = await promiseRetry(
|
||||
async (retry, number) => {
|
||||
try {
|
||||
return await key.downloadAsync();
|
||||
} catch (e: any) {
|
||||
if (
|
||||
e.name === 'UnexpectedAppleResponse' &&
|
||||
e.message.includes(RESOURCE_DOES_NOT_EXIST_MESSAGE)
|
||||
) {
|
||||
const secondsToRetry = Math.pow(factor, number);
|
||||
Log.log(
|
||||
`Received an unexpected response from Apple, retrying in ${secondsToRetry} seconds...`
|
||||
);
|
||||
Analytics.logEvent(SubmissionEvent.API_KEY_DOWNLOAD_RETRY, {
|
||||
errorName: e.name,
|
||||
reason: e.message,
|
||||
retry: number,
|
||||
});
|
||||
return retry(e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
{
|
||||
retries,
|
||||
factor,
|
||||
minTimeout,
|
||||
}
|
||||
);
|
||||
Analytics.logEvent(SubmissionEvent.API_KEY_DOWNLOAD_SUCCESS, {});
|
||||
return keyP8;
|
||||
} catch (e: any) {
|
||||
if (
|
||||
e.name === 'UnexpectedAppleResponse' &&
|
||||
e.message.includes(RESOURCE_DOES_NOT_EXIST_MESSAGE)
|
||||
) {
|
||||
Log.warn(
|
||||
`Unable to download Api Key from Apple at this time. Create and upload your key manually by running 'eas credentials' ${learnMore(
|
||||
'https://expo.fyi/creating-asc-api-key'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
Analytics.logEvent(SubmissionEvent.API_KEY_DOWNLOAD_FAIL, {
|
||||
errorName: e.name,
|
||||
reason: e.message,
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an App Store Connect API Key.
|
||||
* **Does not support App Store Connect API (CI).**
|
||||
@@ -140,7 +71,7 @@ export async function createAscApiKeyAsync(
|
||||
roles: roles ?? [UserRole.ADMIN],
|
||||
keyType: keyType ?? ApiKeyType.PUBLIC_API,
|
||||
});
|
||||
const keyP8 = await downloadWithRetryAsync(key);
|
||||
const keyP8 = await key.downloadAsync();
|
||||
if (!keyP8) {
|
||||
const { nickname, roles } = key.attributes;
|
||||
const humanReadableKey = `App Store Connect Key '${nickname}' (${
|
||||
|
||||
@@ -484,7 +484,6 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_vpn_api
|
||||
name: 'Push Notifications',
|
||||
// com.apple.developer.aps-environment
|
||||
entitlement: 'aps-environment',
|
||||
capability: CapabilityType.PUSH_NOTIFICATIONS,
|
||||
validateOptions: validateDevProdString,
|
||||
@@ -523,181 +522,6 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
validateOptions: validateDevProdString,
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.coremedia.hls.low-latency',
|
||||
name: 'Low Latency HLS',
|
||||
capability: CapabilityType.HLS_LOW_LATENCY,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.associated-domains.mdm-managed',
|
||||
name: 'MDM Managed Associated Domains',
|
||||
capability: CapabilityType.MDM_MANAGED_ASSOCIATED_DOMAINS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.fileprovider.testing-mode',
|
||||
name: 'FileProvider TestingMode',
|
||||
capability: CapabilityType.FILE_PROVIDER_TESTING_MODE,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.healthkit.recalibrate-estimates',
|
||||
name: 'Recalibrate Estimates',
|
||||
capability: CapabilityType.HEALTH_KIT_RECALIBRATE_ESTIMATES,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.maps',
|
||||
name: 'Maps',
|
||||
capability: CapabilityType.MAPS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.user-management',
|
||||
name: 'TV Services',
|
||||
capability: CapabilityType.USER_MANAGEMENT,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.networking.custom-protocol',
|
||||
name: 'Custom Network Protocol',
|
||||
capability: CapabilityType.NETWORK_CUSTOM_PROTOCOL,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.system-extension.install',
|
||||
name: 'System Extension',
|
||||
capability: CapabilityType.SYSTEM_EXTENSION_INSTALL,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.push-to-talk',
|
||||
name: 'Push to Talk',
|
||||
capability: CapabilityType.PUSH_TO_TALK,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.transport.usb',
|
||||
name: 'DriverKit USB Transport (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_USB_TRANSPORT_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.kernel.increased-memory-limit',
|
||||
name: 'Increased Memory Limit',
|
||||
capability: CapabilityType.INCREASED_MEMORY_LIMIT,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.communicates-with-drivers',
|
||||
name: 'Communicates with Drivers',
|
||||
capability: CapabilityType.DRIVER_KIT_COMMUNICATES_WITH_DRIVERS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.media-device-discovery-extension',
|
||||
name: 'Media Device Discovery',
|
||||
capability: CapabilityType.MEDIA_DEVICE_DISCOVERY,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.allow-third-party-userclients',
|
||||
name: 'DriverKit Allow Third Party UserClients',
|
||||
capability: CapabilityType.DRIVER_KIT_ALLOW_THIRD_PARTY_USER_CLIENTS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.weatherkit',
|
||||
name: 'WeatherKit',
|
||||
capability: CapabilityType.WEATHER_KIT,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.on-demand-install-capable',
|
||||
name: 'On Demand Install Capable for App Clip Extensions',
|
||||
capability: CapabilityType.ON_DEMAND_INSTALL_EXTENSIONS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.scsicontroller',
|
||||
name: 'DriverKit Family SCSIController (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_SCSI_CONTROLLER_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.serial',
|
||||
name: 'DriverKit Family Serial (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_SERIAL_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.networking',
|
||||
name: 'DriverKit Family Networking (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_NETWORKING_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.hid.eventservice',
|
||||
name: 'DriverKit Family HID EventService (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_HID_EVENT_SERVICE_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.hid.device',
|
||||
name: 'DriverKit Family HID Device (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_HID_DEVICE_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit',
|
||||
name: 'DriverKit for Development',
|
||||
capability: CapabilityType.DRIVER_KIT_PUBLIC,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.transport.hid',
|
||||
name: 'DriverKit Transport HID (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_TRANSPORT_HID_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.driverkit.family.audio',
|
||||
name: 'DriverKit Family Audio (development)',
|
||||
capability: CapabilityType.DRIVER_KIT_FAMILY_AUDIO_PUB,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.shared-with-you',
|
||||
name: 'Shared with You',
|
||||
capability: CapabilityType.SHARED_WITH_YOU,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
|
||||
// These don't appear to have entitlements, so it's unclear how we can automatically enable / disable them at this time.
|
||||
// TODO: Maybe add a warning about manually enabling features?
|
||||
@@ -710,9 +534,24 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
// },
|
||||
// {
|
||||
// entitlement: '',
|
||||
// name: 'FileProvider TestingMode',
|
||||
// capability: 'FILEPROVIDER_TESTINGMODE',
|
||||
// },
|
||||
// {
|
||||
// entitlement: '',
|
||||
// name: 'HLS Interstitial Previews',
|
||||
// capability: 'HLS_INTERSTITIAL_PREVIEW',
|
||||
// },
|
||||
// {
|
||||
// entitlement: '',
|
||||
// name: 'Low Latency HLS',
|
||||
// capability: 'COREMEDIA_HLS_LOW_LATENCY',
|
||||
// },
|
||||
// {
|
||||
// entitlement: '',
|
||||
// name: 'MDM Managed Associated Domains',
|
||||
// capability: 'MDM_MANAGED_ASSOCIATED_DOMAINS',
|
||||
// },
|
||||
|
||||
// "Game Controllers" doesn't appear in Dev Portal but it does show up in Xcode,
|
||||
// toggling in Xcode causes no network request to be sent.
|
||||
|
||||
@@ -67,7 +67,7 @@ async function getRequiredContractMessagesAsync(
|
||||
// prompt the user to open an issue so we can address the new contract state if it ever appears.
|
||||
// TODO: Maybe a silent analytic would be better
|
||||
Log.error(
|
||||
`\nUnexpected Apple developer contract status "${status}". Open an issue on https://github.com/expo/eas-cli`
|
||||
`\nUnexpected Apple developer contract status "${status}". Please open an issue on https://github.com/expo/eas-cli`
|
||||
);
|
||||
Log.newLine();
|
||||
return { messages: (await getContractMessagesAsync(context)) ?? [], isFatal: false };
|
||||
|
||||
@@ -62,7 +62,7 @@ export async function ensureBundleIdExistsWithNameAsync(
|
||||
spinner.fail(
|
||||
`The bundle identifier ${chalk.bold(bundleIdentifier)} is not available to team "${
|
||||
authCtx.team.name
|
||||
}" (${authCtx.team.id}), change it in your app config and try again.`
|
||||
}" (${authCtx.team.id}), please change it in your app config and try again.`
|
||||
);
|
||||
} else {
|
||||
spinner.fail(`Failed to register bundle identifier ${chalk.dim(bundleIdentifier)}`);
|
||||
@@ -196,13 +196,13 @@ export async function ensureAppExistsAsync(
|
||||
} catch (error: any) {
|
||||
if (error.message.match(/An App ID with Identifier '(.*)' is not available/)) {
|
||||
throw new Error(
|
||||
`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${userAuthCtx.authState?.session.provider.name}. Change it in your app config and try again.\n`
|
||||
`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${userAuthCtx.authState?.session.provider.name}. Please change it in your app config and try again.\n`
|
||||
);
|
||||
}
|
||||
|
||||
spinner.fail(`Failed to create App Store app ${chalk.dim(name)}`);
|
||||
error.message +=
|
||||
'\nVisit https://appstoreconnect.apple.com and resolve any warnings, then try again.';
|
||||
'\nPlease visit https://appstoreconnect.apple.com and resolve any warnings, then try again.';
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -12,8 +12,8 @@ const { MaxKeysCreatedError } = Keys;
|
||||
|
||||
export const APPLE_KEYS_TOO_MANY_GENERATED_ERROR = `
|
||||
You can have only ${chalk.underline('two')} Apple Keys generated on your Apple Developer account.
|
||||
Revoke the old ones or reuse existing from your other apps.
|
||||
Remember that Apple Keys are not application specific!
|
||||
Please revoke the old ones or reuse existing from your other apps.
|
||||
Please remember that Apple Keys are not application specific!
|
||||
`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,10 @@ import assert from 'assert';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { GradleBuildContext, resolveGradleBuildContextAsync } from '../../project/android/gradle';
|
||||
import { getProjectAccountName, getProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
getProjectAccountName,
|
||||
promptToCreateProjectIfNotExistsAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { findAccountByName } from '../../user/Account';
|
||||
import { ensureActorHasUsername, ensureLoggedInAsync } from '../../user/actions';
|
||||
@@ -77,6 +80,12 @@ export class ManageAndroid {
|
||||
while (true) {
|
||||
try {
|
||||
if (ctx.hasProjectContext) {
|
||||
const maybeProjectId = await promptToCreateProjectIfNotExistsAsync(ctx.exp);
|
||||
if (!maybeProjectId) {
|
||||
throw new Error(
|
||||
'Your project must be registered with EAS in order to use the credentials manager.'
|
||||
);
|
||||
}
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx, gradleContext);
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
appLookupParams
|
||||
@@ -147,10 +156,12 @@ export class ManageAndroid {
|
||||
buildProfile: BuildProfile<Platform.ANDROID>
|
||||
): Promise<GradleBuildContext | undefined> {
|
||||
assert(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.');
|
||||
|
||||
// ensure the project exists on the EAS server
|
||||
await getProjectIdAsync(ctx.exp);
|
||||
|
||||
const maybeProjectId = await promptToCreateProjectIfNotExistsAsync(ctx.exp);
|
||||
if (!maybeProjectId) {
|
||||
throw new Error(
|
||||
'Your project must be registered with EAS in order to use the credentials manager.'
|
||||
);
|
||||
}
|
||||
return await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
|
||||
import { resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { getProjectAccountName, getProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
getProjectAccountName,
|
||||
promptToCreateProjectIfNotExistsAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { confirmAsync, promptAsync, selectAsync } from '../../prompts';
|
||||
import { Account, findAccountByName } from '../../user/Account';
|
||||
import { ensureActorHasUsername, ensureLoggedInAsync } from '../../user/actions';
|
||||
@@ -172,9 +175,12 @@ export class ManageIos {
|
||||
targets: Target[];
|
||||
}> {
|
||||
assert(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.');
|
||||
|
||||
// ensure the project exists on the EAS server
|
||||
await getProjectIdAsync(ctx.exp);
|
||||
const maybeProjectId = await promptToCreateProjectIfNotExistsAsync(ctx.exp);
|
||||
if (!maybeProjectId) {
|
||||
throw new Error(
|
||||
'Your project must be registered with EAS in order to use the credentials manager.'
|
||||
);
|
||||
}
|
||||
|
||||
const app = { account, projectName: ctx.exp.slug };
|
||||
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ export class SelectIosDistributionTypeGraphqlFromBuildProfile {
|
||||
|
||||
if (ctx.nonInteractive) {
|
||||
throw new Error(
|
||||
'Unable to determine type of internal distribution. Run this command in interactive mode.'
|
||||
'Unable to determine type of internal distribution. Please run this command in interactive mode.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { selectPlatformAsync } from '../../platform';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { ManageAndroid } from './ManageAndroid';
|
||||
import { ManageIos } from './ManageIos';
|
||||
|
||||
export class SelectPlatform {
|
||||
constructor(private readonly flagPlatform?: string) {}
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const platform = await selectPlatformAsync(this.flagPlatform);
|
||||
const { platform } = await promptAsync({
|
||||
type: 'select',
|
||||
name: 'platform',
|
||||
message: 'Select platform',
|
||||
choices: [
|
||||
{ value: 'android', title: 'Android' },
|
||||
{ value: 'ios', title: 'iOS' },
|
||||
],
|
||||
});
|
||||
|
||||
if (platform === 'ios') {
|
||||
return await new ManageIos(new SelectPlatform(platform), process.cwd()).runAsync();
|
||||
return await new ManageIos(new SelectPlatform(), process.cwd()).runAsync();
|
||||
}
|
||||
return await new ManageAndroid(new SelectPlatform(platform), process.cwd()).runAsync();
|
||||
return await new ManageAndroid(new SelectPlatform(), process.cwd()).runAsync();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ const EXPERT_PROMPT = (): void => {
|
||||
}
|
||||
Log.warn(`
|
||||
In this mode, we won't be able to make sure that your credentials are valid.
|
||||
Double check that you're uploading valid files for your app otherwise you may encounter strange errors!
|
||||
Please double check that you're uploading valid files for your app otherwise you may encounter strange errors!
|
||||
When building for IOS make sure you've created your App ID on the Apple Developer Portal, that your App ID
|
||||
is in app.json as \`bundleIdentifier\`, and that the provisioning profile you
|
||||
upload matches that Team ID and App ID.
|
||||
|
||||
@@ -18,7 +18,7 @@ Internal distribution means that you won't need upload your app archive to App S
|
||||
Your app archive (.ipa) will be installable on your equipment as long as you sign your application with an adhoc provisiong profile.
|
||||
The provisioning profile needs to contain the UDIDs (unique identifiers) of your iPhones and iPads.
|
||||
|
||||
First of all, choose the Expo account under which you want to register your devices.
|
||||
First of all, please choose the Expo account under which you want to register your devices.
|
||||
Later, authenticate with Apple and choose your desired Apple Team (if your Apple ID has access to multiple teams).`;
|
||||
|
||||
export default class DeviceManager {
|
||||
|
||||
@@ -55,7 +55,7 @@ export const graphqlClient = createUrqlClient({
|
||||
},
|
||||
}) as StricterClient;
|
||||
|
||||
/* Specify additionalTypenames in your Graphql queries */
|
||||
/* Please specify additionalTypenames in your Graphql queries */
|
||||
export interface StricterClient extends Client {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
query<Data = any, Variables extends object = {}>(
|
||||
@@ -70,7 +70,7 @@ export async function withErrorHandlingAsync<T>(promise: Promise<OperationResult
|
||||
|
||||
if (error) {
|
||||
if (error.graphQLErrors.some(e => e?.extensions?.isTransient)) {
|
||||
Log.error(`We've encountered a transient error. Try again shortly.`);
|
||||
Log.error(`We've encountered a transient error, please try again shortly.`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
+24
-232
@@ -17,9 +17,13 @@ export type Scalars = {
|
||||
Boolean: boolean;
|
||||
Int: number;
|
||||
Float: number;
|
||||
/** Date custom scalar type */
|
||||
DateTime: any;
|
||||
/** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSON: any;
|
||||
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSONObject: any;
|
||||
/** The `Upload` scalar type represents a file upload. */
|
||||
Upload: any;
|
||||
};
|
||||
|
||||
@@ -105,8 +109,6 @@ export type Account = {
|
||||
createdAt: Scalars['DateTime'];
|
||||
/** Environment secrets for an account */
|
||||
environmentSecrets: Array<EnvironmentSecret>;
|
||||
/** GitHub App installations for an account */
|
||||
githubAppInstallations: Array<GitHubAppInstallation>;
|
||||
/** Android credentials for account */
|
||||
googleServiceAccountKeys: Array<GoogleServiceAccountKey>;
|
||||
id: Scalars['ID'];
|
||||
@@ -280,8 +282,8 @@ export type AccountMutation = {
|
||||
grantActorPermissions: Account;
|
||||
/** Rename this account and the primary user's username if this account is a personal account */
|
||||
rename: Account;
|
||||
/** Requests a refund for the specified charge by requesting a manual refund from support */
|
||||
requestRefund?: Maybe<Scalars['Boolean']>;
|
||||
/** Requests a refund for the specified charge. Returns true if auto-refund was possible, otherwise requests a manual refund from support and returns false. */
|
||||
requestRefund: Scalars['Boolean'];
|
||||
/** Revoke specified Permissions for Actor. Actor must already have at least one permission on the account. */
|
||||
revokeActorPermissions: Account;
|
||||
/**
|
||||
@@ -803,23 +805,9 @@ export type AndroidJobKeystoreInput = {
|
||||
keystorePassword: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AndroidJobOverridesInput = {
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildType?: InputMaybe<AndroidBuildType>;
|
||||
builderEnvironment?: InputMaybe<AndroidBuilderEnvironmentInput>;
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']>;
|
||||
experimental?: InputMaybe<Scalars['JSONObject']>;
|
||||
gradleCommand?: InputMaybe<Scalars['String']>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']>;
|
||||
secrets?: InputMaybe<AndroidJobSecretsInput>;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
version?: InputMaybe<AndroidJobVersionInput>;
|
||||
};
|
||||
|
||||
export type AndroidJobSecretsInput = {
|
||||
buildCredentials?: InputMaybe<AndroidJobBuildCredentialsInput>;
|
||||
environmentSecrets?: InputMaybe<Scalars['JSONObject']>;
|
||||
};
|
||||
|
||||
export type AndroidJobVersionInput = {
|
||||
@@ -1946,15 +1934,8 @@ export type BuildMutation = {
|
||||
createIosBuild: CreateBuildResult;
|
||||
/** Delete an EAS Build build */
|
||||
deleteBuild: Build;
|
||||
/** Retry an Android EAS Build */
|
||||
retryAndroidBuild: Build;
|
||||
/**
|
||||
* Retry an EAS Build build
|
||||
* @deprecated Use retryAndroidBuild and retryIosBuild instead
|
||||
*/
|
||||
/** Retry an EAS Build build */
|
||||
retryBuild: Build;
|
||||
/** Retry an iOS EAS Build */
|
||||
retryIosBuild: Build;
|
||||
};
|
||||
|
||||
|
||||
@@ -1984,22 +1965,10 @@ export type BuildMutationDeleteBuildArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationRetryAndroidBuildArgs = {
|
||||
buildId: Scalars['ID'];
|
||||
jobOverrides?: InputMaybe<AndroidJobOverridesInput>;
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationRetryBuildArgs = {
|
||||
buildId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationRetryIosBuildArgs = {
|
||||
buildId: Scalars['ID'];
|
||||
jobOverrides?: InputMaybe<IosJobOverridesInput>;
|
||||
};
|
||||
|
||||
export type BuildOrBuildJob = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
@@ -2199,11 +2168,6 @@ export type CreateEnvironmentSecretInput = {
|
||||
value: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CreateGitHubAppInstallationInput = {
|
||||
accountId: Scalars['ID'];
|
||||
installationIdentifier: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type CreateIosSubmissionInput = {
|
||||
appId: Scalars['ID'];
|
||||
archiveUrl?: InputMaybe<Scalars['String']>;
|
||||
@@ -2442,38 +2406,6 @@ export type GetSignedAssetUploadSpecificationsResult = {
|
||||
specifications: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type GitHubAppInstallation = {
|
||||
__typename?: 'GitHubAppInstallation';
|
||||
account: Account;
|
||||
actor?: Maybe<Actor>;
|
||||
id: Scalars['ID'];
|
||||
installationIdentifier: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type GitHubAppInstallationMutation = {
|
||||
__typename?: 'GitHubAppInstallationMutation';
|
||||
/** Create a GitHub App installation for an Account */
|
||||
createGitHubAppInstallationForAccount: GitHubAppInstallation;
|
||||
/** Delete a GitHub App installation by ID */
|
||||
deleteGitHubAppInstallation: GitHubAppInstallation;
|
||||
};
|
||||
|
||||
|
||||
export type GitHubAppInstallationMutationCreateGitHubAppInstallationForAccountArgs = {
|
||||
githubAppInstallationData: CreateGitHubAppInstallationInput;
|
||||
};
|
||||
|
||||
|
||||
export type GitHubAppInstallationMutationDeleteGitHubAppInstallationArgs = {
|
||||
githubAppInstallationId: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type GitHubAppQuery = {
|
||||
__typename?: 'GitHubAppQuery';
|
||||
appIdentifier: Scalars['String'];
|
||||
clientIdentifier: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GoogleServiceAccountKey = {
|
||||
__typename?: 'GoogleServiceAccountKey';
|
||||
account: Account;
|
||||
@@ -2744,28 +2676,9 @@ export type IosJobInput = {
|
||||
version?: InputMaybe<IosJobVersionInput>;
|
||||
};
|
||||
|
||||
export type IosJobOverridesInput = {
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildConfiguration?: InputMaybe<Scalars['String']>;
|
||||
/** @deprecated */
|
||||
buildType?: InputMaybe<IosBuildType>;
|
||||
builderEnvironment?: InputMaybe<IosBuilderEnvironmentInput>;
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']>;
|
||||
/** @deprecated */
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
experimental?: InputMaybe<Scalars['JSONObject']>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']>;
|
||||
scheme?: InputMaybe<Scalars['String']>;
|
||||
secrets?: InputMaybe<IosJobSecretsInput>;
|
||||
simulator?: InputMaybe<Scalars['Boolean']>;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
version?: InputMaybe<IosJobVersionInput>;
|
||||
};
|
||||
|
||||
export type IosJobSecretsInput = {
|
||||
buildCredentials?: InputMaybe<Array<InputMaybe<IosJobTargetCredentialsInput>>>;
|
||||
environmentSecrets?: InputMaybe<Scalars['JSONObject']>;
|
||||
};
|
||||
|
||||
export type IosJobTargetCredentialsInput = {
|
||||
@@ -3219,8 +3132,6 @@ export type RootMutation = {
|
||||
emailSubscription: EmailSubscriptionMutation;
|
||||
/** Mutations that create and delete EnvironmentSecrets */
|
||||
environmentSecret: EnvironmentSecretMutation;
|
||||
/** Mutations for GitHub App installations */
|
||||
githubAppInstallation: GitHubAppInstallationMutation;
|
||||
/** Mutations that modify a Google Service Account Key */
|
||||
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
|
||||
/** Mutations that modify the build credentials for an iOS app */
|
||||
@@ -3301,8 +3212,6 @@ export type RootQuery = {
|
||||
clientBuilds: ClientBuildQuery;
|
||||
/** Top-level query object for querying Experimentation configuration. */
|
||||
experimentation: ExperimentationQuery;
|
||||
/** Top-level query object for querying GitHub App information and resources it has access to. */
|
||||
githubApp: GitHubAppQuery;
|
||||
/** Top-level query object for querying Stripe Invoices. */
|
||||
invoice: InvoiceQuery;
|
||||
/**
|
||||
@@ -3317,8 +3226,6 @@ export type RootQuery = {
|
||||
meActor?: Maybe<Actor>;
|
||||
project: ProjectQuery;
|
||||
snack: SnackQuery;
|
||||
/** Top-level query object for querying Expo status page services. */
|
||||
statuspageService: StatuspageServiceQuery;
|
||||
submissions: SubmissionQuery;
|
||||
/** fetch all updates in a group */
|
||||
updatesByGroup: Array<Update>;
|
||||
@@ -3478,110 +3385,6 @@ export enum StandardOffer {
|
||||
YearlySub = 'YEARLY_SUB'
|
||||
}
|
||||
|
||||
/** Incident for a given component from Expo status page API. */
|
||||
export type StatuspageIncident = {
|
||||
__typename?: 'StatuspageIncident';
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
/** Impact of an incident from Expo status page. */
|
||||
impact: StatuspageIncidentImpact;
|
||||
name: Scalars['String'];
|
||||
resolvedAt?: Maybe<Scalars['DateTime']>;
|
||||
/** Shortlink to the incident from Expo status page. */
|
||||
shortlink: Scalars['String'];
|
||||
/** Current status of an incident from Expo status page. */
|
||||
status: StatuspageIncidentStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
/** List of all updates for an incident from Expo status page. */
|
||||
updates: Array<StatuspageIncidentUpdate>;
|
||||
};
|
||||
|
||||
/** Possible Incident impact values from Expo status page API. */
|
||||
export enum StatuspageIncidentImpact {
|
||||
Critical = 'CRITICAL',
|
||||
Maintenance = 'MAINTENANCE',
|
||||
Major = 'MAJOR',
|
||||
Minor = 'MINOR',
|
||||
None = 'NONE'
|
||||
}
|
||||
|
||||
/** Possible Incident statuses from Expo status page API. */
|
||||
export enum StatuspageIncidentStatus {
|
||||
Completed = 'COMPLETED',
|
||||
Identified = 'IDENTIFIED',
|
||||
Investigating = 'INVESTIGATING',
|
||||
InProgress = 'IN_PROGRESS',
|
||||
Monitoring = 'MONITORING',
|
||||
Resolved = 'RESOLVED',
|
||||
Scheduled = 'SCHEDULED',
|
||||
Verifying = 'VERIFYING'
|
||||
}
|
||||
|
||||
/** Update for an Incident from Expo status page API. */
|
||||
export type StatuspageIncidentUpdate = {
|
||||
__typename?: 'StatuspageIncidentUpdate';
|
||||
/** Text of an update from Expo status page. */
|
||||
body: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
/** Status set at the moment of update. */
|
||||
status: StatuspageIncidentStatus;
|
||||
};
|
||||
|
||||
/** Service monitored by Expo status page. */
|
||||
export type StatuspageService = {
|
||||
__typename?: 'StatuspageService';
|
||||
/** Description of a service from Expo status page. */
|
||||
description?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
/**
|
||||
* List of last inicdents for a service from Expo status page (we always query for 50 latest incidents for all services)
|
||||
* sorted by createdAt field in descending order.
|
||||
*/
|
||||
incidents: Array<StatuspageIncident>;
|
||||
/** Name of a service monitored by Expo status page. */
|
||||
name: StatuspageServiceName;
|
||||
/** Current status of a service from Expo status page. */
|
||||
status: StatuspageServiceStatus;
|
||||
};
|
||||
|
||||
/** Name of a service monitored by Expo status page. */
|
||||
export enum StatuspageServiceName {
|
||||
EasBuild = 'EAS_BUILD',
|
||||
EasSubmit = 'EAS_SUBMIT',
|
||||
EasUpdate = 'EAS_UPDATE'
|
||||
}
|
||||
|
||||
export type StatuspageServiceQuery = {
|
||||
__typename?: 'StatuspageServiceQuery';
|
||||
/** Query services from Expo status page by names. */
|
||||
byServiceNames: Array<StatuspageService>;
|
||||
};
|
||||
|
||||
|
||||
export type StatuspageServiceQueryByServiceNamesArgs = {
|
||||
serviceNames: Array<StatuspageServiceName>;
|
||||
};
|
||||
|
||||
/** Possible statuses for a service. */
|
||||
export enum StatuspageServiceStatus {
|
||||
DegradedPerformance = 'DEGRADED_PERFORMANCE',
|
||||
MajorOutage = 'MAJOR_OUTAGE',
|
||||
Operational = 'OPERATIONAL',
|
||||
PartialOutage = 'PARTIAL_OUTAGE',
|
||||
UnderMaintenance = 'UNDER_MAINTENANCE'
|
||||
}
|
||||
|
||||
export type StripeCoupon = {
|
||||
__typename?: 'StripeCoupon';
|
||||
amountOff?: Maybe<Scalars['String']>;
|
||||
appliesTo?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
percentOff?: Maybe<Scalars['Float']>;
|
||||
valid: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
/** Represents an EAS Submission */
|
||||
export type Submission = ActivityTimelineProjectActivity & {
|
||||
__typename?: 'Submission';
|
||||
@@ -3696,7 +3499,6 @@ export type SubscriptionDetails = {
|
||||
addons: Array<AddonDetails>;
|
||||
cancelledAt?: Maybe<Scalars['DateTime']>;
|
||||
concurrencies?: Maybe<Concurrencies>;
|
||||
coupon?: Maybe<StripeCoupon>;
|
||||
endedAt?: Maybe<Scalars['DateTime']>;
|
||||
futureSubscription?: Maybe<FutureSubscription>;
|
||||
id: Scalars['ID'];
|
||||
@@ -4619,6 +4421,13 @@ export type SetAppStoreConnectApiKeyForSubmissionsMutationVariables = Exact<{
|
||||
|
||||
export type SetAppStoreConnectApiKeyForSubmissionsMutation = { __typename?: 'RootMutation', iosAppCredentials: { __typename?: 'IosAppCredentialsMutation', setAppStoreConnectApiKeyForSubmissions: { __typename?: 'IosAppCredentials', id: string, iosAppBuildCredentialsList: Array<{ __typename?: 'IosAppBuildCredentials', id: string, iosDistributionType: IosDistributionType, distributionCertificate?: { __typename?: 'AppleDistributionCertificate', id: string, certificateP12?: string | null, certificatePassword?: string | null, serialNumber: string, developerPortalIdentifier?: string | null, validityNotBefore: any, validityNotAfter: any, updatedAt: any, appleTeam?: { __typename?: 'AppleTeam', id: string, appleTeamIdentifier: string, appleTeamName?: string | null } | null, iosAppBuildCredentialsList: Array<{ __typename?: 'IosAppBuildCredentials', id: string, iosAppCredentials: { __typename?: 'IosAppCredentials', id: string, app: { __typename?: 'App', id: string, fullName: string, slug: string }, appleAppIdentifier: { __typename?: 'AppleAppIdentifier', id: string, bundleIdentifier: string } }, provisioningProfile?: { __typename?: 'AppleProvisioningProfile', id: string, developerPortalIdentifier?: string | null } | null }> } | null, provisioningProfile?: { __typename?: 'AppleProvisioningProfile', id: string, expiration: any, developerPortalIdentifier?: string | null, provisioningProfile?: string | null, updatedAt: any, status: string, appleTeam?: { __typename?: 'AppleTeam', id: string, appleTeamIdentifier: string, appleTeamName?: string | null } | null, appleDevices: Array<{ __typename?: 'AppleDevice', id: string, identifier: string, name?: string | null, model?: string | null, deviceClass?: AppleDeviceClass | null }> } | null }>, app: { __typename?: 'App', id: string, fullName: string, slug: string }, appleTeam?: { __typename?: 'AppleTeam', id: string, appleTeamIdentifier: string, appleTeamName?: string | null } | null, appleAppIdentifier: { __typename?: 'AppleAppIdentifier', id: string, bundleIdentifier: string }, pushKey?: { __typename?: 'ApplePushKey', id: string, keyIdentifier: string, updatedAt: any, appleTeam?: { __typename?: 'AppleTeam', id: string, appleTeamIdentifier: string, appleTeamName?: string | null } | null, iosAppCredentialsList: Array<{ __typename?: 'IosAppCredentials', id: string, app: { __typename?: 'App', id: string, fullName: string, slug: string }, appleAppIdentifier: { __typename?: 'AppleAppIdentifier', id: string, bundleIdentifier: string } }> } | null, appStoreConnectApiKeyForSubmissions?: { __typename?: 'AppStoreConnectApiKey', id: string, issuerIdentifier: string, keyIdentifier: string, name?: string | null, roles?: Array<AppStoreConnectUserRole> | null, createdAt: any, updatedAt: any, appleTeam?: { __typename?: 'AppleTeam', id: string, appleTeamIdentifier: string, appleTeamName?: string | null } | null } | null } } };
|
||||
|
||||
export type AppByFullNameQueryVariables = Exact<{
|
||||
fullName: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type AppByFullNameQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byFullName: { __typename?: 'App', id: string, fullName: string, slug: string } } };
|
||||
|
||||
export type AppStoreConnectApiKeyByAccountQueryVariables = Exact<{
|
||||
accountName: Scalars['String'];
|
||||
}>;
|
||||
@@ -4863,13 +4672,6 @@ export type DeleteWebhookMutationVariables = Exact<{
|
||||
|
||||
export type DeleteWebhookMutation = { __typename?: 'RootMutation', webhook: { __typename?: 'WebhookMutation', deleteWebhook: { __typename?: 'DeleteWebhookResult', id: string } } };
|
||||
|
||||
export type AppByFullNameQueryVariables = Exact<{
|
||||
fullName: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type AppByFullNameQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byFullName: { __typename?: 'App', id: string, fullName: string, slug: string } } };
|
||||
|
||||
export type LatestAppVersionQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
platform: AppPlatform;
|
||||
@@ -4903,13 +4705,6 @@ export type BuildsByIdQueryVariables = Exact<{
|
||||
|
||||
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', 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 BuildsWithSubmissionsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', 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 GetAllBuildsForAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
offset: Scalars['Int'];
|
||||
@@ -4942,6 +4737,14 @@ export type EnvironmentSecretsByAppIdQueryVariables = Exact<{
|
||||
|
||||
export type EnvironmentSecretsByAppIdQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, environmentSecrets: Array<{ __typename?: 'EnvironmentSecret', id: string, name: string, createdAt: any }> } } };
|
||||
|
||||
export type ProjectByUsernameAndSlugQueryVariables = Exact<{
|
||||
username: Scalars['String'];
|
||||
slug: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ProjectByUsernameAndSlugQuery = { __typename?: 'RootQuery', project: { __typename?: 'ProjectQuery', byUsernameAndSlug: { __typename?: 'App', id: string } | { __typename?: 'Snack', id: string } } };
|
||||
|
||||
export type GetAssetMetadataQueryVariables = Exact<{
|
||||
storageKeys: Array<Scalars['String']> | Scalars['String'];
|
||||
}>;
|
||||
@@ -4956,13 +4759,6 @@ export type GetAssetLimitPerUpdateGroupForAppQueryVariables = Exact<{
|
||||
|
||||
export type GetAssetLimitPerUpdateGroupForAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, assetLimitPerUpdateGroup: number } } };
|
||||
|
||||
export type StatuspageServiceByServiceNamesQueryVariables = Exact<{
|
||||
serviceNames: Array<StatuspageServiceName> | StatuspageServiceName;
|
||||
}>;
|
||||
|
||||
|
||||
export type StatuspageServiceByServiceNamesQuery = { __typename?: 'RootQuery', statuspageService: { __typename?: 'StatuspageServiceQuery', byServiceNames: Array<{ __typename?: 'StatuspageService', id: string, name: StatuspageServiceName, status: StatuspageServiceStatus, incidents: Array<{ __typename?: 'StatuspageIncident', id: string, status: StatuspageIncidentStatus, name: string, impact: StatuspageIncidentImpact, shortlink: string }> }> } };
|
||||
|
||||
export type SubmissionsByIdQueryVariables = Exact<{
|
||||
submissionId: Scalars['ID'];
|
||||
}>;
|
||||
@@ -5024,12 +4820,8 @@ export type AppFragment = { __typename?: 'App', id: string, fullName: string, sl
|
||||
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', 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, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', 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 EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, createdAt: any };
|
||||
|
||||
export type StatuspageServiceFragment = { __typename?: 'StatuspageService', id: string, name: StatuspageServiceName, status: StatuspageServiceStatus, incidents: Array<{ __typename?: 'StatuspageIncident', id: string, status: StatuspageIncidentStatus, name: string, impact: StatuspageIncidentImpact, shortlink: string }> };
|
||||
|
||||
export type SubmissionFragment = { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null };
|
||||
|
||||
export type UpdateBranchFragment = { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, createdAt: any, message?: string | null, runtimeVersion: string, group: string, platform: string, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'User', username: string, id: string } | null }> };
|
||||
|
||||
@@ -6,16 +6,13 @@ import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildStatus,
|
||||
BuildWithSubmissionsFragment,
|
||||
BuildsByIdQuery,
|
||||
BuildsByIdQueryVariables,
|
||||
BuildsWithSubmissionsByIdQuery,
|
||||
BuildsWithSubmissionsByIdQueryVariables,
|
||||
DistributionType,
|
||||
GetAllBuildsForAppQuery,
|
||||
GetAllBuildsForAppQueryVariables,
|
||||
} from '../generated';
|
||||
import { BuildFragmentNode, BuildFragmentWithSubmissionsNode } from '../types/Build';
|
||||
import { BuildFragmentNode } from '../types/Build';
|
||||
|
||||
type BuildsQuery = {
|
||||
offset?: number;
|
||||
@@ -66,35 +63,6 @@ export const BuildQuery = {
|
||||
return data.builds.byId;
|
||||
},
|
||||
|
||||
async withSubmissionsByIdAsync(
|
||||
buildId: string,
|
||||
{ useCache = true }: { useCache?: boolean } = {}
|
||||
): Promise<BuildWithSubmissionsFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<BuildsWithSubmissionsByIdQuery, BuildsWithSubmissionsByIdQueryVariables>(
|
||||
gql`
|
||||
query BuildsWithSubmissionsByIdQuery($buildId: ID!) {
|
||||
builds {
|
||||
byId(buildId: $buildId) {
|
||||
id
|
||||
...BuildWithSubmissionsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(BuildFragmentWithSubmissionsNode)}
|
||||
`,
|
||||
{ buildId },
|
||||
{
|
||||
requestPolicy: useCache ? 'cache-first' : 'network-only',
|
||||
additionalTypenames: ['Build'],
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.builds.byId;
|
||||
},
|
||||
async allForAppAsync(
|
||||
appId: string,
|
||||
{ limit = 10, offset = 0, filter }: BuildsQuery
|
||||
|
||||
@@ -75,11 +75,11 @@ export const EnvironmentSecretsQuery = {
|
||||
},
|
||||
async allAsync(
|
||||
projectAccountName: string,
|
||||
projectId: string
|
||||
projectFullName: string
|
||||
): Promise<EnvironmentSecretWithScope[]> {
|
||||
const [accountSecrets, appSecrets] = await Promise.all([
|
||||
this.byAccountNameAsync(projectAccountName),
|
||||
this.byAppIdAsync(projectId),
|
||||
this.byAppIdAsync(projectFullName),
|
||||
]);
|
||||
|
||||
return [
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import { Project, ProjectByUsernameAndSlugQuery } from '../generated';
|
||||
|
||||
type ProjectQueryResult = Pick<Project, 'id'>;
|
||||
|
||||
export const ProjectQuery = {
|
||||
async byUsernameAndSlugAsync(username: string, slug: string): Promise<ProjectQueryResult> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<ProjectByUsernameAndSlugQuery>(
|
||||
gql`
|
||||
query ProjectByUsernameAndSlugQuery($username: String!, $slug: String!) {
|
||||
project {
|
||||
byUsernameAndSlug(username: $username, slug: $slug) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ username, slug },
|
||||
{
|
||||
additionalTypenames: ['App', 'Snack'] /* typenames that use the Project type*/,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.project.byUsernameAndSlug;
|
||||
},
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
StatuspageServiceByServiceNamesQuery,
|
||||
StatuspageServiceByServiceNamesQueryVariables,
|
||||
StatuspageServiceFragment,
|
||||
StatuspageServiceName,
|
||||
} from '../generated';
|
||||
import { StatuspageServiceFragmentNode } from '../types/StatuspageService';
|
||||
|
||||
export const StatuspageServiceQuery = {
|
||||
async statuspageServicesAsync(
|
||||
serviceNames: StatuspageServiceName[]
|
||||
): Promise<StatuspageServiceFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<StatuspageServiceByServiceNamesQuery, StatuspageServiceByServiceNamesQueryVariables>(
|
||||
gql`
|
||||
query StatuspageServiceByServiceNamesQuery($serviceNames: [StatuspageServiceName!]!) {
|
||||
statuspageService {
|
||||
byServiceNames(serviceNames: $serviceNames) {
|
||||
id
|
||||
...StatuspageServiceFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(StatuspageServiceFragmentNode)}
|
||||
`,
|
||||
{ serviceNames },
|
||||
{
|
||||
additionalTypenames: ['StatuspageService', 'StatuspageIncident'],
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.statuspageService.byServiceNames;
|
||||
},
|
||||
};
|
||||
@@ -1,8 +1,5 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { SubmissionFragmentNode } from './Submission';
|
||||
|
||||
export const BuildFragmentNode = gql`
|
||||
fragment BuildFragment on Build {
|
||||
id
|
||||
@@ -52,17 +49,3 @@ export const BuildFragmentNode = gql`
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
|
||||
export const BuildFragmentWithSubmissionsNode = gql`
|
||||
${print(SubmissionFragmentNode)}
|
||||
${print(BuildFragmentNode)}
|
||||
|
||||
fragment BuildWithSubmissionsFragment on Build {
|
||||
id
|
||||
...BuildFragment
|
||||
submissions {
|
||||
id
|
||||
...SubmissionFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,16 +0,0 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const StatuspageServiceFragmentNode = gql`
|
||||
fragment StatuspageServiceFragment on StatuspageService {
|
||||
id
|
||||
name
|
||||
status
|
||||
incidents {
|
||||
id
|
||||
status
|
||||
name
|
||||
impact
|
||||
shortlink
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1,128 +0,0 @@
|
||||
import path from 'path';
|
||||
|
||||
import { getStaticConfigFilePath, loadConfigAsync } from '../config';
|
||||
import { MetadataValidationError } from '../errors';
|
||||
|
||||
describe(getStaticConfigFilePath, () => {
|
||||
const projectDir = '/app';
|
||||
|
||||
it(`returns same file for store.config.json`, () => {
|
||||
const metadataPath = 'store.config.json';
|
||||
const expectedFile = path.join(projectDir, metadataPath);
|
||||
|
||||
expect(getStaticConfigFilePath({ projectDir, metadataPath })).toBe(expectedFile);
|
||||
});
|
||||
|
||||
it(`returns store.config.json for store.config.js`, () => {
|
||||
const metadataPath = 'store.config.js';
|
||||
const expectedFile = path.join(projectDir, 'store.config.json');
|
||||
|
||||
expect(getStaticConfigFilePath({ projectDir, metadataPath })).toBe(expectedFile);
|
||||
});
|
||||
|
||||
it(`returns store.staging.json file for store.staging.js`, () => {
|
||||
const metadataPath = 'store.staging.js';
|
||||
const expectedFile = path.join(projectDir, 'store.staging.json');
|
||||
|
||||
expect(getStaticConfigFilePath({ projectDir, metadataPath })).toBe(expectedFile);
|
||||
});
|
||||
|
||||
// This shouldn't be used IRL, but it tests if this function is working properly
|
||||
it(`returns custom-name.json file for custom-name.js`, () => {
|
||||
const metadataPath = 'custom-name.js';
|
||||
const expectedFile = path.join(projectDir, 'custom-name.json');
|
||||
|
||||
expect(getStaticConfigFilePath({ projectDir, metadataPath })).toBe(expectedFile);
|
||||
});
|
||||
});
|
||||
|
||||
describe(loadConfigAsync, () => {
|
||||
const projectDir = path.resolve(__dirname, 'fixtures');
|
||||
|
||||
it(`throws when file doesn't exist`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'doesnt-exist.json' })
|
||||
).rejects.toThrow('file not found');
|
||||
});
|
||||
|
||||
it(`throws when validation errors are found without skipping validation`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid.config.json' })
|
||||
).rejects.toThrow('errors found');
|
||||
});
|
||||
|
||||
it(`returns config from "store.config.json"`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'store.config.json' })
|
||||
).resolves.toHaveProperty('apple.copyright', 'ACME');
|
||||
});
|
||||
|
||||
it(`returns config from "store.config.js"`, async () => {
|
||||
const year = new Date().getFullYear();
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'store.config.js' })
|
||||
).resolves.toHaveProperty('apple.copyright', `${year} ACME`);
|
||||
});
|
||||
|
||||
it(`returns config from "store.function.js"`, async () => {
|
||||
const year = new Date().getFullYear();
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'store.function.js' })
|
||||
).resolves.toHaveProperty('apple.copyright', `${year} ACME`);
|
||||
});
|
||||
|
||||
it(`returns config from "store.async.js"`, async () => {
|
||||
const year = new Date().getFullYear();
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'store.async.js' })
|
||||
).resolves.toHaveProperty('apple.copyright', `${year} ACME`);
|
||||
});
|
||||
|
||||
it(`returns invalid config from "invalid.config.json`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid.config.json', skipValidation: true })
|
||||
).resolves.toMatchObject({ configVersion: -1 });
|
||||
});
|
||||
|
||||
it(`returns invalid config from "invalid.config.js`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid.config.js', skipValidation: true })
|
||||
).resolves.toMatchObject({ configVersion: -1 });
|
||||
});
|
||||
|
||||
it(`returns invalid config from "invalid.function.js`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid.function.js', skipValidation: true })
|
||||
).resolves.toMatchObject({ configVersion: -1 });
|
||||
});
|
||||
|
||||
it(`returns invalid config from "invalid.async.js`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid.async.js', skipValidation: true })
|
||||
).resolves.toMatchObject({ configVersion: -1 });
|
||||
});
|
||||
|
||||
it(`throws invalid config type from "invalid-type.config.json"`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid-type.config.json' })
|
||||
).rejects.toThrow(MetadataValidationError);
|
||||
});
|
||||
|
||||
it(`throws invalid config type from "invalid-type.config.js"`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid-type.config.js' })
|
||||
).rejects.toThrow(MetadataValidationError);
|
||||
});
|
||||
|
||||
it(`throws invalid config type from "invalid-type.function.js"`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid-type.function.js' })
|
||||
).rejects.toThrow(MetadataValidationError);
|
||||
});
|
||||
|
||||
it(`throws invalid config type from "invalid-type.async.js"`, async () => {
|
||||
await expect(
|
||||
loadConfigAsync({ projectDir, metadataPath: 'invalid-type.async.js' })
|
||||
).rejects.toThrow(MetadataValidationError);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = async function () {
|
||||
return await Promise.resolve(null);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = 1337;
|
||||
@@ -1,5 +0,0 @@
|
||||
[
|
||||
{
|
||||
"configVersion": -1
|
||||
}
|
||||
]
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function () {
|
||||
return true;
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = async function () {
|
||||
return await Promise.resolve({ configVersion: -1 });
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
configVersion: -1,
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"configVersion": -1
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function () {
|
||||
return { configVersion: -1 };
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = async function () {
|
||||
const config = require('./store.config.json');
|
||||
config.apple.copyright = `${new Date().getFullYear()} ACME`;
|
||||
return await Promise.resolve(config);
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
const config = require('./store.config.json');
|
||||
|
||||
config.apple.copyright = `${new Date().getFullYear()} ACME`;
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"configVersion": 0,
|
||||
"apple": {
|
||||
"copyright": "ACME",
|
||||
"info": {
|
||||
"en-US": {
|
||||
"title": "App title",
|
||||
"subtitle": "Subtitle for your app",
|
||||
"description": "A longer description of what your app does",
|
||||
"keywords": [
|
||||
"keyword"
|
||||
],
|
||||
"releaseNotes": "Bug fixes and improved stability",
|
||||
"promoText": "Short tagline for your app",
|
||||
"marketingUrl": "https://example.com/en/marketing",
|
||||
"supportUrl": "https://example.com/en/support",
|
||||
"privacyPolicyUrl": "https://example.com/en/privacy",
|
||||
"privacyChoicesUrl": "https://example.com/en/choices"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = function () {
|
||||
const config = require('./store.config.json');
|
||||
config.apple.copyright = `${new Date().getFullYear()} ACME`;
|
||||
return config;
|
||||
};
|
||||
@@ -1,12 +1,9 @@
|
||||
import Ajv from 'ajv';
|
||||
import assert from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { AppleConfigReader } from './apple/config/reader';
|
||||
import { AppleConfigWriter } from './apple/config/writer';
|
||||
import { AppleMetadata } from './apple/types';
|
||||
import { MetadataValidationError } from './errors';
|
||||
|
||||
export interface MetadataConfig {
|
||||
/** The store configuration version */
|
||||
@@ -15,68 +12,6 @@ export interface MetadataConfig {
|
||||
apple?: AppleMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the dynamic config from the user.
|
||||
* It supports methods, async methods, or objects (json).
|
||||
*/
|
||||
async function resolveDynamicConfigAsync(configFile: string): Promise<unknown> {
|
||||
const userConfigOrFunction = await import(configFile).then(file => file.default ?? file);
|
||||
|
||||
return typeof userConfigOrFunction === 'function'
|
||||
? await userConfigOrFunction()
|
||||
: userConfigOrFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the static configuration file path, based on the metadata context.
|
||||
* This uses any custom name provided, but swaps out the extension for `.json`.
|
||||
*/
|
||||
export function getStaticConfigFilePath({
|
||||
projectDir,
|
||||
metadataPath,
|
||||
}: {
|
||||
projectDir: string;
|
||||
metadataPath: string;
|
||||
}): string {
|
||||
const configFile = path.join(projectDir, metadataPath);
|
||||
const configExtension = path.extname(configFile);
|
||||
|
||||
return path.join(projectDir, `${path.basename(configFile, configExtension)}.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the store configuration from a metadata context.
|
||||
* This can load `.json` and `.js` config files, using `require`.
|
||||
* It throws MetadataValidationErrors when the file doesn't exist, or contains errors.
|
||||
* The user is prompted to try anyway when errors are found.
|
||||
*/
|
||||
export async function loadConfigAsync({
|
||||
projectDir,
|
||||
metadataPath,
|
||||
skipValidation = false,
|
||||
}: {
|
||||
projectDir: string;
|
||||
metadataPath: string;
|
||||
skipValidation?: boolean;
|
||||
}): Promise<MetadataConfig> {
|
||||
const configFile = path.join(projectDir, metadataPath);
|
||||
if (!(await fs.pathExists(configFile))) {
|
||||
throw new MetadataValidationError(`Metadata store config file not found: "${configFile}"`);
|
||||
}
|
||||
|
||||
const configData = await resolveDynamicConfigAsync(configFile);
|
||||
|
||||
if (!skipValidation) {
|
||||
const { valid, errors: validationErrors } = validateConfig(configData);
|
||||
|
||||
if (!valid) {
|
||||
throw new MetadataValidationError(`Metadata store config errors found`, validationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
return configData as MetadataConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the JSON Schema validation to normalize defaults and flag early config errors.
|
||||
* This includes validating the known store limitations for every configurable property.
|
||||
|
||||
@@ -6,7 +6,7 @@ import Log from '../log';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { AppleData } from './apple/data';
|
||||
import { createAppleTasks } from './apple/tasks';
|
||||
import { createAppleWriter, getStaticConfigFilePath } from './config';
|
||||
import { createAppleWriter } from './config';
|
||||
import { MetadataContext, ensureMetadataAppStoreAuthenticatedAsync } from './context';
|
||||
import { MetadataDownloadError, MetadataValidationError } from './errors';
|
||||
import { subscribeTelemetry } from './utils/telemetry';
|
||||
@@ -16,16 +16,15 @@ import { subscribeTelemetry } from './utils/telemetry';
|
||||
* Note, only App Store is supported at this time.
|
||||
*/
|
||||
export async function downloadMetadataAsync(metadataCtx: MetadataContext): Promise<string> {
|
||||
const filePath = getStaticConfigFilePath(metadataCtx);
|
||||
const filePath = path.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
|
||||
const fileExists = await fs.pathExists(filePath);
|
||||
|
||||
if (fileExists) {
|
||||
const filePathRelative = path.relative(metadataCtx.projectDir, filePath);
|
||||
const overwrite = await confirmAsync({
|
||||
message: `Do you want to overwrite the existing "${filePathRelative}"?`,
|
||||
message: `Do you want to overwrite the existing store configuration "${metadataCtx.metadataPath}"?`,
|
||||
});
|
||||
if (!overwrite) {
|
||||
throw new MetadataValidationError(`Store config already exists at "${filePath}"`);
|
||||
throw new MetadataValidationError(`Store configuration already exists at "${filePath}"`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ export async function downloadMetadataAsync(metadataCtx: MetadataContext): Promi
|
||||
);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log('Downloading App Store config...');
|
||||
Log.log('Downloading App Store configuration...');
|
||||
|
||||
const errors: Error[] = [];
|
||||
const config = createAppleWriter();
|
||||
@@ -60,7 +59,7 @@ export async function downloadMetadataAsync(metadataCtx: MetadataContext): Promi
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.writeJSON(filePath, config.toSchema(), { spaces: 2 });
|
||||
await fs.writeJson(filePath, config.toSchema(), { spaces: 2 });
|
||||
} finally {
|
||||
unsubscribeTelemetry();
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ export function handleMetadataError(error: Error): void {
|
||||
Log.error(error.errors.map(err => err.message).join('\n\n'));
|
||||
}
|
||||
Log.newLine();
|
||||
Log.log('Check the logs for any configuration issues.');
|
||||
Log.log('If this issue persists, open a new issue at:');
|
||||
Log.log('Please check the logs for any configuration issues.');
|
||||
Log.log('If this issue persists, please open a new issue at:');
|
||||
// TODO: add execution ID to the issue template link
|
||||
Log.log(link('https://github.com/expo/eas-cli'));
|
||||
return;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { MetadataEvent } from '../analytics/events';
|
||||
import Log from '../log';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { AppleData } from './apple/data';
|
||||
import { createAppleTasks } from './apple/tasks';
|
||||
import { MetadataConfig, createAppleReader, loadConfigAsync } from './config';
|
||||
import { createAppleReader, validateConfig } from './config';
|
||||
import { MetadataContext, ensureMetadataAppStoreAuthenticatedAsync } from './context';
|
||||
import { MetadataUploadError, MetadataValidationError, logMetadataValidationError } from './errors';
|
||||
import { subscribeTelemetry } from './utils/telemetry';
|
||||
@@ -15,7 +18,28 @@ import { subscribeTelemetry } from './utils/telemetry';
|
||||
export async function uploadMetadataAsync(
|
||||
metadataCtx: MetadataContext
|
||||
): Promise<{ appleLink: string }> {
|
||||
const storeConfig = await loadConfigWithValidationPromptAsync(metadataCtx);
|
||||
const filePath = path.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
|
||||
if (!(await fs.pathExists(filePath))) {
|
||||
throw new MetadataValidationError(`Store configuration file not found "${filePath}"`);
|
||||
}
|
||||
|
||||
const fileData = await fs.readJson(filePath);
|
||||
const { valid, errors: validationErrors } = validateConfig(fileData);
|
||||
if (!valid) {
|
||||
const error = new MetadataValidationError(`Store configuration errors found`, validationErrors);
|
||||
logMetadataValidationError(error);
|
||||
Log.newLine();
|
||||
Log.warn(
|
||||
'Without further updates, the current store configuration may fail to be synchronized with the App Store or pass App Store review.'
|
||||
);
|
||||
const attempt = await confirmAsync({
|
||||
message: 'Do you still want to push the store configuration?',
|
||||
});
|
||||
if (!attempt) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const { app, auth } = await ensureMetadataAppStoreAuthenticatedAsync(metadataCtx);
|
||||
const { unsubscribeTelemetry, executionId } = subscribeTelemetry(
|
||||
MetadataEvent.APPLE_METADATA_UPLOAD,
|
||||
@@ -26,7 +50,7 @@ export async function uploadMetadataAsync(
|
||||
Log.log('Uploading App Store configuration...');
|
||||
|
||||
const errors: Error[] = [];
|
||||
const config = createAppleReader(storeConfig);
|
||||
const config = createAppleReader(fileData);
|
||||
const tasks = createAppleTasks(metadataCtx, {
|
||||
// We need to resolve a different version as soon as possible.
|
||||
// This version is the parent model of all changes we are going to push.
|
||||
@@ -59,25 +83,3 @@ export async function uploadMetadataAsync(
|
||||
|
||||
return { appleLink: `https://appstoreconnect.apple.com/apps/${app.id}/appstore` };
|
||||
}
|
||||
|
||||
async function loadConfigWithValidationPromptAsync(
|
||||
metadataCtx: MetadataContext
|
||||
): Promise<MetadataConfig> {
|
||||
try {
|
||||
return await loadConfigAsync(metadataCtx);
|
||||
} catch (error) {
|
||||
if (error instanceof MetadataValidationError) {
|
||||
logMetadataValidationError(error);
|
||||
Log.newLine();
|
||||
Log.warn(
|
||||
'Without further updates, the current store configuration can fail to be synchronized with the App Store or pass App Store review.'
|
||||
);
|
||||
|
||||
if (await confirmAsync({ message: 'Do you still want to push the store configuration?' })) {
|
||||
return await loadConfigAsync({ ...metadataCtx, skipValidation: true });
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export function ora(options?: Options | string): Ora {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log = logReal;
|
||||
// eslint-disable-next-line no-console
|
||||
console.info = infoReal;
|
||||
console.info = logReal;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn = warnReal;
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { getConfig, modifyConfigAsync } from '@expo/config';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { Actor, getUserAsync } from '../../user/User';
|
||||
import { ensureProjectExistsAsync } from '../ensureProjectExists';
|
||||
import {
|
||||
ensureProjectExistsAsync,
|
||||
findProjectIdByAccountNameAndSlugNullableAsync,
|
||||
} from '../ensureProjectExists';
|
||||
import {
|
||||
findProjectRootAsync,
|
||||
getProjectAccountName,
|
||||
getProjectAccountNameAsync,
|
||||
getProjectIdAsync,
|
||||
promptToCreateProjectIfNotExistsAsync,
|
||||
} from '../projectUtils';
|
||||
|
||||
jest.mock('@expo/config');
|
||||
@@ -17,10 +20,6 @@ jest.mock('../../prompts');
|
||||
jest.mock('../../user/User');
|
||||
jest.mock('../ensureProjectExists');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe(findProjectRootAsync, () => {
|
||||
beforeEach(() => {
|
||||
vol.reset();
|
||||
@@ -34,7 +33,7 @@ describe(findProjectRootAsync, () => {
|
||||
'/app'
|
||||
);
|
||||
await expect(findProjectRootAsync({ cwd: '/app' })).rejects.toThrow(
|
||||
'Run this command inside a project directory.'
|
||||
'Please run this command inside a project directory.'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -181,54 +180,71 @@ describe(getProjectAccountNameAsync, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(getProjectIdAsync, () => {
|
||||
describe(promptToCreateProjectIfNotExistsAsync, () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(getUserAsync).mockReset();
|
||||
jest.mocked(findProjectIdByAccountNameAndSlugNullableAsync).mockReset();
|
||||
});
|
||||
|
||||
it('returns the existing project', async () => {
|
||||
jest.mocked(getUserAsync).mockImplementation(
|
||||
async (): Promise<Actor> => ({
|
||||
__typename: 'User',
|
||||
id: 'user_id',
|
||||
username: 'notnotbrent',
|
||||
accounts: [
|
||||
{ id: 'account_id_1', name: 'notnotbrent' },
|
||||
{ id: 'account_id_2', name: 'dominik' },
|
||||
],
|
||||
username: 'quin',
|
||||
accounts: [{ id: 'account_id_1', name: 'quin' }],
|
||||
isExpoAdmin: false,
|
||||
})
|
||||
);
|
||||
jest
|
||||
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
|
||||
.mockImplementation(async () => 'already-existing-project-id');
|
||||
const exp: any = {};
|
||||
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
|
||||
expect(projectId).toBe('already-existing-project-id');
|
||||
});
|
||||
it('makes a new project if none exists', async () => {
|
||||
jest.mock('../../prompts');
|
||||
jest.mocked(getUserAsync).mockImplementation(
|
||||
async (): Promise<Actor> => ({
|
||||
__typename: 'User',
|
||||
id: 'user_id',
|
||||
username: 'quin',
|
||||
accounts: [{ id: 'account_id_1', name: 'quin' }],
|
||||
isExpoAdmin: false,
|
||||
})
|
||||
);
|
||||
jest
|
||||
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
|
||||
.mockImplementation(async () => null);
|
||||
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
|
||||
|
||||
it('gets the project ID from app config if exists', async () => {
|
||||
await expect(
|
||||
getProjectIdAsync({ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } })
|
||||
).resolves.toEqual('1234');
|
||||
// user agrees to make new project
|
||||
jest.mocked(confirmAsync).mockImplementation(async () => true);
|
||||
const exp: any = {};
|
||||
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
|
||||
expect(projectId).toBe('new-project-id');
|
||||
});
|
||||
it('aborts making a new project if the user declines', async () => {
|
||||
jest.mock('../../prompts');
|
||||
jest.mocked(getUserAsync).mockImplementation(
|
||||
async (): Promise<Actor> => ({
|
||||
__typename: 'User',
|
||||
id: 'user_id',
|
||||
username: 'quin',
|
||||
accounts: [{ id: 'account_id_1', name: 'quin' }],
|
||||
isExpoAdmin: false,
|
||||
})
|
||||
);
|
||||
jest
|
||||
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
|
||||
.mockImplementation(async () => null);
|
||||
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
|
||||
|
||||
it('fetches the project ID when not in app config, and sets it in the config', async () => {
|
||||
jest.mocked(getConfig).mockReturnValue({ exp: { name: 'test', slug: 'test' } } as any);
|
||||
jest.mocked(modifyConfigAsync).mockResolvedValue({
|
||||
type: 'success',
|
||||
config: { expo: { name: 'test', slug: 'test', extra: { eas: { projectId: '2345' } } } },
|
||||
});
|
||||
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => '2345');
|
||||
|
||||
const projectId = await getProjectIdAsync({ name: 'test', slug: 'test' }, {}, { cwd: '/app' });
|
||||
expect(projectId).toBe('2345');
|
||||
|
||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(1);
|
||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/app', {
|
||||
extra: { eas: { projectId: '2345' } },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not throw if writing the ID back to the config fails', async () => {
|
||||
jest.mocked(getConfig).mockReturnValue({ exp: { name: 'test', slug: 'test' } } as any);
|
||||
jest.mocked(modifyConfigAsync).mockResolvedValue({
|
||||
type: 'fail',
|
||||
config: null,
|
||||
});
|
||||
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => '4567');
|
||||
|
||||
const projectId = await getProjectIdAsync({ name: 'test', slug: 'test' }, {}, { cwd: '/app' });
|
||||
expect(projectId).toBe('4567');
|
||||
// user doesnt agree to make new project
|
||||
jest.mocked(confirmAsync).mockImplementation(async () => false);
|
||||
const exp: any = {};
|
||||
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
|
||||
expect(projectId).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import terminalLink from 'terminal-link';
|
||||
import { getProjectDashboardUrl } from '../build/utils/url';
|
||||
import { AppPrivacy } from '../graphql/generated';
|
||||
import { AppMutation } from '../graphql/mutations/AppMutation';
|
||||
import { AppQuery } from '../graphql/queries/AppQuery';
|
||||
import { ProjectQuery } from '../graphql/queries/ProjectQuery';
|
||||
import { ora } from '../ora';
|
||||
import { findAccountByName } from '../user/Account';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
@@ -71,13 +71,12 @@ export async function ensureProjectExistsAsync(projectInfo: ProjectInfo): Promis
|
||||
* @param slug project slug
|
||||
* @returns A promise resolving to Project ID, null if it doesn't exist
|
||||
*/
|
||||
async function findProjectIdByAccountNameAndSlugNullableAsync(
|
||||
export async function findProjectIdByAccountNameAndSlugNullableAsync(
|
||||
accountName: string,
|
||||
slug: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const { id } = await AppQuery.byFullNameAsync(`@${accountName}/${slug}`);
|
||||
return id;
|
||||
return await findProjectIdByAccountNameAndSlugAsync(accountName, slug);
|
||||
} catch (err: any) {
|
||||
if (err.graphQLErrors?.some((it: any) => it.extensions?.errorCode !== 'EXPERIENCE_NOT_FOUND')) {
|
||||
throw err;
|
||||
@@ -86,6 +85,20 @@ async function findProjectIdByAccountNameAndSlugNullableAsync(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds project by `@accountName/slug` and returns its ID
|
||||
* @param accountName account name
|
||||
* @param slug project slug
|
||||
* @returns A promise resolving to Project ID
|
||||
*/
|
||||
async function findProjectIdByAccountNameAndSlugAsync(
|
||||
accountName: string,
|
||||
slug: string
|
||||
): Promise<string> {
|
||||
const { id } = await ProjectQuery.byUsernameAndSlugAsync(accountName, slug);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new project on EAS servers
|
||||
* @returns Created project's ID
|
||||
|
||||
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user