Compare commits

..
529 changed files with 7623 additions and 12476 deletions

No files matched your search

View File
File renamed without changes.
-1
View File
@@ -1,2 +1 @@
* text eol=lf
**/yarn.lock linguist-generated
+20
View File
@@ -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
+1 -4
View File
@@ -27,11 +27,8 @@ jobs:
if: ${{ !matrix.coverage }}
- run: yarn test --coverage
if: ${{ matrix.coverage }}
env:
# Oclif seems to cause OOM errors when testing with coverage enabled.
# 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:
+3
View File
@@ -19,3 +19,6 @@ node_modules
# Tests
coverage
# Copies of script for testing ESM imports
packages/*/scripts/testESMImports.ts
+1 -2
View File
@@ -4,6 +4,5 @@
"singleQuote": true,
"bracketSameLine": true,
"trailingComma": "es5",
"arrowParens": "avoid",
"endOfLine": "auto"
"arrowParens": "avoid"
}
-86
View File
@@ -8,97 +8,11 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
- Warn for misconfigured channel configuration when using EAS Update. ([#1082](https://github.com/expo/eas-cli/pull/1082)) by [@kbrandwijk](https://github.com/kbrandwijk))
### 🐛 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))
### 🧹 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))
## [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))
### 🧹 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))
- 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
### 🎉 New features
- Add new option to `eas device:create` - allow importing devices from Apple Developer Portal. ([#1236](https://github.com/expo/eas-cli/pull/1236) by [@dsokal](https://github.com/dsokal))
- Add support for attaching messages to builds. ([#1237](https://github.com/expo/eas-cli/pull/1237) by [@dsokal](https://github.com/dsokal))
- Introduce interactive pagination for `branch` commands. ([#1213](https://github.com/expo/eas-cli/pull/1213) by [@kgc00](https://github.com/kgc00))
## [0.56.0](https://github.com/expo/eas-cli/releases/tag/v0.56.0) - 2022-07-28
### 🎉 New features
- Display build queue type when waiting. ([#1217](https://github.com/expo/eas-cli/pull/1217) by [@dsokal](https://github.com/dsokal))
- Add better message on how to upgrade EAS CLI. ([#1222](https://github.com/expo/eas-cli/pull/1222) by [@jeremybarbet](https://github.com/jeremybarbet), [#1231](https://github.com/expo/eas-cli/pull/1231) by [@wkozyra95](https://github.com/wkozyra95))
- Invoke export with sourcemaps on update. ([#1228](https://github.com/expo/eas-cli/pull/1228) by [@kbrandwijk](https://github.com/kbrandwijk))
### 🐛 Bug fixes
- Show spinner instead of silently timing out during asset upload. ([#1206](https://github.com/expo/eas-cli/pull/1206) by [@wschurman](https://github.com/wschurman))
- Fix build archive S3 URLs. ([#1207](https://github.com/expo/eas-cli/pull/1207) by [@dsokal](https://github.com/dsokal))
- `autoIncrement` option on iOS will update versions of all targets that depend on an application target. ([#1219](https://github.com/expo/eas-cli/pull/1219) by [@wkozyra95](https://github.com/wkozyra95))
### 🧹 Chores
- Increase max number of chained "extends" in build profiles. ([#1208](https://github.com/expo/eas-cli/pull/1208) by [@wkozyra95](https://github.com/wkozyra95))
- Change wording on build warning for bundle id/package name. ([#1211](https://github.com/expo/eas-cli/pull/1211) by [@kbrandwijk](https://github.com/kbrandwijk))
## [0.55.1](https://github.com/expo/eas-cli/releases/tag/v0.55.1) - 2022-07-12
### 🐛 Bug fixes
- Fix asset upload crash. ([#1205](https://github.com/expo/eas-cli/pull/1205) by [@wschurman](https://github.com/wschurman))
## [0.55.0](https://github.com/expo/eas-cli/releases/tag/v0.55.0) - 2022-07-11
### 🎉 New features
- Show bundler output by default for eas update command. ([#1171](https://github.com/expo/eas-cli/pull/1171) by [@kgc00](https://github.com/kgc00/))
- Allow users to skip metadata validation. ([#1175](https://github.com/expo/eas-cli/pull/1175) by [@byCedric](https://github.com/byCedric))
- Add experimental `--resource-class` flag for Build commands. ([#1138](https://github.com/expo/eas-cli/pull/1138) by [@christopherwalter](https://github.com/christopherwalter))
- Truncate long messages for `eas update` command, rather than failing. ([#1178](https://github.com/expo/eas-cli/pull/1178) by [@kgc00](https://github.com/kgc00))
- Add review details to metadata store configuration. ([#1184](https://github.com/expo/eas-cli/pull/1184) by [@byCedric](https://github.com/byCedric))
- Override applicationId via env. ([#1203](https://github.com/expo/eas-cli/pull/1203) by [@wkozyra95](https://github.com/wkozyra95))
### 🐛 Bug fixes
-3
View File
@@ -6,9 +6,6 @@ packages/eas-cli/src/commands/build @dsokal @wkozyra95
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
packages/eas-cli/schema @bycedric
packages/eas-cli/src/metadata @bycedric
packages/eas-cli/src/commands/metadata @bycedric
+130
View File
@@ -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."
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.cjs'),
require('./packages/eas-json/jest.config.cjs'),
],
testPathIgnorePatterns: ['.*'],
};
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
testPathIgnorePatterns: ['.*'],
};
+9 -1
View File
@@ -1,7 +1,15 @@
module.exports = {
preset: 'ts-jest',
preset: 'ts-jest/presets/default-esm',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
globals: {
'ts-jest': {
useESM: true,
},
},
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.60.0"
"version": "0.54.1"
}
+16 -14
View File
@@ -1,5 +1,6 @@
{
"private": true,
"type": "module",
"workspaces": [
"packages/*"
],
@@ -13,26 +14,27 @@
"eas": "packages/eas-cli/bin/run",
"lint": "eslint .",
"release": "./scripts/bin/run update-local-plugin && lerna version --exact",
"test": "jest",
"clean": "lerna run clean && rm -rf node_modules coverage"
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"clean": "lerna run clean && rm -rf node_modules coverage",
"test-esm-imports": "lerna run test-esm-imports"
},
"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",
"typescript": "4.7.4"
},
"volta": {
"node": "18.6.0",
"yarn": "1.22.19"
"node": "18.2.0",
"yarn": "1.22.18"
}
}
@@ -1,25 +1,7 @@
const rootEslintrc = require('../../.eslintrc.js');
const rootEslintrc = require('../../.eslintrc.cjs');
module.exports = {
rules: {
'no-restricted-properties': [
'warn',
{
object: 'it',
property: 'only',
message: 'it.only should not be committed to main.',
},
{
object: 'test',
property: 'only',
message: 'test.only should not be committed to main.',
},
{
object: 'describe',
property: 'only',
message: 'describe.only should not be committed to main.',
},
],
'no-restricted-imports': [
'error',
{
+49 -98
View File
@@ -49,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)
@@ -102,7 +100,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -119,7 +117,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -136,7 +134,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -150,7 +148,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -199,7 +197,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -219,7 +217,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -227,20 +225,16 @@ list all branches
```
USAGE
$ eas branch:list [--offset <value>] [--limit <value>] [--json] [--non-interactive]
$ eas branch:list [--json]
FLAGS
--json Enable JSON output, non-JSON messages will be printed to stderr.
--limit=<value> The number of query items to list at once. The default value is 50 (the maximum is 100). Using a
lower value may help increase command speed.
--non-interactive Run the command in non-interactive mode.
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
--json return output as JSON
DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -259,7 +253,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -267,23 +261,19 @@ view a branch
```
USAGE
$ eas branch:view [NAME] [--offset <value>] [--limit <value>] [--json] [--non-interactive]
$ eas branch:view [NAME] [--json]
ARGUMENTS
NAME Name of the branch to view
FLAGS
--json Enable JSON output, non-JSON messages will be printed to stderr.
--limit=<value> The number of query items to list at once. The default value is 50 (the maximum is 100). Using a
lower value may help increase command speed.
--non-interactive Run the command in non-interactive mode.
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
--json return a json with the branch's ID name and recent update groups.
DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -292,10 +282,9 @@ start a build
```
USAGE
$ eas build [-p android|ios|all] [--json] [--profile <value>] [--non-interactive] [--local] [--output
<value>] [--wait] [--clear-cache] [--auto-submit | --auto-submit-with-profile <value>] [-m <value>]
<value>] [--wait] [--clear-cache] [--auto-submit | --auto-submit-with-profile <value>]
FLAGS
-m, --message=<value> A short message describing the build
-p, --platform=(android|ios|all)
--auto-submit Submit on build complete using the submit profile with the same name as the
build profile
@@ -313,7 +302,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -327,7 +316,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -344,7 +333,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -379,7 +368,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/v0.60.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -412,7 +401,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -442,44 +431,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/v0.60.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/v0.60.0/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
view a build for your project
@@ -495,7 +446,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -515,7 +466,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -536,7 +487,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -553,7 +504,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -573,7 +524,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -591,7 +542,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -605,7 +556,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -619,7 +570,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/v0.60.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -637,7 +588,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -654,7 +605,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -668,7 +619,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -682,7 +633,7 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -764,7 +715,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/v0.60.0/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -781,7 +732,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/v0.60.0/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas project:info`
@@ -795,7 +746,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -812,7 +763,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -832,7 +783,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/v0.60.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -849,7 +800,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -863,7 +814,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -893,7 +844,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -923,7 +874,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -940,7 +891,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -960,7 +911,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -979,7 +930,7 @@ DESCRIPTION
view the recent updates for a branch
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -999,7 +950,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1019,7 +970,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1036,7 +987,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1053,7 +1004,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1074,7 +1025,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1091,7 +1042,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.60.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.54.1/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
@@ -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
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env node
require('@oclif/core')
.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'));
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env node
import { Errors, flush, run } from '@oclif/core';
run(undefined, import.meta.url)
.then(flush)
.catch(Errors.handle);
+1 -1
View File
@@ -14,7 +14,7 @@ generates:
dedupeOperationSuffix: true
hooks:
afterOneFileWrite:
- 'node ./scripts/annotate-graphql-codegen.js'
- ./annotate-graphql-codegen.sh
./graphql.schema.json:
plugins:
- 'introspection'
+1559 -3614
View File
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,5 @@
module.exports = {
...require('../../jest/jest.config.js'),
...require('../../jest/jest.config.cjs'),
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
+2
View File
@@ -1,3 +1,5 @@
import { jest } from '@jest/globals';
/* eslint-disable no-console */
beforeAll(() => {
console.log = jest.fn();
+39 -38
View File
@@ -1,7 +1,8 @@
{
"name": "eas-cli",
"type": "module",
"description": "EAS command line tool",
"version": "0.60.0",
"version": "0.54.1",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
@@ -10,30 +11,29 @@
"dependencies": {
"@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/eas-build-job": "0.2.85",
"@expo/eas-json": "0.60.0",
"@expo/config": "6.0.23",
"@expo/config-plugins": "4.1.4",
"@expo/config-types": "45.0.0",
"@expo/eas-build-job": "0.2.77",
"@expo/eas-json": "0.54.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.5",
"@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 +55,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",
@@ -78,39 +78,39 @@
"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",
"@jest/globals": "28.1.2",
"@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/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",
"form-data": "4.0.0",
"memfs": "3.4.7",
"globby": "13.1.2",
"memfs": "3.4.3",
"mockdate": "3.0.5",
"nock": "13.2.9",
"rimraf": "3.0.2",
"nock": "13.2.4",
"tempy": "0.7.0",
"ts-deepmerge": "2.0.5",
"ts-mockito": "2.6.1",
"ts-node": "10.9.1",
"ts-deepmerge": "2.0.1",
"ts-node": "10.8.2",
"typescript": "4.7.4"
},
"engines": {
"node": ">=14.0.0"
"node": ">=14.16"
},
"files": [
"/bin",
@@ -170,11 +170,11 @@
},
"warn-if-update-available": {
"timeoutInDays": 0.5,
"message": "<%= chalk('★') %> <%= chalk.bold(config.name + '@' + latest) %> <%= chalk.bold('is now available.') %>\n<%= chalk.dim('To upgrade, run ') %><%= chalk.dim(chalk.bold('npm install -g eas-cli')) %><%= chalk.dim('.') %>\n<%= chalk.dim('Proceeding with outdated version.') %>\n"
"message": "<%= chalk.bold('★') %> <%= chalk.bold(config.name + '@' + latest) %> <%= chalk.bold('is now available, please upgrade.') %>\n<%= chalk.dim('Proceeding with outdated version') %>\n"
},
"update": {
"node": {
"version": "18.6.0"
"version": "12.13.0"
},
"s3": {
"templates": {
@@ -194,19 +194,20 @@
},
"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",
"typecheck": "tsc",
"test": "jest",
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
"version": "yarn oclif readme && node scripts/patch-readme.cjs && 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",
"test-esm-imports": "cp ../../scripts/src/testESMImports.ts scripts/ && ts-node-esm ./scripts/testESMImports.ts 'build/**/*.js'"
},
"volta": {
"node": "18.6.0",
"yarn": "1.22.19"
"node": "18.2.0",
"yarn": "1.22.18"
}
}
+234 -39
View File
@@ -3,6 +3,21 @@
"type": "object",
"definitions": {
"apple": {
"AppleLocalizedPreviewFolder": {
"anyOf": [
{
"description": "Path to folder containing preview files",
"type": "string"
},
{
"type": "array",
"uniqueItems": true,
"items": {
"type": "string"
}
}
]
},
"AppleKidsAge": {
"enum": [
"FIVE_AND_UNDER",
@@ -449,43 +464,62 @@
},
"AppleRelease": {
"type": "object",
"description": "Configure how the app is released in the app store.",
"additionalProperties": false,
"defaultSnippets": [
{
"body": {
"automaticRelease": true,
"usesThirdPartyContent": false,
"usesNonExemptEncryption": false
}
}
],
"properties": {
"automaticRelease": {
"description": "If and how the app should automatically be released when approved by App Store review.",
"markdownDescription": "If and how the app should automatically be released when approved by App Store review.\n- `false` - Manually release the app after store approval. (default behavior)\n- `true` - Automatically release after store approval.\n- `Date` - Automatically schedule release on this date, after store approval.\n\nApple does not guarantee that your app is available at the chosen scheduled release date.",
"oneOf": [
"autoReleaseDate": {
"description": "ISO Date to release the app automatically. Leave undefined to manually release the app to the public.",
"defaultSnippets": [
{
"body": "$CURRENT_YEAR-${1:$CURRENT_MONTH}-${2:$CURRENT_DATE}"
}
],
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "boolean"
}
],
"defaultSnippets": [
{
"label": "Manual release",
"description": "Manually release the app after store approval",
"body": false
"type": "string",
"format": "date"
},
{
"label": "Automatic release",
"description": "Automatically release the app after store approval",
"body": true
},
{
"label": "Scheduled release",
"description": "Automatically release the app on this date, and store approval",
"body": "$CURRENT_YEAR-${1:$CURRENT_MONTH}-${2:$CURRENT_DATE}T${3:$CURRENT_HOUR}:00:00+00:00"
"type": "number"
}
]
},
"phasedRelease": {
"isPhasedReleaseEnabled": {
"type": "boolean",
"description": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates. Keep in mind that this version will still be available to all users as a manual update from the App Store. You can pause the phased release for up to 30 days or release this update to all users at any time.\n@see https://help.apple.com/app-store-connect/#/dev3d65fcee1",
"markdownDescription": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates.\n\nKeep in mind that this version will still be available to all users as a manual update from the App Store.\n\nYou can pause the phased release for up to 30 days or release this update to all users at any time.\n\n[Learn More](https://help.apple.com/app-store-connect/#/dev3d65fcee1)"
"description": "Release update over 7-day period instead of instantly.",
"meta": {
"storeInfo": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates. Keep in mind that this version will still be available to all users as a manual update from the App Store. You can pause the phased release for up to 30 days or release this update to all users at any time. [Learn More](https://help.apple.com/app-store-connect/#/dev3d65fcee1)."
}
},
"shouldResetRatings": {
"type": "boolean",
"description": "Should App Store ratings be reset for this version of the app.",
"meta": {
"storeInfo": "You can reset your app’s summary rating for all countries or regions when you release this version. Keep in mind that once this version is released, you won’t be able to restore the rating. Your app’s existing customer reviews will still appear on the App Store."
}
},
"usesThirdPartyContent": {
"type": "boolean"
},
"automaticRelease": {
"type": "boolean",
"description": "Should the app automatically be released when approved by App Store review."
},
"usesNonExemptEncryption": {
"type": "boolean",
"description": "Alternative to setting `ITSAppUsesNonExemptEncryption` in the binary's `Info.plist`."
}
}
},
@@ -572,15 +606,11 @@
"description": "Configuration that is specific to the Apple App Store.",
"additionalProperties": false,
"properties": {
"version": {
"type": "string",
"description": "The version string to edit, or create, if it doesn't exist. All the metadata will be stored on this app version.",
"minLength": 1,
"defaultSnippets": [
{
"body": "${1:1}.${2:0}"
}
]
"advisory": {
"$ref": "#/definitions/apple/AppleAdvisory"
},
"categories": {
"$ref": "#/definitions/apple/AppleCategory"
},
"copyright": {
"type": "string",
@@ -596,12 +626,6 @@
"storeInfo": "The name of the person or entity that owns the exclusive rights to your app, preceded by the year the rights were obtained (for example, \"2008 Acme Inc.\"). Do not provide a URL."
}
},
"advisory": {
"$ref": "#/definitions/apple/AppleAdvisory"
},
"categories": {
"$ref": "#/definitions/apple/AppleCategory"
},
"info": {
"description": "Localized core app info",
"type": "object",
@@ -765,6 +789,177 @@
}
}
},
"preview": {
"anyOf": [
{
"description": "Path to localized folders containing preview files",
"type": "string"
},
{
"description": "Localized previews object. Ex: `\"en-US\": [\"image.png\"]`",
"type": "object",
"additionalProperties": false,
"properties": {
"ar-SA": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Arabic"
},
"ca": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Catalan"
},
"zh-Hans": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Chinese (Simplified)"
},
"zh-Hant": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Chinese (Traditional)"
},
"hr": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Croatian"
},
"cs": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Czech"
},
"da": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Danish"
},
"nl-NL": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Dutch"
},
"en-AU": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "English (Australia)"
},
"en-CA": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "English (Canada)"
},
"en-GB": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "English (U.K.)"
},
"en-US": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "English (U.S.)"
},
"fi": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Finnish"
},
"fr-FR": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "French"
},
"fr-CA": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "French (Canada)"
},
"de-DE": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "German"
},
"el": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Greek"
},
"he": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Hebrew"
},
"hi": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Hindi"
},
"hu": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Hungarian"
},
"id": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Indonesian"
},
"it": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Italian"
},
"ja": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Japanese"
},
"ko": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Korean"
},
"ms": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Malay"
},
"no": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Norwegian"
},
"pl": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Polish"
},
"pt-BR": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Portuguese (Brazil)"
},
"pt-PT": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Portuguese (Portugal)"
},
"ro": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Romanian"
},
"ru": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Russian"
},
"sk": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Slovak"
},
"es-MX": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Spanish (Mexico)"
},
"es-ES": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Spanish (Spain)"
},
"sv": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Swedish"
},
"th": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Thai"
},
"tr": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Turkish"
},
"uk": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Ukrainian"
},
"vi": {
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
"description": "Vietnamese"
}
}
}
]
},
"release": {
"$ref": "#/definitions/apple/AppleRelease",
"description": "Release strategy"
@@ -1,4 +1,4 @@
const fs = require('fs/promises');
const { promises: fs } = require('fs');
(async () => {
// Patch `oclif readme` path and link generation
-7
View File
@@ -1,7 +0,0 @@
const spawn = require('@expo/spawn-async');
(async () => {
if (!process.env.CLI_SIZE_CHECK) {
await spawn('yarn', ['oclif', 'manifest'], { stdio: 'inherit' });
}
})();
+10
View File
@@ -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
+1 -1
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import nock from 'nock';
import { ApiV2Error, api, getExpoApiBaseUrl } from '../api';
import { ApiV2Error, api, getExpoApiBaseUrl } from '../api.js';
describe('apiClient', () => {
it('converts Expo APIv2 error to ApiV2Error', async () => {
@@ -1,4 +1,4 @@
import { Actor } from '../user/User';
import { Actor } from '../user/User.js';
interface MockProject {
projectRoot: string;
@@ -1,17 +1,17 @@
import { fixS3Url } from '../uploads';
import { fixArchiveUrl } from '../uploads.js';
describe(fixS3Url, () => {
describe(fixArchiveUrl, () => {
it('fixes broken links', () => {
const input =
'https://submission-service-archives.s3.amazonaws.com/production%2Fdc98ca84-1473-4cb3-ae81-8c7b291cb27e%2F4424aa95-b985-4e2f-8755-9507b1037c1c';
const expectedOutput =
'https://submission-service-archives.s3.amazonaws.com/production/dc98ca84-1473-4cb3-ae81-8c7b291cb27e/4424aa95-b985-4e2f-8755-9507b1037c1c';
expect(fixS3Url(input)).toBe(expectedOutput);
expect(fixArchiveUrl(input)).toBe(expectedOutput);
});
it('does not change correct urls', () => {
const correctUrl =
'https://submission-service-archives.s3.amazonaws.com/production/dc98ca84-1473-4cb3-ae81-8c7b291cb27e/4424aa95-b985-4e2f-8755-9507b1037c1c';
expect(fixS3Url(correctUrl)).toBe(correctUrl);
expect(fixArchiveUrl(correctUrl)).toBe(correctUrl);
});
});
+1 -1
View File
@@ -1,4 +1,4 @@
import { Analytics, Event } from './events';
import { Analytics, Event } from './events.js';
export type TrackingContext = Record<string, string | number | boolean>;
+1 -1
View File
@@ -1,4 +1,4 @@
import { logEvent } from './rudderstackClient';
import { logEvent } from './rudderstackClient.js';
export type Event = BuildEvent | SubmissionEvent | MetadataEvent;
export enum SubmissionEvent {
@@ -1,10 +1,12 @@
import RudderAnalytics from '@expo/rudder-sdk-node';
import RudderAnalyticsModule from '@expo/rudder-sdk-node';
import os from 'os';
import { URL } from 'url';
import { v4 as uuidv4 } from 'uuid';
import UserSettings from '../user/UserSettings';
import { easCliVersion } from '../utils/easCli';
import UserSettings from '../user/UserSettings.js';
import { easCliVersion } from '../utils/easCli.js';
const RudderAnalytics = RudderAnalyticsModule.default;
const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
darwin: 'Mac',
@@ -12,7 +14,7 @@ const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
linux: 'Linux',
};
let rudderstackClient: RudderAnalytics | null = null;
let rudderstackClient: RudderAnalyticsModule.default | null = null;
let userIdentified = false;
let identifyData: {
userId: string;
+2 -2
View File
@@ -1,7 +1,7 @@
import { JSONValue } from '@expo/json-file';
import fetch, { RequestError, RequestInit } from './fetch';
import { getAccessToken, getSessionSecret } from './user/sessionStorage';
import fetch, { RequestError, RequestInit } from './fetch.js';
import { getAccessToken, getSessionSecret } from './user/sessionStorage.js';
export class ApiV2Error extends Error {
readonly name = 'ApiV2Error';
-109
View File
@@ -1,109 +0,0 @@
import chalk from 'chalk';
import CliTable from 'cli-table3';
import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { UpdateBranchFragment } from '../graphql/generated';
import { BranchQuery } from '../graphql/queries/BranchQuery';
import Log from '../log';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../update/utils';
import { printJsonOnlyOutput } from '../utils/json';
import {
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
export const BRANCHES_LIMIT = 50;
export async function selectBranchFromPaginatedQueryAsync(
projectId: string,
promptTitle: string,
options: PaginatedQueryOptions
): Promise<UpdateBranchFragment> {
if (options.nonInteractive) {
throw new Error('Unable to select a branch in non-interactive mode.');
}
const selectedBranch = await paginatedQueryWithSelectPromptAsync({
limit: options.limit ?? BRANCHES_LIMIT,
offset: options.offset,
queryToPerform: (limit, offset) => queryBranchesForProjectAsync(limit, offset, projectId),
promptOptions: {
title: promptTitle,
getIdentifierForQueryItem: updateBranchFragment => updateBranchFragment.id,
createDisplayTextForSelectionPromptListItem: updateBranchFragment =>
updateBranchFragment.name,
},
});
if (!selectedBranch) {
throw new Error(`Could not find any branches for project "${projectId}"`);
}
return selectedBranch;
}
export async function listAndRenderPaginatedBranchesAsync(
projectId: string,
options: PaginatedQueryOptions
): Promise<void> {
if (options.nonInteractive) {
const branches = await queryBranchesForProjectAsync(
options.limit ?? BRANCHES_LIMIT,
options.offset,
projectId
);
renderPageOfBranches(branches, options);
} else {
await paginatedQueryWithConfirmPromptAsync({
limit: options.limit ?? BRANCHES_LIMIT,
offset: options.offset,
queryToPerform: (limit, offset) => queryBranchesForProjectAsync(limit, offset, projectId),
promptOptions: {
title: 'Load more branches?',
renderListItems: branches => renderPageOfBranches(branches, options),
},
});
}
}
async function queryBranchesForProjectAsync(
limit: number,
offset: number,
projectId: string
): Promise<UpdateBranchFragment[]> {
return await BranchQuery.listBranchesAsync({
appId: projectId,
limit,
offset,
});
}
function renderPageOfBranches(
currentPage: UpdateBranchFragment[],
{ json }: PaginatedQueryOptions
): void {
if (json) {
printJsonOnlyOutput(currentPage);
} else {
const table = new CliTable({
head: ['Branch', ...UPDATE_COLUMNS],
wordWrap: true,
});
table.push(
...currentPage.map(branch => [
branch.name,
formatUpdate(branch.updates[0]),
branch.updates[0]?.runtimeVersion ?? 'N/A',
branch.updates[0]?.group ?? 'N/A',
getPlatformsForGroup({
updates: branch.updates,
group: branch.updates[0]?.group,
}),
])
);
Log.addNewLineIfNone();
Log.log(chalk.bold('Branches:'));
Log.addNewLineIfNone();
Log.log(table.toString());
}
}
@@ -1,14 +0,0 @@
import { truncateGitCommitMessage } from '../metadata';
describe(truncateGitCommitMessage, () => {
it('returns undefined if no message was passed', () => {
expect(truncateGitCommitMessage(undefined)).toBeUndefined();
});
it('returns commit message', () => {
expect(truncateGitCommitMessage('a'.repeat(10))).toBe('a'.repeat(10));
});
it('truncates long commit messages', () => {
expect(truncateGitCommitMessage('a'.repeat(5000))).toBe(`${'a'.repeat(4093)}...`);
});
});
@@ -7,13 +7,16 @@ import { vol } from 'memfs';
import os from 'os';
import path from 'path';
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
import {
getAppBuildGradleAsync,
resolveConfigValue,
} from '../../../project/android/gradleUtils.js';
import {
BumpStrategy,
bumpVersionAsync,
bumpVersionInAppJsonAsync,
maybeResolveVersionsAsync,
} from '../version';
} from '../version.js';
const fsReal = jest.requireActual('fs').promises as typeof fs;
jest.mock('fs');
@@ -89,7 +92,7 @@ describe(bumpVersionAsync, () => {
expect(resolveConfigValue(buildGradle, 'versionName')).toBe('3.0.0');
});
it('bumps expo.version and gradle versionCode when strategy = BumpStrategy.APP_VERSION', async () => {
it('bumps expo.version and gradle versionCode when strategy = BumpStrategy.SHORT_VERSION', async () => {
const nativeVersionName = '1.0.0'; // this should be overwritten by bumpVersionAsync
const fakeExp = initBareWorkflowProject({ versionName: nativeVersionName });
+19 -34
View File
@@ -1,35 +1,33 @@
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { AppVersionSource } from '@expo/eas-json';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import AndroidCredentialsProvider, {
AndroidCredentials,
} from '../../credentials/android/AndroidCredentialsProvider';
import { BuildParamsInput } from '../../graphql/generated';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
import Log from '../../log';
} from '../../credentials/android/AndroidCredentialsProvider.js';
import { BuildParamsInput } from '../../graphql/generated.js';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation.js';
import Log from '../../log.js';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
} from '../../project/android/applicationId';
import { resolveGradleBuildContextAsync } from '../../project/android/gradle';
import { toggleConfirmAsync } from '../../prompts';
import { findAccountByName } from '../../user/Account';
} from '../../project/android/applicationId.js';
import { resolveGradleBuildContextAsync } from '../../project/android/gradle.js';
import { toggleConfirmAsync } from '../../prompts.js';
import { findAccountByName } from '../../user/Account.js';
import { nullthrows } from '../../utils/nullthrows.js';
import {
BuildRequestSender,
CredentialsResult,
JobData,
prepareBuildRequestForPlatformAsync,
} from '../build';
import { AndroidBuildContext, BuildContext, CommonContext } from '../context';
import { transformMetadata } from '../graphql';
import { logCredentialsSource } from '../utils/credentials';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteVersionCodeAsync } from './version';
} from '../build.js';
import { AndroidBuildContext, BuildContext, CommonContext } from '../context.js';
import { transformMetadata } from '../graphql.js';
import { logCredentialsSource } from '../utils/credentials.js';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate.js';
import { transformJob } from './graphql.js';
import { prepareJobAsync } from './prepareJob.js';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration.js';
export async function createAndroidContextAsync(
ctx: CommonContext<Platform.ANDROID>
@@ -62,18 +60,8 @@ This means that it will most likely produce an AAB and you will not be able to i
}
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
const versionCodeOverride =
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? await resolveRemoteVersionCodeAsync({
projectDir: ctx.projectDir,
projectId: ctx.projectId,
exp: ctx.exp,
applicationId,
buildProfile,
})
: undefined;
return { applicationId, gradleContext, versionCodeOverride };
return { applicationId, gradleContext };
}
export async function prepareAndroidBuildAsync(
@@ -88,10 +76,7 @@ export async function prepareAndroidBuildAsync(
await syncProjectConfigurationAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
localAutoIncrement:
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
buildProfile: ctx.buildProfile,
});
},
prepareJobAsync: async (
@@ -1,7 +1,7 @@
import { Android } from '@expo/eas-build-job';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated.js';
import { transformProjectArchive, transformWorkflow } from '../graphql.js';
export function transformJob(job: Android.Job): AndroidJobInput {
return {
@@ -13,7 +13,6 @@ export function transformJob(job: Android.Job): AndroidJobInput {
secrets: job.secrets,
builderEnvironment: job.builderEnvironment,
cache: job.cache,
version: job.version?.versionCode ? { versionCode: job.version.versionCode } : undefined,
gradleCommand: job.gradleCommand,
artifactPath: job.artifactPath,
username: job.username,
@@ -3,11 +3,11 @@ import { BuildProfile } from '@expo/eas-json';
import path from 'path';
import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider.js';
import { getUsername } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { getVcsClient } from '../../vcs/index.js';
import { BuildContext } from '../context.js';
interface JobData {
projectArchive: ArchiveSource;
@@ -75,11 +75,6 @@ export async function prepareJobAsync(
artifactPath: buildProfile.artifactPath,
buildType,
username,
...(ctx.android.versionCodeOverride && {
version: {
versionCode: ctx.android.versionCodeOverride,
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
},
@@ -1,28 +1,31 @@
import { ExpoConfig } from '@expo/config';
import { AndroidConfig } from '@expo/config-plugins';
import ConfigPlugins from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
import { AndroidVersionAutoIncrement, BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import Log from '../../log';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import Log from '../../log.js';
import { isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule.js';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version.js';
const { AndroidConfig } = ConfigPlugins;
export async function syncProjectConfigurationAsync({
projectDir,
exp,
localAutoIncrement,
buildProfile,
}: {
projectDir: string;
exp: ExpoConfig;
localAutoIncrement?: AndroidVersionAutoIncrement;
buildProfile: BuildProfile<Platform.ANDROID>;
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
const { autoIncrement } = buildProfile;
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
+18 -128
View File
@@ -1,24 +1,21 @@
import { ExpoConfig } from '@expo/config';
import { AndroidConfig, Updates } from '@expo/config-plugins';
import ConfigPlugins from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import { AppPlatform } from '../../graphql/generated';
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
import Log from '../../log';
import { ora } from '../../ora';
import Log from '../../log.js';
import {
getAppBuildGradleAsync,
parseGradleCommand,
resolveConfigValue,
} from '../../project/android/gradleUtils';
import { getNextVersionCode } from '../../project/android/versions';
import { resolveWorkflowAsync } from '../../project/workflow';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
} from '../../project/android/gradleUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { updateAppJsonConfigAsync } from '../utils/appJson.js';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version.js';
const { AndroidConfig } = ConfigPlugins;
export enum BumpStrategy {
APP_VERSION,
@@ -51,10 +48,9 @@ export async function bumpVersionAsync({
await bumpVersionInAppJsonAsync({ bumpStrategy, projectDir, exp });
Log.log('Updated versions in app.json');
await updateNativeVersionsAsync({
await writeVersionsToBuildGradleAsync({
projectDir,
version: exp.version,
versionCode: exp.android?.versionCode,
exp,
});
Log.log('Synchronized versions with build gradle');
}
@@ -80,7 +76,7 @@ export async function bumpVersionInAppJsonAsync({
await bumpAppVersionAsync({ appVersion, projectDir, exp });
} else {
const versionCode = AndroidConfig.Version.getVersionCode(exp);
const bumpedVersionCode = getNextVersionCode(versionCode);
const bumpedVersionCode = versionCode + 1;
Log.log(
`Bumping ${chalk.bold('expo.android.versionCode')} from ${chalk.bold(
versionCode
@@ -122,33 +118,21 @@ export async function maybeResolveVersionsAsync(
}
}
export async function updateNativeVersionsAsync({
async function writeVersionsToBuildGradleAsync({
projectDir,
version,
versionCode,
exp,
}: {
projectDir: string;
version?: string;
versionCode?: number;
}): Promise<void> {
exp: ExpoConfig;
}): Promise<string> {
const buildGradle = await readBuildGradleAsync(projectDir);
if (!buildGradle) {
throw new Error('This project is missing a build.gradle file.');
}
let updatedBuildGradle = buildGradle;
if (version !== undefined) {
updatedBuildGradle = updatedBuildGradle.replace(
new RegExp(`versionName ".*"`),
`versionName "${version}"`
);
}
if (versionCode !== undefined) {
updatedBuildGradle = updatedBuildGradle.replace(
new RegExp(`versionCode.*`),
`versionCode ${versionCode}`
);
}
let updatedBuildGradle = AndroidConfig.Version.setVersionName(exp, buildGradle);
updatedBuildGradle = AndroidConfig.Version.setVersionCode(exp, updatedBuildGradle);
await writeBuildGradleAsync({ projectDir, buildGradle: updatedBuildGradle });
return updatedBuildGradle;
}
async function readBuildGradleAsync(projectDir: string): Promise<string | undefined> {
@@ -169,97 +153,3 @@ async function writeBuildGradleAsync({
const buildGradlePath = AndroidConfig.Paths.getAppBuildGradleFilePath(projectDir);
await fs.writeFile(buildGradlePath, buildGradle);
}
/**
* Returns buildNumber that will be used for the next build. If current build profile
* has an 'autoIncrement' option set, it increments the version on server.
*/
export async function resolveRemoteVersionCodeAsync({
projectDir,
projectId,
exp,
applicationId,
buildProfile,
}: {
projectDir: string;
projectId: string;
exp: ExpoConfig;
applicationId: string;
buildProfile: BuildProfile<Platform.ANDROID>;
}): Promise<string> {
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
AppPlatform.Android,
applicationId
);
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile);
let currentBuildVersion: string;
if (remoteVersions?.buildVersion) {
currentBuildVersion = remoteVersions.buildVersion;
} else {
if (localVersions.appBuildVersion) {
Log.warn(
'No remote versions are configured for this project, versionCode will be initialized based on the value from the local project.'
);
currentBuildVersion = localVersions.appBuildVersion;
} else {
Log.error(
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
);
throw new Error('Remote versions are not configured.');
}
}
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
return currentBuildVersion;
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
const spinner = ora(
`Initializing versionCode with ${chalk.bold(currentBuildVersion)}.`
).start();
try {
await AppVersionMutation.createAppVersionAsync({
appId: projectId,
platform: AppPlatform.Android,
applicationIdentifier: applicationId,
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
buildVersion: currentBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
});
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
} catch (err) {
spinner.fail(`Failed to initialize versionCode with ${chalk.bold(currentBuildVersion)}.`);
throw err;
}
return currentBuildVersion;
} else {
const nextBuildVersion = getNextVersionCode(currentBuildVersion);
const spinner = ora(
`Incrementing versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
).start();
try {
await AppVersionMutation.createAppVersionAsync({
appId: projectId,
platform: AppPlatform.Android,
applicationIdentifier: applicationId,
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
buildVersion: String(nextBuildVersion),
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
});
spinner.succeed(
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
);
} catch (err) {
spinner.fail(
`Failed to increment versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
);
throw err;
}
return String(nextBuildVersion);
}
}
+23 -29
View File
@@ -3,11 +3,10 @@ import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
import fs from 'fs-extra';
import nullthrows from 'nullthrows';
import { withAnalyticsAsync } from '../analytics/common';
import { BuildEvent } from '../analytics/events';
import { getExpoWebsiteBaseUrl } from '../api';
import { withAnalyticsAsync } from '../analytics/common.js';
import { BuildEvent } from '../analytics/events.js';
import { getExpoWebsiteBaseUrl } from '../api.js';
import {
AppPlatform,
BuildFragment,
@@ -15,26 +14,27 @@ import {
BuildPriority,
BuildStatus,
UploadSessionType,
} from '../graphql/generated';
import { BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import Log, { learnMore } from '../log';
import { Ora, ora } from '../ora';
} from '../graphql/generated.js';
import { BuildResult } from '../graphql/mutations/BuildMutation.js';
import { BuildQuery } from '../graphql/queries/BuildQuery.js';
import Log, { learnMore } from '../log.js';
import { Ora, ora } from '../ora.js';
import {
appPlatformDisplayNames,
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform';
import { uploadFileAtPathToS3Async } from '../uploads';
import { formatBytes } from '../utils/files';
import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise';
import { getVcsClient } from '../vcs';
import { BuildContext } from './context';
import { runLocalBuildAsync } from './local';
import { collectMetadataAsync } from './metadata';
import { printDeprecationWarnings } from './utils/printBuildInfo';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
} from '../platform.js';
import { uploadAsync } from '../uploads.js';
import { formatBytes } from '../utils/files.js';
import { nullthrows } from '../utils/nullthrows.js';
import { createProgressTracker } from '../utils/progress.js';
import { sleepAsync } from '../utils/promise.js';
import { getVcsClient } from '../vcs/index.js';
import { BuildContext } from './context.js';
import { runLocalBuildAsync } from './local.js';
import { collectMetadataAsync } from './metadata.js';
import { printDeprecationWarnings } from './utils/printBuildInfo.js';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository.js';
export interface CredentialsResult<Credentials> {
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
@@ -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 {
@@ -179,7 +179,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
const projectTarball = await makeProjectTarballAsync();
projectTarballPath = projectTarball.path;
const { bucketKey } = await uploadFileAtPathToS3Async(
const { bucketKey } = await uploadAsync(
UploadSessionType.EasBuildProjectSources,
projectTarball.path,
createProgressTracker({
@@ -352,7 +352,7 @@ async function handleSingleBuildProgressAsync(
);
}
Log.newLine();
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
Log.log('Waiting in queue');
queueProgressBar.start(
build.initialQueuePosition + 1,
build.initialQueuePosition - build.queuePosition + 1,
@@ -385,12 +385,6 @@ async function handleSingleBuildProgressAsync(
return { refetch: true };
}
const priorityToQueueDisplayName: Record<BuildPriority, string> = {
[BuildPriority.Normal]: 'queue',
[BuildPriority.NormalPlus]: 'queue',
[BuildPriority.High]: 'priority queue',
};
const statusToDisplayName: Record<BuildStatus, string> = {
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
[BuildStatus.InQueue]: 'in queue',
+5 -5
View File
@@ -3,11 +3,11 @@ import { EasJson, EasJsonReader } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import Log from '../log';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
import Log from '../log.js';
import { resolveWorkflowAsync } from '../project/workflow.js';
import { easCliVersion } from '../utils/easCli.js';
import { getVcsClient } from '../vcs/index.js';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository.js';
interface ConfigureParams {
projectDir: string;
+11 -15
View File
@@ -1,35 +1,34 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJson } from '@expo/eas-json';
import { BuildProfile } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { TrackingContext } from '../analytics/common';
import { CredentialsContext } from '../credentials/context';
import { Target } from '../credentials/ios/types';
import { BuildResourceClass } from '../graphql/generated';
import { GradleBuildContext } from '../project/android/gradle';
import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { LocalBuildOptions } from './local';
import { TrackingContext } from '../analytics/common.js';
import { CredentialsContext } from '../credentials/context.js';
import { Target } from '../credentials/ios/types.js';
import { BuildResourceClass } from '../graphql/generated.js';
import { GradleBuildContext } from '../project/android/gradle.js';
import { XcodeBuildContext } from '../project/ios/scheme.js';
import { Actor } from '../user/User.js';
import { LocalBuildOptions } from './local.js';
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
export interface AndroidBuildContext {
applicationId: string;
gradleContext?: GradleBuildContext;
versionCodeOverride?: string;
}
export interface IosBuildContext {
bundleIdentifier: string;
applicationTargetBuildSettings: XCBuildConfiguration['buildSettings'];
applicationTarget: Target;
targets: Target[];
xcodeBuildContext: XcodeBuildContext;
buildNumberOverride?: string;
}
export interface BuildContext<T extends Platform> {
accountName: string;
easJsonCliConfig: EasJson['cli'];
buildProfile: BuildProfile<T>;
buildProfileName: string;
resourceClass: BuildResourceClass;
@@ -38,13 +37,10 @@ export interface BuildContext<T extends Platform> {
exp: ExpoConfig;
localBuildOptions: LocalBuildOptions;
nonInteractive: boolean;
noWait: boolean;
runFromCI: boolean;
platform: T;
projectDir: string;
projectId: string;
projectName: string;
message?: string;
trackingCtx: TrackingContext;
user: Actor;
workflow: Workflow;
+18 -30
View File
@@ -1,48 +1,43 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJson } from '@expo/eas-json';
import JsonFile from '@expo/json-file';
import getenv from 'getenv';
import { BuildProfile } from '@expo/eas-json';
import JsonFileModule from '@expo/json-file';
import resolveFrom from 'resolve-from';
import { v4 as uuidv4 } from 'uuid';
import { TrackingContext } from '../analytics/common';
import { Analytics, BuildEvent } from '../analytics/events';
import { CredentialsContext } from '../credentials/context';
import { BuildResourceClass } from '../graphql/generated';
import { getExpoConfig } from '../project/expoConfig';
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { findAccountByName } from '../user/Account';
import { ensureLoggedInAsync } from '../user/actions';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { TrackingContext } from '../analytics/common.js';
import { Analytics, BuildEvent } from '../analytics/events.js';
import { CredentialsContext } from '../credentials/context.js';
import { BuildResourceClass } from '../graphql/generated.js';
import { getExpoConfig } from '../project/expoConfig.js';
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils.js';
import { resolveWorkflowAsync } from '../project/workflow.js';
import { findAccountByName } from '../user/Account.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { createAndroidContextAsync } from './android/build.js';
import { BuildContext, CommonContext } from './context.js';
import { createIosContextAsync } from './ios/build.js';
import { LocalBuildOptions } from './local.js';
const JsonFile = JsonFileModule.default;
export async function createBuildContextAsync<T extends Platform>({
buildProfileName,
buildProfile,
easJsonCliConfig,
clearCache = false,
localBuildOptions,
nonInteractive,
noWait,
nonInteractive = false,
platform,
projectDir,
resourceClass,
message,
}: {
buildProfileName: string;
buildProfile: BuildProfile<T>;
easJsonCliConfig: EasJson['cli'];
clearCache: boolean;
localBuildOptions: LocalBuildOptions;
nonInteractive: boolean;
noWait: boolean;
platform: T;
projectDir: string;
resourceClass: BuildResourceClass;
message?: string;
}): Promise<BuildContext<T>> {
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
@@ -52,7 +47,6 @@ export async function createBuildContextAsync<T extends Platform>({
const projectId = await getProjectIdAsync(exp, { env: buildProfile.env });
const workflow = await resolveWorkflowAsync(projectDir, platform);
const accountId = findAccountByName(user.accounts, accountName)?.id;
const runFromCI = getenv.boolish('CI', false);
const credentialsCtx = new CredentialsContext({
exp,
@@ -74,8 +68,6 @@ export async function createBuildContextAsync<T extends Platform>({
project_id: projectId,
project_type: workflow,
...devClientProperties,
no_wait: noWait,
run_from_ci: runFromCI,
};
Analytics.logEvent(BuildEvent.BUILD_COMMAND, trackingCtx);
@@ -84,13 +76,11 @@ export async function createBuildContextAsync<T extends Platform>({
buildProfile,
buildProfileName,
resourceClass,
easJsonCliConfig,
clearCache,
credentialsCtx,
exp,
localBuildOptions,
nonInteractive,
noWait,
platform,
projectDir,
projectId,
@@ -98,8 +88,6 @@ export async function createBuildContextAsync<T extends Platform>({
trackingCtx,
user,
workflow,
message,
runFromCI,
};
if (platform === Platform.ANDROID) {
const common = commonContext as CommonContext<Platform.ANDROID>;
+1 -1
View File
@@ -8,7 +8,7 @@ import {
DistributionType,
ProjectArchiveSourceInput,
ProjectArchiveSourceType,
} from '../graphql/generated';
} from '../graphql/generated.js';
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
if (archiveSource.type === ArchiveSourceType.S3) {
@@ -1,11 +1,10 @@
import { ExpoConfig } from '@expo/config';
import { IOSConfig } from '@expo/config-plugins';
import { InfoPlist } from '@expo/config-plugins';
import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import type { XCBuildConfiguration } from 'xcode';
import { readPlistAsync } from '../../../utils/plist';
import { readPlistAsync } from '../../../utils/plist.js';
import {
BumpStrategy,
bumpVersionAsync,
@@ -14,16 +13,10 @@ import {
getInfoPlistPath,
readBuildNumberAsync,
readShortVersionAsync,
} from '../version';
} from '../version.js';
jest.mock('fs');
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
IOSConfig.Target,
'getXCBuildConfigurationFromPbxproj'
);
const getPbxproj = jest.spyOn(IOSConfig.XcodeUtils, 'getPbxproj');
afterAll(async () => {
// do not remove the following line
// this fixes a weird error with tempy in @expo/image-utils
@@ -58,40 +51,18 @@ describe(evaluateTemplateString, () => {
// bare workflow
describe(bumpVersionAsync, () => {
beforeEach(() => {
getPbxproj.mockImplementation(() => ({} as any));
});
afterEach(() => {
getXCBuildConfigurationFromPbxproj.mockReset();
getPbxproj.mockReset();
});
it('bumps expo.ios.buildNumber and CFBundleVersion when strategy = BumpStrategy.BUILD_NUMBER', async () => {
const fakeExp = initBareWorkflowProject();
getXCBuildConfigurationFromPbxproj.mockImplementation(
() =>
({
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.BUILD_NUMBER,
projectDir: '/repo',
exp: fakeExp,
targets: [
{
targetName: 'example',
bundleIdentifier: 'example.com',
entitlements: {},
},
],
buildSettings: {},
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -103,31 +74,17 @@ describe(bumpVersionAsync, () => {
it('bumps expo.ios.buildNumber and CFBundleVersion for non default Info.plist location', async () => {
const fakeExp = initBareWorkflowProject({ infoPlistName: 'Info2.plist' });
getXCBuildConfigurationFromPbxproj.mockImplementation(
() =>
({
buildSettings: {
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
},
} as XCBuildConfiguration)
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.BUILD_NUMBER,
projectDir: '/repo',
exp: fakeExp,
targets: [
{
targetName: 'example',
bundleIdentifier: 'example.com',
entitlements: {},
},
],
buildSettings: {
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
},
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info2.plist'
)) as IOSConfig.InfoPlist;
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info2.plist')) as InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -136,33 +93,18 @@ describe(bumpVersionAsync, () => {
expect(infoPlist['CFBundleVersion']).toBe('2');
});
it('bumps expo.version and CFBundleShortVersionString when strategy = BumpStrategy.APP_VERSION', async () => {
it('bumps expo.version and CFBundleShortVersionString when strategy = BumpStrategy.SHORT_VERSION', async () => {
const fakeExp = initBareWorkflowProject();
getXCBuildConfigurationFromPbxproj.mockImplementation(
() =>
({
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.APP_VERSION,
projectDir: '/repo',
exp: fakeExp,
targets: [
{
targetName: 'example',
bundleIdentifier: 'example.com',
entitlements: {},
},
],
buildSettings: {},
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
expect(fakeExp.version).toBe('1.0.1');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.1');
@@ -173,31 +115,16 @@ describe(bumpVersionAsync, () => {
it('does not bump any version when strategy = BumpStrategy.NOOP', async () => {
const fakeExp = initBareWorkflowProject();
getXCBuildConfigurationFromPbxproj.mockImplementation(
() =>
({
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.NOOP,
projectDir: '/repo',
exp: fakeExp,
targets: [
{
targetName: 'example',
bundleIdentifier: 'example.com',
entitlements: {},
},
],
buildSettings: {},
});
const appJSON = await fs.readJSON('/repo/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
const infoPlist = (await readPlistAsync('/repo/ios/myproject/Info.plist')) as InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -353,9 +280,9 @@ describe(getInfoPlistPath, () => {
function initBareWorkflowProject({
appVersion = '1.0.0',
buildNumber = '1',
version = '1',
infoPlistName = 'Info.plist',
}: { appVersion?: string; buildNumber?: string; infoPlistName?: string } = {}): ExpoConfig {
}: { appVersion?: string; version?: string; infoPlistName?: string } = {}): ExpoConfig {
const fakeExp: ExpoConfig = {
name: 'myproject',
slug: 'myproject',
@@ -376,7 +303,7 @@ function initBareWorkflowProject({
<key>CFBundleShortVersionString</key>
<string>${appVersion}</string>
<key>CFBundleVersion</key>
<string>${buildNumber}</string>
<string>${version}</string>
</dict>
</plist>`,
},
+38 -32
View File
@@ -1,21 +1,23 @@
import ConfigPlugins from '@expo/config-plugins';
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { AppVersionSource } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { IosCredentials } from '../../credentials/ios/types';
import { BuildParamsInput } from '../../graphql/generated';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier';
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
import { BuildContext, CommonContext, IosBuildContext } from '../context';
import { transformMetadata } from '../graphql';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
import { ensureIosCredentialsAsync } from './credentials';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteBuildNumberAsync } from './version';
import { IosCredentials, Target } from '../../credentials/ios/types.js';
import { BuildParamsInput } from '../../graphql/generated.js';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation.js';
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier.js';
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme.js';
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target.js';
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build.js';
import { BuildContext, CommonContext, IosBuildContext } from '../context.js';
import { transformMetadata } from '../graphql.js';
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate.js';
import { ensureIosCredentialsAsync } from './credentials.js';
import { transformJob } from './graphql.js';
import { prepareJobAsync } from './prepareJob.js';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration.js';
const { IOSConfig } = ConfigPlugins;
export async function createIosContextAsync(
ctx: CommonContext<Platform.IOS>
@@ -44,22 +46,14 @@ export async function createIosContextAsync(
env: buildProfile.env,
});
const applicationTarget = findApplicationTarget(targets);
const buildNumberOverride =
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? await resolveRemoteBuildNumberAsync({
projectDir: ctx.projectDir,
projectId: ctx.projectId,
exp: ctx.exp,
applicationTarget,
buildProfile,
})
: undefined;
const applicationTargetBuildSettings = resolveBuildSettings(ctx, applicationTarget);
return {
bundleIdentifier: applicationTarget.bundleIdentifier,
applicationTarget,
applicationTargetBuildSettings,
targets,
xcodeBuildContext,
buildNumberOverride,
};
}
@@ -75,11 +69,8 @@ export async function prepareIosBuildAsync(
await syncProjectConfigurationAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
targets: ctx.ios.targets,
localAutoIncrement:
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
buildProfile: ctx.buildProfile,
buildSettings: ctx.ios.applicationTargetBuildSettings,
});
},
prepareJobAsync: async (
@@ -108,3 +99,18 @@ export async function prepareIosBuildAsync(
},
});
}
function resolveBuildSettings(
ctx: CommonContext<Platform.IOS>,
applicationTarget: Target
): XCBuildConfiguration['buildSettings'] {
if (ctx.workflow === Workflow.MANAGED) {
return {};
}
const project = IOSConfig.XcodeUtils.getPbxproj(ctx.projectDir);
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
targetName: applicationTarget.targetName,
buildConfiguration: applicationTarget.buildConfiguration,
});
return xcBuildConfiguration?.buildSettings ?? {};
}
@@ -1,11 +1,11 @@
import { Platform } from '@expo/eas-build-job';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { IosCredentials, Target } from '../../credentials/ios/types';
import { CredentialsResult } from '../build';
import { BuildContext } from '../context';
import { logCredentialsSource } from '../utils/credentials';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider.js';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils.js';
import { IosCredentials, Target } from '../../credentials/ios/types.js';
import { CredentialsResult } from '../build.js';
import { BuildContext } from '../context.js';
import { logCredentialsSource } from '../utils/credentials.js';
export async function ensureIosCredentialsAsync(
buildCtx: BuildContext<Platform.IOS>,
+3 -4
View File
@@ -1,8 +1,8 @@
import { Env, Ios } from '@expo/eas-build-job';
import nullthrows from 'nullthrows';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated.js';
import { nullthrows } from '../../utils/nullthrows.js';
import { transformProjectArchive, transformWorkflow } from '../graphql.js';
export function transformJob(job: Ios.Job): IosJobInput {
return {
@@ -14,7 +14,6 @@ export function transformJob(job: Ios.Job): IosJobInput {
secrets: transformIosSecrets(job.secrets),
builderEnvironment: job.builderEnvironment,
cache: job.cache,
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
scheme: job.scheme,
buildConfiguration: job.buildConfiguration,
artifactPath: job.artifactPath,
+5 -10
View File
@@ -2,11 +2,11 @@ import { ArchiveSource, Ios, Job, Platform, sanitizeJob } from '@expo/eas-build-
import path from 'path';
import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { getUsername } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types.js';
import { getUsername } from '../../project/projectUtils.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import { getVcsClient } from '../../vcs/index.js';
import { BuildContext } from '../context.js';
interface JobData {
projectArchive: ArchiveSource;
@@ -66,11 +66,6 @@ export async function prepareJobAsync(
buildConfiguration: ctx.buildProfile.buildConfiguration,
artifactPath: ctx.buildProfile.artifactPath,
username,
...(ctx.ios.buildNumberOverride && {
version: {
buildNumber: ctx.ios.buildNumberOverride,
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
},
@@ -1,32 +1,33 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { IosVersionAutoIncrement } from '@expo/eas-json';
import { BuildProfile, IosVersionAutoIncrement } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { Target } from '../../credentials/ios/types';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import { isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule.js';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version.js';
export async function syncProjectConfigurationAsync({
projectDir,
exp,
targets,
localAutoIncrement,
buildProfile,
buildSettings,
}: {
projectDir: string;
exp: ExpoConfig;
targets: Target[];
localAutoIncrement?: IosVersionAutoIncrement;
buildProfile: BuildProfile<Platform.IOS>;
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
const { autoIncrement } = buildProfile;
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
if (isExpoUpdatesInstalled(projectDir)) {
await syncUpdatesConfigurationAsync(projectDir, exp);
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, buildSettings });
} else {
await bumpVersionInAppJsonAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
}
+52 -187
View File
@@ -1,25 +1,18 @@
import { ExpoConfig } from '@expo/config';
import { IOSConfig, Updates } from '@expo/config-plugins';
import { ExpoConfig, getConfigFilePaths } from '@expo/config';
import ConfigPlugins, { InfoPlist } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import path from 'path';
import type { XCBuildConfiguration } from 'xcode';
import { Target } from '../../credentials/ios/types';
import { AppPlatform } from '../../graphql/generated';
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { findApplicationTarget } from '../../project/ios/target';
import { getNextBuildNumber, isValidBuildNumber } from '../../project/ios/versions';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import uniqBy from '../../utils/expodash/uniqBy';
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
import Log from '../../log.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { promptAsync } from '../../prompts.js';
import { readPlistAsync, writePlistAsync } from '../../utils/plist.js';
import { updateAppJsonConfigAsync } from '../utils/appJson.js';
import { bumpAppVersionAsync } from '../utils/version.js';
const { IOSConfig } = ConfigPlugins;
export enum BumpStrategy {
APP_VERSION,
@@ -31,25 +24,21 @@ export async function bumpVersionAsync({
bumpStrategy,
projectDir,
exp,
targets,
buildSettings,
}: {
projectDir: string;
exp: ExpoConfig;
bumpStrategy: BumpStrategy;
targets: Target[];
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<void> {
if (bumpStrategy === BumpStrategy.NOOP) {
return;
}
ensureStaticConfigExists(projectDir);
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
await bumpVersionInAppJsonAsync({ bumpStrategy, projectDir, exp });
Log.log('Updated versions in app.json');
await updateNativeVersionsAsync({
projectDir,
version: exp.version,
buildNumber: exp.ios?.buildNumber,
targets,
});
await writeVersionsToInfoPlistAsync({ projectDir, exp, infoPlist, buildSettings });
Log.log('Synchronized versions with Info.plist');
}
@@ -72,8 +61,10 @@ export async function bumpVersionInAppJsonAsync({
await bumpAppVersionAsync({ appVersion, projectDir, exp });
} else {
const buildNumber = IOSConfig.Version.getBuildNumber(exp);
if (isValidBuildNumber(buildNumber)) {
const bumpedBuildNumber = getNextBuildNumber(buildNumber);
if (buildNumber.match(/^\d+(\.\d+)*$/)) {
const comps = buildNumber.split('.');
comps[comps.length - 1] = String(Number(comps[comps.length - 1]) + 1);
const bumpedBuildNumber = comps.join('.');
Log.log(
`Bumping ${chalk.bold('expo.ios.buildNumber')} from ${chalk.bold(
buildNumber
@@ -132,21 +123,12 @@ export async function readBuildNumberAsync(
export async function maybeResolveVersionsAsync(
projectDir: string,
exp: ExpoConfig,
targets: Target[]
buildSettings: XCBuildConfiguration['buildSettings']
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
const applicationTarget = findApplicationTarget(targets);
try {
return {
appBuildVersion: await readBuildNumberAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
),
appVersion: await readShortVersionAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
),
appBuildVersion: await readBuildNumberAsync(projectDir, exp, buildSettings),
appVersion: await readShortVersionAsync(projectDir, exp, buildSettings),
};
} catch (err: any) {
Log.warn('Failed to read app versions.');
@@ -157,6 +139,23 @@ export async function maybeResolveVersionsAsync(
}
}
async function writeVersionsToInfoPlistAsync({
projectDir,
exp,
infoPlist,
buildSettings,
}: {
projectDir: string;
exp: ExpoConfig;
infoPlist: InfoPlist;
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<InfoPlist> {
let updatedInfoPlist = IOSConfig.Version.setVersion(exp, infoPlist);
updatedInfoPlist = IOSConfig.Version.setBuildNumber(exp, updatedInfoPlist);
await writeInfoPlistAsync({ projectDir, infoPlist: updatedInfoPlist, buildSettings });
return updatedInfoPlist;
}
export function getInfoPlistPath(
projectDir: string,
buildSettings: XCBuildConfiguration['buildSettings']
@@ -179,60 +178,29 @@ export function getInfoPlistPath(
async function readInfoPlistAsync(
projectDir: string,
buildSettings: XCBuildConfiguration['buildSettings']
): Promise<IOSConfig.InfoPlist> {
): Promise<InfoPlist> {
const infoPlistPath = getInfoPlistPath(projectDir, buildSettings);
return ((await readPlistAsync(infoPlistPath)) ?? {}) as IOSConfig.InfoPlist;
return ((await readPlistAsync(infoPlistPath)) ?? {}) as InfoPlist;
}
export async function updateNativeVersionsAsync({
async function writeInfoPlistAsync({
projectDir,
version,
buildNumber,
targets,
infoPlist,
buildSettings,
}: {
projectDir: string;
version?: string;
buildNumber?: string;
targets: Target[];
infoPlist: InfoPlist;
buildSettings: XCBuildConfiguration['buildSettings'];
}): Promise<void> {
const project = IOSConfig.XcodeUtils.getPbxproj(projectDir);
const iosDir = path.join(projectDir, 'ios');
const infoPlistFiles: string[] = [];
for (const target of targets) {
const { targetName, buildConfiguration } = target;
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
targetName,
buildConfiguration,
});
const infoPlist = xcBuildConfiguration?.buildSettings?.INFOPLIST_FILE;
if (infoPlist) {
const evaluatedInfoPlistPath = trimQuotes(
evaluateTemplateString(infoPlist, {
SRCROOT: iosDir,
})
);
const absolutePath = path.isAbsolute(evaluatedInfoPlistPath)
? evaluatedInfoPlistPath
: path.join(iosDir, evaluatedInfoPlistPath);
infoPlistFiles.push(path.normalize(absolutePath));
}
}
const uniqueInfoPlistPaths = uniqBy(infoPlistFiles, i => i);
for (const infoPlistPath of uniqueInfoPlistPaths) {
const infoPlist = (await readPlistAsync(infoPlistPath)) as IOSConfig.InfoPlist;
if (buildNumber) {
infoPlist.CFBundleVersion = buildNumber;
}
if (version) {
infoPlist.CFBundleShortVersionString = version;
}
await writePlistAsync(infoPlistPath, infoPlist);
}
const infoPlistPath = getInfoPlistPath(projectDir, buildSettings);
await writePlistAsync(infoPlistPath, infoPlist);
}
function trimQuotes(s: string): string {
return s?.startsWith('"') && s.endsWith('"') ? s.slice(1, -1) : s;
function ensureStaticConfigExists(projectDir: string): void {
const paths = getConfigFilePaths(projectDir);
if (!paths.staticConfigPath) {
throw new Error('autoIncrement option is not supported when using app.config.js');
}
}
export function evaluateTemplateString(
@@ -250,106 +218,3 @@ export function evaluateTemplateString(
}
});
}
/**
* Returns buildNumber that will be used for the next build. If current build profile
* has an 'autoIncrement' option set, it increments the version on server.
*/
export async function resolveRemoteBuildNumberAsync({
projectDir,
projectId,
exp,
applicationTarget,
buildProfile,
}: {
projectDir: string;
projectId: string;
exp: ExpoConfig;
applicationTarget: Target;
buildProfile: BuildProfile<Platform.IOS>;
}): Promise<string> {
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
AppPlatform.Ios,
applicationTarget.bundleIdentifier
);
const localBuildNumber = await readBuildNumberAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
);
const localShortVersion = await readShortVersionAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
);
let currentBuildVersion: string;
if (remoteVersions?.buildVersion) {
currentBuildVersion = remoteVersions.buildVersion;
} else {
if (localBuildNumber) {
Log.warn(
'No remote versions are configured for this project, buildNumber will be initialized based on the value from the local project.'
);
currentBuildVersion = localBuildNumber;
} else {
Log.error(
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
);
throw new Error('Remote versions are not configured.');
}
}
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
return currentBuildVersion;
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
const spinner = ora(
`Initializing buildNumber with ${chalk.bold(currentBuildVersion)}.`
).start();
try {
await AppVersionMutation.createAppVersionAsync({
appId: projectId,
platform: AppPlatform.Ios,
applicationIdentifier: applicationTarget.bundleIdentifier,
storeVersion: localShortVersion ?? '1.0.0',
buildVersion: currentBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
});
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
} catch (err) {
spinner.fail(`Failed to initialize buildNumber with ${chalk.bold(currentBuildVersion)}.`);
throw err;
}
return currentBuildVersion;
} else {
const nextBuildVersion = getNextBuildNumber(currentBuildVersion);
const spinner = ora(
`Incrementing buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
).start();
try {
await AppVersionMutation.createAppVersionAsync({
appId: projectId,
platform: AppPlatform.Ios,
applicationIdentifier: applicationTarget.bundleIdentifier,
storeVersion: localShortVersion ?? '1.0.0',
buildVersion: nextBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
});
spinner.succeed(
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
);
} catch (err) {
spinner.fail(
`Failed to increment buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
nextBuildVersion
)}.`
);
throw err;
}
return nextBuildVersion;
}
}
+10 -17
View File
@@ -1,12 +1,12 @@
import { Job, Metadata } from '@expo/eas-build-job';
import spawnAsync from '@expo/spawn-async';
import { Job } from '@expo/eas-build-job';
import { default as spawnAsync } from '@expo/spawn-async';
import { ChildProcess } from 'child_process';
import semver from 'semver';
import { ora } from '../ora';
import { ora } from '../ora.js';
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
+19 -52
View File
@@ -1,36 +1,37 @@
import { Updates } from '@expo/config-plugins';
import ConfigPlugins from '@expo/config-plugins';
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
import Log from '../log';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
import Log from '../log.js';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils.js';
import {
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
} from '../update/android/UpdatesModule';
} from '../update/android/UpdatesModule.js';
import {
readChannelSafelyAsync as readIosChannelSafelyAsync,
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
} from '../update/ios/UpdatesModule';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
} from '../update/ios/UpdatesModule.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { easCliVersion } from '../utils/easCli.js';
import { getVcsClient } from '../vcs/index.js';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version.js';
import { BuildContext } from './context.js';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version.js';
const { Updates } = ConfigPlugins;
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<Metadata> {
const vcsClient = getVcsClient();
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
? 'simulator'
: ctx.buildProfile.distribution) ?? 'store';
const metadata: Metadata = {
const metadata = {
trackingContext: ctx.trackingCtx,
...(await maybeResolveVersionsAsync(ctx)),
cliVersion: easCliVersion,
@@ -44,20 +45,14 @@ export async function collectMetadataAsync<T extends Platform>(
appName: ctx.exp.name,
appIdentifier: resolveAppIdentifier(ctx),
buildProfile: ctx.buildProfileName,
gitCommitHash: await vcsClient.getCommitHashAsync(),
gitCommitMessage: truncateGitCommitMessage(
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
),
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
gitCommitHash: await getVcsClient().getCommitHashAsync(),
isGitWorkingTreeDirty: await getVcsClient().hasUncommittedChangesAsync(),
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
ctx as BuildContext<Platform.IOS>
),
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
};
return sanitizeMetadata(metadata);
}
@@ -67,32 +62,14 @@ async function maybeResolveVersionsAsync<T extends Platform>(
): Promise<{ appBuildVersion?: string; appVersion?: string }> {
if (ctx.platform === Platform.IOS) {
const iosContext = ctx as BuildContext<Platform.IOS>;
const resolvedVersion = await maybeResolveIosVersionsAsync(
return await maybeResolveIosVersionsAsync(
ctx.projectDir,
ctx.exp,
iosContext.ios.targets
iosContext.ios.applicationTargetBuildSettings
);
if (iosContext.ios.buildNumberOverride) {
return {
...resolvedVersion,
appBuildVersion: iosContext.ios.buildNumberOverride,
};
}
return resolvedVersion;
} else if (ctx.platform === Platform.ANDROID) {
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
ctx.projectDir,
ctx.exp,
androidCtx.buildProfile
);
if (androidCtx.android.versionCodeOverride) {
return {
...resolvedVersion,
appBuildVersion: androidCtx.android.versionCodeOverride,
};
}
return resolvedVersion;
return await maybeResolveAndroidVersionsAsync(ctx.projectDir, ctx.exp, androidCtx.buildProfile);
} else {
throw new Error(`Unsupported platform ${ctx.platform}`);
}
@@ -174,13 +151,3 @@ function resolveIosEnterpriseProvisioning(
): IosEnterpriseProvisioning | undefined {
return ctx.buildProfile.enterpriseProvisioning;
}
export function truncateGitCommitMessage(
msg: string | undefined,
maxLength: number = 4096
): string | undefined {
if (msg === undefined) {
return undefined;
}
return msg.length > maxLength ? `${msg.substring(0, maxLength - 3)}...` : msg;
}
+30 -81
View File
@@ -1,60 +1,45 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import {
AppVersionSource,
BuildProfile,
EasJson,
EasJsonReader,
SubmitProfile,
} from '@expo/eas-json';
import { BuildProfile, EasJsonReader, SubmitProfile } from '@expo/eas-json';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import {
AppPlatform,
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';
} from '../graphql/generated.js';
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform.js';
import Log from '../log.js';
import {
RequestedPlatform,
appPlatformDisplayNames,
appPlatformEmojis,
toPlatforms,
} from '../platform';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
} from '../platform.js';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk.js';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig.js';
import { createSubmissionContextAsync } from '../submit/context.js';
import {
validateAppConfigForRemoteVersionSource,
validateBuildProfileVersionSettings,
} from '../project/remoteVersionSource';
import { createSubmissionContextAsync } from '../submit/context';
import {
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish,
submitAsync,
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
} from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
import { checkBuildProfileConfigMatchesProjectConfigAsync } from '../update/utils';
import { printJsonOnlyOutput } from '../utils/json';
import { ProfileData, getProfilesAsync } from '../utils/profiles';
import { getVcsClient } from '../vcs';
import { prepareAndroidBuildAsync } from './android/build';
import { BuildRequestSender, waitForBuildEndAsync } from './build';
import { ensureProjectConfiguredAsync } from './configure';
import { BuildContext } from './context';
import { createBuildContextAsync } from './createContext';
import { prepareIosBuildAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { UserInputResourceClass } from './types';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
import { ensureRepoIsCleanAsync } from './utils/repository';
} from '../submit/submit.js';
import { printSubmissionDetailsUrls } from '../submit/utils/urls.js';
import { printJsonOnlyOutput } from '../utils/json.js';
import { nullthrows } from '../utils/nullthrows.js';
import { ProfileData, getProfilesAsync } from '../utils/profiles.js';
import { getVcsClient } from '../vcs/index.js';
import { prepareAndroidBuildAsync } from './android/build.js';
import { BuildRequestSender, waitForBuildEndAsync } from './build.js';
import { ensureProjectConfiguredAsync } from './configure.js';
import { BuildContext } from './context.js';
import { createBuildContextAsync } from './createContext.js';
import { prepareIosBuildAsync } from './ios/build.js';
import { LocalBuildOptions } from './local.js';
import { UserInputResourceClass } from './types.js';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient.js';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo.js';
import { ensureRepoIsCleanAsync } from './utils/repository.js';
let metroConfigValidated = false;
let sdkVersionChecked = false;
@@ -70,7 +55,6 @@ export interface BuildFlags {
submitProfile?: string;
localBuildOptions: LocalBuildOptions;
userInputResourceClass?: UserInputResourceClass;
message?: string;
}
const platformToGraphQLResourceClassMapping: Record<
@@ -96,7 +80,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
nonInteractive: flags.nonInteractive,
});
const easJsonReader = new EasJsonReader(projectDir);
const easJsonCliConfig: EasJson['cli'] = (await easJsonReader.getCliConfigAsync()) ?? {};
const platforms = toPlatforms(flags.requestedPlatform);
const buildProfiles = await getProfilesAsync({
@@ -112,22 +95,12 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
buildProfiles,
});
for (const buildProfile of buildProfiles) {
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
}
const startedBuilds: {
build: BuildWithSubmissionsFragment | BuildFragment;
build: BuildFragment;
buildProfile: ProfileData<'build'>;
}[] = [];
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
// Check only first buildprofile (there should be only one unique one)
// Warn only once
if (buildProfiles.length > 0) {
await checkBuildProfileConfigMatchesProjectConfigAsync(projectDir, buildProfiles[0]);
}
for (const buildProfile of buildProfiles) {
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
projectDir,
@@ -138,7 +111,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
platformToGraphQLResourceClassMapping[buildProfile.platform][
flags.userInputResourceClass ?? UserInputResourceClass.DEFAULT
],
easJsonCliConfig,
});
if (maybeBuild) {
startedBuilds.push({ build: maybeBuild, buildProfile });
@@ -177,7 +149,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
submitProfile,
nonInteractive: flags.nonInteractive,
});
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(startedBuild.build.id);
submissions.push(submission);
}
@@ -198,30 +169,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);
}
}
@@ -231,14 +188,12 @@ async function prepareAndStartBuildAsync({
moreBuilds,
buildProfile,
resourceClass,
easJsonCliConfig,
}: {
projectDir: string;
flags: BuildFlags;
moreBuilds: boolean;
buildProfile: ProfileData<'build'>;
resourceClass: BuildResourceClass;
easJsonCliConfig: EasJson['cli'];
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
const buildCtx = await createBuildContextAsync({
buildProfileName: buildProfile.profileName,
@@ -246,12 +201,9 @@ async function prepareAndStartBuildAsync({
clearCache: flags.clearCache,
buildProfile: buildProfile.profile,
nonInteractive: flags.nonInteractive,
noWait: !flags.wait,
platform: buildProfile.platform,
projectDir,
localBuildOptions: flags.localBuildOptions,
easJsonCliConfig,
message: flags.message,
});
if (moreBuilds) {
@@ -263,10 +215,7 @@ async function prepareAndStartBuildAsync({
)}`
);
}
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
validateAppConfigForRemoteVersionSource(buildCtx.exp, buildProfile.platform);
}
if (buildCtx.workflow === Workflow.MANAGED) {
if (!sdkVersionChecked) {
await checkExpoSdkIsSupportedAsync(buildCtx);
+3 -1
View File
@@ -1,7 +1,9 @@
import * as ExpoConfig from '@expo/config';
import JsonFile from '@expo/json-file';
import JsonFileModule from '@expo/json-file';
import assert from 'assert';
const JsonFile = JsonFileModule.default;
export async function updateAppJsonConfigAsync(
{
projectDir,
@@ -2,8 +2,8 @@ import { Platform } from '@expo/eas-build-job';
import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import Log from '../../log';
import { requestedPlatformDisplayNames } from '../../platform';
import Log from '../../log.js';
import { requestedPlatformDisplayNames } from '../../platform.js';
export function logCredentialsSource(
credentialsSource: CredentialsSource,
@@ -3,15 +3,15 @@ import { Errors } from '@oclif/core';
import chalk from 'chalk';
import resolveFrom from 'resolve-from';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames } from '../../platform';
import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCli';
import { ProfileData } from '../../utils/profiles';
import { getVcsClient } from '../../vcs';
import { reviewAndCommitChangesAsync } from './repository';
import { toAppPlatform } from '../../graphql/types/AppPlatform.js';
import Log, { learnMore } from '../../log.js';
import { appPlatformDisplayNames } from '../../platform.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { confirmAsync } from '../../prompts.js';
import { expoCommandAsync } from '../../utils/expoCli.js';
import { ProfileData } from '../../utils/profiles.js';
import { getVcsClient } from '../../vcs/index.js';
import { reviewAndCommitChangesAsync } from './repository.js';
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
projectDir,
@@ -4,10 +4,10 @@ import {
AppPlatform,
BuildFragment,
BuildStatus as GraphQLBuildStatus,
} from '../../graphql/generated';
import { appPlatformDisplayNames } from '../../platform';
import formatFields from '../../utils/formatFields';
import { getBuildLogsUrl } from './url';
} from '../../graphql/generated.js';
import { appPlatformDisplayNames } from '../../platform.js';
import formatFields from '../../utils/formatFields.js';
import { getBuildLogsUrl } from './url.js';
export function formatGraphQLBuild(build: BuildFragment): string {
const actor = getActorName(build);
@@ -11,10 +11,11 @@ import {
DistributionType,
EasBuildDeprecationInfo,
EasBuildDeprecationInfoType,
} from '../../graphql/generated';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
} from '../../graphql/generated.js';
import Log, { learnMore } from '../../log.js';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform.js';
import { printJsonOnlyOutput } from '../../utils/json.js';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url.js';
export function printLogsUrls(builds: BuildFragment[]): void {
if (builds.length === 1) {
@@ -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));
}
}
}
@@ -4,13 +4,13 @@ import path from 'path';
import tar from 'tar';
import { v4 as uuidv4 } from 'uuid';
import Log from '../../log';
import { ora } from '../../ora';
import { confirmAsync, promptAsync } from '../../prompts';
import { formatBytes } from '../../utils/files';
import { getTmpDirectory } from '../../utils/paths';
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
import { getVcsClient } from '../../vcs';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { confirmAsync, promptAsync } from '../../prompts.js';
import { formatBytes } from '../../utils/files.js';
import { getTmpDirectory } from '../../utils/paths.js';
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer.js';
import { getVcsClient } from '../../vcs/index.js';
export async function maybeBailOnRepoStatusAsync(): Promise<void> {
if (!(await getVcsClient().isCommitRequiredAsync())) {
+2 -2
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import { getExpoApiBaseUrl, getExpoWebsiteBaseUrl } from '../../api';
import { AppPlatform, BuildFragment } from '../../graphql/generated';
import { getExpoApiBaseUrl, getExpoWebsiteBaseUrl } from '../../api.js';
import { AppPlatform, BuildFragment } from '../../graphql/generated.js';
export function getProjectDashboardUrl(accountName: string, projectName: string): string {
return new URL(
+4 -4
View File
@@ -1,11 +1,11 @@
import { ExpoConfig, getConfigFilePaths } from '@expo/config';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import semver from 'semver';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { updateAppJsonConfigAsync } from './appJson';
import Log from '../../log.js';
import { promptAsync } from '../../prompts.js';
import { nullthrows } from '../../utils/nullthrows.js';
import { updateAppJsonConfigAsync } from './appJson.js';
export function ensureStaticConfigExists(projectDir: string): void {
const paths = getConfigFilePaths(projectDir);
+3 -3
View File
@@ -2,9 +2,9 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import fs from 'fs-extra';
import path from 'path';
import Log, { learnMore } from '../log';
import { getVcsClient } from '../vcs';
import { CommonContext } from './context';
import Log, { learnMore } from '../log.js';
import { getVcsClient } from '../vcs/index.js';
import { CommonContext } from './context.js';
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
@@ -7,13 +7,13 @@ import {
flushAsync as flushAnalyticsAsync,
initAsync as initAnalyticsAsync,
logEvent,
} from '../analytics/rudderstackClient';
import { findProjectRootAsync } from '../project/projectUtils';
import { getUserAsync } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { setVcsClient } from '../vcs';
import GitClient from '../vcs/clients/git';
} from '../analytics/rudderstackClient.js';
import { findProjectRootAsync } from '../project/projectUtils.js';
import { getUserAsync } from '../user/User.js';
import { ensureLoggedInAsync } from '../user/actions.js';
import { easCliVersion } from '../utils/easCli.js';
import GitClient from '../vcs/clients/git.js';
import { setVcsClient } from '../vcs/index.js';
export default abstract class EasCommand extends Command {
/**
@@ -1,8 +1,8 @@
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
import { getUserAsync } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand from '../EasCommand';
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient.js';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants.js';
import { getUserAsync } from '../../user/User.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
import EasCommand from '../EasCommand.js';
jest.mock('../../user/User');
jest.mock('../../user/actions', () => ({ ensureLoggedInAsync: jest.fn() }));
@@ -1,58 +0,0 @@
import { Flags } from '@oclif/core';
export const getPaginatedQueryOptions = (
flags: Record<keyof typeof EasPaginatedQueryFlags, any>
): PaginatedQueryOptions => {
return {
json: flags.json ?? false,
offset: flags.offset ?? 0,
nonInteractive: flags['non-interactive'] ?? false,
...('limit' in flags && { limit: flags.limit }),
};
};
const parseFlagInputStringAsInteger = (
input: string,
flagName: string,
lowerLimit: number,
upperLimit: number
): number => {
const inputAsNumber = Number(input);
if (isNaN(inputAsNumber)) {
throw new Error(`Unable to parse ${input} as a number`);
}
if (inputAsNumber < lowerLimit || inputAsNumber > upperLimit) {
throw new Error(`--${flagName} must be between ${lowerLimit} and ${upperLimit}`);
}
return inputAsNumber;
};
export const EasPaginatedQueryFlags = {
offset: Flags.integer({
description: 'Start queries from specified index. Use for paginating results. Defaults to 0.',
// eslint-disable-next-line async-protect/async-suffix
parse: async input =>
parseFlagInputStringAsInteger(input, 'offset', 0, Number.MAX_SAFE_INTEGER),
}),
limit: Flags.integer({
description:
'The number of query items to list at once. The default value is 50 (the maximum is 100). Using a lower value may help increase command speed.',
// eslint-disable-next-line async-protect/async-suffix
parse: async input => parseFlagInputStringAsInteger(input, 'limit', 1, 100),
}),
json: Flags.boolean({
description: 'Enable JSON output, non-JSON messages will be printed to stderr.',
}),
'non-interactive': Flags.boolean({
description: 'Run the command in non-interactive mode.',
}),
};
// options required to control a paginated query
// from user input
export type PaginatedQueryOptions = {
nonInteractive: boolean;
json: boolean;
limit?: number;
offset: number;
};
@@ -1,6 +1,6 @@
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { showLoginPromptAsync } from '../../user/actions';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { showLoginPromptAsync } from '../../user/actions.js';
export default class AccountLogin extends EasCommand {
static description = 'log in with your Expo account';
@@ -1,6 +1,6 @@
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { logoutAsync } from '../../user/User';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { logoutAsync } from '../../user/User.js';
export default class AccountLogout extends EasCommand {
static description = 'log out';
@@ -1,8 +1,8 @@
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { getActorDisplayName, getUserAsync } from '../../user/User';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { getActorDisplayName, getUserAsync } from '../../user/User.js';
export default class AccountView extends EasCommand {
static description = 'show the username you are logged in as';
+3 -3
View File
@@ -1,6 +1,6 @@
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import UserSettings from '../user/UserSettings';
import EasCommand from '../commandUtils/EasCommand.js';
import Log from '../log.js';
import UserSettings from '../user/UserSettings.js';
export default class AnalyticsView extends EasCommand {
static description = 'display or change analytics settings';
+10 -10
View File
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
CreateUpdateBranchForAppMutation,
CreateUpdateBranchForAppMutationVariables,
UpdateBranch,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { getVcsClient } from '../../vcs';
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { getVcsClient } from '../../vcs/index.js';
export async function createUpdateBranchOnAppAsync({
appId,
@@ -1,25 +1,25 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
DeleteUpdateBranchMutation,
DeleteUpdateBranchMutationVariables,
DeleteUpdateBranchResult,
GetBranchInfoQuery,
GetBranchInfoQueryVariables,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
async function getBranchInfoAsync({
appId,
+89 -11
View File
@@ -1,28 +1,106 @@
import { listAndRenderPaginatedBranchesAsync } from '../../branch/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import CliTable from 'cli-table3';
import { print } from 'graphql';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
BranchesByAppQuery,
BranchesByAppQueryVariables,
UpdateBranchFragment,
} from '../../graphql/generated.js';
import { UpdateBranchFragmentNode } from '../../graphql/types/UpdateBranch.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
const BRANCHES_LIMIT = 10_000;
export async function listBranchesAsync({
projectId,
}: {
projectId: string;
}): Promise<UpdateBranchFragment[]> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<BranchesByAppQuery, BranchesByAppQueryVariables>(
gql`
query BranchesByAppQuery($appId: String!, $limit: Int!) {
app {
byId(appId: $appId) {
id
updateBranches(offset: 0, limit: $limit) {
id
...UpdateBranchFragment
}
}
}
}
${print(UpdateBranchFragmentNode)}
`,
{
appId: projectId,
limit: BRANCHES_LIMIT,
},
{ additionalTypenames: ['UpdateBranch'] }
)
.toPromise()
);
return data?.app?.byId.updateBranches ?? [];
}
export default class BranchList extends EasCommand {
static description = 'list all branches';
static flags = {
...EasPaginatedQueryFlags,
json: Flags.boolean({
description: 'return output as JSON',
default: false,
}),
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BranchList);
const options = getPaginatedQueryOptions(flags);
if (options.json) {
if (flags.json) {
enableJsonOutput();
}
const projectDir = await findProjectRootAsync();
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp);
await listAndRenderPaginatedBranchesAsync(projectId, options);
const branches = await listBranchesAsync({ projectId });
if (flags.json) {
printJsonOnlyOutput(branches);
} else {
const table = new CliTable({
head: ['Branch', ...UPDATE_COLUMNS],
wordWrap: true,
});
table.push(
...branches.map(branch => [
branch.name,
formatUpdate(branch.updates[0]),
branch.updates[0]?.runtimeVersion ?? 'N/A',
branch.updates[0]?.group ?? 'N/A',
getPlatformsForGroup({
updates: branch.updates,
group: branch.updates[0]?.group,
}),
])
);
Log.addNewLineIfNone();
Log.log(chalk.bold('Branches with their most recent update group:'));
Log.log(table.toString());
if (branches.length >= BRANCHES_LIMIT) {
Log.warn(`Showing first ${BRANCHES_LIMIT} branches, some results might be omitted.`);
}
}
}
}
@@ -1,4 +1,4 @@
import EasCommand from '../../commandUtils/EasCommand';
import EasCommand from '../../commandUtils/EasCommand.js';
export default class BranchPublish extends EasCommand {
static description = 'deprecated, use "eas update"';
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
EditUpdateBranchInput,
EditUpdateBranchMutation,
EditUpdateBranchMutationVariables,
UpdateBranch,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
async function renameUpdateBranchOnAppAsync({
appId,
+76 -25
View File
@@ -1,10 +1,17 @@
import { selectBranchFromPaginatedQueryAsync } from '../../branch/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { listAndRenderUpdatesOnBranchByNameAsync } from '../../update/queries';
import { enableJsonOutput } from '../../utils/json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import Table from 'cli-table3';
import EasCommand from '../../commandUtils/EasCommand.js';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils.js';
import groupBy from '../../utils/expodash/groupBy.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
export default class BranchView extends EasCommand {
static description = 'view a branch';
@@ -18,17 +25,18 @@ export default class BranchView extends EasCommand {
];
static flags = {
...EasPaginatedQueryFlags,
json: Flags.boolean({
description: `return a json with the branch's ID name and recent update groups.`,
default: false,
}),
};
async runAsync(): Promise<void> {
let {
args: { name: branchName },
flags,
args: { name },
flags: { json: jsonFlag },
} = await this.parse(BranchView);
const options = getPaginatedQueryOptions(flags);
if (options.json) {
if (jsonFlag) {
enableJsonOutput();
}
@@ -36,20 +44,63 @@ export default class BranchView extends EasCommand {
const exp = getExpoConfig(projectDir);
const projectId = await getProjectIdAsync(exp);
// provide help to a user if they ran the command with missing args
if (!branchName) {
if (options.nonInteractive) {
throw new Error('Branch name may not be empty.');
if (!name) {
const validationMessage = 'Branch name may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
}
({ name: branchName } = await selectBranchFromPaginatedQueryAsync(
projectId,
'Which branch would you like to view?',
// discard limit and offset because this query is not those flag's intended target
{ json: options.json, nonInteractive: options.nonInteractive, offset: 0 }
));
({ name } = await promptAsync({
type: 'text',
name: 'name',
message: 'Please enter the name of the branch to view:',
validate: value => (value ? true : validationMessage),
}));
}
await listAndRenderUpdatesOnBranchByNameAsync(projectId, branchName, options);
const { app } = await UpdateQuery.viewBranchAsync({
appId: projectId,
name,
});
const UpdateBranch = app?.byId.updateBranchByName;
if (!UpdateBranch) {
throw new Error(`Could not find branch "${name}"`);
}
const updates = Object.values(groupBy(UpdateBranch.updates, u => u.group)).map(
group => group[0]
);
if (jsonFlag) {
printJsonOnlyOutput({ ...UpdateBranch, updates });
} else {
const groupTable = new Table({
head: UPDATE_COLUMNS,
wordWrap: true,
});
for (const update of updates) {
groupTable.push([
formatUpdate(update),
update.runtimeVersion,
update.group,
getPlatformsForGroup({
updates: UpdateBranch.updates,
group: update.group,
}),
]);
}
Log.addNewLineIfNone();
Log.log(chalk.bold('Branch:'));
Log.log(
formatFields([
{ label: 'Name', value: UpdateBranch.name },
{ label: 'ID', value: UpdateBranch.id },
])
);
Log.addNewLineIfNone();
Log.log(chalk.bold('Recently published update groups:'));
Log.log(groupTable.toString());
}
}
}
+11 -11
View File
@@ -1,25 +1,25 @@
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
Build,
BuildStatus,
CancelBuildMutation,
CancelBuildMutationVariables,
} from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { appPlatformEmojis } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { appPlatformEmojis } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { confirmAsync, selectAsync } from '../../prompts';
} from '../../project/projectUtils.js';
import { confirmAsync, selectAsync } from '../../prompts.js';
async function cancelBuildAsync(buildId: string): Promise<Pick<Build, 'id' | 'status'>> {
const data = await withErrorHandlingAsync(
@@ -2,18 +2,18 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { cleanUpOldEasBuildGradleScriptAsync } from '../../build/android/syncProjectConfiguration';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import EasCommand from '../../commandUtils/EasCommand';
import Log, { learnMore } from '../../log';
import { RequestedPlatform } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { getVcsClient } from '../../vcs';
import { cleanUpOldEasBuildGradleScriptAsync } from '../../build/android/syncProjectConfiguration.js';
import { ensureProjectConfiguredAsync } from '../../build/configure.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log, { learnMore } from '../../log.js';
import { RequestedPlatform } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, isExpoUpdatesInstalled } from '../../project/projectUtils.js';
import { resolveWorkflowAsync } from '../../project/workflow.js';
import { promptAsync } from '../../prompts.js';
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule.js';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule.js';
import { getVcsClient } from '../../vcs/index.js';
export default class BuildConfigure extends EasCommand {
static description = 'configure the project to support EAS Build';
+7 -18
View File
@@ -1,13 +1,13 @@
import { Errors, Flags } from '@oclif/core';
import path from 'path';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import { UserInputResourceClass } from '../../build/types';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { enableJsonOutput } from '../../utils/json';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit.js';
import { UserInputResourceClass } from '../../build/types.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform.js';
import { findProjectRootAsync } from '../../project/projectUtils.js';
import { enableJsonOutput } from '../../utils/json.js';
interface RawBuildFlags {
platform?: string;
@@ -23,7 +23,6 @@ interface RawBuildFlags {
'auto-submit': boolean;
'auto-submit-with-profile'?: string;
'resource-class'?: UserInputResourceClass;
message?: string;
}
export default class Build extends EasCommand {
@@ -87,10 +86,6 @@ export default class Build extends EasCommand {
hidden: true,
description: 'The instance type that will be used to run this build [experimental]',
}),
message: Flags.string({
char: 'm',
description: 'A short message describing the build',
}),
};
async runAsync(): Promise<void> {
@@ -148,11 +143,6 @@ export default class Build extends EasCommand {
Log.newLine();
}
const message = flags['message'];
if (message && message.length > 1024) {
Errors.error('Message cannot be longer than 1024 characters.', { exit: 1 });
}
const profile = flags['profile'];
return {
requestedPlatform,
@@ -173,7 +163,6 @@ export default class Build extends EasCommand {
autoSubmit: flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined,
submitProfile: flags['auto-submit-with-profile'] ?? profile,
userInputResourceClass: flags['resource-class'] ?? UserInputResourceClass.DEFAULT,
message,
};
}
}
@@ -4,14 +4,14 @@ import fs from 'fs-extra';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { getTmpDirectory } from '../../utils/paths';
import { getVcsClient } from '../../vcs';
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { RequestedPlatform } from '../../platform.js';
import { findProjectRootAsync } from '../../project/projectUtils.js';
import { getTmpDirectory } from '../../utils/paths.js';
import { getVcsClient } from '../../vcs/index.js';
enum InspectStage {
ARCHIVE = 'archive',
+11 -11
View File
@@ -1,25 +1,25 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { BuildDistributionType, BuildStatus } from '../../build/types';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import { BuildDistributionType, BuildStatus } from '../../build/types.js';
import { formatGraphQLBuild } from '../../build/utils/formatBuild.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import {
AppPlatform,
DistributionType,
BuildStatus as GraphQLBuildStatus,
} from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { RequestedPlatform } from '../../platform.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../project/projectUtils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
export default class BuildList extends EasCommand {
static description = 'list all builds for your project';
@@ -1,113 +0,0 @@
import { getRuntimeVersionNullable } from '@expo/config-plugins/build/utils/Updates';
import { Platform } from '@expo/eas-build-job';
import { EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../../commandUtils/EasCommand';
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
import Log from '../../../log';
import { selectPlatformAsync } from '../../../platform';
import { VERSION_CODE_REQUIREMENTS, isValidVersionCode } from '../../../project/android/versions';
import { getApplicationIdentifierAsync } from '../../../project/applicationIdentifier';
import { getExpoConfig } from '../../../project/expoConfig';
import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/ios/versions';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../../project/projectUtils';
import {
ensureVersionSourceIsRemoteAsync,
getBuildVersionName,
validateAppConfigForRemoteVersionSource,
} from '../../../project/remoteVersionSource';
import { promptAsync } from '../../../prompts';
export default class BuildVersionSetView extends EasCommand {
static description = 'Update version of an app.';
static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios'],
}),
profile: Flags.string({
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
};
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSetView);
const projectDir = await findProjectRootAsync();
const platform = await selectPlatformAsync(flags.platform);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const profile = await easJsonReader.getBuildProfileAsync(platform, flags.profile ?? undefined);
const exp = getExpoConfig(projectDir, { env: profile.env });
const projectId = await getProjectIdAsync(exp);
const projectFullName = await getProjectFullNameAsync(exp);
validateAppConfigForRemoteVersionSource(exp, platform);
const applicationIdentifier = await getApplicationIdentifierAsync(
projectDir,
exp,
profile,
platform
);
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
toAppPlatform(platform),
applicationIdentifier
);
const currentStateMessage = remoteVersions?.buildVersion
? `Project ${chalk.bold(projectFullName)} with ${getApplicationIdentifierName(
platform
)} "${applicationIdentifier}" is configured with ${getBuildVersionName(platform)} ${
remoteVersions.buildVersion
}.`
: `Project ${chalk.bold(projectFullName)} with ${getApplicationIdentifierName(
platform
)} "${applicationIdentifier}" does not have any ${getBuildVersionName(
platform
)} configured.`;
const versionPromptMessage = remoteVersions?.buildVersion
? `What version would you like to set?`
: `What version would you like to initialize it with?`;
Log.log(currentStateMessage);
const { version } = await promptAsync({
type: platform === Platform.ANDROID ? 'number' : 'text',
name: 'version',
message: versionPromptMessage,
validate:
platform === Platform.ANDROID
? value => isValidVersionCode(value) || `Invalid value: ${VERSION_CODE_REQUIREMENTS}.`
: value => isValidBuildNumber(value) || `Invalid value: ${BUILD_NUMBER_REQUIREMENTS}.`,
});
await AppVersionMutation.createAppVersionAsync({
appId: projectId,
platform: toAppPlatform(platform),
applicationIdentifier,
storeVersion: exp.version ?? '1.0.0',
buildVersion: String(version),
runtimeVersion: getRuntimeVersionNullable(exp, platform) ?? undefined,
});
}
}
function getApplicationIdentifierName(platform: Platform): string {
if (platform === Platform.ANDROID) {
return 'application ID';
} else {
return 'bundle identifier';
}
}
@@ -1,183 +0,0 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../../build/android/version';
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../../build/ios/version';
import EasCommand from '../../../commandUtils/EasCommand';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
import Log from '../../../log';
import {
appPlatformDisplayNames,
selectRequestedPlatformAsync,
toPlatforms,
} from '../../../platform';
import { getAppBuildGradleAsync } from '../../../project/android/gradleUtils';
import { VERSION_CODE_REQUIREMENTS, isValidVersionCode } from '../../../project/android/versions';
import { getApplicationIdentifierAsync } from '../../../project/applicationIdentifier';
import { getExpoConfig } from '../../../project/expoConfig';
import { resolveXcodeBuildContextAsync } from '../../../project/ios/scheme';
import { resolveTargetsAsync } from '../../../project/ios/target';
import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/ios/versions';
import { findProjectRootAsync, getProjectIdAsync } from '../../../project/projectUtils';
import {
ensureVersionSourceIsRemoteAsync,
getBuildVersionName,
validateAppConfigForRemoteVersionSource,
} from '../../../project/remoteVersionSource';
import { resolveWorkflowAsync } from '../../../project/workflow';
import { getProfilesAsync } from '../../../utils/profiles';
interface SyncContext<T extends Platform> {
projectDir: string;
exp: ExpoConfig;
workflow: Workflow;
profile: BuildProfile<T>;
buildVersion: string;
}
export default class BuildVersionSyncView extends EasCommand {
public static description = 'Update a version in native code with a value stored on EAS servers';
public static flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios', 'all'],
}),
profile: Flags.string({
description:
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
};
public async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildVersionSyncView);
const projectDir = await findProjectRootAsync();
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
const easJsonReader = new EasJsonReader(projectDir);
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
const platforms = toPlatforms(requestedPlatform);
const buildProfiles = await getProfilesAsync({
type: 'build',
easJsonReader,
platforms,
profileName: flags.profile ?? undefined,
});
for (const profileInfo of buildProfiles) {
const exp = getExpoConfig(projectDir, { env: profileInfo.profile.env });
const projectId = await getProjectIdAsync(exp);
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
const platformDisplayName = appPlatformDisplayNames[toAppPlatform(profileInfo.platform)];
const applicationIdentifier = await getApplicationIdentifierAsync(
projectDir,
exp,
profileInfo.profile,
profileInfo.platform
);
const remoteVersions = await AppVersionQuery.latestVersionAsync(
projectId,
toAppPlatform(profileInfo.platform),
applicationIdentifier
);
const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform);
if (!remoteVersions?.buildVersion) {
Log.warn(
`Skipping versions sync for ${platformDisplayName}. There are no versions configured on Expo servers, use "eas build:version:set" or run a build to initialize it.`
);
continue;
}
if (workflow === Workflow.MANAGED) {
Log.warn(
`The remote value for the ${platformDisplayName} ${getBuildVersionName(
profileInfo.platform
)} is ${chalk.bold(
remoteVersions?.buildVersion
)}, but it was not synced to the local project. This command has no effect on projects using managed workflow.`
);
continue;
}
if (profileInfo.platform === Platform.ANDROID) {
this.syncAndroidAsync({
projectDir,
exp,
profile: profileInfo.profile as BuildProfile<Platform.ANDROID>,
workflow,
buildVersion: remoteVersions.buildVersion,
});
} else {
this.syncIosAsync({
projectDir,
exp,
profile: profileInfo.profile as BuildProfile<Platform.IOS>,
workflow,
buildVersion: remoteVersions.buildVersion,
});
}
Log.withTick(
`Successfully updated the ${platformDisplayName} ${getBuildVersionName(
profileInfo.platform
)} in native code to ${chalk.bold(remoteVersions?.buildVersion)}.`
);
}
}
private async syncIosAsync({
workflow,
projectDir,
exp,
profile,
buildVersion,
}: SyncContext<Platform.IOS>): Promise<void> {
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{ exp, projectDir, nonInteractive: false },
profile
);
const targets = await resolveTargetsAsync({
projectDir,
exp,
xcodeBuildContext,
env: profile.env,
});
if (!isValidBuildNumber(buildVersion)) {
throw new Error(`Invalid buildNumber (${buildVersion}), ${BUILD_NUMBER_REQUIREMENTS}.`);
}
if (workflow === Workflow.GENERIC) {
await updateIosNativeVersionsAsync({
projectDir,
buildNumber: buildVersion,
targets,
});
}
}
private async syncAndroidAsync({
projectDir,
workflow,
buildVersion,
}: SyncContext<Platform.ANDROID>): Promise<void> {
if (!isValidVersionCode(buildVersion)) {
throw new Error(`Invalid versionCode (${buildVersion}), ${VERSION_CODE_REQUIREMENTS}.`);
}
if (workflow === Workflow.GENERIC) {
const buildGradle = await getAppBuildGradleAsync(projectDir);
const isMultiFlavor =
buildGradle.android?.productFlavors || buildGradle.android?.flavorDimensions;
if (isMultiFlavor) {
throw new Error(
"Versions in native code can't be automatically synced in multi-flavor Android projects. If you are using EAS Build with app version source set to remote, the correct values will be injected at the build time."
);
}
await updateAndroidNativeVersionsAsync({ projectDir, versionCode: Number(buildVersion) });
}
}
}
+9 -9
View File
@@ -1,18 +1,18 @@
import { Flags } from '@oclif/core';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import { BuildFragment } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { formatGraphQLBuild } from '../../build/utils/formatBuild.js';
import EasCommand from '../../commandUtils/EasCommand.js';
import { BuildFragment } from '../../graphql/generated.js';
import { BuildQuery } from '../../graphql/queries/BuildQuery.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../project/projectUtils.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
export default class BuildView extends EasCommand {
static description = 'view a build for your project';
+12 -12
View File
@@ -1,25 +1,25 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
CreateUpdateChannelOnAppMutation,
CreateUpdateChannelOnAppMutationVariables,
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { createUpdateBranchOnAppAsync } from '../branch/create';
} from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { createUpdateBranchOnAppAsync } from '../branch/create.js';
export async function createUpdateChannelOnAppAsync({
appId,
@@ -1,24 +1,24 @@
import { Flags } from '@oclif/core';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
DeleteUpdateChannelMutation,
DeleteUpdateChannelMutationVariables,
DeleteUpdateChannelResult,
GetChannelInfoQuery,
GetChannelInfoQueryVariables,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
export default class ChannelDelete extends EasCommand {
static hidden = true;
+10 -10
View File
@@ -1,9 +1,9 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
GetChannelByNameToEditQuery,
GetChannelByNameToEditQueryVariables,
@@ -11,13 +11,13 @@ import {
UpdateChannel,
UpdateChannelBranchMappingMutation,
UpdateChannelBranchMappingMutationVariables,
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
async function getChannelByNameForAppAsync({
appId,
+10 -10
View File
@@ -1,18 +1,18 @@
import { Flags } from '@oclif/core';
import gql from 'graphql-tag';
import { gql } from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import EasCommand from '../../commandUtils/EasCommand.js';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client.js';
import {
GetAllChannelsForAppQuery,
GetAllChannelsForAppQueryVariables,
} from '../../graphql/generated';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { logChannelDetails } from './view';
} from '../../graphql/generated.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { logChannelDetails } from './view.js';
const CHANNEL_LIMIT = 10_000;
@@ -1,21 +1,21 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { GetChannelByNameForAppQuery, UpdateBranch } from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import EasCommand from '../../commandUtils/EasCommand.js';
import { GetChannelByNameForAppQuery, UpdateBranch } from '../../graphql/generated.js';
import { BranchQuery } from '../../graphql/queries/BranchQuery.js';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, selectAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { updateChannelBranchMappingAsync } from './edit';
import { BranchMapping, getBranchMapping } from './view';
} from '../../project/projectUtils.js';
import { promptAsync, selectAsync } from '../../prompts.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
import { updateChannelBranchMappingAsync } from './edit.js';
import { BranchMapping, getBranchMapping } from './view.js';
async function promptForRolloutPercentAsync({
promptMessage,
@@ -3,20 +3,20 @@ import assert from 'assert';
import chalk from 'chalk';
import Table from 'cli-table3';
import EasCommand from '../../commandUtils/EasCommand';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import EasCommand from '../../commandUtils/EasCommand.js';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery.js';
import Log from '../../log.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
import {
FormatUpdateParameter,
UPDATE_COLUMNS,
formatUpdate,
getPlatformsForGroup,
} from '../../update/utils';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
} from '../../update/utils.js';
import formatFields from '../../utils/formatFields.js';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json.js';
export type BranchMapping = {
version: number;
+7 -7
View File
@@ -4,13 +4,13 @@ import { EasJsonReader } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../commandUtils/EasCommand';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { appPlatformEmojis } from '../platform';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync } from '../project/projectUtils';
import { selectAsync } from '../prompts';
import EasCommand from '../commandUtils/EasCommand.js';
import { toAppPlatform } from '../graphql/types/AppPlatform.js';
import Log from '../log.js';
import { appPlatformEmojis } from '../platform.js';
import { getExpoConfig } from '../project/expoConfig.js';
import { findProjectRootAsync } from '../project/projectUtils.js';
import { selectAsync } from '../prompts.js';
export default class Config extends EasCommand {
static description = 'display project configuration (app.json + eas.json)';
+2 -2
View File
@@ -1,5 +1,5 @@
import EasCommand from '../commandUtils/EasCommand';
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
import EasCommand from '../commandUtils/EasCommand.js';
import { SelectPlatform } from '../credentials/manager/SelectPlatform.js';
export default class Credentials extends EasCommand {
static description = 'manage credentials';
@@ -1,8 +1,8 @@
import EasCommand from '../../commandUtils/EasCommand';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi';
import { createContextAsync } from '../../devices/context';
import DeviceManager from '../../devices/manager';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand from '../../commandUtils/EasCommand.js';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi.js';
import { createContextAsync } from '../../devices/context.js';
import DeviceManager from '../../devices/manager.js';
import { ensureLoggedInAsync } from '../../user/actions.js';
export default class DeviceCreate extends EasCommand {
static description = 'register new Apple Devices to use for internal distribution';
+19 -14
View File
@@ -1,24 +1,29 @@
import { Device, DeviceStatus } from '@expo/apple-utils';
import AppleUtils from '@expo/apple-utils';
import { Flags } from '@oclif/core';
import assert from 'assert';
import EasCommand from '../../commandUtils/EasCommand';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import EasCommand from '../../commandUtils/EasCommand.js';
import { chooseDevicesToDeleteAsync } from '../../credentials/ios/actions/DeviceUtils.js';
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation.js';
import {
AppleDeviceQuery,
AppleDeviceQueryResult,
AppleDevicesByTeamIdentifierQueryResult,
} from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import { authenticateAsync, getRequestContext } from '../../credentials/ios/appstore/authenticate';
import formatDevice from '../../devices/utils/formatDevice';
import { AppleDevice, Maybe } from '../../graphql/generated';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
} from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery.js';
import {
authenticateAsync,
getRequestContext,
} from '../../credentials/ios/appstore/authenticate.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import { AppleDevice, Maybe } from '../../graphql/generated.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
import { promptAsync, toggleConfirmAsync } from '../../prompts.js';
const { Device, DeviceStatus } = AppleUtils;
export default class DeviceDelete extends EasCommand {
static description = 'remove a registered device from your account';
+9 -9
View File
@@ -2,15 +2,15 @@ import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { Ora, ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import EasCommand from '../../commandUtils/EasCommand.js';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import Log from '../../log.js';
import { Ora, ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
import { promptAsync } from '../../prompts.js';
export default class BuildList extends EasCommand {
static description = 'list all registered devices for your account';
+7 -7
View File
@@ -1,10 +1,10 @@
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { ora } from '../../ora';
import { getExpoConfig } from '../../project/expoConfig';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import EasCommand from '../../commandUtils/EasCommand.js';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery.js';
import formatDevice from '../../devices/utils/formatDevice.js';
import Log from '../../log.js';
import { ora } from '../../ora.js';
import { getExpoConfig } from '../../project/expoConfig.js';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils.js';
export default class DeviceView extends EasCommand {
static description = 'view a device for your project';
Loaded 100 of 529 files, more files were not shown because too many files have changed in this diff. Show more