mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ed1efb7d6 | ||
|
|
b7bfec0366 | ||
|
|
a5308f0d37 | ||
|
|
3ad644bb9d | ||
|
|
233e5451a8 | ||
|
|
ce954cdecc | ||
|
|
59bd1caf98 | ||
|
|
86877fdda3 | ||
|
|
9299c9b7cc | ||
|
|
64fb2e5b3a | ||
|
|
91fdbac773 | ||
|
|
507eadb914 | ||
|
|
f1cde7a291 | ||
|
|
8b9ad47d16 | ||
|
|
192984b47d | ||
|
|
2c36810ce7 | ||
|
|
bfa63f95fe | ||
|
|
c414b9f975 | ||
|
|
6946a085cb | ||
|
|
317d2fcbb6 | ||
|
|
dd61b4bb70 | ||
|
|
80fbf7cf62 | ||
|
|
26b6a17160 | ||
|
|
7f4e2eef95 | ||
|
|
88b683655c | ||
|
|
f3aa3d98d6 | ||
|
|
f852b9329d | ||
|
|
a2340531b0 | ||
|
|
39df3a1364 | ||
|
|
2f1d454582 | ||
|
|
e2560ea867 | ||
|
|
346d136b8b | ||
|
|
dd77312510 | ||
|
|
120a965368 | ||
|
|
050336b6fd | ||
|
|
7fc3e3fcdb | ||
|
|
2d117e5b4a | ||
|
|
131377bd2c | ||
|
|
ed61b0c41b | ||
|
|
9f4d3bfc56 | ||
|
|
306e7b1d71 | ||
|
|
a2b73844f0 | ||
|
|
ebbe786cd0 | ||
|
|
190172f06c | ||
|
|
2232a5ed59 | ||
|
|
ef3e9079e5 | ||
|
|
ec10eff1e6 | ||
|
|
3e3ec87622 | ||
|
|
f1cf92d63a | ||
|
|
7096daa116 | ||
|
|
6429e70a9a | ||
|
|
083381da82 | ||
|
|
1c1af2da86 | ||
|
|
72ce75e1ee | ||
|
|
ea553a1025 | ||
|
|
3a6bcb3f2e |
No files matched your search
@@ -10,6 +10,8 @@ jobs:
|
||||
if: ${{ github.event.issue.pull_request && github.event.issue.user.login == github.event.comment.user.login && startsWith(github.event.comment.body, '/changelog-entry ') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
@@ -36,10 +36,10 @@ jobs:
|
||||
yarn pretarball-ci
|
||||
yarn oclif pack:tarballs --no-xz --targets linux-x64,linux-arm
|
||||
- id: x64
|
||||
run: echo "::set-output name=filename::$(ls eas-*-x64.tar.gz)"
|
||||
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||
- id: arm
|
||||
run: echo "::set-output name=filename::$(ls eas-*-arm.tar.gz)"
|
||||
run: echo "filename=$(ls eas-*-arm.tar.gz)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||
- name: Upload linux-x64 tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
yarn pretarball-ci
|
||||
yarn oclif pack:tarballs --no-xz --targets darwin-x64
|
||||
- id: x64
|
||||
run: echo "::set-output name=filename::$(ls eas-*-x64.tar.gz)"
|
||||
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||
- name: Upload darwin-x64 tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -105,10 +105,10 @@ jobs:
|
||||
yarn oclif pack:win
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli
|
||||
- id: x64
|
||||
run: echo "::set-output name=filename::$(ls eas-*-x64.exe)"
|
||||
run: echo "filename=$(ls eas-*-x64.exe)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist/win32
|
||||
- id: x86
|
||||
run: echo "::set-output name=filename::$(ls eas-*-x86.exe)"
|
||||
run: echo "filename=$(ls eas-*-x86.exe)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist/win32
|
||||
- name: Upload x64 installer
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -147,12 +147,22 @@ jobs:
|
||||
run: yarn lerna publish from-package --yes --no-verify-access
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||
- name: Add latest-eas-build-staging tag
|
||||
run: |
|
||||
VERSION=$(cat lerna.json | jq -r .version)
|
||||
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
||||
npm dist-tag add eas-cli@$VERSION latest-eas-build-staging
|
||||
echo "Run \"npm dist-tag add eas-cli@$VERSION latest-eas-build\" to promote it to production."
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||
release-changelog:
|
||||
name: Update changelog and publish release
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-to-npm
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
@@ -8,6 +8,96 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Support variant detection for package*UniversalApk Gradle Commands. ([#1708](https://github.com/expo/eas-cli/pull/1708) by [@frw](https://github.com/frw))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [3.6.1](https://github.com/expo/eas-cli/releases/tag/v3.6.1) - 2023-02-20
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Disable analytics when running behind a proxy. ([#1696](https://github.com/expo/eas-cli/pull/1696) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Clarify missing owner field error message for Robot users. ([#1702](https://github.com/expo/eas-cli/pull/1702) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Validate the platform for local builds earlier. ([#1698](https://github.com/expo/eas-cli/pull/1698) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Display build message when picking build in `eas submit`, `eas build:resign`, and `eas build:run` commands. ([#1700](https://github.com/expo/eas-cli/pull/1700) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Add a link directly to a build phase logs in the EAS CLI build error message. ([#1699](https://github.com/expo/eas-cli/pull/1699) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [3.6.0](https://github.com/expo/eas-cli/releases/tag/v3.6.0) - 2023-02-14
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Use `sdkVersion` as default runtime version policy when running `eas update:configure`. ([#1669](https://github.com/expo/eas-cli/pull/1669) by [@jonsamp](https://github.com/jonsamp))
|
||||
- Warn when project ID but no owner specified and mismatch logged in user. ([#1667](https://github.com/expo/eas-cli/pull/1667) by [@wschurman](https://github.com/wschurman))
|
||||
- Return the build `message` if one is available. ([#1691](https://github.com/expo/eas-cli/pull/1691) by [@raulriera](https://github.com/raulriera))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix suggested application identifier to match owning account name. ([#1670](https://github.com/expo/eas-cli/pull/1670) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add Xcode 14.2 image to the VSCode schema. ([#1693](https://github.com/expo/eas-cli/pull/1693) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [3.5.2](https://github.com/expo/eas-cli/releases/tag/v3.5.2) - 2023-02-05
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Use the new developer-mdn.apple.com subdomain instead of developer.apple.com. ([#1673](https://github.com/expo/eas-cli/pull/1673) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
## [3.5.1](https://github.com/expo/eas-cli/releases/tag/v3.5.1) - 2023-02-01
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix the issue with the `eas.json` envs being not available when resolving dynamic config. ([#1666](https://github.com/expo/eas-cli/pull/1666) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [3.5.0](https://github.com/expo/eas-cli/releases/tag/v3.5.0) - 2023-01-31
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Set `m1-medium` resource class for SDK version `>=48` and RN version `>=0.71` builds with unspecified resource class. ([#1655](https://github.com/expo/eas-cli/pull/1655) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix undefined branchMappingLogic. ([#1653](https://github.com/expo/eas-cli/pull/1653) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Fix using local credentials for internal distribution builds with universal provisioning. ([#1657](https://github.com/expo/eas-cli/pull/1657) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Move image validation to the server side and better handle server validation errors in the eas-cli. ([#1650](https://github.com/expo/eas-cli/pull/1650) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Update metadata on the worker when using git-based builds. ([#1651](https://github.com/expo/eas-cli/pull/1651) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
## [3.4.1](https://github.com/expo/eas-cli/releases/tag/v3.4.1) - 2023-01-23
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix incorrect exit code for successful local builds. ([#1649](https://github.com/expo/eas-cli/pull/1649) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [3.4.0](https://github.com/expo/eas-cli/releases/tag/v3.4.0) - 2023-01-20
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `--json` flag to `eas config` command. ([#1568](https://github.com/expo/eas-cli/pull/1568) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Add M1 resource class configuration to default eas.json when running eas build:configure on a new project. ([#1637](https://github.com/expo/eas-cli/pull/1637) by [@brentvatne](https://github.com/brentvatne))
|
||||
- Add prompt to switch iOS builds to M1 if the build queue is long. ([#1642](https://github.com/expo/eas-cli/pull/1642) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix running commands with `--json` flag when EAS_NO_VCS is set. ([#1641](https://github.com/expo/eas-cli/pull/1641) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove unnecessary workaround for trailing backslash in .gitignore. ([#1622](https://github.com/expo/eas-cli/pull/1622) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Internal command that will be run on EAS worker when building from GitHub. ([#1568](https://github.com/expo/eas-cli/pull/1568) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Make the disabled tier error message more descriptive. ([#1635](https://github.com/expo/eas-cli/pull/1635) by [@dsokal](https://github.com/dsokal))
|
||||
- Control some of the EAS Build error messages server-side. ([#1639](https://github.com/expo/eas-cli/pull/1639) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
## [3.3.2](https://github.com/expo/eas-cli/releases/tag/v3.3.2) - 2023-01-12
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Add missing key information about updates in `eas update --json` and `eas update:view --json`. ([#1619](https://github.com/expo/eas-cli/pull/1619) by [@byCedric](https://github.com/byCedric))
|
||||
|
||||
@@ -12,3 +12,7 @@ The algorithm works as follows:
|
||||
- If there are any entries in the "🛠 Breaking changes" section, bump the MAJOR version.
|
||||
- Otherwise, if there are any entries in the "🎉 New features", bump the MINOR version.
|
||||
- Otherwise, bump the PATCH version.
|
||||
|
||||
## Updating EAS CLI version on EAS Build
|
||||
|
||||
The release process promotes the new version to staging. After verifying it works there, you can promote it to production with `npm dist-tag add eas-cli@{version} latest-eas-build`.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "3.3.2",
|
||||
"version": "3.7.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+2
-3
@@ -14,7 +14,7 @@
|
||||
"eas": "packages/eas-cli/bin/run",
|
||||
"lint": "eslint .",
|
||||
"lint-changelog": "./scripts/bin/run lint-changelog",
|
||||
"release": "./scripts/bin/run release",
|
||||
"release": "lerna run rebuild && ./scripts/bin/run release",
|
||||
"test": "jest",
|
||||
"clean": "lerna run clean && rm -rf node_modules coverage"
|
||||
},
|
||||
@@ -27,8 +27,7 @@
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"jest": "29.3.1",
|
||||
"jest-watch-typeahead": "2.2.1",
|
||||
"lerna": "6.3.0",
|
||||
"nx": "15.4.5",
|
||||
"lerna": "6.4.1",
|
||||
"oclif": "3.4.3",
|
||||
"prettier": "2.8.2",
|
||||
"ts-jest": "29.0.3",
|
||||
|
||||
+55
-53
@@ -130,7 +130,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -147,7 +147,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -164,7 +164,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -178,7 +178,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -228,7 +228,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -249,7 +249,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -269,7 +269,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -289,7 +289,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -312,7 +312,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -342,7 +342,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -359,7 +359,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -376,7 +376,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -411,7 +411,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -448,7 +448,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -474,7 +474,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -498,7 +498,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -545,7 +545,7 @@ DESCRIPTION
|
||||
Update version of an app.
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -564,7 +564,7 @@ DESCRIPTION
|
||||
Update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -581,7 +581,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -602,7 +602,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -624,7 +624,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -644,7 +644,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -667,7 +667,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -675,18 +675,20 @@ display project configuration (app.json + eas.json)
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas config [-p android|ios] [-e <value>]
|
||||
$ eas config [-p android|ios] [-e <value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
|
||||
eas.json.
|
||||
-p, --platform=(android|ios)
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -703,7 +705,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -717,7 +719,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -737,7 +739,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -758,7 +760,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -772,7 +774,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -786,7 +788,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -874,7 +876,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -891,7 +893,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -908,7 +910,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -922,7 +924,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -936,7 +938,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -958,7 +960,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -981,7 +983,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -999,7 +1001,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1013,7 +1015,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1033,7 +1035,7 @@ DESCRIPTION
|
||||
read environment secrets from env file and store on the server
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1063,7 +1065,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1095,7 +1097,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1113,7 +1115,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1134,7 +1136,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1156,7 +1158,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1180,7 +1182,7 @@ DESCRIPTION
|
||||
rollback to an existing update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1200,7 +1202,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1221,7 +1223,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1241,7 +1243,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1259,7 +1261,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1281,7 +1283,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1298,7 +1300,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.3.2/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.7.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
Generated
+3558
-2329
File diff suppressed because it is too large.
Load diff
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "3.3.2",
|
||||
"version": "3.7.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "0.0.0-alpha.34",
|
||||
"@expo/apple-utils": "0.0.0-alpha.37",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "7.0.3",
|
||||
"@expo/config-plugins": "5.0.4",
|
||||
"@expo/config-types": "47.0.0",
|
||||
"@expo/eas-build-job": "0.2.102",
|
||||
"@expo/eas-json": "3.3.2",
|
||||
"@expo/eas-build-job": "0.2.106",
|
||||
"@expo/eas-json": "3.7.0",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.0.33",
|
||||
@@ -202,7 +202,8 @@
|
||||
"repository": "expo/eas-cli",
|
||||
"scripts": {
|
||||
"postpack": "rimraf oclif.manifest.json",
|
||||
"prepack": "rimraf build && yarn build && node ./scripts/prepack.js",
|
||||
"prepack": "yarn rebuild && node ./scripts/prepack.js",
|
||||
"rebuild": "rimraf build && yarn build",
|
||||
"pretarball-ci": "./scripts/pretarball-ci.sh",
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"watch": "yarn build --watch --preserveWatchOutput",
|
||||
|
||||
@@ -437,8 +437,8 @@
|
||||
"$ref": "#/definitions/apple/AppleKidsAge"
|
||||
}
|
||||
],
|
||||
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer.apple.com/news/?id=091202019a",
|
||||
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer.apple.com/news/?id=091202019a)"
|
||||
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer-mdn.apple.com/news/?id=091202019a",
|
||||
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer-mdn.apple.com/news/?id=091202019a)"
|
||||
},
|
||||
"seventeenPlus": {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -133,7 +133,9 @@ export async function createAnalyticsAsync(): Promise<AnalyticsWithOrchestration
|
||||
await UserSettings.setAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, false);
|
||||
}
|
||||
|
||||
const analyticsEnabled = await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true);
|
||||
const analyticsEnabled =
|
||||
!process.env.https_proxy && // disable analytics if running behind proxy
|
||||
(await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true));
|
||||
if (!analyticsEnabled) {
|
||||
return new NoOpAnalytics();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,13 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
|
||||
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildPriority,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
} from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { confirmAsync, selectAsync } from '../../prompts';
|
||||
|
||||
@@ -85,5 +91,6 @@ function createMockBuildFragment({
|
||||
},
|
||||
},
|
||||
status: BuildStatus.InQueue,
|
||||
resourceClass: BuildResourceClass.AndroidMedium,
|
||||
};
|
||||
}
|
||||
@@ -62,7 +62,7 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
|
||||
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx);
|
||||
}
|
||||
|
||||
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
|
||||
@@ -124,7 +124,8 @@ export async function prepareAndroidBuildAsync(
|
||||
}
|
||||
|
||||
function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean {
|
||||
return !ctx.buildProfile.withoutCredentials;
|
||||
const isCustomBuild = ctx.buildProfile.config !== undefined;
|
||||
return !ctx.buildProfile.withoutCredentials && !isCustomBuild;
|
||||
}
|
||||
|
||||
async function ensureAndroidCredentialsAsync(
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { Android } from '@expo/eas-build-job';
|
||||
|
||||
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
|
||||
import { transformProjectArchive, transformWorkflow } from '../graphql';
|
||||
import {
|
||||
transformBuildMode,
|
||||
transformBuildTrigger,
|
||||
transformProjectArchive,
|
||||
transformWorkflow,
|
||||
} from '../graphql';
|
||||
|
||||
export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
return {
|
||||
type: transformWorkflow(job.type),
|
||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||
projectArchive: transformProjectArchive(job.projectArchive),
|
||||
projectRootDirectory: job.projectRootDirectory,
|
||||
releaseChannel: job.releaseChannel,
|
||||
@@ -21,6 +27,8 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
buildType: job.buildType && transformBuildType(job.buildType),
|
||||
developmentClient: job.developmentClient,
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import path from 'path';
|
||||
import slash from 'slash';
|
||||
|
||||
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
||||
import { getUsername } from '../../project/projectUtils';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
import { BuildContext } from '../context';
|
||||
@@ -49,11 +50,15 @@ export async function prepareJobAsync(
|
||||
}
|
||||
: {};
|
||||
|
||||
let buildType = ctx.buildProfile.buildType;
|
||||
if (!buildType && !buildProfile.gradleCommand && ctx.buildProfile.distribution === 'internal') {
|
||||
let buildType = buildProfile.buildType;
|
||||
if (!buildType && !buildProfile.gradleCommand && buildProfile.distribution === 'internal') {
|
||||
buildType = Android.BuildType.APK;
|
||||
}
|
||||
|
||||
const maybeCustomBuildConfigPath = buildProfile.config
|
||||
? getCustomBuildConfigPath(buildProfile.config)
|
||||
: undefined;
|
||||
|
||||
const job: Android.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.ANDROID,
|
||||
@@ -75,8 +80,8 @@ export async function prepareJobAsync(
|
||||
secrets: {
|
||||
...buildCredentials,
|
||||
},
|
||||
releaseChannel: ctx.buildProfile.releaseChannel,
|
||||
updates: { channel: ctx.buildProfile.channel },
|
||||
releaseChannel: buildProfile.releaseChannel,
|
||||
updates: { channel: buildProfile.channel },
|
||||
developmentClient: buildProfile.developmentClient,
|
||||
gradleCommand: buildProfile.gradleCommand,
|
||||
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
|
||||
@@ -89,10 +94,15 @@ export async function prepareJobAsync(
|
||||
},
|
||||
}),
|
||||
experimental: {
|
||||
prebuildCommand: ctx.buildProfile.prebuildCommand,
|
||||
prebuildCommand: buildProfile.prebuildCommand,
|
||||
},
|
||||
mode: BuildMode.BUILD,
|
||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
triggeredBy: BuildTrigger.EAS_CLI,
|
||||
...(maybeCustomBuildConfigPath && {
|
||||
customBuildConfig: {
|
||||
path: maybeCustomBuildConfigPath,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
return sanitizeJob(job);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
|
||||
import { CredentialsSource } from '@expo/eas-json';
|
||||
import { CredentialsSource, EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
import cliProgress from 'cli-progress';
|
||||
import fs from 'fs-extra';
|
||||
@@ -14,10 +15,11 @@ import {
|
||||
BuildFragment,
|
||||
BuildParamsInput,
|
||||
BuildPriority,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
UploadSessionType,
|
||||
} from '../graphql/generated';
|
||||
import { BuildResult } from '../graphql/mutations/BuildMutation';
|
||||
import { BuildMutation, BuildResult } from '../graphql/mutations/BuildMutation';
|
||||
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||
import Log, { learnMore, link } from '../log';
|
||||
import { Ora, ora } from '../ora';
|
||||
@@ -26,13 +28,16 @@ import {
|
||||
appPlatformEmojis,
|
||||
requestedPlatformDisplayNames,
|
||||
} from '../platform';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { uploadFileAtPathToGCSAsync } from '../uploads';
|
||||
import { formatBytes } from '../utils/files';
|
||||
import { printJsonOnlyOutput } from '../utils/json';
|
||||
import { createProgressTracker } from '../utils/progress';
|
||||
import { sleepAsync } from '../utils/promise';
|
||||
import { getVcsClient } from '../vcs';
|
||||
import { BuildContext } from './context';
|
||||
import { runLocalBuildAsync } from './local';
|
||||
import { transformMetadata } from './graphql';
|
||||
import { LocalBuildMode, runLocalBuildAsync } from './local';
|
||||
import { collectMetadataAsync } from './metadata';
|
||||
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
||||
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
||||
@@ -107,15 +112,24 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
);
|
||||
}
|
||||
|
||||
const projectArchive = ctx.localBuildOptions.enable
|
||||
? ({
|
||||
type: ArchiveSourceType.PATH,
|
||||
path: (await makeProjectTarballAsync()).path,
|
||||
} as const)
|
||||
: ({
|
||||
type: ArchiveSourceType.GCS,
|
||||
bucketKey: await uploadProjectAsync(ctx),
|
||||
} as const);
|
||||
let projectArchive: ArchiveSource | undefined;
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
projectArchive = {
|
||||
type: ArchiveSourceType.PATH,
|
||||
path: (await makeProjectTarballAsync()).path,
|
||||
};
|
||||
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||
projectArchive = {
|
||||
type: ArchiveSourceType.PATH,
|
||||
path: process.cwd(),
|
||||
};
|
||||
} else if (!ctx.localBuildOptions.localBuildMode) {
|
||||
projectArchive = {
|
||||
type: ArchiveSourceType.GCS,
|
||||
bucketKey: await uploadProjectAsync(ctx),
|
||||
};
|
||||
}
|
||||
assert(projectArchive);
|
||||
|
||||
const metadata = await collectMetadataAsync(ctx);
|
||||
const buildParams = resolveBuildParamsInput(ctx);
|
||||
@@ -125,22 +139,41 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
});
|
||||
|
||||
return async () => {
|
||||
if (ctx.localBuildOptions.enable) {
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
await runLocalBuildAsync(job, metadata, ctx.localBuildOptions);
|
||||
return undefined;
|
||||
} else {
|
||||
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||
await BuildMutation.updateBuildMetadataAsync(ctx.graphqlClient, {
|
||||
buildId: nullthrows(process.env.EAS_BUILD_ID),
|
||||
metadata: transformMetadata(metadata),
|
||||
});
|
||||
printJsonOnlyOutput({ job, metadata });
|
||||
return undefined;
|
||||
} else if (!ctx.localBuildOptions.localBuildMode) {
|
||||
try {
|
||||
return await sendBuildRequestAsync(builder, job, metadata, buildParams);
|
||||
} catch (error: any) {
|
||||
handleBuildRequestError(error, job.platform);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown localBuildMode.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const SERVER_SIDE_DEFINED_ERRORS = [
|
||||
'TURTLE_DEPRECATED_JOB_FORMAT',
|
||||
'EAS_BUILD_FREE_TIER_DISABLED',
|
||||
'EAS_BUILD_FREE_TIER_DISABLED_IOS',
|
||||
'EAS_BUILD_FREE_TIER_DISABLED_ANDROID',
|
||||
'VALIDATION_ERROR',
|
||||
];
|
||||
|
||||
function handleBuildRequestError(error: any, platform: Platform): never {
|
||||
if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'TURTLE_DEPRECATED_JOB_FORMAT') {
|
||||
Log.error('EAS Build API has changed. Upgrade to the latest eas-cli version.');
|
||||
Log.debug(JSON.stringify(error.graphQLErrors, null, 2));
|
||||
|
||||
if (SERVER_SIDE_DEFINED_ERRORS.includes(error?.graphQLErrors?.[0]?.extensions?.errorCode)) {
|
||||
Log.error(error?.graphQLErrors?.[0]?.message);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE'
|
||||
@@ -151,31 +184,6 @@ function handleBuildRequestError(error: any, platform: Platform): never {
|
||||
)} for updates.`
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED') {
|
||||
Log.error(
|
||||
`EAS Build free tier is temporarily disabled and we are not accepting any new builds. Try again later. ${learnMore(
|
||||
'https://expo.fyi/eas-build-queues'
|
||||
)}`
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED_IOS'
|
||||
) {
|
||||
Log.error(
|
||||
`EAS Build free tier is temporarily disabled for iOS and we are not accepting any new builds. Try again later. ${learnMore(
|
||||
'https://expo.fyi/eas-build-queues'
|
||||
)}`
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED_ANDROID'
|
||||
) {
|
||||
Log.error(
|
||||
`EAS Build free tier is temporarily disabled for Android and we are not accepting any new builds. Try again later. ${learnMore(
|
||||
'https://expo.fyi/eas-build-queues'
|
||||
)}`
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS'
|
||||
) {
|
||||
@@ -285,7 +293,12 @@ export type MaybeBuildFragment = BuildFragment | null;
|
||||
|
||||
export async function waitForBuildEndAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
|
||||
{
|
||||
buildIds,
|
||||
accountName,
|
||||
projectDir,
|
||||
nonInteractive,
|
||||
}: { buildIds: string[]; accountName: string; projectDir: string; nonInteractive: boolean },
|
||||
{ intervalSec = 10 } = {}
|
||||
): Promise<MaybeBuildFragment[]> {
|
||||
let spinner;
|
||||
@@ -302,7 +315,10 @@ export async function waitForBuildEndAsync(
|
||||
const builds = await getBuildsSafelyAsync(graphqlClient, buildIds);
|
||||
const { refetch } =
|
||||
builds.length === 1
|
||||
? await handleSingleBuildProgressAsync({ build: builds[0], accountName }, { spinner })
|
||||
? await handleSingleBuildProgressAsync(
|
||||
{ build: builds[0], accountName, projectDir, nonInteractive },
|
||||
{ spinner }
|
||||
)
|
||||
: await handleMultipleBuildsProgressAsync({ builds }, { spinner, originalSpinnerText });
|
||||
if (!refetch) {
|
||||
return builds;
|
||||
@@ -337,7 +353,17 @@ const queueProgressBar = new cliProgress.SingleBar(
|
||||
);
|
||||
|
||||
async function handleSingleBuildProgressAsync(
|
||||
{ build, accountName }: { build: MaybeBuildFragment; accountName: string },
|
||||
{
|
||||
build,
|
||||
accountName,
|
||||
projectDir,
|
||||
nonInteractive,
|
||||
}: {
|
||||
build: MaybeBuildFragment;
|
||||
accountName: string;
|
||||
projectDir: string;
|
||||
nonInteractive: boolean;
|
||||
},
|
||||
{ spinner }: { spinner: Ora }
|
||||
): Promise<BuildProgressResult> {
|
||||
if (build === null) {
|
||||
@@ -376,7 +402,8 @@ async function handleSingleBuildProgressAsync(
|
||||
if (
|
||||
!queueProgressBarStarted &&
|
||||
typeof build.initialQueuePosition === 'number' &&
|
||||
typeof build.queuePosition === 'number'
|
||||
typeof build.queuePosition === 'number' &&
|
||||
typeof build.estimatedWaitTimeLeftSeconds === 'number'
|
||||
) {
|
||||
spinner.stopAndPersist();
|
||||
if (build.priority !== BuildPriority.High) {
|
||||
@@ -388,6 +415,55 @@ async function handleSingleBuildProgressAsync(
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
build.platform === AppPlatform.Ios &&
|
||||
[BuildResourceClass.IosIntelLarge, BuildResourceClass.IosIntelMedium].includes(
|
||||
build.resourceClass
|
||||
)
|
||||
) {
|
||||
let askToSwitchToM1 = false;
|
||||
if (
|
||||
build.priority === BuildPriority.High &&
|
||||
build.estimatedWaitTimeLeftSeconds >= /* 10 minutes */ 10 * 60
|
||||
) {
|
||||
Log.newLine();
|
||||
Log.warn(
|
||||
`Warning: Priority queue wait time for legacy iOS Intel workers is longer than usual (more than 10 minutes).`
|
||||
);
|
||||
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
|
||||
Log.warn(
|
||||
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
|
||||
learnMoreMessage: 'Learn more on switching to M1 workers.',
|
||||
})
|
||||
);
|
||||
askToSwitchToM1 = true;
|
||||
} else if (
|
||||
build.priority !== BuildPriority.High &&
|
||||
build.estimatedWaitTimeLeftSeconds >= /* 120 minutes */ 120 * 60
|
||||
) {
|
||||
Log.newLine();
|
||||
Log.warn(
|
||||
`Warning: Free tier queue wait time for legacy iOS Intel workers is longer than usual.`
|
||||
);
|
||||
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
|
||||
Log.warn(
|
||||
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
|
||||
learnMoreMessage: 'Learn more on switching to M1 workers.',
|
||||
})
|
||||
);
|
||||
askToSwitchToM1 = true;
|
||||
}
|
||||
if (!nonInteractive && askToSwitchToM1) {
|
||||
const shouldSwitchToM1 = await confirmAsync({
|
||||
message: `Switch iOS builds to M1 workers (modifies build profiles in eas.json)?`,
|
||||
});
|
||||
if (shouldSwitchToM1) {
|
||||
await updateIosBuildProfilesToUseM1WorkersAsync(projectDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.newLine();
|
||||
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
|
||||
queueProgressBar.start(
|
||||
@@ -529,3 +605,21 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
|
||||
getExpoWebsiteBaseUrl()
|
||||
).toString();
|
||||
}
|
||||
|
||||
async function updateIosBuildProfilesToUseM1WorkersAsync(projectDir: string): Promise<void> {
|
||||
const easJsonAccessor = new EasJsonAccessor(projectDir);
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
|
||||
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
for (const profileName of profileNames) {
|
||||
easJsonRawObject.build[profileName].ios = {
|
||||
...easJsonRawObject.build[profileName].ios,
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
};
|
||||
}
|
||||
return easJsonRawObject;
|
||||
});
|
||||
await easJsonAccessor.writeAsync();
|
||||
Log.withTick('Updated eas.json. Your next builds will run on M1 workers.');
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
|
||||
import { EasJson, EasJsonAccessor, ResourceClass } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
@@ -53,11 +53,21 @@ const EAS_JSON_MANAGED_DEFAULT: EasJson = {
|
||||
development: {
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
ios: {
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
preview: {
|
||||
distribution: 'internal',
|
||||
ios: {
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
production: {
|
||||
ios: {
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
production: {},
|
||||
},
|
||||
submit: {
|
||||
production: {},
|
||||
@@ -76,12 +86,20 @@ const EAS_JSON_BARE_DEFAULT: EasJson = {
|
||||
},
|
||||
ios: {
|
||||
buildConfiguration: 'Debug',
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
preview: {
|
||||
distribution: 'internal',
|
||||
ios: {
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
production: {
|
||||
ios: {
|
||||
resourceClass: ResourceClass.M1_MEDIUM,
|
||||
},
|
||||
},
|
||||
production: {},
|
||||
},
|
||||
submit: {
|
||||
production: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import getenv from 'getenv';
|
||||
import resolveFrom from 'resolve-from';
|
||||
@@ -9,7 +9,6 @@ import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/An
|
||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import { BuildResourceClass } from '../graphql/generated';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../project/workflow';
|
||||
import { Actor } from '../user/User';
|
||||
@@ -17,6 +16,7 @@ import { createAndroidContextAsync } from './android/build';
|
||||
import { BuildContext, CommonContext } from './context';
|
||||
import { createIosContextAsync } from './ios/build';
|
||||
import { LocalBuildOptions } from './local';
|
||||
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
|
||||
|
||||
export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfileName,
|
||||
@@ -28,7 +28,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
noWait,
|
||||
platform,
|
||||
projectDir,
|
||||
resourceClass,
|
||||
resourceClassFlag,
|
||||
message,
|
||||
actor,
|
||||
graphqlClient,
|
||||
@@ -44,7 +44,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
noWait: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
resourceClass: BuildResourceClass;
|
||||
resourceClassFlag?: ResourceClass;
|
||||
message?: string;
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
@@ -52,7 +52,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
getDynamicProjectConfigAsync: DynamicConfigContextFn;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
|
||||
|
||||
const projectName = exp.slug;
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
@@ -88,6 +87,14 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
};
|
||||
analytics.logEvent(BuildEvent.BUILD_COMMAND, analyticsEventProperties);
|
||||
|
||||
const resourceClass = await resolveBuildResourceClassAsync(
|
||||
buildProfile,
|
||||
platform,
|
||||
projectDir,
|
||||
exp,
|
||||
resourceClassFlag
|
||||
);
|
||||
|
||||
const commonContext: CommonContext<T> = {
|
||||
accountName: account.name,
|
||||
buildProfile,
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { ArchiveSource, ArchiveSourceType, Metadata, Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
ArchiveSource,
|
||||
ArchiveSourceType,
|
||||
BuildTrigger,
|
||||
Metadata,
|
||||
Workflow,
|
||||
} from '@expo/eas-build-job';
|
||||
|
||||
import {
|
||||
BuildCredentialsSource,
|
||||
@@ -7,6 +13,7 @@ import {
|
||||
BuildMode,
|
||||
BuildWorkflow,
|
||||
DistributionType,
|
||||
BuildTrigger as GraphQLBuildTrigger,
|
||||
ProjectArchiveSourceInput,
|
||||
ProjectArchiveSourceType,
|
||||
} from '../graphql/generated';
|
||||
@@ -84,10 +91,24 @@ export function transformIosEnterpriseProvisioning(
|
||||
}
|
||||
}
|
||||
|
||||
export function transformBuildMode(buildMode: Metadata['buildMode']): BuildMode {
|
||||
// TODO: check what in metadata
|
||||
export function transformBuildMode(buildMode: string): BuildMode {
|
||||
if (buildMode === 'build') {
|
||||
return BuildMode.Build;
|
||||
} else {
|
||||
} else if (buildMode === 'resign') {
|
||||
return BuildMode.Resign;
|
||||
} else if (buildMode === 'custom') {
|
||||
return BuildMode.Custom;
|
||||
} else {
|
||||
throw new Error(`Unsupported build mode: ${buildMode}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildTrigger {
|
||||
if (buildTrigger === BuildTrigger.EAS_CLI) {
|
||||
return GraphQLBuildTrigger.EasCli;
|
||||
} else if (buildTrigger === BuildTrigger.GIT_BASED_INTEGRATION) {
|
||||
return GraphQLBuildTrigger.GitBasedIntegration;
|
||||
}
|
||||
throw new Error('Unknown build trigger');
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export async function createIosContextAsync(
|
||||
const { buildProfile } = ctx;
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
|
||||
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx);
|
||||
}
|
||||
|
||||
checkNodeEnvVariable(ctx);
|
||||
|
||||
@@ -55,5 +55,6 @@ export async function ensureIosCredentialsForBuildResignAsync(
|
||||
}
|
||||
|
||||
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
|
||||
return !buildCtx.buildProfile.simulator;
|
||||
const isCustomBuild = buildCtx.buildProfile.config !== undefined;
|
||||
return !buildCtx.buildProfile.simulator && !isCustomBuild;
|
||||
}
|
||||
@@ -2,16 +2,22 @@ import { Ios } from '@expo/eas-build-job';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
|
||||
import { transformProjectArchive, transformWorkflow } from '../graphql';
|
||||
import {
|
||||
transformBuildMode,
|
||||
transformBuildTrigger,
|
||||
transformProjectArchive,
|
||||
transformWorkflow,
|
||||
} from '../graphql';
|
||||
|
||||
export function transformJob(job: Ios.Job): IosJobInput {
|
||||
return {
|
||||
type: transformWorkflow(job.type),
|
||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||
projectArchive: transformProjectArchive(job.projectArchive),
|
||||
projectRootDirectory: nullthrows(job.projectRootDirectory),
|
||||
releaseChannel: job.releaseChannel,
|
||||
updates: job.updates,
|
||||
secrets: transformIosSecrets(job.secrets),
|
||||
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
|
||||
builderEnvironment: job.builderEnvironment,
|
||||
cache: job.cache,
|
||||
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
|
||||
@@ -23,6 +29,8 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
developmentClient: job.developmentClient,
|
||||
simulator: job.simulator,
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ import {
|
||||
Platform,
|
||||
sanitizeJob,
|
||||
} from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import nullthrows from 'nullthrows';
|
||||
import path from 'path';
|
||||
import slash from 'slash';
|
||||
|
||||
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
|
||||
import { IosJobSecretsInput } from '../../graphql/generated';
|
||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
||||
import { getUsername } from '../../project/projectUtils';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
import { BuildContext } from '../context';
|
||||
@@ -33,10 +35,11 @@ export async function prepareJobAsync(
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData
|
||||
): Promise<Job> {
|
||||
const username = getUsername(ctx.exp, ctx.user);
|
||||
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
|
||||
const projectRootDirectory =
|
||||
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
|
||||
const username = getUsername(ctx.exp, ctx.user);
|
||||
const buildCredentials: Ios.Job['secrets']['buildCredentials'] = {};
|
||||
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
|
||||
if (jobData.credentials) {
|
||||
const targetNames = Object.keys(jobData.credentials);
|
||||
for (const targetName of targetNames) {
|
||||
@@ -44,38 +47,41 @@ export async function prepareJobAsync(
|
||||
}
|
||||
}
|
||||
|
||||
const maybeCustomBuildConfigPath = buildProfile.config
|
||||
? getCustomBuildConfigPath(buildProfile.config)
|
||||
: undefined;
|
||||
|
||||
const job: Ios.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.IOS,
|
||||
projectArchive: jobData.projectArchive,
|
||||
projectRootDirectory,
|
||||
builderEnvironment: {
|
||||
image: ctx.buildProfile.image,
|
||||
node: ctx.buildProfile.node,
|
||||
yarn: ctx.buildProfile.yarn,
|
||||
bundler: ctx.buildProfile.bundler,
|
||||
cocoapods: ctx.buildProfile.cocoapods,
|
||||
fastlane: ctx.buildProfile.fastlane,
|
||||
expoCli: ctx.buildProfile.expoCli,
|
||||
env: ctx.buildProfile.env,
|
||||
image: buildProfile.image,
|
||||
node: buildProfile.node,
|
||||
yarn: buildProfile.yarn,
|
||||
bundler: buildProfile.bundler,
|
||||
cocoapods: buildProfile.cocoapods,
|
||||
fastlane: buildProfile.fastlane,
|
||||
expoCli: buildProfile.expoCli,
|
||||
env: buildProfile.env,
|
||||
},
|
||||
cache: {
|
||||
...cacheDefaults,
|
||||
...ctx.buildProfile.cache,
|
||||
...buildProfile.cache,
|
||||
clear: ctx.clearCache,
|
||||
},
|
||||
secrets: {
|
||||
buildCredentials,
|
||||
},
|
||||
releaseChannel: ctx.buildProfile.releaseChannel,
|
||||
updates: { channel: ctx.buildProfile.channel },
|
||||
developmentClient: ctx.buildProfile.developmentClient,
|
||||
simulator: ctx.buildProfile.simulator,
|
||||
releaseChannel: buildProfile.releaseChannel,
|
||||
updates: { channel: buildProfile.channel },
|
||||
developmentClient: buildProfile.developmentClient,
|
||||
simulator: buildProfile.simulator,
|
||||
scheme: jobData.buildScheme,
|
||||
buildConfiguration: ctx.buildProfile.buildConfiguration,
|
||||
applicationArchivePath:
|
||||
ctx.buildProfile.applicationArchivePath ?? ctx.buildProfile.artifactPath,
|
||||
buildArtifactPaths: ctx.buildProfile.buildArtifactPaths,
|
||||
buildConfiguration: buildProfile.buildConfiguration,
|
||||
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
|
||||
buildArtifactPaths: buildProfile.buildArtifactPaths,
|
||||
username,
|
||||
...(ctx.ios.buildNumberOverride && {
|
||||
version: {
|
||||
@@ -83,10 +89,15 @@ export async function prepareJobAsync(
|
||||
},
|
||||
}),
|
||||
experimental: {
|
||||
prebuildCommand: ctx.buildProfile.prebuildCommand,
|
||||
prebuildCommand: buildProfile.prebuildCommand,
|
||||
},
|
||||
mode: BuildMode.BUILD,
|
||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
triggeredBy: BuildTrigger.EAS_CLI,
|
||||
...(maybeCustomBuildConfigPath && {
|
||||
customBuildConfig: {
|
||||
path: maybeCustomBuildConfigPath,
|
||||
},
|
||||
}),
|
||||
};
|
||||
return sanitizeJob(job);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,28 @@ import semver from 'semver';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.120';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.128';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
* Local build that users can run on their own machines. Instead
|
||||
* of sending build request to EAS Servers it's passing it as an argument
|
||||
* to local-build-plugin, that will run the build locally.
|
||||
*
|
||||
* Triggered when running `eas build --local`.
|
||||
*/
|
||||
LOCAL_BUILD_PLUGIN = 'local-build-plugin',
|
||||
/**
|
||||
* Type of local build that is not accessible to users directly. When
|
||||
* cloud build is triggered by git based integration, we are running
|
||||
* in this mode. Instead of sending build request to EAS Servers it's
|
||||
* printing it to the stdout as JSON, so EAS Build worker can read it.
|
||||
*/
|
||||
INTERNAL = 'internal',
|
||||
}
|
||||
|
||||
export interface LocalBuildOptions {
|
||||
enable: boolean;
|
||||
localBuildMode?: LocalBuildMode;
|
||||
skipCleanup?: boolean;
|
||||
skipNativeBuild?: boolean;
|
||||
artifactsDir?: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { BuildMode, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import resolveFrom from 'resolve-from';
|
||||
@@ -37,7 +37,7 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
credentialsSource: ctx.buildProfile.credentialsSource,
|
||||
sdkVersion: ctx.exp.sdkVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(ctx.exp, ctx.platform) ?? undefined,
|
||||
reactNativeVersion: await getReactNativeVersionAsync(ctx),
|
||||
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
|
||||
...channelOrReleaseChannel,
|
||||
distribution,
|
||||
appName: ctx.exp.name,
|
||||
@@ -57,6 +57,7 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
}),
|
||||
runWithNoWaitFlag: ctx.noWait,
|
||||
runFromCI: ctx.runFromCI,
|
||||
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
}
|
||||
@@ -155,11 +156,9 @@ async function getNativeChannelAsync<T extends Platform>(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function getReactNativeVersionAsync(
|
||||
ctx: BuildContext<Platform>
|
||||
): Promise<string | undefined> {
|
||||
export async function getReactNativeVersionAsync(projectDir: string): Promise<string | undefined> {
|
||||
try {
|
||||
const reactNativePackageJsonPath = resolveFrom(ctx.projectDir, 'react-native/package.json');
|
||||
const reactNativePackageJsonPath = resolveFrom(projectDir, 'react-native/package.json');
|
||||
return (await fs.readJson(reactNativePackageJsonPath)).version;
|
||||
} catch (err) {
|
||||
Log.debug('Failed to resolve react-native version:');
|
||||
|
||||
@@ -99,10 +99,6 @@ export async function listAndSelectBuildOnAppAsync(
|
||||
}
|
||||
}
|
||||
|
||||
function formatBuildChoiceValue(value: string | undefined | null): string {
|
||||
return value ? chalk.bold(value) : 'Unknown';
|
||||
}
|
||||
|
||||
function createBuildToPartialChoiceMaker(
|
||||
selectPromptDisabledFunction?: (build: BuildFragment) => boolean
|
||||
) {
|
||||
@@ -119,19 +115,34 @@ function createBuildToPartialChoiceMaker(
|
||||
? `${build.gitCommitHash.slice(0, 7)} "${chalk.bold(
|
||||
splitCommitMessage[0] + (splitCommitMessage.length > 1 ? '…' : '')
|
||||
)}"`
|
||||
: 'Unknown';
|
||||
: null;
|
||||
|
||||
const descriptionItems: { name: string; value: string | null }[] = [
|
||||
{ name: 'Version', value: build.appVersion ? chalk.bold(build.appVersion) : null },
|
||||
{ name: 'Commit', value: formattedCommitData },
|
||||
{
|
||||
name: 'Message',
|
||||
value: build.message
|
||||
? chalk.bold(
|
||||
build.message.length > 200 ? `${build.message.slice(0, 200)}...` : build.message
|
||||
)
|
||||
: null,
|
||||
},
|
||||
{
|
||||
name: build.platform === AppPlatform.Ios ? 'Build number' : 'Version code',
|
||||
value: build.appBuildVersion ? chalk.bold(build.appBuildVersion) : null,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredDescriptionArray: string[] = descriptionItems
|
||||
.filter(item => item.value)
|
||||
.map(item => `${chalk.bold(item.name)}: ${item.value}`);
|
||||
|
||||
return {
|
||||
title: `${chalk.bold(`ID:`)} ${build.id} (${chalk.bold(
|
||||
`${fromNow(new Date(build.completedAt))} ago`
|
||||
)})`,
|
||||
description: [
|
||||
`\t${chalk.bold(`Version:`)} ${formatBuildChoiceValue(build.appVersion)}`,
|
||||
`\t${chalk.bold(
|
||||
build.platform === AppPlatform.Ios ? 'Build number:' : 'Version code:'
|
||||
)} ${formatBuildChoiceValue(build.appBuildVersion)}`,
|
||||
`\t${chalk.bold(`Commit:`)} ${formattedCommitData}`,
|
||||
].join('\n'),
|
||||
description: filteredDescriptionArray.length > 0 ? filteredDescriptionArray.join('\n') : '',
|
||||
disabled: selectPromptDisabledFunction?.(build),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGr
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
BuildWithSubmissionsFragment,
|
||||
SubmissionFragment,
|
||||
@@ -32,6 +31,7 @@ import {
|
||||
appPlatformEmojis,
|
||||
toPlatforms,
|
||||
} from '../platform';
|
||||
import { validateCustomBuildConfigAsync } from '../project/customBuildConfig';
|
||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
|
||||
@@ -84,81 +84,6 @@ export interface BuildFlags {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
|
||||
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
|
||||
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
|
||||
[ResourceClass.M1_EXPERIMENTAL]: BuildResourceClass.IosM1Large,
|
||||
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosM1Medium,
|
||||
[ResourceClass.M1_LARGE]: BuildResourceClass.IosM1Large,
|
||||
[ResourceClass.INTEL_MEDIUM]: BuildResourceClass.IosIntelMedium,
|
||||
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
|
||||
};
|
||||
|
||||
const androidResourceClassToBuildResourceClassMapping: Record<
|
||||
Exclude<
|
||||
ResourceClass,
|
||||
| ResourceClass.M1_EXPERIMENTAL
|
||||
| ResourceClass.M1_MEDIUM
|
||||
| ResourceClass.M1_LARGE
|
||||
| ResourceClass.INTEL_MEDIUM
|
||||
>,
|
||||
BuildResourceClass
|
||||
> = {
|
||||
[ResourceClass.DEFAULT]: BuildResourceClass.AndroidDefault,
|
||||
[ResourceClass.LARGE]: BuildResourceClass.AndroidLarge,
|
||||
[ResourceClass.MEDIUM]: BuildResourceClass.AndroidMedium,
|
||||
};
|
||||
|
||||
function resolveBuildResourceClass(
|
||||
profile: ProfileData<'build'>,
|
||||
resourceClassFlag?: ResourceClass
|
||||
): BuildResourceClass {
|
||||
if (
|
||||
profile.platform !== Platform.IOS &&
|
||||
resourceClassFlag &&
|
||||
[
|
||||
ResourceClass.M1_EXPERIMENTAL,
|
||||
ResourceClass.M1_MEDIUM,
|
||||
ResourceClass.M1_LARGE,
|
||||
ResourceClass.INTEL_MEDIUM,
|
||||
].includes(resourceClassFlag)
|
||||
) {
|
||||
throw new Error(`Resource class ${resourceClassFlag} is only available for iOS builds`);
|
||||
}
|
||||
|
||||
const profileResourceClass = profile.profile.resourceClass;
|
||||
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
|
||||
Log.warn(
|
||||
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing the "${resourceClassFlag}" as the override.`
|
||||
);
|
||||
}
|
||||
const resourceClass = resourceClassFlag ?? profileResourceClass ?? ResourceClass.DEFAULT;
|
||||
|
||||
if (profile.platform === Platform.IOS && resourceClass === ResourceClass.M1_EXPERIMENTAL) {
|
||||
Log.warn(`Resource class ${chalk.bold('m1-experimental')} is deprecated.`);
|
||||
}
|
||||
if (
|
||||
profile.platform === Platform.IOS &&
|
||||
[ResourceClass.LARGE, ResourceClass.M1_LARGE].includes(resourceClass)
|
||||
) {
|
||||
Log.warn(
|
||||
`Large resource classes are not available for iOS builds yet. Your build will use the medium resource class.`
|
||||
);
|
||||
}
|
||||
|
||||
return profile.platform === Platform.ANDROID
|
||||
? androidResourceClassToBuildResourceClassMapping[
|
||||
resourceClass as Exclude<
|
||||
ResourceClass,
|
||||
| ResourceClass.M1_EXPERIMENTAL
|
||||
| ResourceClass.M1_MEDIUM
|
||||
| ResourceClass.M1_LARGE
|
||||
| ResourceClass.INTEL_MEDIUM
|
||||
>
|
||||
]
|
||||
: iosResourceClassToBuildResourceClassMapping[resourceClass];
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
analytics: Analytics,
|
||||
@@ -194,6 +119,7 @@ export async function runBuildAndSubmitAsync(
|
||||
|
||||
for (const buildProfile of buildProfiles) {
|
||||
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
||||
await validateCustomBuildConfigAsync(projectDir, buildProfile.profile);
|
||||
}
|
||||
|
||||
const startedBuilds: {
|
||||
@@ -208,7 +134,6 @@ export async function runBuildAndSubmitAsync(
|
||||
flags,
|
||||
moreBuilds: platforms.length > 1,
|
||||
buildProfile,
|
||||
resourceClass: resolveBuildResourceClass(buildProfile, flags.resourceClass),
|
||||
easJsonCliConfig,
|
||||
actor,
|
||||
graphqlClient,
|
||||
@@ -221,7 +146,7 @@ export async function runBuildAndSubmitAsync(
|
||||
buildCtxByPlatform[toAppPlatform(buildProfile.platform)] = buildCtx;
|
||||
}
|
||||
|
||||
if (flags.localBuildOptions.enable) {
|
||||
if (flags.localBuildOptions.localBuildMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,6 +200,8 @@ export async function runBuildAndSubmitAsync(
|
||||
const builds = await waitForBuildEndAsync(graphqlClient, {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
accountName,
|
||||
projectDir,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
if (!flags.json) {
|
||||
printBuildResults(builds);
|
||||
@@ -314,7 +241,6 @@ async function prepareAndStartBuildAsync({
|
||||
flags,
|
||||
moreBuilds,
|
||||
buildProfile,
|
||||
resourceClass,
|
||||
easJsonCliConfig,
|
||||
actor,
|
||||
graphqlClient,
|
||||
@@ -325,7 +251,6 @@ async function prepareAndStartBuildAsync({
|
||||
flags: BuildFlags;
|
||||
moreBuilds: boolean;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
resourceClass: BuildResourceClass;
|
||||
easJsonCliConfig: EasJson['cli'];
|
||||
actor: Actor;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
@@ -334,7 +259,7 @@ async function prepareAndStartBuildAsync({
|
||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||
const buildCtx = await createBuildContextAsync({
|
||||
buildProfileName: buildProfile.profileName,
|
||||
resourceClass,
|
||||
resourceClassFlag: flags.resourceClass,
|
||||
clearCache: flags.clearCache,
|
||||
buildProfile: buildProfile.profile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
|
||||
@@ -38,6 +38,10 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Message',
|
||||
value: build.message,
|
||||
},
|
||||
{
|
||||
label: 'Distribution',
|
||||
value: build.distribution?.toLowerCase(),
|
||||
|
||||
@@ -15,6 +15,32 @@ import Log, { learnMore, link } from '../../log';
|
||||
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
|
||||
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
|
||||
|
||||
function terminalLinkFallback(url: string, text: string): string {
|
||||
return `${text} (${url})`;
|
||||
}
|
||||
|
||||
const errorCodeToErrorMessageOverride: Record<string, (build: BuildFragment) => string> = {
|
||||
EAS_BUILD_UNKNOWN_FASTLANE_ERROR: build =>
|
||||
`The ${link(getBuildLogsUrl(build, 'run-fastlane'), {
|
||||
text: '"Run fastlane"',
|
||||
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-fastlane'), '"Run fastlane"'),
|
||||
})} step failed with an unknown error. Refer to the ${link(
|
||||
getBuildLogsUrl(build, 'xcode-logs'),
|
||||
{
|
||||
text: '"Xcode logs"',
|
||||
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'xcode-logs'), '"Xcode logs"'),
|
||||
}
|
||||
)} phase for additional, more detailed logs`,
|
||||
EAS_BUILD_UNKNOWN_GRADLE_ERROR: build =>
|
||||
`Gradle build failed with unknown error. See logs for the ${link(
|
||||
getBuildLogsUrl(build, 'run-gradlew'),
|
||||
{
|
||||
text: '"Run gradlew"',
|
||||
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-gradlew'), '"Run gradlew"'),
|
||||
}
|
||||
)} phase for more information.`,
|
||||
};
|
||||
|
||||
export function printLogsUrls(builds: BuildFragment[]): void {
|
||||
if (builds.length === 1) {
|
||||
Log.log(`Build details: ${link(getBuildLogsUrl(builds[0]))}`);
|
||||
@@ -31,7 +57,6 @@ 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');
|
||||
@@ -42,8 +67,8 @@ export function printBuildResults(builds: (BuildFragment | null)[]): void {
|
||||
}
|
||||
|
||||
function printBuildResult(build: BuildFragment): void {
|
||||
Log.addNewLineIfNone();
|
||||
if (build.status === BuildStatus.Errored) {
|
||||
Log.addNewLineIfNone();
|
||||
const userError = build.error;
|
||||
Log.error(
|
||||
`${appPlatformEmojis[build.platform]} ${
|
||||
@@ -51,11 +76,12 @@ function printBuildResult(build: BuildFragment): void {
|
||||
} build failed${userError ? ':' : ''}`
|
||||
);
|
||||
if (userError) {
|
||||
printUserError(userError);
|
||||
printUserError(userError, build);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (build.status === BuildStatus.Canceled) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.error(
|
||||
`${appPlatformEmojis[build.platform]} ${
|
||||
appPlatformDisplayNames[build.platform]
|
||||
@@ -65,6 +91,7 @@ function printBuildResult(build: BuildFragment): void {
|
||||
}
|
||||
|
||||
if (build.distribution === DistributionType.Internal) {
|
||||
Log.addNewLineIfNone();
|
||||
const logsUrl = getBuildLogsUrl(build);
|
||||
// It's tricky to install the .apk file directly on Android so let's fallback
|
||||
// to the build details page and let people press the button to download there
|
||||
@@ -82,9 +109,14 @@ function printBuildResult(build: BuildFragment): void {
|
||||
} else {
|
||||
// TODO: it looks like buildUrl could possibly be undefined, based on the code below.
|
||||
// we should account for this case better if it is possible
|
||||
const url = build.artifacts?.buildUrl ?? '';
|
||||
Log.log(`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`);
|
||||
Log.log(`${link(url)}`);
|
||||
const url = build.artifacts?.buildUrl;
|
||||
if (url) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`
|
||||
);
|
||||
Log.log(link(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +137,22 @@ export function printDeprecationWarnings(deprecationInfo?: EasBuildDeprecationIn
|
||||
}
|
||||
}
|
||||
|
||||
export function printUserError(error: BuildError): void {
|
||||
Log.error(error.message);
|
||||
if (error.docsUrl) {
|
||||
Log.error(learnMore(error.docsUrl, { dim: false }));
|
||||
export function printUserError(error: BuildError, build: BuildFragment): void {
|
||||
const maybeErrorMessageOverride = maybeGetErrorMessageOverride(error, build);
|
||||
if (maybeErrorMessageOverride) {
|
||||
Log.error(maybeErrorMessageOverride);
|
||||
} else {
|
||||
Log.error(error.message);
|
||||
if (error.docsUrl) {
|
||||
Log.error(learnMore(error.docsUrl, { dim: false }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function maybeGetErrorMessageOverride(error: BuildError, build: BuildFragment): string | null {
|
||||
if (!(error.errorCode in errorCodeToErrorMessageOverride)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return errorCodeToErrorMessageOverride[error.errorCode](build);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, ResourceClass } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import semver from 'semver';
|
||||
|
||||
import { BuildResourceClass } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { getReactNativeVersionAsync } from '../metadata';
|
||||
|
||||
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
|
||||
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
|
||||
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
|
||||
[ResourceClass.M1_EXPERIMENTAL]: BuildResourceClass.IosM1Large,
|
||||
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosM1Medium,
|
||||
[ResourceClass.M1_LARGE]: BuildResourceClass.IosM1Large,
|
||||
[ResourceClass.INTEL_MEDIUM]: BuildResourceClass.IosIntelMedium,
|
||||
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
|
||||
};
|
||||
|
||||
const androidResourceClassToBuildResourceClassMapping: Record<
|
||||
Exclude<
|
||||
ResourceClass,
|
||||
| ResourceClass.M1_EXPERIMENTAL
|
||||
| ResourceClass.M1_MEDIUM
|
||||
| ResourceClass.M1_LARGE
|
||||
| ResourceClass.INTEL_MEDIUM
|
||||
>,
|
||||
BuildResourceClass
|
||||
> = {
|
||||
[ResourceClass.DEFAULT]: BuildResourceClass.AndroidDefault,
|
||||
[ResourceClass.LARGE]: BuildResourceClass.AndroidLarge,
|
||||
[ResourceClass.MEDIUM]: BuildResourceClass.AndroidMedium,
|
||||
};
|
||||
|
||||
export async function resolveBuildResourceClassAsync<T extends Platform>(
|
||||
profile: BuildProfile<T>,
|
||||
platform: Platform,
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
resourceClassFlag?: ResourceClass
|
||||
): Promise<BuildResourceClass> {
|
||||
const profileResourceClass = profile.resourceClass;
|
||||
|
||||
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
|
||||
Log.warn(
|
||||
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing the "${resourceClassFlag}" as the override.`
|
||||
);
|
||||
}
|
||||
|
||||
const selectedResourceClass = resourceClassFlag ?? profileResourceClass;
|
||||
|
||||
return platform === Platform.IOS
|
||||
? await resolveIosResourceClassAsync(exp, projectDir, resourceClassFlag ?? profileResourceClass)
|
||||
: resolveAndroidResourceClass(selectedResourceClass);
|
||||
}
|
||||
|
||||
function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): BuildResourceClass {
|
||||
if (
|
||||
selectedResourceClass &&
|
||||
[
|
||||
ResourceClass.M1_EXPERIMENTAL,
|
||||
ResourceClass.M1_MEDIUM,
|
||||
ResourceClass.M1_LARGE,
|
||||
ResourceClass.INTEL_MEDIUM,
|
||||
].includes(selectedResourceClass)
|
||||
) {
|
||||
throw new Error(`Resource class ${selectedResourceClass} is only available for iOS builds`);
|
||||
}
|
||||
|
||||
const resourceClass = selectedResourceClass ?? ResourceClass.DEFAULT;
|
||||
|
||||
return androidResourceClassToBuildResourceClassMapping[
|
||||
resourceClass as Exclude<
|
||||
ResourceClass,
|
||||
| ResourceClass.M1_EXPERIMENTAL
|
||||
| ResourceClass.M1_MEDIUM
|
||||
| ResourceClass.M1_LARGE
|
||||
| ResourceClass.INTEL_MEDIUM
|
||||
>
|
||||
];
|
||||
}
|
||||
|
||||
async function resolveIosResourceClassAsync(
|
||||
exp: ExpoConfig,
|
||||
projectDir: string,
|
||||
selectedResourceClass?: ResourceClass
|
||||
): Promise<BuildResourceClass> {
|
||||
const resourceClass =
|
||||
selectedResourceClass ?? (await resolveIosDefaultRequestedResourceClassAsync(exp, projectDir));
|
||||
|
||||
if (resourceClass === ResourceClass.M1_EXPERIMENTAL) {
|
||||
Log.warn(`Resource class ${chalk.bold('m1-experimental')} is deprecated.`);
|
||||
}
|
||||
|
||||
return iosResourceClassToBuildResourceClassMapping[resourceClass];
|
||||
}
|
||||
|
||||
async function resolveIosDefaultRequestedResourceClassAsync(
|
||||
exp: ExpoConfig,
|
||||
projectDir: string
|
||||
): Promise<ResourceClass> {
|
||||
const { sdkVersion } = exp;
|
||||
const reactNativeVersion = await getReactNativeVersionAsync(projectDir);
|
||||
if (
|
||||
(sdkVersion && semver.satisfies(sdkVersion, '>=48')) ||
|
||||
(reactNativeVersion && semver.satisfies(reactNativeVersion, '>=0.71.0'))
|
||||
) {
|
||||
return ResourceClass.M1_MEDIUM;
|
||||
} else {
|
||||
return ResourceClass.DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,14 @@ export function getProjectDashboardUrl(accountName: string, projectName: string)
|
||||
).toString();
|
||||
}
|
||||
|
||||
export function getBuildLogsUrl(build: BuildFragment): string {
|
||||
export function getBuildLogsUrl(build: BuildFragment, hash?: string): string {
|
||||
const { project } = build;
|
||||
const url =
|
||||
project.__typename !== 'App'
|
||||
? `/builds/${build.id}`
|
||||
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}`;
|
||||
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}${
|
||||
hash ? `#${hash}` : ''
|
||||
}`;
|
||||
|
||||
return new URL(url, getExpoWebsiteBaseUrl()).toString();
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export function getBranchMapping(branchMappingString?: string): {
|
||||
}
|
||||
|
||||
const isRollout = branchMapping.data.length === 2;
|
||||
const rolloutPercent = branchMapping.data[0].branchMappingLogic.operand;
|
||||
const rolloutPercent = branchMapping.data[0]?.branchMappingLogic.operand;
|
||||
|
||||
switch (branchMapping.data.length) {
|
||||
case 0:
|
||||
|
||||
@@ -5,7 +5,7 @@ import ContextField, { ContextOptions } from './ContextField';
|
||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||
|
||||
export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
|
||||
export type DynamicConfigContextFn = (options: ExpoConfigOptions) => Promise<{
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
projectDir: string;
|
||||
|
||||
+73
-2
@@ -15,7 +15,6 @@ jest.mock('fs');
|
||||
|
||||
jest.mock('../../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../contextUtils/findProjectDirAndVerifyProjectSetupAsync');
|
||||
jest.mock('../../../../user/User');
|
||||
jest.mock('../../../../ora', () => ({
|
||||
ora: () => ({
|
||||
start: () => ({ succeed: () => {}, fail: () => {} }),
|
||||
@@ -107,7 +106,79 @@ describe(getProjectIdAsync, () => {
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`Project config: Project identified by "extra.eas.projectId" (notnotbrent) is not owned by owner specified in the "owner" field (wat). ${learnMore(
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (notnotbrent) does not match owner specified in the "owner" field (wat). ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the owner is not specified and is different than logged in user', async () => {
|
||||
jest.mocked(getConfig).mockReturnValue({
|
||||
exp: { name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
|
||||
} as any);
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@totallybrent/test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'totallybrent' } as any,
|
||||
});
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManager,
|
||||
{ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (totallybrent) does not match the logged in user (notnotbrent) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "totallybrent" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when the owner is not specified and is different than logged in user which is a robot', async () => {
|
||||
const sessionManagerMock = mock<SessionManager>();
|
||||
when(sessionManagerMock.ensureLoggedInAsync(anything())).thenResolve({
|
||||
actor: {
|
||||
__typename: 'Robot',
|
||||
id: 'robot_id',
|
||||
accounts: [
|
||||
{
|
||||
id: 'account_id_1',
|
||||
name: 'notnotbrent',
|
||||
users: [{ role: Role.Admin, actor: { id: 'robot_id' } }],
|
||||
},
|
||||
{
|
||||
id: 'account_id_2',
|
||||
name: 'dominik',
|
||||
users: [{ role: Role.ViewOnly, actor: { id: 'robot_id' } }],
|
||||
},
|
||||
],
|
||||
isExpoAdmin: false,
|
||||
featureGates: {},
|
||||
},
|
||||
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
|
||||
});
|
||||
const sessionManagerRobot = instance(sessionManagerMock);
|
||||
|
||||
jest.mocked(getConfig).mockReturnValue({
|
||||
exp: { name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
|
||||
} as any);
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@totallybrent/test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'totallybrent' } as any,
|
||||
});
|
||||
|
||||
await expect(
|
||||
getProjectIdAsync(
|
||||
sessionManagerRobot,
|
||||
{ name: 'test', slug: 'test', extra: { eas: { projectId: '1234' } } },
|
||||
{ nonInteractive: false }
|
||||
)
|
||||
).rejects.toThrow(
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (totallybrent) must be specified in "owner" field when using a robot access token. To ensure all libraries work correctly, "owner": "totallybrent" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getExpoConfig } from '../../../project/expoConfig';
|
||||
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||
import { toAppPrivacy } from '../../../project/projectUtils';
|
||||
import SessionManager from '../../../user/SessionManager';
|
||||
import { Actor } from '../../../user/User';
|
||||
import { Actor, getActorUsername } from '../../../user/User';
|
||||
import { createGraphqlClient } from './createGraphqlClient';
|
||||
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
|
||||
|
||||
@@ -93,14 +93,40 @@ export async function getProjectIdAsync(
|
||||
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, localProjectId);
|
||||
if (exp.owner && exp.owner !== appForProjectId.ownerAccount.name) {
|
||||
throw new Error(
|
||||
`Project config: Project identified by "extra.eas.projectId" (${
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (${
|
||||
appForProjectId.ownerAccount.name
|
||||
}) is not owned by owner specified in the "owner" field (${exp.owner}). ${learnMore(
|
||||
}) does not match owner specified in the "owner" field (${exp.owner}). ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
const actorUsername = getActorUsername(actor);
|
||||
if (!exp.owner && appForProjectId.ownerAccount.name !== actorUsername) {
|
||||
if (actorUsername) {
|
||||
throw new Error(
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (${
|
||||
appForProjectId.ownerAccount.name
|
||||
}) does not match the logged in user (${actorUsername}) and the "owner" field is not specified. To ensure all libraries work correctly, "owner": "${
|
||||
appForProjectId.ownerAccount.name
|
||||
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
// robot caller
|
||||
throw new Error(
|
||||
`Project config: Owner of project identified by "extra.eas.projectId" (${
|
||||
appForProjectId.ownerAccount.name
|
||||
}) must be specified in "owner" field when using a robot access token. To ensure all libraries work correctly, "owner": "${
|
||||
appForProjectId.ownerAccount.name
|
||||
}" should be added to the project config, which can be done automatically by re-running "eas init". ${learnMore(
|
||||
'https://expo.fyi/eas-project-id'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (exp.slug && exp.slug !== appForProjectId.slug) {
|
||||
throw new Error(
|
||||
`Project config: Slug for project identified by "extra.eas.projectId" (${
|
||||
|
||||
@@ -6,6 +6,7 @@ import figures from 'figures';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { LocalBuildMode } from '../../build/local';
|
||||
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
@@ -126,7 +127,7 @@ export default class Build extends EasCommand {
|
||||
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
if (!flags.localBuildOptions.enable) {
|
||||
if (!flags.localBuildOptions.localBuildMode) {
|
||||
await maybeWarnAboutEasOutagesAsync(
|
||||
graphqlClient,
|
||||
flags.autoSubmit
|
||||
@@ -194,13 +195,11 @@ export default class Build extends EasCommand {
|
||||
nonInteractive,
|
||||
localBuildOptions: flags['local']
|
||||
? {
|
||||
enable: true,
|
||||
localBuildMode: LocalBuildMode.LOCAL_BUILD_PLUGIN,
|
||||
verbose: true,
|
||||
artifactPath: flags.output && path.resolve(process.cwd(), flags.output),
|
||||
}
|
||||
: {
|
||||
enable: false,
|
||||
},
|
||||
: {},
|
||||
wait: flags['wait'],
|
||||
clearCache: flags['clear-cache'],
|
||||
json: flags['json'],
|
||||
@@ -216,7 +215,7 @@ export default class Build extends EasCommand {
|
||||
): Promise<BuildFlags> {
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.requestedPlatform);
|
||||
|
||||
if (flags.localBuildOptions.enable) {
|
||||
if (flags.localBuildOptions.localBuildMode) {
|
||||
if (flags.autoSubmit) {
|
||||
// TODO: implement this
|
||||
Errors.error('Auto-submits are not yet supported when building locally', { exit: 1 });
|
||||
@@ -228,6 +227,13 @@ export default class Build extends EasCommand {
|
||||
});
|
||||
} else if (process.platform !== 'darwin' && requestedPlatform === RequestedPlatform.Ios) {
|
||||
Errors.error('Unsupported platform, macOS is required to build apps for iOS', { exit: 1 });
|
||||
} else if (
|
||||
!['linux', 'darwin'].includes(process.platform) &&
|
||||
requestedPlatform === RequestedPlatform.Android
|
||||
) {
|
||||
Errors.error('Unsupported platform, macOS or Linux is required to build apps for Android', {
|
||||
exit: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { LocalBuildMode } from '../../build/local';
|
||||
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import Log from '../../log';
|
||||
@@ -110,7 +111,7 @@ export default class BuildInspect extends EasCommand {
|
||||
requestedPlatform: flags.platform,
|
||||
profile: flags.profile,
|
||||
localBuildOptions: {
|
||||
enable: true,
|
||||
localBuildMode: LocalBuildMode.LOCAL_BUILD_PLUGIN,
|
||||
...(flags.stage === InspectStage.PRE_BUILD ? { skipNativeBuild: true } : {}),
|
||||
...(flags.stage === InspectStage.POST_BUILD ? { skipCleanup: true } : {}),
|
||||
verbose: flags.verbose,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import { handleDeprecatedEasJsonAsync } from '.';
|
||||
import { LocalBuildMode } from '../../build/local';
|
||||
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
/**
|
||||
* This command will be run on the EAS Build workers, when building
|
||||
* directly from git. This command resolves credentials and other
|
||||
* build configuration, that normally would be included in the
|
||||
* job and metadata objects, and prints them to stdout.
|
||||
*/
|
||||
export default class BuildInternal extends EasCommand {
|
||||
static override hidden = true;
|
||||
|
||||
static override flags = {
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: ['android', 'ios'],
|
||||
required: true,
|
||||
}),
|
||||
profile: Flags.string({
|
||||
char: 'e',
|
||||
description:
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.ProjectDir,
|
||||
...this.ContextOptions.Analytics,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildInternal);
|
||||
// This command is always run with implicit --non-interactive and --json options
|
||||
enableJsonOutput();
|
||||
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
analytics,
|
||||
} = await this.getContextAsync(BuildInternal, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
graphqlClient,
|
||||
analytics,
|
||||
projectDir,
|
||||
{
|
||||
requestedPlatform: flags.platform as RequestedPlatform,
|
||||
profile: flags.profile,
|
||||
nonInteractive: true,
|
||||
wait: false,
|
||||
clearCache: false,
|
||||
json: true,
|
||||
autoSubmit: false,
|
||||
localBuildOptions: {
|
||||
localBuildMode: LocalBuildMode.INTERNAL,
|
||||
},
|
||||
},
|
||||
actor,
|
||||
getDynamicProjectConfigAsync
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -199,6 +199,8 @@ export default class BuildResign extends EasCommand {
|
||||
const buildResult = await waitForBuildEndAsync(graphqlClient, {
|
||||
buildIds: [newBuild.id],
|
||||
accountName: account.name,
|
||||
nonInteractive,
|
||||
projectDir,
|
||||
});
|
||||
if (!flags.json) {
|
||||
printBuildResults(buildResult);
|
||||
|
||||
@@ -46,7 +46,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSetView);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
loggedIn: { graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(BuildVersionSetView, {
|
||||
@@ -67,13 +67,14 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
|
||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync(
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
profile,
|
||||
buildProfile: profile,
|
||||
platform,
|
||||
actor
|
||||
);
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSyncView);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
loggedIn: { graphqlClient },
|
||||
getDynamicProjectConfigAsync,
|
||||
projectDir,
|
||||
} = await this.getContextAsync(BuildVersionSyncView, {
|
||||
@@ -89,13 +89,14 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
|
||||
const platformDisplayName = appPlatformDisplayNames[toAppPlatform(profileInfo.platform)];
|
||||
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync(
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
profileInfo.profile,
|
||||
profileInfo.platform,
|
||||
actor
|
||||
);
|
||||
buildProfile: profileInfo.profile,
|
||||
platform: profileInfo.platform,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
|
||||
@@ -5,10 +5,12 @@ import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
|
||||
import { toAppPlatform } from '../graphql/types/AppPlatform';
|
||||
import Log from '../log';
|
||||
import { appPlatformEmojis } from '../platform';
|
||||
import { selectAsync } from '../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../utils/json';
|
||||
|
||||
export default class Config extends EasCommand {
|
||||
static override description = 'display project configuration (app.json + eas.json)';
|
||||
@@ -21,6 +23,11 @@ export default class Config extends EasCommand {
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
// This option is used only on EAS Build worker to read build profile from eas.json.
|
||||
'eas-json-only': Flags.boolean({
|
||||
hidden: true,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -30,6 +37,9 @@ export default class Config extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(Config);
|
||||
if (flags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
const { platform: maybePlatform, profile: maybeProfile } = flags;
|
||||
const { getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(Config, {
|
||||
nonInteractive: false,
|
||||
@@ -61,21 +71,37 @@ export default class Config extends EasCommand {
|
||||
]));
|
||||
|
||||
const profile = await EasJsonUtils.getBuildProfileAsync(accessor, platform, profileName);
|
||||
const { exp: config } = await getDynamicProjectConfigAsync({
|
||||
env: profile.env,
|
||||
isPublicConfig: true,
|
||||
});
|
||||
if (flags['eas-json-only']) {
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput({ buildProfile: profile });
|
||||
} else {
|
||||
const appPlatform = toAppPlatform(platform);
|
||||
const platformEmoji = appPlatformEmojis[appPlatform];
|
||||
Log.log(`${platformEmoji} ${chalk.bold(`Build profile "${profileName}"`)}`);
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(profile, null, 2));
|
||||
}
|
||||
} else {
|
||||
const { exp: appConfig } = await getDynamicProjectConfigAsync({
|
||||
env: profile.env,
|
||||
isPublicConfig: true,
|
||||
});
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold(getProjectConfigDescription(projectDir)));
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(config, null, 2));
|
||||
Log.newLine();
|
||||
Log.newLine();
|
||||
const appPlatform = toAppPlatform(platform);
|
||||
const platformEmoji = appPlatformEmojis[appPlatform];
|
||||
Log.log(`${platformEmoji} ${chalk.bold(`Build profile "${profileName}"`)}`);
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(profile, null, 2));
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput({ buildProfile: profile, appConfig });
|
||||
} else {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold(getProjectConfigDescription(projectDir)));
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(appConfig, null, 2));
|
||||
Log.newLine();
|
||||
Log.newLine();
|
||||
const appPlatform = toAppPlatform(platform);
|
||||
const platformEmoji = appPlatformEmojis[appPlatform];
|
||||
Log.log(`${platformEmoji} ${chalk.bold(`Build profile "${profileName}"`)}`);
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(profile, null, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -344,12 +344,38 @@ describe(ProjectInit.name, () => {
|
||||
describe('when it is already configured', () => {
|
||||
beforeEach(() => {
|
||||
mockTestProject({ configuredProjectId: '1234', configuredOwner: jester.accounts[0].name });
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
});
|
||||
|
||||
it('does not configure', async () => {
|
||||
await new ProjectInit([], commandOptions).run();
|
||||
expect(saveProjectIdToAppConfigAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('checks owner and slug consistency', async () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
|
||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
||||
|
||||
await new ProjectInit([], commandOptions).run();
|
||||
|
||||
expect(confirmAsync).toHaveBeenCalled();
|
||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when it is not yet configured', () => {
|
||||
@@ -365,6 +391,14 @@ describe(ProjectInit.name, () => {
|
||||
|
||||
it('prompts for confirmation to link', async () => {
|
||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
await new ProjectInit([], commandOptions).run();
|
||||
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '123456');
|
||||
expect(confirmAsync).toHaveBeenCalledTimes(1);
|
||||
@@ -384,6 +418,14 @@ describe(ProjectInit.name, () => {
|
||||
it('asks to create it', async () => {
|
||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||
jest.mocked(AppMutation.createAppAsync).mockResolvedValue('0129');
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '0129',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
await new ProjectInit([], commandOptions).run();
|
||||
expect(saveProjectIdToAppConfigAsync).toHaveBeenCalledWith('/test-project', '0129');
|
||||
expect(confirmAsync).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -194,7 +194,6 @@ export default class ProjectInit extends EasCommand {
|
||||
}
|
||||
|
||||
private static async initializeWithExplicitIDAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
projectDir: string,
|
||||
{ force, nonInteractive }: InitializeMethodOptions
|
||||
@@ -203,17 +202,13 @@ export default class ProjectInit extends EasCommand {
|
||||
force,
|
||||
nonInteractive,
|
||||
});
|
||||
await this.ensureOwnerSlugConsistencyAsync(graphqlClient, projectId, projectDir, {
|
||||
force,
|
||||
nonInteractive,
|
||||
});
|
||||
}
|
||||
|
||||
private static async initializeWithInteractiveSelectionAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
actor: Actor,
|
||||
projectDir: string
|
||||
): Promise<void> {
|
||||
): Promise<string> {
|
||||
const exp = getExpoConfig(projectDir);
|
||||
const existingProjectId = exp.extra?.eas?.projectId;
|
||||
|
||||
@@ -223,7 +218,7 @@ export default class ProjectInit extends EasCommand {
|
||||
existingProjectId
|
||||
)}). To re-configure, remove the "extra.eas.projectId" field from your app config.`
|
||||
);
|
||||
return;
|
||||
return existingProjectId;
|
||||
}
|
||||
|
||||
const allAccounts = actor.accounts;
|
||||
@@ -287,7 +282,7 @@ export default class ProjectInit extends EasCommand {
|
||||
}
|
||||
|
||||
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, existingProjectIdOnServer);
|
||||
return;
|
||||
return existingProjectIdOnServer;
|
||||
}
|
||||
|
||||
if (!accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(accountName)) {
|
||||
@@ -323,24 +318,36 @@ export default class ProjectInit extends EasCommand {
|
||||
}
|
||||
|
||||
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, createdProjectId);
|
||||
return createdProjectId;
|
||||
}
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
flags: { id, force, 'non-interactive': nonInteractive },
|
||||
flags: { id: idArgument, force, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(ProjectInit);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
projectDir,
|
||||
} = await this.getContextAsync(ProjectInit, { nonInteractive });
|
||||
|
||||
if (id) {
|
||||
await ProjectInit.initializeWithExplicitIDAsync(graphqlClient, id, projectDir, {
|
||||
let idForConsistency: string;
|
||||
if (idArgument) {
|
||||
await ProjectInit.initializeWithExplicitIDAsync(idArgument, projectDir, {
|
||||
force,
|
||||
nonInteractive,
|
||||
});
|
||||
idForConsistency = idArgument;
|
||||
} else {
|
||||
await ProjectInit.initializeWithInteractiveSelectionAsync(graphqlClient, actor, projectDir);
|
||||
idForConsistency = await ProjectInit.initializeWithInteractiveSelectionAsync(
|
||||
graphqlClient,
|
||||
actor,
|
||||
projectDir
|
||||
);
|
||||
}
|
||||
|
||||
await ProjectInit.ensureOwnerSlugConsistencyAsync(graphqlClient, idForConsistency, projectDir, {
|
||||
force,
|
||||
nonInteractive,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ jest.mock('../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../../graphql/queries/UpdateQuery');
|
||||
jest.mock('../../../ora', () => ({
|
||||
ora: () => ({
|
||||
start: () => ({ succeed: () => {}, fail: () => {} }),
|
||||
start: () => ({ succeed: () => {}, fail: () => {}, stop: () => {} }),
|
||||
}),
|
||||
}));
|
||||
jest.mock('../../../project/publish', () => ({
|
||||
@@ -246,6 +246,8 @@ function mockTestExport({
|
||||
uniqueAssetCount: platforms.length,
|
||||
uniqueUploadedAssetCount: platforms.length,
|
||||
assetLimitPerUpdateGroup: 9001,
|
||||
launchAssetCount: 2,
|
||||
uniqueUploadedAssetPaths: [],
|
||||
});
|
||||
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
@@ -211,7 +211,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
projectId,
|
||||
});
|
||||
|
||||
const { exp } = await getDynamicProjectConfigAsync();
|
||||
const { exp } = await getDynamicProjectConfigAsync({});
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
let realizedPlatforms: PublishPlatform[] = [];
|
||||
@@ -318,10 +318,34 @@ export default class UpdatePublish extends EasCommand {
|
||||
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
|
||||
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
|
||||
unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
|
||||
const uploadAssetSuccessMessage = uploadedAssetCount
|
||||
? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'platform' : 'platforms'}`
|
||||
: `Uploaded: No changes detected`;
|
||||
assetSpinner.succeed(uploadAssetSuccessMessage);
|
||||
|
||||
// NOTE(cedric): we assume that bundles are always uploaded, and always are part of
|
||||
// `uploadedAssetCount`, perferably we don't assume. For that, we need to refactor the
|
||||
// `uploadAssetsAsync` and be able to determine asset type from the uploaded assets.
|
||||
const uploadedBundleCount = uploadResults.launchAssetCount;
|
||||
const uploadedNormalAssetCount = Math.max(0, uploadedAssetCount - uploadedBundleCount);
|
||||
const reusedNormalAssetCount = uploadResults.uniqueAssetCount - uploadedNormalAssetCount;
|
||||
|
||||
assetSpinner.stop();
|
||||
Log.withTick(
|
||||
`Uploaded ${uploadedBundleCount} app ${uploadedBundleCount === 1 ? 'bundle' : 'bundles'}`
|
||||
);
|
||||
if (uploadedNormalAssetCount === 0) {
|
||||
Log.withTick(`Uploading assets skipped - no new assets found`);
|
||||
} else {
|
||||
let message = `Uploaded ${uploadedNormalAssetCount} ${
|
||||
uploadedNormalAssetCount === 1 ? 'asset' : 'assets'
|
||||
}`;
|
||||
if (reusedNormalAssetCount > 0) {
|
||||
message += ` (reused ${reusedNormalAssetCount} ${
|
||||
reusedNormalAssetCount === 1 ? 'asset' : 'assets'
|
||||
})`;
|
||||
}
|
||||
Log.withTick(message);
|
||||
}
|
||||
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
|
||||
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
|
||||
}
|
||||
} catch (e) {
|
||||
assetSpinner.fail('Failed to upload');
|
||||
throw e;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { getAppFromContextAsync } from './actions/BuildCredentialsUtils';
|
||||
import { SetUpBuildCredentials } from './actions/SetUpBuildCredentials';
|
||||
import { SetUpPushKey } from './actions/SetUpPushKey';
|
||||
import { App, IosCredentials, Target } from './types';
|
||||
import { isAdHocProfile } from './utils/provisioningProfile';
|
||||
import { isAdHocProfile, isEnterpriseUniversalProfile } from './utils/provisioningProfile';
|
||||
|
||||
interface Options {
|
||||
app: App;
|
||||
@@ -143,17 +143,32 @@ export default class IosCredentialsProvider {
|
||||
|
||||
private assertProvisioningProfileType(provisioningProfile: string, targetName?: string): void {
|
||||
const isAdHoc = isAdHocProfile(provisioningProfile);
|
||||
if (this.options.distribution === 'internal' && !isAdHoc) {
|
||||
throw new Error(
|
||||
`You must use an adhoc provisioning profile${
|
||||
targetName ? ` (target '${targetName})'` : ''
|
||||
} for internal distribution`
|
||||
);
|
||||
} else if (this.options.distribution !== 'internal' && isAdHoc) {
|
||||
const isEnterprise = isEnterpriseUniversalProfile(provisioningProfile);
|
||||
if (this.options.distribution === 'internal') {
|
||||
if (this.options.enterpriseProvisioning === 'universal' && !isEnterprise) {
|
||||
throw new Error(
|
||||
`You must use a universal provisioning profile${
|
||||
targetName ? ` (target '${targetName})'` : ''
|
||||
} for internal distribution if you specified "enterpriseProvisioning": "universal" in eas.json`
|
||||
);
|
||||
} else if (this.options.enterpriseProvisioning === 'adhoc' && !isAdHoc) {
|
||||
throw new Error(
|
||||
`You must use an adhoc provisioning profile${
|
||||
targetName ? ` (target '${targetName})'` : ''
|
||||
} for internal distribution if you specified "enterpriseProvisioning": "adhoc" in eas.json`
|
||||
);
|
||||
} else if (!this.options.enterpriseProvisioning && !isEnterprise && !isAdHoc) {
|
||||
throw new Error(
|
||||
`You must use an adhoc provisioning profile${
|
||||
targetName ? ` (target '${targetName})'` : ''
|
||||
} for internal distribution.`
|
||||
);
|
||||
}
|
||||
} else if (isAdHoc) {
|
||||
throw new Error(
|
||||
`You can't use an adhoc provisioning profile${
|
||||
targetName ? ` (target '${targetName}')` : ''
|
||||
} for app store distribution`
|
||||
} for app store distribution.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -348,7 +348,7 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
'com.apple.developer.healthkit': true,
|
||||
})
|
||||
).rejects.toThrowError(
|
||||
`https://developer.apple.com/account/resources/identifiers/bundleId/edit/XXX333`
|
||||
`https://developer-mdn.apple.com/account/resources/identifiers/bundleId/edit/XXX333`
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { formatContractMessage } from '../contractMessages';
|
||||
const expiredMembership = {
|
||||
subject: 'Developer Program Membership Expired',
|
||||
message:
|
||||
"Your membership has expired, and your apps have been removed from the App Store until you renew your membership. To renew, a user with the Account Holder role must <a href='http://developer.apple.com/membercenter/index.action' target='_blank'>sign in</a> and renew the membership on the Apple Developer website. <a href='https://developer.apple.com/support/renewal/' target='_blank'>Learn More</a>.",
|
||||
"Your membership has expired, and your apps have been removed from the App Store until you renew your membership. To renew, a user with the Account Holder role must <a href='http://developer-mdn.apple.com/membercenter/index.action' target='_blank'>sign in</a> and renew the membership on the Apple Developer website. <a href='https://developer-mdn.apple.com/support/renewal/' target='_blank'>Learn More</a>.",
|
||||
};
|
||||
const paymentReturned = {
|
||||
subject: 'Payment Returned',
|
||||
@@ -15,12 +15,12 @@ const paymentReturned = {
|
||||
const licenseAgreementUpdated = {
|
||||
subject: 'Apple Developer Program License Agreement Updated',
|
||||
message:
|
||||
'The updated Apple Developer Program License Agreement needs to be reviewed. In order to update your existing apps and submit new apps to the App Store, the Account Holder must review and accept the updated agreement by signing in to their <a href="https://developer.apple.com/account" target="_blank">account</a> on the Apple Developer website.',
|
||||
'The updated Apple Developer Program License Agreement needs to be reviewed. In order to update your existing apps and submit new apps to the App Store, the Account Holder must review and accept the updated agreement by signing in to their <a href="https://developer-mdn.apple.com/account" target="_blank">account</a> on the Apple Developer website.',
|
||||
};
|
||||
const paidLicenseAgreementUpdated = {
|
||||
subject: '',
|
||||
message:
|
||||
'<b>Review the updated Paid Applications Schedule.</b><br />In order to update your existing apps, create new in-app purchases, and submit new apps to the App Store, the user with the Legal role (Account Holder) must review and accept the Paid Applications Schedule (Schedule 2 to the Apple Developer Program License Agreement) in the Agreements, Tax, and Banking module.<br /><br /> To accept this agreement, they must have already accepted the latest version of the Apple Developer Program License Agreement in their <a href="http://developer.apple.com/membercenter/index.action">account on the developer website<a/>.<br />',
|
||||
'<b>Review the updated Paid Applications Schedule.</b><br />In order to update your existing apps, create new in-app purchases, and submit new apps to the App Store, the user with the Legal role (Account Holder) must review and accept the Paid Applications Schedule (Schedule 2 to the Apple Developer Program License Agreement) in the Agreements, Tax, and Banking module.<br /><br /> To accept this agreement, they must have already accepted the latest version of the Apple Developer Program License Agreement in their <a href="http://developer-mdn.apple.com/membercenter/index.action">account on the developer website<a/>.<br />',
|
||||
};
|
||||
|
||||
describe(formatContractMessage, () => {
|
||||
|
||||
@@ -111,7 +111,7 @@ export async function syncCapabilitiesForEntitlementsAsync(
|
||||
inspect(modifiedRequest, { depth: null, colors: true })
|
||||
);
|
||||
throw new Error(
|
||||
`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`
|
||||
`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer-mdn.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ type CapabilityClassifier = {
|
||||
|
||||
// NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
|
||||
// Last Updated: July 22nd, 2021
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements
|
||||
export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
{
|
||||
name: 'HomeKit',
|
||||
@@ -413,7 +413,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_default-data-protection
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_default-data-protection
|
||||
name: 'Data Protection',
|
||||
entitlement: 'com.apple.developer.default-data-protection',
|
||||
capability: CapabilityType.DATA_PROTECTION,
|
||||
@@ -446,7 +446,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_networkextension
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_networkextension
|
||||
name: 'Network Extensions',
|
||||
entitlement: 'com.apple.developer.networking.networkextension',
|
||||
capability: CapabilityType.NETWORK_EXTENSIONS,
|
||||
@@ -465,7 +465,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_nfc_readersession_formats
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_nfc_readersession_formats
|
||||
name: 'NFC Tag Reading',
|
||||
entitlement: 'com.apple.developer.nfc.readersession.formats',
|
||||
capability: CapabilityType.NFC_TAG_READING,
|
||||
@@ -482,7 +482,7 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_vpn_api
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_vpn_api
|
||||
name: 'Push Notifications',
|
||||
// com.apple.developer.aps-environment
|
||||
entitlement: 'aps-environment',
|
||||
@@ -515,8 +515,8 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
},
|
||||
{
|
||||
// Not in Xcode
|
||||
// https://developer.apple.com/documentation/devicecheck/preparing_to_use_the_app_attest_service
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
|
||||
// https://developer-mdn.apple.com/documentation/devicecheck/preparing_to_use_the_app_attest_service
|
||||
// https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment
|
||||
name: 'App Attest',
|
||||
entitlement: 'com.apple.developer.devicecheck.appattest-environment',
|
||||
capability: CapabilityType.APP_ATTEST,
|
||||
@@ -725,10 +725,10 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
// it's not clear if it needs to be updated.
|
||||
|
||||
// "Contact Notes" requires the user to ask Apple in a form:
|
||||
// https://developer.apple.com/contact/request/contact-note-field
|
||||
// com.apple.developer.contacts.notes: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes/
|
||||
// https://developer-mdn.apple.com/contact/request/contact-note-field
|
||||
// com.apple.developer.contacts.notes: https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes/
|
||||
|
||||
// "Exposure Notification" requires the user to ask Apple in a form:
|
||||
// https://developer.apple.com/contact/request/exposure-notification-entitlement
|
||||
// com.apple.developer.exposure-notification: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_exposure-notification/
|
||||
// https://developer-mdn.apple.com/contact/request/exposure-notification-entitlement
|
||||
// com.apple.developer.exposure-notification: https://developer-mdn.apple.com/documentation/bundleresources/entitlements/com_apple_developer_exposure-notification/
|
||||
];
|
||||
@@ -26,6 +26,11 @@ export function isAdHocProfile(dataBase64: string): boolean {
|
||||
return Array.isArray(provisionedDevices);
|
||||
}
|
||||
|
||||
export function isEnterpriseUniversalProfile(dataBase64: string): boolean {
|
||||
const profilePlist = parse(dataBase64);
|
||||
return !!profilePlist['ProvisionsAllDevices'];
|
||||
}
|
||||
|
||||
export function parse(dataBase64: string): PlistObject {
|
||||
try {
|
||||
const buffer = Buffer.from(dataBase64, 'base64');
|
||||
|
||||
+247
-78
@@ -20,7 +20,6 @@ export type Scalars = {
|
||||
DateTime: any;
|
||||
JSON: any;
|
||||
JSONObject: any;
|
||||
Upload: any;
|
||||
};
|
||||
|
||||
export type AcceptUserInvitationResult = {
|
||||
@@ -127,11 +126,15 @@ export type Account = {
|
||||
subscription?: Maybe<SubscriptionDetails>;
|
||||
/** @deprecated No longer needed */
|
||||
subscriptionChangesPending?: Maybe<Scalars['Boolean']>;
|
||||
/** Coalesced project activity for an app using pagination */
|
||||
timelineActivity: TimelineActivityConnection;
|
||||
/** @deprecated See isCurrent */
|
||||
unlimitedBuilds: Scalars['Boolean'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
/** Account query object for querying EAS usage metrics */
|
||||
usageMetrics: AccountUsageMetrics;
|
||||
/** Owning UserActor of this account if personal account */
|
||||
userActorOwner?: Maybe<UserActor>;
|
||||
/** Pending user invitations for this account */
|
||||
userInvitations: Array<UserInvitation>;
|
||||
/** Actors associated with this account and permissions they hold */
|
||||
@@ -263,6 +266,19 @@ export type AccountSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An account is a container owning projects, credentials, billing and other organization
|
||||
* data and settings. Actors may own and be members of accounts.
|
||||
*/
|
||||
export type AccountTimelineActivityArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<TimelineActivityFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type AccountDataInput = {
|
||||
name: Scalars['String'];
|
||||
};
|
||||
@@ -494,6 +510,7 @@ export type AccountUsageMetricAndCost = {
|
||||
/** The limit, in units, allowed by this plan */
|
||||
limit: Scalars['Float'];
|
||||
metricType: UsageMetricType;
|
||||
service: EasService;
|
||||
serviceMetric: EasServiceMetric;
|
||||
/** Total cost of this particular metric, in cents */
|
||||
totalCost: Scalars['Float'];
|
||||
@@ -509,6 +526,7 @@ export type AccountUsageMetrics = {
|
||||
|
||||
export type AccountUsageMetricsByBillingPeriodArgs = {
|
||||
date: Scalars['DateTime'];
|
||||
service?: InputMaybe<EasService>;
|
||||
};
|
||||
|
||||
|
||||
@@ -532,7 +550,7 @@ export enum ActivityTimelineProjectActivityType {
|
||||
Update = 'UPDATE'
|
||||
}
|
||||
|
||||
/** A user or robot that can authenticate with Expo services and be a member of accounts. */
|
||||
/** A regular user, SSO user, or robot that can authenticate with Expo services and be a member of accounts. */
|
||||
export type Actor = {
|
||||
/** Access Tokens belonging to this actor */
|
||||
accessTokens: Array<AccessToken>;
|
||||
@@ -555,7 +573,7 @@ export type Actor = {
|
||||
};
|
||||
|
||||
|
||||
/** A user or robot that can authenticate with Expo services and be a member of accounts. */
|
||||
/** A regular user, SSO user, or robot that can authenticate with Expo services and be a member of accounts. */
|
||||
export type ActorFeatureGatesArgs = {
|
||||
filter?: InputMaybe<Array<Scalars['String']>>;
|
||||
};
|
||||
@@ -785,9 +803,11 @@ export type AndroidJobInput = {
|
||||
/** @deprecated */
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildArtifactPaths?: InputMaybe<Array<Scalars['String']>>;
|
||||
buildProfile?: InputMaybe<Scalars['String']>;
|
||||
buildType?: InputMaybe<AndroidBuildType>;
|
||||
builderEnvironment?: InputMaybe<AndroidBuilderEnvironmentInput>;
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
customBuildConfig?: InputMaybe<CustomBuildConfigInput>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']>;
|
||||
experimental?: InputMaybe<Scalars['JSONObject']>;
|
||||
gradleCommand?: InputMaybe<Scalars['String']>;
|
||||
@@ -796,6 +816,7 @@ export type AndroidJobInput = {
|
||||
projectRootDirectory: Scalars['String'];
|
||||
releaseChannel?: InputMaybe<Scalars['String']>;
|
||||
secrets?: InputMaybe<AndroidJobSecretsInput>;
|
||||
triggeredBy?: InputMaybe<BuildTrigger>;
|
||||
type: BuildWorkflow;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
@@ -814,6 +835,7 @@ export type AndroidJobOverridesInput = {
|
||||
/** @deprecated */
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildArtifactPaths?: InputMaybe<Array<Scalars['String']>>;
|
||||
buildProfile?: InputMaybe<Scalars['String']>;
|
||||
buildType?: InputMaybe<AndroidBuildType>;
|
||||
builderEnvironment?: InputMaybe<AndroidBuilderEnvironmentInput>;
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
@@ -830,6 +852,7 @@ export type AndroidJobOverridesInput = {
|
||||
|
||||
export type AndroidJobSecretsInput = {
|
||||
buildCredentials?: InputMaybe<AndroidJobBuildCredentialsInput>;
|
||||
robotAccessToken?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AndroidJobVersionInput = {
|
||||
@@ -928,10 +951,8 @@ export type App = Project & {
|
||||
/** Classic update release channel names that have at least one build */
|
||||
buildsReleaseChannels: Array<Scalars['String']>;
|
||||
deployment?: Maybe<Deployment>;
|
||||
deploymentNew?: Maybe<DeploymentNew>;
|
||||
/** Deployments associated with this app */
|
||||
deployments: Array<Deployment>;
|
||||
deploymentsNew: DeploymentsConnection;
|
||||
deployments: DeploymentsConnection;
|
||||
description: Scalars['String'];
|
||||
/** Environment secrets for an app */
|
||||
environmentSecrets: Array<EnvironmentSecret>;
|
||||
@@ -984,6 +1005,8 @@ export type App = Project & {
|
||||
slug: Scalars['String'];
|
||||
/** EAS Submissions associated with this app */
|
||||
submissions: Array<Submission>;
|
||||
/** Coalesced project activity for an app using pagination */
|
||||
timelineActivity: TimelineActivityConnection;
|
||||
/** @deprecated 'likes' have been deprecated. */
|
||||
trendScore: Scalars['Float'];
|
||||
/** get an EAS branch owned by the app by name */
|
||||
@@ -1053,14 +1076,6 @@ export type AppBuildsArgs = {
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentArgs = {
|
||||
channel: Scalars['String'];
|
||||
options?: InputMaybe<DeploymentOptions>;
|
||||
runtimeVersion: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentNewArgs = {
|
||||
channel: Scalars['String'];
|
||||
runtimeVersion: Scalars['String'];
|
||||
};
|
||||
@@ -1068,14 +1083,6 @@ export type AppDeploymentNewArgs = {
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentsArgs = {
|
||||
limit: Scalars['Int'];
|
||||
mostRecentlyUpdatedAt?: InputMaybe<Scalars['DateTime']>;
|
||||
options?: InputMaybe<DeploymentOptions>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentsNewArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
@@ -1124,6 +1131,16 @@ export type AppSubmissionsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppTimelineActivityArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
filter?: InputMaybe<TimelineActivityFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppUpdateBranchByNameArgs = {
|
||||
name: Scalars['String'];
|
||||
@@ -1895,6 +1912,7 @@ export enum BuildIosEnterpriseProvisioning {
|
||||
/** Represents an Standalone App build job */
|
||||
export type BuildJob = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
__typename?: 'BuildJob';
|
||||
accountUserActor?: Maybe<UserActor>;
|
||||
activityTimestamp: Scalars['DateTime'];
|
||||
actor?: Maybe<Actor>;
|
||||
app?: Maybe<App>;
|
||||
@@ -2002,6 +2020,7 @@ export type BuildMetrics = {
|
||||
|
||||
export enum BuildMode {
|
||||
Build = 'BUILD',
|
||||
Custom = 'CUSTOM',
|
||||
Resign = 'RESIGN'
|
||||
}
|
||||
|
||||
@@ -2029,6 +2048,8 @@ export type BuildMutation = {
|
||||
retryBuild: Build;
|
||||
/** Retry an iOS EAS Build */
|
||||
retryIosBuild: Build;
|
||||
/** Update metadata for EAS Build build */
|
||||
updateBuildMetadata: Build;
|
||||
};
|
||||
|
||||
|
||||
@@ -2074,6 +2095,12 @@ export type BuildMutationRetryIosBuildArgs = {
|
||||
jobOverrides?: InputMaybe<IosJobOverridesInput>;
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationUpdateBuildMetadataArgs = {
|
||||
buildId: Scalars['ID'];
|
||||
metadata: BuildMetadataInput;
|
||||
};
|
||||
|
||||
export type BuildOrBuildJob = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
@@ -2175,6 +2202,8 @@ export enum BuildResourceClass {
|
||||
IosLarge = 'IOS_LARGE',
|
||||
IosM1Large = 'IOS_M1_LARGE',
|
||||
IosM1Medium = 'IOS_M1_MEDIUM',
|
||||
IosM2Medium = 'IOS_M2_MEDIUM',
|
||||
IosM2ProMedium = 'IOS_M2_PRO_MEDIUM',
|
||||
IosMedium = 'IOS_MEDIUM',
|
||||
Legacy = 'LEGACY'
|
||||
}
|
||||
@@ -2188,6 +2217,11 @@ export enum BuildStatus {
|
||||
New = 'NEW'
|
||||
}
|
||||
|
||||
export enum BuildTrigger {
|
||||
EasCli = 'EAS_CLI',
|
||||
GitBasedIntegration = 'GIT_BASED_INTEGRATION'
|
||||
}
|
||||
|
||||
export type BuildUpdatesInput = {
|
||||
channel?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
@@ -2198,11 +2232,6 @@ export enum BuildWorkflow {
|
||||
Unknown = 'UNKNOWN'
|
||||
}
|
||||
|
||||
export enum CacheControlScope {
|
||||
Private = 'PRIVATE',
|
||||
Public = 'PUBLIC'
|
||||
}
|
||||
|
||||
export type Card = {
|
||||
__typename?: 'Card';
|
||||
brand?: Maybe<Scalars['String']>;
|
||||
@@ -2214,13 +2243,13 @@ export type Card = {
|
||||
|
||||
export type Charge = {
|
||||
__typename?: 'Charge';
|
||||
amount?: Maybe<Scalars['Int']>;
|
||||
createdAt?: Maybe<Scalars['DateTime']>;
|
||||
amount: Scalars['Int'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
invoiceId?: Maybe<Scalars['String']>;
|
||||
paid?: Maybe<Scalars['Boolean']>;
|
||||
paid: Scalars['Boolean'];
|
||||
receiptUrl?: Maybe<Scalars['String']>;
|
||||
wasRefunded?: Maybe<Scalars['Boolean']>;
|
||||
wasRefunded: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
/** Represents a client build request */
|
||||
@@ -2335,6 +2364,10 @@ export type CreateSubmissionResult = {
|
||||
submission: Submission;
|
||||
};
|
||||
|
||||
export type CustomBuildConfigInput = {
|
||||
path: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DeleteAccessTokenResult = {
|
||||
__typename?: 'DeleteAccessTokenResult';
|
||||
id: Scalars['ID'];
|
||||
@@ -2423,16 +2456,19 @@ export type DeployServerlessFunctionResult = {
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type Deployment = {
|
||||
__typename?: 'Deployment';
|
||||
channel?: Maybe<UpdateChannel>;
|
||||
/**
|
||||
* The name of this deployment's associated channel. It is specified separately from the `channel`
|
||||
* field to allow specifying a deployment before an EAS Update channel has been created.
|
||||
*/
|
||||
channelName: Scalars['String'];
|
||||
builds: DeploymentBuildsConnection;
|
||||
channel: UpdateChannel;
|
||||
id: Scalars['ID'];
|
||||
mostRecentlyUpdatedAt: Scalars['DateTime'];
|
||||
recentBuilds: Array<Build>;
|
||||
runtimeVersion: Scalars['String'];
|
||||
runtime: Runtime;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type DeploymentBuildsArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type DeploymentBuildEdge = {
|
||||
@@ -2451,33 +2487,10 @@ export type DeploymentBuildsConnection = {
|
||||
export type DeploymentEdge = {
|
||||
__typename?: 'DeploymentEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: DeploymentNew;
|
||||
node: Deployment;
|
||||
};
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type DeploymentNew = {
|
||||
__typename?: 'DeploymentNew';
|
||||
builds: DeploymentBuildsConnection;
|
||||
channel: UpdateChannel;
|
||||
id: Scalars['ID'];
|
||||
runtime: Runtime;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type DeploymentNewBuildsArgs = {
|
||||
after?: InputMaybe<Scalars['String']>;
|
||||
before?: InputMaybe<Scalars['String']>;
|
||||
first?: InputMaybe<Scalars['Int']>;
|
||||
last?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type DeploymentOptions = {
|
||||
/** Max number of associated builds to return */
|
||||
buildListMaxSize?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** Represents the connection over the deploymentsNew edge of an App */
|
||||
/** Represents the connection over the deployments edge of an App */
|
||||
export type DeploymentsConnection = {
|
||||
__typename?: 'DeploymentsConnection';
|
||||
edges: Array<DeploymentEdge>;
|
||||
@@ -2503,6 +2516,11 @@ export enum EasBuildDeprecationInfoType {
|
||||
|
||||
export type EasBuildOrClassicBuildJob = Build | BuildJob;
|
||||
|
||||
export enum EasService {
|
||||
Builds = 'BUILDS',
|
||||
Updates = 'UPDATES'
|
||||
}
|
||||
|
||||
export enum EasServiceMetric {
|
||||
AssetsRequests = 'ASSETS_REQUESTS',
|
||||
BandwidthUsage = 'BANDWIDTH_USAGE',
|
||||
@@ -3087,10 +3105,12 @@ export type IosJobInput = {
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildArtifactPaths?: InputMaybe<Array<Scalars['String']>>;
|
||||
buildConfiguration?: InputMaybe<Scalars['String']>;
|
||||
buildProfile?: InputMaybe<Scalars['String']>;
|
||||
/** @deprecated */
|
||||
buildType?: InputMaybe<IosBuildType>;
|
||||
builderEnvironment?: InputMaybe<IosBuilderEnvironmentInput>;
|
||||
cache?: InputMaybe<BuildCacheInput>;
|
||||
customBuildConfig?: InputMaybe<CustomBuildConfigInput>;
|
||||
developmentClient?: InputMaybe<Scalars['Boolean']>;
|
||||
/** @deprecated */
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
@@ -3102,6 +3122,7 @@ export type IosJobInput = {
|
||||
scheme?: InputMaybe<Scalars['String']>;
|
||||
secrets?: InputMaybe<IosJobSecretsInput>;
|
||||
simulator?: InputMaybe<Scalars['Boolean']>;
|
||||
triggeredBy?: InputMaybe<BuildTrigger>;
|
||||
type: BuildWorkflow;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
@@ -3114,6 +3135,7 @@ export type IosJobOverridesInput = {
|
||||
artifactPath?: InputMaybe<Scalars['String']>;
|
||||
buildArtifactPaths?: InputMaybe<Array<Scalars['String']>>;
|
||||
buildConfiguration?: InputMaybe<Scalars['String']>;
|
||||
buildProfile?: InputMaybe<Scalars['String']>;
|
||||
/** @deprecated */
|
||||
buildType?: InputMaybe<IosBuildType>;
|
||||
builderEnvironment?: InputMaybe<IosBuilderEnvironmentInput>;
|
||||
@@ -3136,6 +3158,7 @@ export type IosJobOverridesInput = {
|
||||
|
||||
export type IosJobSecretsInput = {
|
||||
buildCredentials?: InputMaybe<Array<InputMaybe<IosJobTargetCredentialsInput>>>;
|
||||
robotAccessToken?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type IosJobTargetCredentialsInput = {
|
||||
@@ -3331,6 +3354,7 @@ export type MeMutationUpdateProfileArgs = {
|
||||
|
||||
export type MeteredBillingStatus = {
|
||||
__typename?: 'MeteredBillingStatus';
|
||||
EAS_BUILD: Scalars['Boolean'];
|
||||
EAS_UPDATE: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
@@ -3468,12 +3492,15 @@ export type Project = {
|
||||
|
||||
export type ProjectArchiveSourceInput = {
|
||||
bucketKey?: InputMaybe<Scalars['String']>;
|
||||
gitRef?: InputMaybe<Scalars['String']>;
|
||||
repositoryUrl?: InputMaybe<Scalars['String']>;
|
||||
type: ProjectArchiveSourceType;
|
||||
url?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum ProjectArchiveSourceType {
|
||||
Gcs = 'GCS',
|
||||
Git = 'GIT',
|
||||
None = 'NONE',
|
||||
S3 = 'S3',
|
||||
Url = 'URL'
|
||||
@@ -3755,10 +3782,15 @@ export type RootQuery = {
|
||||
*/
|
||||
me?: Maybe<User>;
|
||||
/**
|
||||
* If authenticated as a any type of Actor, this is the appropriate top-level
|
||||
* If authenticated as any type of Actor, this is the appropriate top-level
|
||||
* query object
|
||||
*/
|
||||
meActor?: Maybe<Actor>;
|
||||
/**
|
||||
* If authenticated as any type of human end user (Actor types User or SSOUser),
|
||||
* this is the appropriate top-level query object
|
||||
*/
|
||||
meUserActor?: Maybe<UserActor>;
|
||||
project: ProjectQuery;
|
||||
snack: SnackQuery;
|
||||
/** Top-level query object for querying SSO Users. */
|
||||
@@ -3770,6 +3802,8 @@ export type RootQuery = {
|
||||
updatesByGroup: Array<Update>;
|
||||
/** Top-level query object for querying Users. */
|
||||
user: UserQuery;
|
||||
/** Top-level query object for querying UserActors. */
|
||||
userActor: UserActorQuery;
|
||||
/** @deprecated Use 'byId' field under 'user'. */
|
||||
userByUserId?: Maybe<User>;
|
||||
/** @deprecated Use 'byUsername' field under 'user'. */
|
||||
@@ -3822,7 +3856,7 @@ export type Runtime = {
|
||||
};
|
||||
|
||||
/** Represents a human SSO (not robot) actor. */
|
||||
export type SsoUser = Actor & {
|
||||
export type SsoUser = Actor & UserActor & {
|
||||
__typename?: 'SSOUser';
|
||||
/** Access Tokens belonging to this actor, none at present */
|
||||
accessTokens: Array<AccessToken>;
|
||||
@@ -3833,6 +3867,7 @@ export type SsoUser = Actor & {
|
||||
appetizeCode?: Maybe<Scalars['String']>;
|
||||
/** Apps this user has published. If this user is the viewer, this field returns the apps the user has access to. */
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
displayName: Scalars['String'];
|
||||
/**
|
||||
@@ -3848,6 +3883,7 @@ export type SsoUser = Actor & {
|
||||
isExpoAdmin: Scalars['Boolean'];
|
||||
lastName?: Maybe<Scalars['String']>;
|
||||
location?: Maybe<Scalars['String']>;
|
||||
notificationSubscriptions: Array<NotificationSubscription>;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
profilePhoto: Scalars['String'];
|
||||
@@ -3880,6 +3916,12 @@ export type SsoUserFeatureGatesArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human SSO (not robot) actor. */
|
||||
export type SsoUserNotificationSubscriptionsArgs = {
|
||||
filter?: InputMaybe<NotificationSubscriptionFilter>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human SSO (not robot) actor. */
|
||||
export type SsoUserSnacksArgs = {
|
||||
limit: Scalars['Int'];
|
||||
@@ -3896,7 +3938,7 @@ export type SsoUserQuery = {
|
||||
|
||||
|
||||
export type SsoUserQueryByIdArgs = {
|
||||
userId: Scalars['String'];
|
||||
userId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
@@ -4280,6 +4322,25 @@ export type SubscriptionDetailsPlanEnablementArgs = {
|
||||
serviceMetric: EasServiceMetric;
|
||||
};
|
||||
|
||||
export type TimelineActivityConnection = {
|
||||
__typename?: 'TimelineActivityConnection';
|
||||
edges: Array<TimelineActivityEdge>;
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type TimelineActivityEdge = {
|
||||
__typename?: 'TimelineActivityEdge';
|
||||
cursor: Scalars['String'];
|
||||
node: ActivityTimelineProjectActivity;
|
||||
};
|
||||
|
||||
export type TimelineActivityFilterInput = {
|
||||
channels?: InputMaybe<Array<Scalars['String']>>;
|
||||
platforms?: InputMaybe<Array<AppPlatform>>;
|
||||
releaseChannels?: InputMaybe<Array<Scalars['String']>>;
|
||||
types?: InputMaybe<Array<ActivityTimelineProjectActivityType>>;
|
||||
};
|
||||
|
||||
export type UnsubscribeFromNotificationResult = {
|
||||
__typename?: 'UnsubscribeFromNotificationResult';
|
||||
notificationSubscription: NotificationSubscription;
|
||||
@@ -4302,6 +4363,8 @@ export type Update = ActivityTimelineProjectActivity & {
|
||||
manifestPermalink: Scalars['String'];
|
||||
message?: Maybe<Scalars['String']>;
|
||||
platform: Scalars['String'];
|
||||
runtime: Runtime;
|
||||
/** @deprecated Use 'runtime' field . */
|
||||
runtimeVersion: Scalars['String'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
@@ -4505,7 +4568,7 @@ export type UsageMetricsTimespan = {
|
||||
};
|
||||
|
||||
/** Represents a human (not robot) actor. */
|
||||
export type User = Actor & {
|
||||
export type User = Actor & UserActor & {
|
||||
__typename?: 'User';
|
||||
/** Access Tokens belonging to this actor */
|
||||
accessTokens: Array<AccessToken>;
|
||||
@@ -4516,6 +4579,7 @@ export type User = Actor & {
|
||||
appetizeCode?: Maybe<Scalars['String']>;
|
||||
/** Apps this user has published */
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
displayName: Scalars['String'];
|
||||
email?: Maybe<Scalars['String']>;
|
||||
@@ -4585,6 +4649,103 @@ export type UserSnacksArgs = {
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActor = {
|
||||
/** Access Tokens belonging to this user actor */
|
||||
accessTokens: Array<AccessToken>;
|
||||
accounts: Array<Account>;
|
||||
/**
|
||||
* Coalesced project activity for all apps belonging to all accounts this user actor belongs to.
|
||||
* Only resolves for the viewer.
|
||||
*/
|
||||
activityTimelineProjectActivities: Array<ActivityTimelineProjectActivity>;
|
||||
appCount: Scalars['Int'];
|
||||
appetizeCode?: Maybe<Scalars['String']>;
|
||||
/** Apps this user has published */
|
||||
apps: Array<App>;
|
||||
bestContactEmail?: Maybe<Scalars['String']>;
|
||||
created: Scalars['DateTime'];
|
||||
/**
|
||||
* Best-effort human readable name for this human actor for use in user interfaces during action attribution.
|
||||
* For example, when displaying a sentence indicating that actor X created a build or published an update.
|
||||
*/
|
||||
displayName: Scalars['String'];
|
||||
/**
|
||||
* Server feature gate values for this user actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
*/
|
||||
featureGates: Scalars['JSONObject'];
|
||||
firstName?: Maybe<Scalars['String']>;
|
||||
fullName?: Maybe<Scalars['String']>;
|
||||
githubUsername?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
industry?: Maybe<Scalars['String']>;
|
||||
isExpoAdmin: Scalars['Boolean'];
|
||||
lastName?: Maybe<Scalars['String']>;
|
||||
location?: Maybe<Scalars['String']>;
|
||||
notificationSubscriptions: Array<NotificationSubscription>;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
profilePhoto: Scalars['String'];
|
||||
/** Snacks associated with this user's personal account */
|
||||
snacks: Array<Snack>;
|
||||
twitterUsername?: Maybe<Scalars['String']>;
|
||||
username: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorActivityTimelineProjectActivitiesArgs = {
|
||||
createdBefore?: InputMaybe<Scalars['DateTime']>;
|
||||
filterTypes?: InputMaybe<Array<ActivityTimelineProjectActivityType>>;
|
||||
limit: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorAppsArgs = {
|
||||
includeUnpublished?: InputMaybe<Scalars['Boolean']>;
|
||||
limit: Scalars['Int'];
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorFeatureGatesArgs = {
|
||||
filter?: InputMaybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorNotificationSubscriptionsArgs = {
|
||||
filter?: InputMaybe<NotificationSubscriptionFilter>;
|
||||
};
|
||||
|
||||
|
||||
/** A human user (type User or SSOUser) that can login to the Expo website, use Expo services, and be a member of accounts. */
|
||||
export type UserActorSnacksArgs = {
|
||||
limit: Scalars['Int'];
|
||||
offset: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type UserActorQuery = {
|
||||
__typename?: 'UserActorQuery';
|
||||
/** Query a UserActor by ID */
|
||||
byId: UserActor;
|
||||
/** Query a UserActor by username */
|
||||
byUsername: UserActor;
|
||||
};
|
||||
|
||||
|
||||
export type UserActorQueryByIdArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type UserActorQueryByUsernameArgs = {
|
||||
username: Scalars['String'];
|
||||
};
|
||||
|
||||
export type UserDataInput = {
|
||||
appetizeCode?: InputMaybe<Scalars['String']>;
|
||||
email?: InputMaybe<Scalars['String']>;
|
||||
@@ -4720,7 +4881,7 @@ export type UserQuery = {
|
||||
|
||||
|
||||
export type UserQueryByIdArgs = {
|
||||
userId: Scalars['String'];
|
||||
userId: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
@@ -5296,7 +5457,7 @@ export type CreateAndroidBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
export type CreateAndroidBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createAndroidBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
|
||||
export type CreateIosBuildMutationVariables = Exact<{
|
||||
appId: Scalars['ID'];
|
||||
@@ -5306,7 +5467,15 @@ export type CreateIosBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
|
||||
|
||||
export type UpdateBuildMetadataMutationVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
metadata: BuildMetadataInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateBuildMetadataMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', updateBuildMetadata: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
|
||||
export type RetryIosBuildMutationVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
@@ -5314,7 +5483,7 @@ export type RetryIosBuildMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
|
||||
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
|
||||
input: CreateEnvironmentSecretInput;
|
||||
@@ -5471,14 +5640,14 @@ export type BuildsByIdQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
|
||||
export type BuildsWithSubmissionsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
|
||||
|
||||
export type ViewBuildsOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5488,7 +5657,7 @@ export type ViewBuildsOnAppQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
|
||||
export type ViewBuildsOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, builds: Array<{ __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
|
||||
|
||||
export type ViewUpdateChannelOnAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -5605,9 +5774,9 @@ export type AccountFragment = { __typename?: 'Account', id: string, name: string
|
||||
|
||||
export type AppFragment = { __typename?: 'App', id: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, users: Array<{ __typename?: 'UserPermission', role?: Role | null, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> } };
|
||||
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
|
||||
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, completedAt?: any | null, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, resourceClass: BuildResourceClass, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
|
||||
|
||||
export type EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, type: EnvironmentSecretType, createdAt: any };
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ import {
|
||||
IosJobOverridesInput,
|
||||
RetryIosBuildMutation,
|
||||
RetryIosBuildMutationVariables,
|
||||
UpdateBuildMetadataMutation,
|
||||
UpdateBuildMetadataMutationVariables,
|
||||
} from '../generated';
|
||||
import { BuildFragmentNode } from '../types/Build';
|
||||
|
||||
@@ -119,6 +121,33 @@ export const BuildMutation = {
|
||||
);
|
||||
return nullthrows(data.build?.createIosBuild);
|
||||
},
|
||||
async updateBuildMetadataAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
input: {
|
||||
buildId: string;
|
||||
metadata: BuildMetadataInput;
|
||||
}
|
||||
): Promise<BuildFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<UpdateBuildMetadataMutation, UpdateBuildMetadataMutationVariables>(
|
||||
gql`
|
||||
mutation UpdateBuildMetadataMutation($buildId: ID!, $metadata: BuildMetadataInput!) {
|
||||
build {
|
||||
updateBuildMetadata(buildId: $buildId, metadata: $metadata) {
|
||||
id
|
||||
...BuildFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(BuildFragmentNode)}
|
||||
`,
|
||||
input
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return nullthrows(data.build?.updateBuildMetadata);
|
||||
},
|
||||
async retryIosBuildAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
input: {
|
||||
|
||||
@@ -52,7 +52,9 @@ export const BuildFragmentNode = gql`
|
||||
priority
|
||||
createdAt
|
||||
updatedAt
|
||||
message
|
||||
completedAt
|
||||
resourceClass
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -90,11 +90,12 @@ export default class Log {
|
||||
*/
|
||||
export function link(
|
||||
url: string,
|
||||
{ text = url, dim = true }: { text?: string; dim?: boolean } = {}
|
||||
{ text = url, fallback, dim = true }: { text?: string; dim?: boolean; fallback?: string } = {}
|
||||
): string {
|
||||
// Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
|
||||
const output = terminalLink(text, url, {
|
||||
fallback: () => (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
|
||||
fallback: () =>
|
||||
fallback ?? (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
|
||||
});
|
||||
return dim ? chalk.dim(output) : output;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ const KEYWORD_CHARACTER_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Keywords are limited to 100 characters when converted to a comma separated string.
|
||||
* @see https://developer.apple.com/app-store/search/
|
||||
* @see https://developer-mdn.apple.com/app-store/search/
|
||||
*/
|
||||
export const infoKeywordLength: IssueRule = {
|
||||
id: 'apple.info.keyword.length',
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface AppleRelease {
|
||||
export interface AppleInfo {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
/** Does not effect ASO https://developer.apple.com/app-store/product-page/ */
|
||||
/** Does not effect ASO https://developer-mdn.apple.com/app-store/product-page/ */
|
||||
promoText?: string;
|
||||
description?: string;
|
||||
keywords?: string[];
|
||||
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
convertAssetToUpdateInfoGroupFormatAsync,
|
||||
filterExportedPlatformsByFlag,
|
||||
filterOutAssetsThatAlreadyExistAsync,
|
||||
getAssetHashFromPath,
|
||||
getBase64URLEncoding,
|
||||
getOriginalPathFromAssetMap,
|
||||
getStorageKey,
|
||||
getStorageKeyForAssetAsync,
|
||||
guessContentTypeFromExtension,
|
||||
@@ -279,9 +281,55 @@ describe(resolveInputDirectoryAsync, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(getAssetHashFromPath, () => {
|
||||
it('returns asset hash from path', () => {
|
||||
expect(getAssetHashFromPath('assets/5b2a819c71d035ca45d223e4c47ed4f9')).toBe(
|
||||
'5b2a819c71d035ca45d223e4c47ed4f9'
|
||||
);
|
||||
});
|
||||
it('returns null for incorrect path', () => {
|
||||
expect(getAssetHashFromPath('assets/this/is/not/a/hash.jpg')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe(getOriginalPathFromAssetMap, () => {
|
||||
// Partial assetmap.json, with only fields we need
|
||||
const fakeAssetMap = {
|
||||
'5b2a819c71d035ca45d223e4c47ed4f9': {
|
||||
httpServerLocation: '/assets/src/assets',
|
||||
name: 'asset-420',
|
||||
type: 'jpg',
|
||||
},
|
||||
};
|
||||
it('returns null path when asset map is null', () => {
|
||||
expect(
|
||||
getOriginalPathFromAssetMap(null, {
|
||||
path: 'assets/5b2a819c71d035ca45d223e4c47ed4f9',
|
||||
ext: 'jpg',
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
it('returns null when asset is not found in asset map', () => {
|
||||
expect(
|
||||
getOriginalPathFromAssetMap(fakeAssetMap, {
|
||||
path: 'assets/fb64d3b2fb71b3d739ad5c13a93e12c5',
|
||||
ext: 'jpg',
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
it('returns reconstructed original path from existing asset in asset map', () => {
|
||||
expect(
|
||||
getOriginalPathFromAssetMap(fakeAssetMap, {
|
||||
path: 'assets/5b2a819c71d035ca45d223e4c47ed4f9',
|
||||
ext: 'jpg',
|
||||
})
|
||||
).toBe('/src/assets/asset-420.jpg');
|
||||
});
|
||||
});
|
||||
|
||||
describe(collectAssetsAsync, () => {
|
||||
it('builds an update info group', async () => {
|
||||
const fakeHash = 'md5-hash-of-jpg';
|
||||
const fakeHash = 'hdahukw8adhawi8fawhfa8';
|
||||
const bundles = {
|
||||
android: 'android-bundle-code',
|
||||
ios: 'ios-bundle-code',
|
||||
@@ -294,6 +342,7 @@ describe(collectAssetsAsync, () => {
|
||||
fileExtension: '.jpg',
|
||||
contentType: 'image/jpeg',
|
||||
path: `${inputDir}/assets/${fakeHash}`,
|
||||
originalPath: `assets/wat.jpg`,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -326,6 +375,24 @@ describe(collectAssetsAsync, () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.resolve(inputDir, 'assetmap.json'),
|
||||
JSON.stringify({
|
||||
[fakeHash]: {
|
||||
__packager_asset: true,
|
||||
fileSystemLocation: '/Users/blah/temp/assets',
|
||||
httpServerLocation: 'assets/assets',
|
||||
width: 2339,
|
||||
height: 1560,
|
||||
scales: [1],
|
||||
files: ['/Users/blah/temp/assets/wat.jpg'],
|
||||
hash: fakeHash,
|
||||
name: 'wat',
|
||||
type: 'jpg',
|
||||
fileHashes: [fakeHash],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(await collectAssetsAsync(inputDir)).toEqual({
|
||||
android: {
|
||||
@@ -410,6 +477,7 @@ describe(uploadAssetsAsync, () => {
|
||||
const androidBundlePath = uuidv4();
|
||||
const iosBundlePath = uuidv4();
|
||||
const dummyFilePath = uuidv4();
|
||||
const dummyOriginalFilePath = uuidv4();
|
||||
const userDefinedPath = uuidv4();
|
||||
const testProjectId = uuidv4();
|
||||
const expectedAssetLimit = 1400;
|
||||
@@ -427,7 +495,15 @@ describe(uploadAssetsAsync, () => {
|
||||
contentType: 'application/javascript',
|
||||
path: androidBundlePath,
|
||||
},
|
||||
assets: [userDefinedAsset, { type: 'jpg', contentType: 'image/jpeg', path: dummyFilePath }],
|
||||
assets: [
|
||||
userDefinedAsset,
|
||||
{
|
||||
type: 'jpg',
|
||||
contentType: 'image/jpeg',
|
||||
path: dummyFilePath,
|
||||
originalPath: dummyOriginalFilePath,
|
||||
},
|
||||
],
|
||||
},
|
||||
ios: {
|
||||
launchAsset: {
|
||||
@@ -435,7 +511,15 @@ describe(uploadAssetsAsync, () => {
|
||||
contentType: 'application/javascript',
|
||||
path: androidBundlePath,
|
||||
},
|
||||
assets: [userDefinedAsset, { type: 'jpg', contentType: 'image/jpeg', path: dummyFilePath }],
|
||||
assets: [
|
||||
userDefinedAsset,
|
||||
{
|
||||
type: 'jpg',
|
||||
contentType: 'image/jpeg',
|
||||
path: dummyFilePath,
|
||||
originalPath: dummyOriginalFilePath,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -490,8 +574,10 @@ describe(uploadAssetsAsync, () => {
|
||||
uploadAssetsAsync(graphqlClient, assetsForUpdateInfoGroup, testProjectId)
|
||||
).resolves.toEqual({
|
||||
assetCount: 6,
|
||||
launchAssetCount: 2,
|
||||
uniqueAssetCount: 3,
|
||||
uniqueUploadedAssetCount: 0,
|
||||
uniqueUploadedAssetPaths: [],
|
||||
assetLimitPerUpdateGroup: expectedAssetLimit,
|
||||
});
|
||||
});
|
||||
@@ -526,8 +612,10 @@ describe(uploadAssetsAsync, () => {
|
||||
uploadAssetsAsync(graphqlClient, assetsForUpdateInfoGroup, testProjectId)
|
||||
).resolves.toEqual({
|
||||
assetCount: 6,
|
||||
launchAssetCount: 2,
|
||||
uniqueAssetCount: 3,
|
||||
uniqueUploadedAssetCount: 2,
|
||||
uniqueUploadedAssetPaths: [],
|
||||
assetLimitPerUpdateGroup: expectedAssetLimit,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import {
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync,
|
||||
@@ -11,6 +14,7 @@ import {
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../prompts');
|
||||
jest.mock('../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -103,6 +107,8 @@ describe(getApplicationIdAsync, () => {
|
||||
describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
|
||||
describe('managed project + android.package missing in app config', () => {
|
||||
it('throws an error if using app.config.js', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.config.js': 'module.exports = { blah: {} };',
|
||||
@@ -110,10 +116,23 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
|
||||
'/app'
|
||||
);
|
||||
await expect(
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '',
|
||||
exp: {} as any,
|
||||
})
|
||||
).rejects.toThrowError(/we can't update this file programmatically/);
|
||||
});
|
||||
it('prompts for the Android package if using app.json', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.json': '{ "expo": {} }',
|
||||
@@ -126,11 +145,26 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
|
||||
}));
|
||||
|
||||
await expect(
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '',
|
||||
exp: {} as any,
|
||||
})
|
||||
).resolves.toBe('com.expo.notdominik');
|
||||
expect(promptAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('puts the Android package in app.json', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.json': '{ "expo": {} }',
|
||||
@@ -143,7 +177,12 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
|
||||
}));
|
||||
|
||||
await expect(
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '',
|
||||
exp: {} as any,
|
||||
})
|
||||
).resolves.toBe('com.expo.notdominik');
|
||||
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
|
||||
expect(appJson).toMatchObject({
|
||||
|
||||
@@ -7,22 +7,31 @@ import fs from 'fs-extra';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { readAppJson } from '../../build/utils/appJson';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import env from '../../env';
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { getProjectConfigDescription, getUsername } from '../../project/projectUtils';
|
||||
import {
|
||||
getOwnerAccountForProjectIdAsync,
|
||||
getProjectConfigDescription,
|
||||
} from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { Actor } from '../../user/User';
|
||||
import { resolveWorkflowAsync } from '../workflow';
|
||||
import { GradleBuildContext } from './gradle';
|
||||
import * as gradleUtils from './gradleUtils';
|
||||
|
||||
export const INVALID_APPLICATION_ID_MESSAGE = `Invalid format of Android applicationId. Only alphanumeric characters, '.' and '_' are allowed, and each '.' must be followed by a letter.`;
|
||||
|
||||
export async function ensureApplicationIdIsDefinedForManagedProjectAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
): Promise<string> {
|
||||
export async function ensureApplicationIdIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
}): Promise<string> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects');
|
||||
|
||||
@@ -31,7 +40,7 @@ export async function ensureApplicationIdIsDefinedForManagedProjectAsync(
|
||||
moduleName: gradleUtils.DEFAULT_MODULE_NAME,
|
||||
});
|
||||
} catch {
|
||||
return await configureApplicationIdAsync(projectDir, exp, actor);
|
||||
return await configureApplicationIdAsync({ graphqlClient, projectDir, projectId, exp });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,11 +119,17 @@ export async function getApplicationIdAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function configureApplicationIdAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
): Promise<string> {
|
||||
async function configureApplicationIdAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
}): Promise<string> {
|
||||
const paths = getConfigFilePaths(projectDir);
|
||||
// we can't automatically update app.config.js
|
||||
if (paths.dynamicConfigPath) {
|
||||
@@ -132,7 +147,11 @@ async function configureApplicationIdAsync(
|
||||
)}`
|
||||
);
|
||||
|
||||
const suggestedAndroidApplicationId = await getSuggestedApplicationIdAsync(exp, actor);
|
||||
const suggestedAndroidApplicationId = await getSuggestedApplicationIdAsync(
|
||||
graphqlClient,
|
||||
exp,
|
||||
projectId
|
||||
);
|
||||
const { packageName } = await promptAsync({
|
||||
name: 'packageName',
|
||||
type: 'text',
|
||||
@@ -174,8 +193,9 @@ export function warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(
|
||||
}
|
||||
|
||||
async function getSuggestedApplicationIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
projectId: string
|
||||
): Promise<string | undefined> {
|
||||
// Attempt to use the ios bundle id first since it's convenient to have them aligned.
|
||||
const maybeBundleId = IOSConfig.BundleIdentifier.getBundleIdentifier(exp);
|
||||
@@ -183,9 +203,9 @@ async function getSuggestedApplicationIdAsync(
|
||||
return maybeBundleId;
|
||||
} else {
|
||||
// the only callsite is heavily interactive
|
||||
const username = getUsername(exp, actor);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
// It's common to use dashes in your node project name, strip them from the suggested package name.
|
||||
const possibleId = `com.${username}.${exp.slug}`.split('-').join('');
|
||||
const possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
|
||||
if (isApplicationIdValid(possibleId)) {
|
||||
return possibleId;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export function parseGradleCommand(cmd: string, buildGradle: AppBuildGradle): Gr
|
||||
const [moduleName, taskName] =
|
||||
splitCmd.length > 1 ? [splitCmd[0], splitCmd[1]] : [undefined, splitCmd[0]];
|
||||
|
||||
const matchResult = taskName.match(/(build|bundle|assemble)(.*)(Release|Debug)/);
|
||||
const matchResult = taskName.match(/(build|bundle|assemble|package)(.*)(Release|Debug)/);
|
||||
if (!matchResult) {
|
||||
throw new Error(`Failed to parse gradle command: ${cmd}`);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { Actor } from '../user/User';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync,
|
||||
getApplicationIdAsync,
|
||||
@@ -16,19 +16,32 @@ import { resolveXcodeBuildContextAsync } from './ios/scheme';
|
||||
import { findApplicationTarget, resolveTargetsAsync } from './ios/target';
|
||||
import { resolveWorkflowAsync } from './workflow';
|
||||
|
||||
export async function getApplicationIdentifierAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
buildProfile: BuildProfile,
|
||||
platform: Platform,
|
||||
actor: Actor
|
||||
): Promise<string> {
|
||||
export async function getApplicationIdentifierAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
buildProfile,
|
||||
platform,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
buildProfile: BuildProfile;
|
||||
platform: Platform;
|
||||
}): Promise<string> {
|
||||
if (platform === Platform.ANDROID) {
|
||||
const profile = buildProfile as BuildProfile<Platform.ANDROID>;
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
|
||||
if (workflow === Workflow.MANAGED) {
|
||||
return await ensureApplicationIdIsDefinedForManagedProjectAsync(projectDir, exp, actor);
|
||||
return await ensureApplicationIdIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
});
|
||||
}
|
||||
|
||||
const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile);
|
||||
@@ -37,7 +50,12 @@ export async function getApplicationIdentifierAsync(
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
const profile = buildProfile as BuildProfile<Platform.IOS>;
|
||||
if (workflow === Workflow.MANAGED) {
|
||||
return await ensureBundleIdentifierIsDefinedForManagedProjectAsync(projectDir, exp, actor);
|
||||
return await ensureBundleIdentifierIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
});
|
||||
}
|
||||
|
||||
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
export async function validateCustomBuildConfigAsync(
|
||||
projectDir: string,
|
||||
profile: BuildProfile<Platform>
|
||||
): Promise<void> {
|
||||
if (!profile.config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeBuildConfigPath = getCustomBuildConfigPath(profile.config);
|
||||
const buildConfigPath = path.join(projectDir, relativeBuildConfigPath);
|
||||
if (!(await fs.pathExists(buildConfigPath))) {
|
||||
throw new Error(
|
||||
`Custom build configuration file ${chalk.bold(relativeBuildConfigPath)} does not exist.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getCustomBuildConfigPath(configFilename: string): string {
|
||||
return path.join('.eas/build', configFilename);
|
||||
}
|
||||
@@ -2,8 +2,11 @@ import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import {
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync,
|
||||
@@ -13,6 +16,7 @@ import {
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../prompts');
|
||||
jest.mock('../../../graphql/queries/AppQuery');
|
||||
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -86,6 +90,7 @@ describe(getBundleIdentifierAsync, () => {
|
||||
describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
|
||||
describe('managed project + ios.bundleIdentifier missing in app config', () => {
|
||||
it('throws an error if using app.config.js', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.config.js': 'module.exports = { blah: {} };',
|
||||
@@ -93,10 +98,23 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
|
||||
'/app'
|
||||
);
|
||||
await expect(
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '1234',
|
||||
exp: {} as any,
|
||||
})
|
||||
).rejects.toThrowError(/we can't update this file programmatically/);
|
||||
});
|
||||
it('prompts for the bundle identifier if using app.json', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.json': '{ "expo": {} }',
|
||||
@@ -109,11 +127,24 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
|
||||
}));
|
||||
|
||||
await expect(
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '1234',
|
||||
exp: {} as any,
|
||||
})
|
||||
).resolves.toBe('com.expo.notdominik');
|
||||
expect(promptAsync).toHaveBeenCalled();
|
||||
});
|
||||
it('puts the bundle identifier in app.json', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>());
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
vol.fromJSON(
|
||||
{
|
||||
'app.json': '{ "expo": {} }',
|
||||
@@ -126,7 +157,12 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
|
||||
}));
|
||||
|
||||
await expect(
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync('/app', {} as any, mockJester)
|
||||
ensureBundleIdentifierIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir: '/app',
|
||||
projectId: '1234',
|
||||
exp: {} as any,
|
||||
})
|
||||
).resolves.toBe('com.expo.notdominik');
|
||||
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
|
||||
expect(appJson).toMatchObject({
|
||||
|
||||
@@ -6,26 +6,37 @@ import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { readAppJson } from '../../build/utils/appJson';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { Actor } from '../../user/User';
|
||||
import { getProjectConfigDescription, getUsername } from '../projectUtils';
|
||||
import { getOwnerAccountForProjectIdAsync, getProjectConfigDescription } from '../projectUtils';
|
||||
import { resolveWorkflowAsync } from '../workflow';
|
||||
|
||||
export const INVALID_BUNDLE_IDENTIFIER_MESSAGE = `Invalid format of iOS bundle identifier. Only alphanumeric characters, '.' and '-' are allowed, and each '.' must be followed by a letter.`;
|
||||
|
||||
export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
): Promise<string> {
|
||||
export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
}): Promise<string> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects');
|
||||
|
||||
try {
|
||||
return await getBundleIdentifierAsync(projectDir, exp);
|
||||
} catch {
|
||||
return await configureBundleIdentifierAsync(projectDir, exp, actor);
|
||||
return await configureBundleIdentifierAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
exp,
|
||||
projectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +111,17 @@ export async function getBundleIdentifierAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function configureBundleIdentifierAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
): Promise<string> {
|
||||
async function configureBundleIdentifierAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
}): Promise<string> {
|
||||
const paths = getConfigFilePaths(projectDir);
|
||||
// we can't automatically update app.config.js
|
||||
if (paths.dynamicConfigPath) {
|
||||
@@ -122,7 +139,11 @@ async function configureBundleIdentifierAsync(
|
||||
)}`
|
||||
);
|
||||
|
||||
const suggestedBundleIdentifier = await getSuggestedBundleIdentifierAsync(exp, actor);
|
||||
const suggestedBundleIdentifier = await getSuggestedBundleIdentifierAsync(
|
||||
graphqlClient,
|
||||
exp,
|
||||
projectId
|
||||
);
|
||||
const { bundleIdentifier } = await promptAsync({
|
||||
name: 'bundleIdentifier',
|
||||
type: 'text',
|
||||
@@ -169,8 +190,9 @@ export function isWildcardBundleIdentifier(bundleIdentifier: string): boolean {
|
||||
}
|
||||
|
||||
async function getSuggestedBundleIdentifierAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
exp: ExpoConfig,
|
||||
actor: Actor
|
||||
projectId: string
|
||||
): Promise<string | undefined> {
|
||||
// Attempt to use the android package name first since it's convenient to have them aligned.
|
||||
const maybeAndroidPackage = AndroidConfig.Package.getPackage(exp);
|
||||
@@ -178,9 +200,9 @@ async function getSuggestedBundleIdentifierAsync(
|
||||
return maybeAndroidPackage;
|
||||
} else {
|
||||
// the only callsite is heavily interactive
|
||||
const username = getUsername(exp, actor);
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
// It's common to use dashes in your node project name, strip them from the suggested package name.
|
||||
const possibleId = `com.${username}.${exp.slug}`.split('-').join('');
|
||||
const possibleId = `com.${account.name}.${exp.slug}`.split('-').join('');
|
||||
if (isBundleIdentifierValid(possibleId)) {
|
||||
return possibleId;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ export function getApplePlatformFromSdkRoot(target: Target): ApplePlatform | nul
|
||||
* Get Apple Platform from the Xcode TARGETED_DEVICE_FAMILY where possible.
|
||||
*
|
||||
* References:
|
||||
* https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
|
||||
* https://developer-mdn.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html
|
||||
* https://stackoverflow.com/questions/39677524/xcode-8-how-to-change-targeted-device-family#comment100316573_39677659
|
||||
*
|
||||
* @returns - Apple Platform when known, defaults to null when unknown
|
||||
|
||||
@@ -15,6 +15,7 @@ import Log from '../log';
|
||||
import { PresignedPost, uploadWithPresignedPostWithRetryAsync } from '../uploads';
|
||||
import { expoCommandAsync, shouldUseVersionedExpoCLI } from '../utils/expoCli';
|
||||
import chunk from '../utils/expodash/chunk';
|
||||
import { truthy } from '../utils/expodash/filter';
|
||||
import uniqBy from '../utils/expodash/uniqBy';
|
||||
|
||||
export type ExpoCLIExportPlatformFlag = Platform | 'all';
|
||||
@@ -30,6 +31,8 @@ export type RawAsset = {
|
||||
fileExtension?: string;
|
||||
contentType: string;
|
||||
path: string;
|
||||
/** Original asset path derrived from asset map, or exported folder */
|
||||
originalPath?: string;
|
||||
};
|
||||
|
||||
type CollectedAssets = {
|
||||
@@ -52,6 +55,16 @@ type UpdateInfoGroup = {
|
||||
[key in Platform]: ManifestFragment;
|
||||
};
|
||||
|
||||
// Partial copy of `@expo/dev-server` `BundleAssetWithFileHashes`
|
||||
type AssetMap = Record<
|
||||
string,
|
||||
{
|
||||
httpServerLocation: string;
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
>;
|
||||
|
||||
const fileMetadataJoi = Joi.object({
|
||||
assets: Joi.array()
|
||||
.required()
|
||||
@@ -170,6 +183,7 @@ export async function buildBundlesAsync({
|
||||
'--output-dir',
|
||||
inputDir,
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
'--platform',
|
||||
platformFlag,
|
||||
]);
|
||||
@@ -182,6 +196,7 @@ export async function buildBundlesAsync({
|
||||
'--experimental-bundle',
|
||||
'--non-interactive',
|
||||
'--dump-sourcemap',
|
||||
'--dump-assetmap',
|
||||
'--platform',
|
||||
platformFlag,
|
||||
]);
|
||||
@@ -246,9 +261,48 @@ export function filterExportedPlatformsByFlag<T extends Partial<Record<Platform,
|
||||
return { [platform]: record[platform] } as T;
|
||||
}
|
||||
|
||||
/** Try to load the asset map for logging the names of assets published */
|
||||
export async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> {
|
||||
const assetMapPath = path.join(distRoot, 'assetmap.json');
|
||||
|
||||
if (!(await fs.pathExists(assetMapPath))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assetMap: AssetMap = JsonFile.read(path.join(distRoot, 'assetmap.json'));
|
||||
// TODO: basic validation?
|
||||
return assetMap;
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
export function getAssetHashFromPath(assetPath: string): string | null {
|
||||
const [, hash] = assetPath.match(new RegExp(/assets\/([a-z0-9]+)$/, 'i')) ?? [];
|
||||
return hash ?? null;
|
||||
}
|
||||
|
||||
// exposed for testing
|
||||
export function getOriginalPathFromAssetMap(
|
||||
assetMap: AssetMap | null,
|
||||
asset: { path: string; ext: string }
|
||||
): string | null {
|
||||
if (!assetMap) {
|
||||
return null;
|
||||
}
|
||||
const assetHash = getAssetHashFromPath(asset.path);
|
||||
const assetMapEntry = assetHash && assetMap[assetHash];
|
||||
|
||||
if (!assetMapEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pathPrefix = assetMapEntry.httpServerLocation.substring('/assets'.length);
|
||||
return `${pathPrefix}/${assetMapEntry.name}.${assetMapEntry.type}`;
|
||||
}
|
||||
|
||||
/** Given a directory, load the metadata.json and collect the assets for each platform. */
|
||||
export async function collectAssetsAsync(dir: string): Promise<CollectedAssets> {
|
||||
const metadata = loadMetadata(dir);
|
||||
const assetmap = await loadAssetMapAsync(dir);
|
||||
|
||||
const collectedAssets: CollectedAssets = {};
|
||||
|
||||
@@ -261,6 +315,7 @@ export async function collectAssetsAsync(dir: string): Promise<CollectedAssets>
|
||||
},
|
||||
assets: metadata.fileMetadata[platform].assets.map(asset => ({
|
||||
fileExtension: asset.ext ? ensureLeadingPeriod(asset.ext) : undefined,
|
||||
originalPath: getOriginalPathFromAssetMap(assetmap, asset) ?? undefined,
|
||||
contentType: guessContentTypeFromExtension(asset.ext),
|
||||
path: path.join(dir, asset.path),
|
||||
})),
|
||||
@@ -294,9 +349,17 @@ export async function filterOutAssetsThatAlreadyExistAsync(
|
||||
}
|
||||
|
||||
type AssetUploadResult = {
|
||||
/** All found assets within the exported folder per platform */
|
||||
assetCount: number;
|
||||
/** The uploaded JS bundles, per platform */
|
||||
launchAssetCount: number;
|
||||
/** All unique assets within the exported folder with platforms combined */
|
||||
uniqueAssetCount: number;
|
||||
/** All unique assets uploaded */
|
||||
uniqueUploadedAssetCount: number;
|
||||
/** All (non-launch) asset original paths, used for logging */
|
||||
uniqueUploadedAssetPaths: string[];
|
||||
/** The asset limit received from the server */
|
||||
assetLimitPerUpdateGroup: number;
|
||||
};
|
||||
|
||||
@@ -308,7 +371,9 @@ export async function uploadAssetsAsync(
|
||||
): Promise<AssetUploadResult> {
|
||||
let assets: RawAsset[] = [];
|
||||
let platform: keyof CollectedAssets;
|
||||
const launchAssets: RawAsset[] = [];
|
||||
for (platform in assetsForUpdateInfoGroup) {
|
||||
launchAssets.push(assetsForUpdateInfoGroup[platform]!.launchAsset);
|
||||
assets = [
|
||||
...assets,
|
||||
assetsForUpdateInfoGroup[platform]!.launchAsset,
|
||||
@@ -335,6 +400,7 @@ export async function uploadAssetsAsync(
|
||||
updateSpinnerText?.(totalAssets, totalAssets);
|
||||
let missingAssets = await filterOutAssetsThatAlreadyExistAsync(graphqlClient, uniqueAssets);
|
||||
const uniqueUploadedAssetCount = missingAssets.length;
|
||||
const uniqueUploadedAssetPaths = missingAssets.map(asset => asset.originalPath).filter(truthy);
|
||||
|
||||
const missingAssetChunks = chunk(missingAssets, 100);
|
||||
const specifications: string[] = [];
|
||||
@@ -372,8 +438,10 @@ export async function uploadAssetsAsync(
|
||||
}
|
||||
return {
|
||||
assetCount: assets.length,
|
||||
launchAssetCount: launchAssets.length,
|
||||
uniqueAssetCount: uniqueAssets.length,
|
||||
uniqueUploadedAssetCount,
|
||||
uniqueUploadedAssetPaths,
|
||||
assetLimitPerUpdateGroup,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -317,30 +317,48 @@ async function handleBuildListSourceAsync(
|
||||
}
|
||||
|
||||
function formatBuildChoice(build: BuildFragment, expiryDate: Date): prompts.Choice {
|
||||
const { id, updatedAt, runtimeVersion, buildProfile, gitCommitHash, gitCommitMessage, channel } =
|
||||
build;
|
||||
|
||||
const formatValue = (field?: string | null): string => (field ? chalk.bold(field) : 'Unknown');
|
||||
|
||||
const {
|
||||
id,
|
||||
updatedAt,
|
||||
runtimeVersion,
|
||||
buildProfile,
|
||||
gitCommitHash,
|
||||
gitCommitMessage,
|
||||
channel,
|
||||
message,
|
||||
} = build;
|
||||
const buildDate = new Date(updatedAt);
|
||||
|
||||
const splitCommitMessage = gitCommitMessage?.split('\n');
|
||||
const formattedCommitData =
|
||||
gitCommitHash && gitCommitMessage
|
||||
? `${gitCommitHash.slice(0, 7)} "${chalk.bold(gitCommitMessage)}"`
|
||||
: 'Unknown';
|
||||
gitCommitHash && splitCommitMessage && splitCommitMessage.length > 0
|
||||
? `${gitCommitHash.slice(0, 7)} "${chalk.bold(
|
||||
splitCommitMessage[0] + (splitCommitMessage.length > 1 ? '…' : '')
|
||||
)}"`
|
||||
: null;
|
||||
|
||||
const title = `${chalk.bold(`ID:`)} ${id} (${chalk.bold(`${fromNow(buildDate)} ago`)})`;
|
||||
|
||||
const description = [
|
||||
`\t${chalk.bold(`Profile:`)} ${formatValue(buildProfile)}`,
|
||||
`\t${chalk.bold(`Channel:`)} ${formatValue(channel)}`,
|
||||
`\t${chalk.bold(`Runtime version:`)} ${formatValue(runtimeVersion)}`,
|
||||
`\t${chalk.bold(`Commit:`)} ${formattedCommitData}`,
|
||||
].join('\n');
|
||||
const descriptionItems: { name: string; value: string | null }[] = [
|
||||
{ name: 'Profile', value: buildProfile ? chalk.bold(buildProfile) : null },
|
||||
{ name: 'Channel', value: channel ? chalk.bold(channel) : null },
|
||||
{ name: 'Runtime version', value: runtimeVersion ? chalk.bold(runtimeVersion) : null },
|
||||
{ name: 'Commit', value: formattedCommitData },
|
||||
{
|
||||
name: 'Message',
|
||||
value: message
|
||||
? chalk.bold(message.length > 200 ? `${message.slice(0, 200)}...` : message)
|
||||
: null,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredDescriptionArray: string[] = descriptionItems
|
||||
.filter(item => item.value)
|
||||
.map(item => `${chalk.bold(item.name)}: ${item.value}`);
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
description: filteredDescriptionArray.length > 0 ? filteredDescriptionArray.join('\n') : '',
|
||||
value: build,
|
||||
disabled: buildDate < expiryDate,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { UpdateBranch, UpdateChannel } from '../../graphql/generated';
|
||||
import { Runtime, UpdateBranch, UpdateChannel } from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import { getBranchNameFromChannelNameAsync } from '../getBranchNameFromChannelNameAsync';
|
||||
|
||||
@@ -121,6 +121,7 @@ function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
|
||||
message: 'my message',
|
||||
createdAt: '2022-12-07T02:24:43.487Z',
|
||||
runtimeVersion: '1.0.0',
|
||||
runtime: {} as Runtime, // Temporary fix to resolve type errors
|
||||
platform: 'ios',
|
||||
manifestFragment: '...',
|
||||
activityTimestamp: '2022-12-07T02:24:43.487Z',
|
||||
|
||||
@@ -18,7 +18,7 @@ import { resolveWorkflowPerPlatformAsync } from '../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from './android/UpdatesModule';
|
||||
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from './ios/UpdatesModule';
|
||||
|
||||
export const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'appVersion' } as const;
|
||||
export const DEFAULT_MANAGED_RUNTIME_VERSION = { policy: 'sdkVersion' } as const;
|
||||
export const DEFAULT_BARE_RUNTIME_VERSION = '1.0.0' as const;
|
||||
|
||||
function getDefaultRuntimeVersion(workflow: Workflow): NonNullable<ExpoConfig['runtimeVersion']> {
|
||||
|
||||
@@ -7,15 +7,25 @@ export type Actor = NonNullable<CurrentUserQuery['meActor']>;
|
||||
* This should be used whenever the "current user" needs to be displayed.
|
||||
* The display name CANNOT be used as project owner.
|
||||
*/
|
||||
export function getActorDisplayName(user?: Actor): string {
|
||||
switch (user?.__typename) {
|
||||
export function getActorDisplayName(actor?: Actor): string {
|
||||
switch (actor?.__typename) {
|
||||
case 'User':
|
||||
return user.username;
|
||||
return actor.username;
|
||||
case 'Robot':
|
||||
return user.firstName ? `${user.firstName} (robot)` : 'robot';
|
||||
return actor.firstName ? `${actor.firstName} (robot)` : 'robot';
|
||||
case 'SSOUser':
|
||||
return user.username ? `${user.username} (sso user)` : 'sso user';
|
||||
return actor.username ? `${actor.username} (sso user)` : 'sso user';
|
||||
default:
|
||||
return 'anonymous';
|
||||
}
|
||||
}
|
||||
|
||||
export function getActorUsername(actor?: Actor): string | null {
|
||||
switch (actor?.__typename) {
|
||||
case 'User':
|
||||
case 'SSOUser':
|
||||
return actor.username;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICyzCCAbOgAwIBAgIJWVXVdVgS9u+DMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
|
||||
BAMTBFRlc3QwHhcNMjIwMjA0MDEzODU2WhcNMjMwMjA0MDEzODU2WjAPMQ0wCwYD
|
||||
VQQDEwRUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1ILmaSl
|
||||
1pUVFgt/nLtzoaGuBW4OPRFN5uU2rYPow3uy4NfULKiir4AWxGETcm4gFIOG3RIb
|
||||
DJys6TAJJLnFEFnzpV+qaNeqaMqnXm4OBspx/WS2rSSGIwr7raKy4yrpPYJB1A2D
|
||||
5fZ6oFoWROyjbHJvPddu0PqbbEWSM3PzdZYOKfF13ofzFOPlsTnlo12QWgksxrcT
|
||||
wgeX7cjHsXaUc+I8U9DRR+9SgKhejWdFNPGcMkEc5BIXwtkTau+8+DwQzXz775Wl
|
||||
hf0eJwfnNQeleblbLQIp8i+RmfIjPSt8W/iTSN64iBROpOvnOeJ9gKX922QDZoqW
|
||||
z9o4RTNCPfyYBQIDAQABoyowKDAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAww
|
||||
CgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAHTyG2f/CReXVrS3zZjpgnje
|
||||
Vn2/XYevnaHNWYIxjlUVGaASE/k6qWkM832f0gaZwJYXsll2YxJa5Jcdqa1yq8QA
|
||||
Ay5YBq8Vv6K6iGTeZ5wTQc3xNys7zcS95eNq7yhDDjbLDbSDdZ8T/1hZqiha2DG5
|
||||
itLGfBH+HywW2G9njLgIzNcmKx3d76y3jlVXqug1/AVFK9in+r/0t0TYlk+opqZb
|
||||
H7cjJSefHusnq2vvvR6gbkZzyzSYP5FMlj/RjRCSJ+VHNwCn3fS+KDlJgVOk0Cxc
|
||||
7n52UGazeFwH3mem89DN+Bhw/uyU0IcIaDLykb6PsTPy2tjHsLpK5kpvfrp2m0o=
|
||||
-----END CERTIFICATE-----
|
||||
MIICzTCCAbWgAwIBAgIJWEL03IR99LhMMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
|
||||
BAMTBHRlc3QwIBcNMjIwNDEyMTcyODIzWhgPMjEyMjA0MTIxNzI4MjNaMA8xDTAL
|
||||
BgNVBAMTBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBS7bz
|
||||
ELiUKOB8QNh0e0Oit7j7vk7ZICdyvBv+AB7PYbgImTvJSZ3CJTfmDYOt57ENd+ke
|
||||
V/RnD9b8u55ijroJBoR8QASVVsEYmgA1cv9owUYkIDxsAjxV9JZp3UgNeRmQ7IKI
|
||||
1J7myNnUQ3RIclSZkJO7ViHGSwTMXBanaO/OwjY6SM6Eca89XMcpOc/0xCBidFkO
|
||||
qn/jnIXk3uPAoLjPMQcX535+AUayevwSr6YqPV0zOGOYJy26n401eH3mu/UZveWy
|
||||
6qp7VumcBmz8AkvTJDgfSv04Pk3gpi4d8q4ABrtM46qWlw6UMMh5R6/i6Ou/8526
|
||||
xThOm3qEpEGXyZ2VAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E
|
||||
DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEATeE0FXXheYjw77pjy6sZ
|
||||
b6POytJdYLawMt7Chn11zP1TLx0LA1R01uH/ld0tVQQyt1XqG9AE3as1VCUYklPA
|
||||
zmwFSOxcSMiAXobEa5NcJ4S490YTjYmHI8fkz6TS+eeLkIr7eZMbpe6ck9V2d1e3
|
||||
0xHshYRc3NceNOKB018QYxbdxuTbV3a6f4yTlCSxKuyYfA5KDMoXaPAKE7Cwddrz
|
||||
1dbQpSI6djWknFjhTh7Bu/zpGWEx7wXTZlx9nIhKrinVowvyUkTaaCfuzdCmZOzS
|
||||
AJsADnJN0LEKDI4q0kDwl1v6U4cJ5Ru7VQv3xho03LPEA/fFKHK6OITDznspiXRs
|
||||
IA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,27 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAp1ILmaSl1pUVFgt/nLtzoaGuBW4OPRFN5uU2rYPow3uy4NfU
|
||||
LKiir4AWxGETcm4gFIOG3RIbDJys6TAJJLnFEFnzpV+qaNeqaMqnXm4OBspx/WS2
|
||||
rSSGIwr7raKy4yrpPYJB1A2D5fZ6oFoWROyjbHJvPddu0PqbbEWSM3PzdZYOKfF1
|
||||
3ofzFOPlsTnlo12QWgksxrcTwgeX7cjHsXaUc+I8U9DRR+9SgKhejWdFNPGcMkEc
|
||||
5BIXwtkTau+8+DwQzXz775Wlhf0eJwfnNQeleblbLQIp8i+RmfIjPSt8W/iTSN64
|
||||
iBROpOvnOeJ9gKX922QDZoqWz9o4RTNCPfyYBQIDAQABAoIBAEKMXVTEqbkJHpPg
|
||||
Cud5nuoAdkhul3cudL+LFw44UtG9V04aSadhgyMuXN/KhIOUzWmbTn6K/vsrLZKp
|
||||
qllTEdAJFuEFha+hZ4O6ZosmVqnYxzGzZvzCdB9n9OYAugmkPZRbRHdk0LscJ3Wz
|
||||
nvvW6sDtWtVL5CV2J28O1LFmQsTXPtEE5aQ5KX3Ix0OW1HBdEMOXRDFFDXhf7AKz
|
||||
nlvl3WRsXLyHYBfKs0pQHy8Gi+zl2OgKZDc9elMdU0Mx0jTJUUDtPSgNAsmdKO26
|
||||
qmkhbGd/kM1+6h+zngY3m48yD9elDxR3WUIYE2x5tT9fv3T6/xn1QfMz/gtToplx
|
||||
qY1DegECgYEA3bGUq91QMIH5rxrr2BBQpniXOTtOPAdvRcRh0/EsYhHcuL+uJEtG
|
||||
W9o9MqrREhXMMeD9Y8RorL+qefJjGq+GRwnPHlDjr6NkGTZfDKUn4IFH1xMbspET
|
||||
Nen5BsY5inUrQ/LSg1+HiVsSDyIJzGVcGMTOh9qC53M/zIGdiEwHgUECgYEAwTZ3
|
||||
AVieag5ETYahGdverxCRKlJ0MFJq3VKEdt6EcZHfeY1gmi17vGZ39q6djmfTT9Il
|
||||
QVP4/mxU2/mg/UgRC+rwiAZs3qlhOrtkrGd9qPOIeZ8PJkoNlvhM2WC2rGjTfXsb
|
||||
2IKtDEVXetwGgm3SXqpjycI5Xj6/9VlHAmCc4cUCgYAv9dT2AWDxvYyopyhSi+UG
|
||||
vpvok73vGqSl8UBAu7IgXUDk7wLbczV7dZE7vtyQDwsn10a6KKmEhcp5q0hpY4On
|
||||
JqYaJuG7A5wKIEsbzzb7SLyj+MxLKzt+tGldX9De9U4w2v1T0nzd6EfV4kVAZMUx
|
||||
zpHnrgwXykUJFxlffSM6gQKBgQCMxKXHsU0Zb/OLmD7fnDWNzsA02YYVfralMW2Z
|
||||
PV25cNIkuUBclC7GgNF+RJI+Ip7uVOkXw5pxo3PgIOuOHWduC2nbcPL49ucD52vd
|
||||
wDjpUyVnlt9uwh1MlPNInRH6YxVTItKS2AJEInEt7ghAFstidTnm0T8Czy0EEFuP
|
||||
+9vREQKBgQDIQxJ5oPIOEibJqn34z6qm5EmUyme/evvHxtQTaVuQi5CrzD5+qw1C
|
||||
jOTHmlJQQsb8b1g9wk7n+JbqWYhkRiAE6dwqvp/zDz1VfR90WbAPYQ8ZiXZ07D2P
|
||||
8P6ZXR3ZyDchuT6oUBXz5WtHUWOzXRJgPbXnFhrkIU8PkZYiPs/4Bw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwUu28xC4lCjgfEDYdHtDore4+75O2SAncrwb/gAez2G4CJk7
|
||||
yUmdwiU35g2DreexDXfpHlf0Zw/W/LueYo66CQaEfEAElVbBGJoANXL/aMFGJCA8
|
||||
bAI8VfSWad1IDXkZkOyCiNSe5sjZ1EN0SHJUmZCTu1YhxksEzFwWp2jvzsI2OkjO
|
||||
hHGvPVzHKTnP9MQgYnRZDqp/45yF5N7jwKC4zzEHF+d+fgFGsnr8Eq+mKj1dMzhj
|
||||
mCctup+NNXh95rv1Gb3lsuqqe1bpnAZs/AJL0yQ4H0r9OD5N4KYuHfKuAAa7TOOq
|
||||
lpcOlDDIeUev4ujrv/OdusU4Tpt6hKRBl8mdlQIDAQABAoIBAEVijWCA/xDH/5T6
|
||||
nfhqCuRM+Mz9CkasYRyxY4bwuh6NIEeN4cUmdMetHnypGzyAr7B6+6ZVwjiAmhaM
|
||||
rpUIUMVOnp4PSNXml1fiZ/LHveD3h9sN60KGJuxf6OJFeUjE0KWSEGVXlVaYgIDO
|
||||
Wd5rk+yv1ifoCiWo5icJY2RiqbpYouveqb/InkQU+irZEwCviNvN1ZyJKqquNgcG
|
||||
FaY7iVd3WlHRealF1OfKHCBvSah9H5UBFWRBWUds2/xNzpCTAAGsmXdG6O9wPuEQ
|
||||
IhJqnSkTiQw49vyOH708VQnBfz/MILV7uTYqQ0Tep3+xkFFy4BSUIb6fE2zJegs5
|
||||
80MxIh0CgYEA4yf8cBcYidTAeoDbl2UXasDHAmNKeR4IxKAR3AJOiETIKDv4vAMP
|
||||
oBvHnUnnUQa5LhihrVXCbVwGqwATDka73K8sW/TkCE7OmuLDpZYvU/XQRWI74rka
|
||||
A/+nbD26NlwnM/a8mJNhbCVns/+PlLq1rK/5WlINcRNVk8PZ1UpG+nsCgYEA2dcM
|
||||
1TBZs1iqa+9AQ6nco5MWbsp3roq3sM06UneK0EGffWXPcmQkBZI9Jg+p5P2o/H13
|
||||
cYvkyKGmnL8qRZViXWEa/NI60kphQmFNGr+J/3PlKCOqZhlyqGhCFN9KLumJL17P
|
||||
XUuzVAa14UsfIFCiv7R33D02o/xw89u5TN+3ky8CgYEAhmkzszPHXk9YcWRsC2JS
|
||||
/+UAHQaZm59M+uPojXGD/JgOg9gwrzd0eH3XmNeRG13KF1+V01YKjOFGRMrve6QZ
|
||||
J1Uz/1mh0NSo5fdGY2XBrYGnclbVLqvum+0bqS3BUMcon1PhdrrGi9J6UYTn9c6h
|
||||
D4S1HF+u+njBS4U5ET85Tp8CgYEAno0bVf+/Cf467BxTFeyIHrZr2W+b9HoagKCf
|
||||
Fm7TpghdYRO9DXE9lqB9yToVWgoV+NAJI6fCeRTPA79PsR2tXnHTBris/2oLqBjR
|
||||
2eoXMsrTu4dZ+r4C6fgYQMDUaZiotMW5ABqdB0drEfNvUEHgcs+TfcVAA9M1EwiV
|
||||
shIStxkCgYBnxccKAfXywLjhVUCa50TsCrL8ToQ7WmWjU1LqN2iO/htFoxeIx4xP
|
||||
N7YauagBhyuypcq7SV3bKsdxlCzKi0cMNEonuRJb1Ejo3iU+4F7rzqsltGTjLtpB
|
||||
pgp5HP8sALzFZlWWif6W1TjzNJ87MIphr/VPcitRc9n4hWcbmxDvZA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,4 +1,5 @@
|
||||
import log from '../log';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import GitNoCommitClient from './clients/gitNoCommit';
|
||||
import NoVcsClient from './clients/noVcs';
|
||||
import { Client } from './vcs';
|
||||
@@ -10,7 +11,11 @@ let vcsClient = resolveDefaultVcsClient();
|
||||
function resolveDefaultVcsClient(): Client {
|
||||
if (process.env.EAS_NO_VCS) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
log.warn(NO_VCS_WARNING);
|
||||
// This log might be printed before cli arguments are evaluated,
|
||||
// so it needs to go to stderr in case command is run in JSON
|
||||
// only mode.
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(chalk.yellow(NO_VCS_WARNING));
|
||||
}
|
||||
return new NoVcsClient();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class Ignore {
|
||||
if (await fs.pathExists(easIgnorePath)) {
|
||||
this.ignoreMapping = [
|
||||
['', createIgnore().add(DEFAULT_IGNORE)],
|
||||
['', createIgnore().add(await this.readIgnoreFileAsync(easIgnorePath))],
|
||||
['', createIgnore().add(await fs.readFile(easIgnorePath, 'utf-8'))],
|
||||
];
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +57,7 @@ export class Ignore {
|
||||
ignoreFilePaths.map(async filePath => {
|
||||
return [
|
||||
filePath.slice(0, filePath.length - GITIGNORE_FILENAME.length),
|
||||
createIgnore().add(await this.readIgnoreFileAsync(path.join(this.rootDir, filePath))),
|
||||
createIgnore().add(await fs.readFile(path.join(this.rootDir, filePath), 'utf-8')),
|
||||
] as const;
|
||||
})
|
||||
);
|
||||
@@ -72,16 +72,6 @@ export class Ignore {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async readIgnoreFileAsync(filePath: string): Promise<string> {
|
||||
const fileContents = await fs.readFile(filePath, 'utf-8');
|
||||
const lines = fileContents.split('\n');
|
||||
// Strip trailing '\'. This logic can be removed after fix upstream is released.
|
||||
// https://github.com/kaelzhang/node-ignore/issues/81
|
||||
return lines
|
||||
.map((line: string) => (line.slice(-1) === '\\' ? line.slice(0, -1) : line))
|
||||
.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
export async function makeShallowCopyAsync(src: string, dst: string): Promise<void> {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "3.3.2",
|
||||
"version": "3.7.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.18.6",
|
||||
"@expo/eas-build-job": "0.2.102",
|
||||
"@expo/eas-build-job": "0.2.106",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "10.1.0",
|
||||
@@ -36,7 +36,8 @@
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"watch": "yarn build --watch --preserveWatchOutput",
|
||||
"typecheck": "tsc",
|
||||
"prepack": "rimraf build && yarn build",
|
||||
"prepack": "yarn rebuild",
|
||||
"rebuild": "rimraf build && yarn build",
|
||||
"test": "jest",
|
||||
"clean": "rimraf build node_modules yarn-error.log"
|
||||
},
|
||||
|
||||
@@ -329,6 +329,7 @@
|
||||
"enum": [
|
||||
"default",
|
||||
"latest",
|
||||
"macos-monterey-12.6-xcode-14.2",
|
||||
"macos-monterey-12.6-xcode-14.1",
|
||||
"macos-monterey-12.6-xcode-14.0",
|
||||
"macos-monterey-12.4-xcode-13.4",
|
||||
@@ -338,8 +339,8 @@
|
||||
"macos-big-sur-11.4-xcode-12.5"
|
||||
],
|
||||
"markdownEnumDescriptions": [
|
||||
"`macos-monterey-12.6-xcode-14.0`",
|
||||
"`macos-monterey-12.6-xcode-14.1`"
|
||||
"`macos-monterey-12.6-xcode-14.1`",
|
||||
"`macos-monterey-12.6-xcode-14.2`"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -399,3 +399,64 @@ test('Android-specific resourceClass', async () => {
|
||||
EasJsonUtils.getBuildProfileAsync(accessor, Platform.ANDROID, 'production')
|
||||
).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
test('build profile with platform-specific custom build config', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
production: {
|
||||
android: {
|
||||
config: 'production.android.yml',
|
||||
},
|
||||
ios: {
|
||||
config: 'production.ios.yml',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'production'
|
||||
);
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
|
||||
expect(androidProfile).toEqual({
|
||||
config: 'production.android.yml',
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
expect(iosProfile).toEqual({
|
||||
config: 'production.ios.yml',
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
});
|
||||
|
||||
test('build profiles with both platform build config', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
production: {
|
||||
config: 'production.yml',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const accessor = new EasJsonAccessor('/project');
|
||||
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
accessor,
|
||||
Platform.ANDROID,
|
||||
'production'
|
||||
);
|
||||
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'production');
|
||||
expect(androidProfile).toEqual({
|
||||
config: 'production.yml',
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
expect(iosProfile).toEqual({
|
||||
config: 'production.yml',
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Android, Ios } from '@expo/eas-build-job';
|
||||
import Joi from 'joi';
|
||||
import semver from 'semver';
|
||||
|
||||
@@ -25,69 +24,98 @@ const CacheSchema = Joi.object({
|
||||
});
|
||||
|
||||
const CommonBuildProfileSchema = Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
distribution: Joi.string().valid('store', 'internal').default('store'),
|
||||
cache: CacheSchema,
|
||||
releaseChannel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
channel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
developmentClient: Joi.boolean(),
|
||||
prebuildCommand: Joi.string(),
|
||||
buildArtifactPaths: Joi.array().items(Joi.string()),
|
||||
// builder
|
||||
resourceClass: Joi.string().valid(...AllowedCommonResourceClasses),
|
||||
|
||||
// build environment
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
|
||||
node: Joi.string().empty(null).custom(semverCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
|
||||
|
||||
// credentials
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
distribution: Joi.string().valid('store', 'internal').default('store'),
|
||||
|
||||
// updates
|
||||
releaseChannel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
channel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
|
||||
// build configuration
|
||||
developmentClient: Joi.boolean(),
|
||||
prebuildCommand: Joi.string(),
|
||||
|
||||
// versions
|
||||
autoIncrement: Joi.alternatives().try(Joi.boolean()),
|
||||
resourceClass: Joi.string().valid(...AllowedCommonResourceClasses),
|
||||
|
||||
// artifacts
|
||||
buildArtifactPaths: Joi.array().items(Joi.string()),
|
||||
|
||||
// cache
|
||||
cache: CacheSchema,
|
||||
|
||||
// custom build configuration
|
||||
config: Joi.string(),
|
||||
});
|
||||
|
||||
const AndroidBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
const PlatformBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
// build environment
|
||||
image: Joi.string(),
|
||||
|
||||
// artifacts
|
||||
artifactPath: Joi.string(),
|
||||
applicationArchivePath: Joi.string(),
|
||||
}).oxor('artifactPath', 'applicationArchivePath')
|
||||
);
|
||||
|
||||
const AndroidBuildProfileSchema = PlatformBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
// builder
|
||||
resourceClass: Joi.string().valid(...AllowedAndroidResourceClasses),
|
||||
|
||||
// build environment
|
||||
ndk: Joi.string().empty(null).custom(semverCheck),
|
||||
|
||||
// credentials
|
||||
withoutCredentials: Joi.boolean(),
|
||||
|
||||
image: Joi.string().valid(...Android.builderBaseImages),
|
||||
ndk: Joi.string().empty(null).custom(semverCheck),
|
||||
// build configuration
|
||||
gradleCommand: Joi.string(),
|
||||
buildType: Joi.string().valid('apk', 'app-bundle'),
|
||||
|
||||
// versions
|
||||
autoIncrement: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('version', 'versionCode')
|
||||
),
|
||||
resourceClass: Joi.string().valid(...AllowedAndroidResourceClasses),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
applicationArchivePath: Joi.string(),
|
||||
buildArtifactPaths: Joi.array().items(Joi.string()),
|
||||
gradleCommand: Joi.string(),
|
||||
|
||||
buildType: Joi.string().valid('apk', 'app-bundle'),
|
||||
}).oxor('artifactPath', 'applicationArchivePath')
|
||||
})
|
||||
);
|
||||
|
||||
const IosBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
const IosBuildProfileSchema = PlatformBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
autoIncrement: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('version', 'buildNumber')
|
||||
),
|
||||
simulator: Joi.boolean(),
|
||||
// builder
|
||||
resourceClass: Joi.string().valid(...AllowedIosResourceClasses),
|
||||
|
||||
image: Joi.string().valid(...Ios.builderBaseImages),
|
||||
// build environment
|
||||
bundler: Joi.string().empty(null).custom(semverCheck),
|
||||
fastlane: Joi.string().empty(null).custom(semverCheck),
|
||||
cocoapods: Joi.string().empty(null).custom(semverCheck),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
applicationArchivePath: Joi.string(),
|
||||
buildArtifactPaths: Joi.array().items(Joi.string()),
|
||||
// credentials
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
|
||||
// build configuration
|
||||
simulator: Joi.boolean(),
|
||||
scheme: Joi.string(),
|
||||
buildConfiguration: Joi.string(),
|
||||
}).oxor('artifactPath', 'applicationArchivePath')
|
||||
|
||||
// versions
|
||||
autoIncrement: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('version', 'buildNumber')
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
export const BuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
|
||||
@@ -29,32 +29,42 @@ export type IosVersionAutoIncrement = VersionAutoIncrement | 'buildNumber';
|
||||
export type AndroidVersionAutoIncrement = VersionAutoIncrement | 'versionCode';
|
||||
|
||||
export interface CommonBuildProfile {
|
||||
credentialsSource: CredentialsSource;
|
||||
distribution: DistributionType;
|
||||
cache?: Omit<Cache, 'clear'>;
|
||||
releaseChannel?: string;
|
||||
channel?: string;
|
||||
developmentClient?: boolean;
|
||||
prebuildCommand?: string;
|
||||
autoIncrement?: boolean;
|
||||
// builder
|
||||
resourceClass?: ResourceClass;
|
||||
buildArtifactPaths?: string[];
|
||||
|
||||
// build environment
|
||||
env?: Record<string, string>;
|
||||
node?: string;
|
||||
yarn?: string;
|
||||
expoCli?: string;
|
||||
env?: Record<string, string>;
|
||||
|
||||
// credentials
|
||||
credentialsSource: CredentialsSource;
|
||||
distribution: DistributionType;
|
||||
|
||||
// updates
|
||||
releaseChannel?: string;
|
||||
channel?: string;
|
||||
|
||||
// build configuration
|
||||
developmentClient?: boolean;
|
||||
prebuildCommand?: string;
|
||||
|
||||
// versions
|
||||
autoIncrement?: boolean;
|
||||
|
||||
// artifacts
|
||||
buildArtifactPaths?: string[];
|
||||
|
||||
// cache
|
||||
cache?: Omit<Cache, 'clear'>;
|
||||
|
||||
// custom build configuration
|
||||
config?: string;
|
||||
}
|
||||
|
||||
export interface AndroidBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
|
||||
withoutCredentials?: boolean;
|
||||
image?: Android.BuilderEnvironment['image'];
|
||||
ndk?: string;
|
||||
autoIncrement?: AndroidVersionAutoIncrement;
|
||||
|
||||
buildType?: Android.BuildType.APK | Android.BuildType.APP_BUNDLE;
|
||||
|
||||
gradleCommand?: string;
|
||||
interface PlatformBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
|
||||
// artifacts
|
||||
/**
|
||||
* @deprecated use applicationArchivePath
|
||||
*/
|
||||
@@ -62,22 +72,39 @@ export interface AndroidBuildProfile extends Omit<CommonBuildProfile, 'autoIncre
|
||||
applicationArchivePath?: string;
|
||||
}
|
||||
|
||||
export interface IosBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
autoIncrement?: IosVersionAutoIncrement;
|
||||
simulator?: boolean;
|
||||
export interface AndroidBuildProfile extends PlatformBuildProfile {
|
||||
// build environment
|
||||
image?: Android.BuilderEnvironment['image'];
|
||||
ndk?: string;
|
||||
|
||||
// credentials
|
||||
withoutCredentials?: boolean;
|
||||
|
||||
// build configuration
|
||||
gradleCommand?: string;
|
||||
buildType?: Android.BuildType.APK | Android.BuildType.APP_BUNDLE;
|
||||
|
||||
// versions
|
||||
autoIncrement?: AndroidVersionAutoIncrement;
|
||||
}
|
||||
|
||||
export interface IosBuildProfile extends PlatformBuildProfile {
|
||||
// build environment
|
||||
image?: Ios.BuilderEnvironment['image'];
|
||||
bundler?: string;
|
||||
fastlane?: string;
|
||||
cocoapods?: string;
|
||||
|
||||
/**
|
||||
* @deprecated use applicationArchivePath
|
||||
*/
|
||||
artifactPath?: string;
|
||||
applicationArchivePath?: string;
|
||||
// credentials
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
|
||||
// build configuration
|
||||
simulator?: boolean;
|
||||
scheme?: string;
|
||||
buildConfiguration?: string;
|
||||
|
||||
// versions
|
||||
autoIncrement?: IosVersionAutoIncrement;
|
||||
}
|
||||
|
||||
export type BuildProfile<TPlatform extends Platform = Platform> = TPlatform extends Platform.ANDROID
|
||||
|
||||
Reference in new issue
Block a user