Compare commits

...
Author SHA1 Message Date
kbrandwijk dd02e8be4a chore: initial setup for ESM 2022-04-24 20:43:37 +02:00
Expo CI 89dda20149 update CHANGELOG.md 2022-04-19 10:40:33 +00:00
Dominik Sokal b591698c31 v0.51.0 2022-04-19 12:32:52 +02:00
Cedric van Putten 66543b658b [eas-cli] Enable update bundle output when debugging (#1070)
* [eas-cli] Enable update bundle output when debugging

* update CHANGELOG.md
2022-04-19 03:15:36 -07:00
Evan Bacon 4769048578 Bump @expo/[email protected] (#1064)
* Bump @expo/[email protected]

* Update CHANGELOG.md
2022-04-14 11:56:15 -05:00
Will Schurman 64c1e47674 [eas-cli] Add non-interactive flag to eas update command (#1066) 2022-04-14 09:39:42 -07:00
Dominik Sokal be3a86abda submit - add custom error message for expired apple certs problem (#1068)
* submit - add custom error message for expired apple certs problem

* PR feedback

* update CHANGELOG.md
2022-04-14 11:49:07 +02:00
Dominik Sokal eb2ca117fc [ENG-4615] unify google service account prompts (#1063)
* unify google service account prompt

* update CHANGELOG.md

* fix tests
2022-04-14 09:50:39 +02:00
Dominik Sokal 5043385077 fix expo-cli reference 2022-04-12 11:54:56 +02:00
Will Schurman bb4c08a52a [eas-cli] Require private-key-path to be specified for updating when code signing configured (#1059) 2022-04-11 10:42:31 -07:00
Expo CI 8b75057fa1 update CHANGELOG.md 2022-04-11 08:27:15 +00:00
Dominik Sokal 80cc849137 v0.50.0 2022-04-11 10:20:16 +02:00
Dominik SokalandBrent Vatne 2d407ea9d4 [ENG-4566] refactor status updates for multi-platform builds (#1058)
* refactor status updates for multi-platform builds

* update CHANGELOG

* Update packages/eas-cli/src/build/build.ts

Co-authored-by: Brent Vatne <[email protected]>

Co-authored-by: Brent Vatne <[email protected]>
2022-04-11 10:07:20 +02:00
Quinlan Jung 91456d1c65 [eas-cli] require userAuthCtx for methods that don't support jwt (#1055)
* [eas-cli] require userAuthCtx for methods that don't support jwt

* changelog
2022-04-08 10:49:19 -07:00
Quinlan Jung 757a0ccad7 fix changelog 2022-04-07 16:38:48 -07:00
4b7cac0392 [ENG-4419][eas-cli] use asc api token to authenticate for EAS build (#1051)
* [eas-cli] enable authentication with asc api key

* Update packages/eas-cli/src/build/createContext.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-cli/src/credentials/ios/appstore/authenticate.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-cli/src/credentials/ios/appstore/authenticate.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-cli/src/credentials/ios/appstore/authenticate.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-cli/src/credentials/ios/appstore/authenticate.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-json/src/build/types.ts

Co-authored-by: Evan Bacon <[email protected]>

* Update packages/eas-json/src/build/types.ts

Co-authored-by: Evan Bacon <[email protected]>

* pr feedback

* Update packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts

Co-authored-by: Dominik Sokal <[email protected]>

* Update packages/eas-cli/src/credentials/ios/appstore/authenticate.ts

Co-authored-by: Dominik Sokal <[email protected]>

* pr feedback

* changelog

* return instantiation back to original state

Co-authored-by: Evan Bacon <[email protected]>
Co-authored-by: Dominik Sokal <[email protected]>
2022-04-07 15:54:43 -07:00
Will Schurman 8ff8022b3f [eas-cli] Use @expo/multipart-body-parser library (#1054) 2022-04-07 12:19:15 -07:00
Wojciech Kozyra a2acea9fe9 do not delete assigned issues 2022-04-07 16:00:25 +02:00
Wojciech KozyraandKim Brandwijk 9abdfcfdbe autoclose stale issues (#1053)
* autoclose stale issues

* Update .github/workflows/stale-issues.yml

Co-authored-by: Kim Brandwijk <[email protected]>

Co-authored-by: Kim Brandwijk <[email protected]>
2022-04-07 15:51:09 +02:00
Dominik Sokal 696f9dde13 [ENG-3864] provide queue position and estimated wait time (#1049)
* update graphql

* show queue position progress + estimated wait time

* refactor

* update CHANGELOG.md

* hide estimated wait time when build is in progress

* add priority queue info
2022-04-06 13:46:06 +02:00
Expo CI 1e73c8c96f update CHANGELOG.md 2022-04-05 10:48:52 +00:00
58 changed files with 1607 additions and 430 deletions

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
name: Close inactive issues
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
ascending: false
operations-per-run: 300
days-before-issue-stale: 30
days-before-issue-close: 7
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity. If there is no activity in the next 7 days, the issue will be closed."
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem."
days-before-pr-stale: -1
days-before-pr-close: -1
enable-statistics: true
exempt-issue-labels: "needs review"
exempt-all-assignees: true
+36
View File
@@ -8,6 +8,42 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
### 🐛 Bug fixes
### 🧹 Chores
## [0.51.0](https://github.com/expo/eas-cli/releases/tag/v0.51.0) - 2022-04-19
### 🎉 New features
- Add non-interactive flag to eas update command. ([#1066](https://github.com/expo/eas-cli/pull/1066) by [@wschurman](https://github.com/wschurman))
- Handle new EAS Submit common error - expired Apple's certificates. ([#1068](https://github.com/expo/eas-cli/pull/1068) by [@dsokal](https://github.com/dsokal))
### 🐛 Bug fixes
- Enable full bundling logging for eas update when debugging. ([#1070](https://github.com/expo/eas-cli/pull/1070) by [@byCedric](https://github.com/byCedric))
### 🧹 Chores
- Upgrade `@expo/[email protected]` (now with license). ([#1064](https://github.com/expo/eas-cli/pull/1064) by [@EvanBacon](https://github.com/EvanBacon))
- Unify Google Service Account key prompts. ([#1063](https://github.com/expo/eas-cli/pull/1063) by [@dsokal](https://github.com/dsokal))
## [0.50.0](https://github.com/expo/eas-cli/releases/tag/v0.50.0) - 2022-04-11
### 🎉 New features
- Provide queue position progress and estimated wait time. ([#1049](https://github.com/expo/eas-cli/pull/1049), [#1058](https://github.com/expo/eas-cli/pull/1058) by [@dsokal](https://github.com/dsokal))
- Enable App Store authentication for ASC Api Keys with environment variables. ([#1051](https://github.com/expo/eas-cli/pull/1051) by [@quinlanj](https://github.com/quinlanj))
### 🐛 Bug fixes
- Prompt for user/password authentication where required. ([#1055](https://github.com/expo/eas-cli/pull/1055) by [@quinlanj](https://github.com/quinlanj))
- Require private-key-path to be specified for updating when code signing configured. ([#1059](https://github.com/expo/eas-cli/pull/1059) by [@wschurman](https://github.com/wschurman))
## [0.49.0](https://github.com/expo/eas-cli/releases/tag/v0.49.0) - 2022-04-05
### 🎉 New features
- Added experimental App Store Connect API provisioning profile regeneration. ([#1038](https://github.com/expo/eas-cli/pull/1038) by [@EvanBacon](https://github.com/EvanBacon))
- Experimental support for iOS App Extensions in managed projects. ([#1039](https://github.com/expo/eas-cli/pull/1039) by [@wkozyra95](https://github.com/wkozyra95))
+5 -5
View File
@@ -1,7 +1,7 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
import easCliConfig from './packages/eas-cli/jest.config.js';
import easJsonConfig from './packages/eas-json/jest.config.js';
export default {
projects: [easCliConfig, easJsonConfig],
testPathIgnorePatterns: ['.*'],
};
+2 -1
View File
@@ -1,7 +1,8 @@
module.exports = {
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
extensionsToTreatAsEsm: ['.ts'],
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.49.0"
"version": "0.51.0"
}
+2 -1
View File
@@ -1,5 +1,6 @@
{
"private": true,
"type":"module",
"workspaces": [
"packages/*"
],
@@ -26,7 +27,7 @@
"jest": "27.5.1",
"jest-watch-typeahead": "1.0.0",
"lerna": "4.0.0",
"oclif": "2.6.3",
"oclif": "3.0.1",
"prettier": "2.6.1",
"ts-jest": "27.1.4",
"typescript": "4.6.3"
+44 -43
View File
@@ -98,7 +98,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -115,7 +115,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -132,7 +132,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -146,7 +146,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -195,7 +195,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -215,7 +215,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -232,7 +232,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -251,7 +251,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -271,7 +271,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -300,7 +300,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -314,7 +314,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -331,7 +331,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -366,7 +366,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -399,7 +399,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -444,7 +444,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -464,7 +464,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -485,7 +485,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -502,7 +502,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -522,7 +522,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -540,7 +540,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -554,7 +554,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -568,7 +568,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -586,7 +586,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -603,7 +603,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -617,7 +617,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -631,7 +631,7 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -710,7 +710,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -727,7 +727,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -747,7 +747,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -764,7 +764,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -778,7 +778,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -808,7 +808,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -817,7 +817,7 @@ publish an update group
```
USAGE
$ eas update [--branch <value>] [--message <value>] [--republish | --input-dir <value> | --skip-bundler]
[--group <value> | | ] [-p android|ios|all] [--json] [--auto] [--private-key-path <value>]
[--group <value> | | ] [-p android|ios|all] [--json] [--auto] [--private-key-path <value>] [--non-interactive]
FLAGS
-p, --platform=(android|ios|all) [default: all]
@@ -827,6 +827,7 @@ FLAGS
--input-dir=<value> [default: dist] Location of the bundle
--json Enable JSON output, non-JSON messages will be printed to stderr
--message=<value> A short message describing the update
--non-interactive Run command in non-interactive mode
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
certificate's directory.
@@ -837,7 +838,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -854,7 +855,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -874,7 +875,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -893,7 +894,7 @@ DESCRIPTION
view the recent updates for a branch
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -913,7 +914,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -933,7 +934,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -950,7 +951,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -967,7 +968,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -988,7 +989,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1005,7 +1006,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.49.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
File renamed without changes.
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env node
require('@oclif/core')
.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'));
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env node
import { Errors, flush, run } from '@oclif/core';
run(undefined, import.meta.url)
.then(flush)
.catch(Errors.handle);
+435
View File
@@ -711,6 +711,39 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "billingPeriod",
"description": null,
"args": [
{
"name": "date",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "BillingPeriod",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "buildJobs",
"description": "Build Jobs associated with this account",
@@ -1224,6 +1257,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "usageMetrics",
"description": "Account query object for querying EAS usage metrics",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AccountUsageMetrics",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "userInvitations",
"description": "Pending user invitations for this account",
@@ -2033,6 +2082,165 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AccountUsageMetric",
"description": null,
"fields": [
{
"name": "metric",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "EASServiceMetric",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metricType",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "UsageMetricType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "timestamp",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "value",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AccountUsageMetrics",
"description": null,
"fields": [
{
"name": "metricsForServiceMetric",
"description": null,
"args": [
{
"name": "granularity",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "UsageMetricsGranularity",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metric",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "EASServiceMetric",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "timespan",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UsageMetricsTimespan",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AccountUsageMetric",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INTERFACE",
"name": "ActivityTimelineProjectActivity",
@@ -9901,6 +10109,65 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "BillingPeriod",
"description": null,
"fields": [
{
"name": "anchor",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "end",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "start",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "Boolean",
@@ -10052,6 +10319,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "estimatedWaitTimeLeftSeconds",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "expirationDate",
"description": null,
@@ -10092,6 +10371,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "initialQueuePosition",
"description": "Queue position is 1-indexed",
"args": [],
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "initiatingActor",
"description": null,
@@ -13693,6 +13984,23 @@
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "EASServiceMetric",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "MANIFEST_REQUESTS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "EditUpdateBranchInput",
@@ -22671,6 +22979,101 @@
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "UsageMetricType",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "REQUEST",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "UsageMetricsGranularity",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "DAY",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "HOUR",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "MINUTE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "TOTAL",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "UsageMetricsTimespan",
"description": null,
"fields": null,
"inputFields": [
{
"name": "end",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "start",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "User",
@@ -23685,6 +24088,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "expires",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
@@ -24045,6 +24464,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "expires",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "Email to which this invitation was sent",
+11 -2
View File
@@ -1,5 +1,14 @@
module.exports = {
...require('../../jest/jest.config.js'),
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import baseConfig from '../../jest/jest.config.js';
const filename = fileURLToPath(import.meta.url);
// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(filename);
export default {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
+9 -8
View File
@@ -1,21 +1,23 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "0.49.0",
"version": "0.51.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
"eas": "./bin/run.js"
},
"type": "module",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.30",
"@expo/apple-utils": "0.0.0-alpha.31",
"@expo/code-signing-certificates": "0.0.2",
"@expo/config": "6.0.19",
"@expo/config-plugins": "4.1.0",
"@expo/config-types": "44.0.0",
"@expo/eas-build-job": "0.2.68",
"@expo/eas-json": "0.49.0",
"@expo/eas-json": "0.51.0",
"@expo/json-file": "8.2.34",
"@expo/multipart-body-parser": "1.1.0",
"@expo/pkcs12": "0.0.7",
"@expo/plist": "0.0.17",
"@expo/plugin-warn-if-update-available": "2.1.0",
@@ -30,9 +32,9 @@
"@urql/core": "2.4.3",
"@urql/exchange-retry": "0.3.2",
"chalk": "4.1.2",
"cli-progress": "3.10.0",
"cli-table3": "0.6.1",
"dateformat": "4.6.3",
"dicer": "0.3.1",
"env-paths": "2.2.0",
"envinfo": "7.8.1",
"fast-deep-equal": "3.1.3",
@@ -65,7 +67,6 @@
"semver": "7.3.5",
"slash": "3.0.0",
"strip-ansi": "6.0.0",
"structured-headers": "0.4.1",
"tar": "6.1.11",
"terminal-link": "2.1.1",
"tslib": "2.3.1",
@@ -79,8 +80,8 @@
"@graphql-codegen/introspection": "2.1.1",
"@graphql-codegen/typescript": "2.4.8",
"@graphql-codegen/typescript-operations": "2.3.5",
"@types/cli-progress": "3.9.2",
"@types/dateformat": "3.0.1",
"@types/dicer": "0.2.2",
"@types/envinfo": "7.8.1",
"@types/fs-extra": "9.0.13",
"@types/getenv": "^1.0.0",
@@ -104,7 +105,7 @@
"typescript": "4.6.3"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.16"
},
"files": [
"/bin",
+279 -129
View File
@@ -1,16 +1,29 @@
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
import fs from 'fs-extra';
import nullthrows from 'nullthrows';
import { withAnalyticsAsync } from '../analytics/common';
import { BuildEvent } from '../analytics/events';
import { BuildFragment, BuildStatus, UploadSessionType } from '../graphql/generated';
import { getExpoWebsiteBaseUrl } from '../api';
import {
AppPlatform,
BuildFragment,
BuildPriority,
BuildStatus,
UploadSessionType,
} from '../graphql/generated';
import { BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import Log, { learnMore } from '../log';
import { ora } from '../ora';
import { requestedPlatformDisplayNames } from '../platform';
import { Ora, ora } from '../ora';
import {
appPlatformDisplayNames,
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform';
import { uploadAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { createProgressTracker } from '../utils/progress';
@@ -100,43 +113,43 @@ export async function prepareBuildRequestForPlatformAsync<
try {
return await sendBuildRequestAsync(builder, job, metadata);
} catch (error: any) {
if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'TURTLE_DEPRECATED_JOB_FORMAT') {
Log.error('EAS Build API has changed, please upgrade to the latest eas-cli version.');
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE'
) {
Log.error(
'EAS Build is down for maintenance, please try again later. Check https://status.expo.dev/ for updates.'
);
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED'
) {
Log.error(
'EAS Build free tier is temporarily disabled, please try again later. Check https://status.expo.dev/ for updates.'
);
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS'
) {
Log.error(
`You have already reached the maximum number of pending ${
requestedPlatformDisplayNames[job.platform]
} builds for your account. Try again later.`
);
throw new Error('Build request failed.');
} else if (error?.graphQLErrors) {
Log.error(
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, please report the issue.'
);
}
throw error;
handleBuildRequestError(error, job.platform);
}
}
};
}
function handleBuildRequestError(error: any, platform: Platform): never {
if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'TURTLE_DEPRECATED_JOB_FORMAT') {
Log.error('EAS Build API has changed, please upgrade to the latest eas-cli version.');
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE'
) {
Log.error(
'EAS Build is down for maintenance, please try again later. Check https://status.expo.dev/ for updates.'
);
throw new Error('Build request failed.');
} else if (error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED') {
Log.error(
'EAS Build free tier is temporarily disabled, please try again later. Check https://status.expo.dev/ for updates.'
);
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS'
) {
Log.error(
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
);
throw new Error('Build request failed.');
} else if (error?.graphQLErrors) {
Log.error(
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, please report the issue.'
);
}
throw error;
}
async function uploadProjectAsync<TPlatform extends Platform>(
ctx: BuildContext<TPlatform>
): Promise<string> {
@@ -208,105 +221,242 @@ async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJ
);
}
type MaybeBuildFragment = BuildFragment | null;
export async function waitForBuildEndAsync(
buildIds: string[],
{ timeoutSec = 3600, intervalSec = 30 } = {}
): Promise<(BuildFragment | null)[]> {
Log.log(
`Waiting for build${buildIds.length > 1 ? 's' : ''} to complete. You can press Ctrl+C to exit.`
);
const spinner = ora().start();
let time = new Date().getTime();
const endTime = time + timeoutSec * 1000;
while (time <= endTime) {
const builds: (BuildFragment | null)[] = await Promise.all(
buildIds.map(async buildId => {
try {
return await BuildQuery.byIdAsync(buildId, { useCache: false });
} catch (err) {
Log.debug('Failed to fetch the build status', err);
return null;
}
})
);
if (builds.length === 1) {
const [build] = builds;
if (build !== null) {
switch (build.status) {
case BuildStatus.Finished:
spinner.succeed('Build finished');
return builds;
case BuildStatus.New:
spinner.text = 'Build created';
break;
case BuildStatus.InQueue:
spinner.text = 'Build queued...';
break;
case BuildStatus.Canceled:
spinner.text = 'Build canceled';
spinner.stopAndPersist();
return builds;
case BuildStatus.InProgress:
spinner.text = 'Build in progress...';
break;
case BuildStatus.Errored:
spinner.fail('Build failed');
if (build.error) {
return builds;
} else {
throw new Error(`Standalone build failed!`);
}
default:
spinner.warn('Unknown status');
throw new Error(`Unknown build status: ${build.status} - aborting!`);
}
} else {
if (!spinner.text) {
spinner.text = 'Could not fetch the build status. Check your network connection.';
}
}
} else {
if (builds.filter(build => build?.status === BuildStatus.Finished).length === builds.length) {
spinner.succeed('All builds have finished');
return builds;
} else if (
builds.filter(build =>
build?.status
? [BuildStatus.Finished, BuildStatus.Errored, BuildStatus.Canceled].includes(
build.status
)
: false
).length === builds.length
) {
spinner.fail('Some of the builds were canceled or failed.');
return builds;
} else {
const newBuilds = builds.filter(build => build?.status === BuildStatus.New).length;
const inQueue = builds.filter(build => build?.status === BuildStatus.InQueue).length;
const inProgress = builds.filter(build => build?.status === BuildStatus.InProgress).length;
const errored = builds.filter(build => build?.status === BuildStatus.Errored).length;
const finished = builds.filter(build => build?.status === BuildStatus.Finished).length;
const canceled = builds.filter(build => build?.status === BuildStatus.Canceled).length;
const unknown =
builds.length - newBuilds - inQueue - inProgress - errored - finished - canceled;
spinner.text = [
newBuilds && `Builds created: ${newBuilds}`,
inQueue && `Builds in queue: ${inQueue}`,
inProgress && `Builds in progress: ${inProgress}`,
canceled && `Builds canceled: ${canceled}`,
errored && chalk.red(`Builds failed: ${errored}`),
finished && chalk.green(`Builds finished: ${finished}`),
unknown && chalk.red(`Builds with unknown status: ${unknown}`),
]
.filter(i => i)
.join('\t');
}
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
{
// 2 hours (max build time limit) + 10 minutes (possible queue time)
timeoutSec = 2 * 60 * 60 + 10 * 60,
intervalSec = 10,
} = {}
): Promise<MaybeBuildFragment[]> {
const b = `build${buildIds.length > 1 ? 's' : ''}`;
Log.log(`Waiting for ${b} to complete. You can press Ctrl+C to exit.`);
const spinner = ora(`Waiting for ${b} to complete.`).start();
const endTime = new Date().getTime() + timeoutSec * 1000;
while (new Date().getTime() <= endTime) {
const builds = await getBuildsSafelyAsync(buildIds);
const { refetch } =
builds.length === 1
? await handleSingleBuildProgressAsync({ build: builds[0], accountName }, { spinner })
: await handleMultipleBuildsProgressAsync({ builds }, { spinner });
if (!refetch) {
return builds;
}
time = new Date().getTime();
await sleepAsync(intervalSec * 1000);
}
spinner.warn('Timed out');
spinner.fail('Timed out');
throw new Error(
'Timeout reached! It is taking longer than expected to finish the build, aborting...'
);
}
async function getBuildsSafelyAsync(buildIds: string[]): Promise<MaybeBuildFragment[]> {
const promises = buildIds.map(async buildId => {
try {
return await BuildQuery.byIdAsync(buildId, { useCache: false });
} catch (err) {
Log.debug('Failed to fetch the build status', err);
return null;
}
});
return await Promise.all(promises);
}
interface BuildProgressResult {
refetch: boolean;
}
let queueProgressBarStarted = false;
const queueProgressBar = new cliProgress.SingleBar(
{ format: '|{bar}| {estimatedWaitTime}' },
cliProgress.Presets.rect
);
async function handleSingleBuildProgressAsync(
{ build, accountName }: { build: MaybeBuildFragment; accountName: string },
{ spinner }: { spinner: Ora }
): Promise<BuildProgressResult> {
if (build === null) {
spinner.text = 'Could not fetch the build status. Check your network connection.';
return { refetch: true };
}
if (queueProgressBarStarted && build?.status && build.status !== BuildStatus.InQueue) {
if (build.status === BuildStatus.InProgress) {
queueProgressBar.update(queueProgressBar.getTotal(), {
estimatedWaitTime: '',
});
}
queueProgressBar.stop();
Log.newLine();
queueProgressBarStarted = false;
spinner.start('Build is about to start');
}
switch (build.status) {
case BuildStatus.Finished:
spinner.succeed('Build finished');
return { refetch: false };
case BuildStatus.New:
spinner.text = `Build is waiting to enter the queue. Check your concurrency limit at ${chalk.underline(
formatAccountSubscriptionsUrl(accountName)
)}.`;
break;
case BuildStatus.InQueue: {
spinner.text = 'Build queued...';
const progressBarPayload =
typeof build.estimatedWaitTimeLeftSeconds === 'number'
? { estimatedWaitTime: formatEstimatedWaitTime(build.estimatedWaitTimeLeftSeconds) }
: { estimatedWaitTime: '' };
if (
!queueProgressBarStarted &&
typeof build.initialQueuePosition === 'number' &&
typeof build.queuePosition === 'number'
) {
spinner.stopAndPersist();
if (build.priority !== BuildPriority.High) {
Log.newLine();
Log.log('Start builds sooner in the priority queue.');
Log.log(
`Sign up for EAS Production or Enterprise at ${chalk.underline(
formatAccountSubscriptionsUrl(accountName)
)}`
);
}
Log.newLine();
Log.log('Waiting in queue');
queueProgressBar.start(
build.initialQueuePosition + 1,
build.initialQueuePosition - build.queuePosition + 1,
progressBarPayload
);
queueProgressBarStarted = true;
}
if (typeof build.queuePosition === 'number') {
queueProgressBar.update(build.queuePosition, progressBarPayload);
}
break;
}
case BuildStatus.Canceled:
spinner.fail('Build canceled');
return { refetch: false };
case BuildStatus.InProgress:
spinner.text = 'Build in progress...';
break;
case BuildStatus.Errored:
spinner.fail('Build failed');
if (build.error) {
return { refetch: false };
} else {
throw new Error('Standalone build failed!');
}
default:
spinner.warn('Unknown status');
throw new Error(`Unknown build status: ${build.status} - aborting!`);
}
return { refetch: true };
}
const statusToDisplayName: Record<BuildStatus, string> = {
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
[BuildStatus.InQueue]: 'in queue',
[BuildStatus.InProgress]: 'in progress',
[BuildStatus.Canceled]: 'canceled',
[BuildStatus.Finished]: 'finished',
[BuildStatus.Errored]: 'failed',
};
const platforms = [AppPlatform.Android, AppPlatform.Ios];
async function handleMultipleBuildsProgressAsync(
{ builds: maybeBuilds }: { builds: MaybeBuildFragment[] },
{ spinner }: { spinner: Ora }
): Promise<BuildProgressResult> {
const buildCount = maybeBuilds.length;
const builds = maybeBuilds.filter<BuildFragment>(isBuildFragment);
const allFinished =
builds.filter(build => build.status === BuildStatus.Finished).length === buildCount;
const allSettled =
builds.filter(build =>
[BuildStatus.Finished, BuildStatus.Errored, BuildStatus.Canceled].includes(build.status)
).length === buildCount;
if (allSettled) {
if (allFinished) {
spinner.succeed(formatSettledBuildsText(builds));
} else {
spinner.fail(formatSettledBuildsText(builds));
}
return { refetch: false };
} else {
spinner.text = formatPendingBuildsText(builds);
return { refetch: true };
}
}
function formatSettledBuildsText(builds: BuildFragment[]): string {
return platforms
.map(platform => {
const build = nullthrows(
builds.find(build => build.platform === platform),
`Build for platform ${platform} must be defined in this context`
);
return `${appPlatformEmojis[platform]} ${
appPlatformDisplayNames[platform]
} build - status: ${chalk.bold(statusToDisplayName[build.status])}`;
})
.join('\n ');
}
function formatPendingBuildsText(builds: BuildFragment[]): string {
return platforms
.map(platform => {
const build = builds.find(build => build.platform === platform);
const status = build ? statusToDisplayName[build.status] : 'unknown';
let extraInfo = '';
if (
build?.status === BuildStatus.InQueue &&
typeof build.initialQueuePosition === 'number' &&
typeof build.queuePosition === 'number'
) {
const percent = Math.floor(
((build.initialQueuePosition - build.queuePosition + 1) /
(build.initialQueuePosition + 1)) *
100
);
const estimatedWaitTime =
typeof build.estimatedWaitTimeLeftSeconds === 'number'
? ` - ${formatEstimatedWaitTime(build.estimatedWaitTimeLeftSeconds)}`
: '';
extraInfo = ` - queue progress: ${chalk.bold(`${percent}%`)}${estimatedWaitTime}`;
}
return `${appPlatformEmojis[platform]} ${
appPlatformDisplayNames[platform]
} build - status: ${chalk.bold(status)}${extraInfo}`;
})
.join('\n ');
}
function isBuildFragment(maybeBuild: MaybeBuildFragment): maybeBuild is BuildFragment {
return maybeBuild !== null;
}
function formatEstimatedWaitTime(estimatedWaitTimeLeftSeconds: number): string {
if (estimatedWaitTimeLeftSeconds < 5 * 60) {
return 'starting soon...';
} else {
const n = Math.floor(estimatedWaitTimeLeftSeconds / (10 * 60)) + 1;
return `starting in about ${n}0 minutes...`;
}
}
function formatAccountSubscriptionsUrl(accountName: string): string {
return new URL(
`/accounts/${accountName}/settings/subscriptions`,
getExpoWebsiteBaseUrl()
).toString();
}
@@ -137,7 +137,11 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
return;
}
const builds = await waitForBuildEndAsync(startedBuilds.map(({ build }) => build.id));
const { accountName } = Object.values(buildCtxByPlatform)[0];
const builds = await waitForBuildEndAsync({
buildIds: startedBuilds.map(({ build }) => build.id),
accountName,
});
printBuildResults(builds, flags.json);
const haveAllBuildsFailedOrCanceled = builds.every(
+38 -2
View File
@@ -194,6 +194,10 @@ export default class UpdatePublish extends EasCommand {
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
required: false,
}),
'non-interactive': Flags.boolean({
default: false,
description: 'Run command in non-interactive mode',
}),
};
async runAsync(): Promise<void> {
@@ -209,6 +213,7 @@ export default class UpdatePublish extends EasCommand {
'skip-bundler': skipBundler,
platform,
'private-key-path': privateKeyPath,
'non-interactive': nonInteractive,
},
} = await this.parse(UpdatePublish);
if (jsonFlag) {
@@ -225,9 +230,24 @@ export default class UpdatePublish extends EasCommand {
isPublicConfig: true,
});
const codeSigningInfo = await getCodeSigningInfoAsync(exp, privateKeyPath);
const { exp: expPrivate } = getConfig(projectDir, {
skipSDKVersionRequirement: true,
isPublicConfig: false,
});
if (!isExpoUpdatesInstalledOrAvailable(projectDir, exp.sdkVersion)) {
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
const hasExpoUpdates = isExpoUpdatesInstalledOrAvailable(projectDir, exp.sdkVersion);
if (!hasExpoUpdates && nonInteractive) {
Errors.error(
`${chalk.bold(
'expo-updates'
)} must already be installed when executing in non-interactive mode`,
{ exit: 1 }
);
}
if (!hasExpoUpdates) {
const install = await confirmAsync({
message: `You are creating an update which requires ${chalk.bold(
'expo-updates'
@@ -254,6 +274,10 @@ export default class UpdatePublish extends EasCommand {
}
if (!branchName) {
if (nonInteractive) {
throw new Error('Must supply --branch or use --auto when in non-interactive mode');
}
const validationMessage = 'Branch name may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
@@ -303,6 +327,10 @@ export default class UpdatePublish extends EasCommand {
if (group) {
updatesToRepublish = await getUpdateGroupAsync({ group });
} else {
if (nonInteractive) {
throw new Error('Must supply --group when in non-interactive mode');
}
// Drop into interactive mode if the user has not specified an update group to republish.
if (jsonFlag) {
throw new Error('You must specify the update group to republish.');
@@ -367,6 +395,10 @@ export default class UpdatePublish extends EasCommand {
oldRuntimeVersion = updatesToRepublishFilteredByPlatform[0].runtimeVersion;
if (!message) {
if (nonInteractive) {
throw new Error('Must supply --message when in non-interactive mode');
}
const validationMessage = 'publish message may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
@@ -385,6 +417,10 @@ export default class UpdatePublish extends EasCommand {
}
if (!message) {
if (nonInteractive) {
throw new Error('Must supply --message or use --auto when in non-interactive mode');
}
const validationMessage = 'publish message may not be empty.';
if (jsonFlag) {
throw new Error(validationMessage);
@@ -1,5 +1,5 @@
import AppStoreApi from '../ios/appstore/AppStoreApi';
import { AuthCtx } from '../ios/appstore/authenticate';
import { AuthCtx } from '../ios/appstore/authenticateTypes';
export const testAuthCtx: AuthCtx = {
appleId: 'test-apple-id',
@@ -1,5 +1,8 @@
import chalk from 'chalk';
import fs from 'fs-extra';
import { GoogleServiceAccountKeyFragment } from '../../../graphql/generated';
import Log from '../../../log';
import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { Account } from '../../../user/Account';
import { CredentialsContext } from '../../context';
@@ -41,14 +44,33 @@ export class CreateGoogleServiceAccountKey {
return detectedPath;
}
const { keyJsonPath } = await promptAsync([
{
type: 'text',
name: 'keyJsonPath',
message: 'Path to Google Service Account Key JSON file:',
validate: (value: string) => value.length > 0 || "Path can't be empty",
Log.log(
`${chalk.bold(
'A Google Service Account JSON key is required to upload your app to Google Play Store'
)}.\n` +
`If you're not sure what this is or how to create one, ${learnMore(
'https://expo.fyi/creating-google-service-account',
{ learnMoreMessage: 'learn more' }
)}`
);
const { filePath } = await promptAsync({
name: 'filePath',
message: 'Path to Google Service Account file:',
initial: 'api-0000000000000000000-111111-aaaaaabbbbbb.json',
type: 'text',
// eslint-disable-next-line async-protect/async-suffix
validate: async (filePath: string) => {
try {
const stats = await fs.stat(filePath);
if (stats.isFile()) {
return true;
}
return 'Input is not a file.';
} catch {
return 'File does not exist.';
}
},
]);
return keyJsonPath;
});
return filePath;
}
}
@@ -9,7 +9,7 @@ import { CreateGoogleServiceAccountKey } from '../CreateGoogleServiceAccountKey'
jest.mock('../../../../prompts');
jest.mock('fs');
asMock(promptAsync).mockImplementation(() => ({
keyJsonPath: '/google-service-account-key.json',
filePath: '/google-service-account-key.json',
}));
beforeEach(() => {
@@ -15,7 +15,7 @@ import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest.mock('fs');
asMock(promptAsync).mockImplementation(() => ({
keyJsonPath: '/google-service-account-key.json',
filePath: '/google-service-account-key.json',
}));
beforeEach(() => {
@@ -10,6 +10,7 @@ import { Actor, getActorDisplayName } from '../user/User';
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
import * as IosGraphqlClient from './ios/api/GraphqlClient';
import AppStoreApi from './ios/appstore/AppStoreApi';
import { AuthenticationMode } from './ios/appstore/authenticateTypes';
export class CredentialsContext {
public readonly android = AndroidGraphqlClient;
@@ -99,6 +100,11 @@ export class CredentialsContext {
return;
}
if (this.appStore.defaultAuthenticationMode === AuthenticationMode.API_KEY) {
await this.appStore.ensureAuthenticatedAsync();
return;
}
Log.log(
chalk.green(
'If you provide your Apple account credentials we will be able to generate all necessary build credentials and fully validate them.'
@@ -94,7 +94,6 @@ async function generateAscApiKeyAsync(
ctx: CredentialsContext,
purpose: AppStoreApiKeyPurpose
): Promise<MinimalAscApiKey> {
//console.log('debug');
await ctx.appStore.ensureAuthenticatedAsync();
const ascApiKey = await ctx.appStore.createAscApiKeyAsync({
nickname: getAscApiKeyName(purpose),
@@ -12,7 +12,7 @@ import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations/AppleProvisioningProfileMutation';
import { ProvisioningProfileStoreInfo } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticate';
import { AuthCtx } from '../appstore/authenticateTypes';
export class ConfigureProvisioningProfile {
constructor(
@@ -84,7 +84,7 @@ export class ConfigureProvisioningProfile {
certP12: certificateP12,
certPassword: certificatePassword,
distCertSerialNumber: serialNumber,
teamId: authCtx.appleId,
teamId: authCtx.team.id,
}
);
@@ -9,7 +9,7 @@ import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
import { AppLookupParams } from '../api/GraphqlClient';
import { AppleProvisioningProfileMutationResult } from '../api/graphql/mutations/AppleProvisioningProfileMutation';
import { ProvisioningProfile } from '../appstore/Credentials.types';
import { AuthCtx } from '../appstore/authenticate';
import { AuthCtx } from '../appstore/authenticateTypes';
import { provisioningProfileSchema } from '../credentials';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { generateProvisioningProfileAsync } from './ProvisioningProfileUtils';
@@ -68,7 +68,7 @@ export class CreateProvisioningProfile {
certP12: this.distributionCertificate.certificateP12,
certPassword: this.distributionCertificate.certificatePassword,
distCertSerialNumber: this.distributionCertificate.serialNumber,
teamId: this.distributionCertificate.appleTeam?.appleTeamIdentifier ?? appleAuthCtx.appleId,
teamId: this.distributionCertificate.appleTeam?.appleTeamIdentifier ?? appleAuthCtx.team.id,
});
}
}
@@ -1,3 +1,4 @@
import Log from '../../../log';
import {
AscApiKey,
AscApiKeyInfo,
@@ -14,7 +15,13 @@ import {
listAscApiKeysAsync,
revokeAscApiKeyAsync,
} from './ascApiKey';
import { AuthCtx, Options as AuthenticateOptions, authenticateAsync } from './authenticate';
import {
Options as AuthenticateOptions,
assertUserAuthCtx,
authenticateAsync,
isUserAuthCtx,
} from './authenticate';
import { AuthCtx, AuthenticationMode, UserAuthCtx } from './authenticateTypes';
import {
createDistributionCertificateAsync,
listDistributionCertificatesAsync,
@@ -34,13 +41,36 @@ import {
} from './provisioningProfile';
import { createOrReuseAdhocProvisioningProfileAsync } from './provisioningProfileAdhoc';
import { createPushKeyAsync, listPushKeysAsync, revokePushKeyAsync } from './pushKey';
import { hasAscEnvVars } from './resolveCredentials';
export default class AppStoreApi {
public authCtx?: AuthCtx;
public defaultAuthenticationMode: AuthenticationMode;
constructor() {
this.defaultAuthenticationMode = hasAscEnvVars()
? AuthenticationMode.API_KEY
: AuthenticationMode.USER;
}
public async ensureUserAuthenticatedAsync(options?: AuthenticateOptions): Promise<UserAuthCtx> {
if (this.authCtx && !isUserAuthCtx(this.authCtx)) {
// already authenticated, but with the wrong type
Log.log(`Only user authentication is supported. Reauthenticating as user...`);
this.authCtx = undefined;
}
const updatedAuthCtx = await this.ensureAuthenticatedAsync({
...options,
mode: AuthenticationMode.USER,
});
return assertUserAuthCtx(updatedAuthCtx);
}
public async ensureAuthenticatedAsync(options?: AuthenticateOptions): Promise<AuthCtx> {
if (!this.authCtx) {
this.authCtx = await authenticateAsync(options);
const mode = options?.mode ?? this.defaultAuthenticationMode;
this.authCtx = await authenticateAsync({ mode, ...options });
}
return this.authCtx;
}
@@ -69,18 +99,18 @@ export default class AppStoreApi {
}
public async listPushKeysAsync(): Promise<PushKeyStoreInfo[]> {
const ctx = await this.ensureAuthenticatedAsync();
return await listPushKeysAsync(ctx);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await listPushKeysAsync(userCtx);
}
public async createPushKeyAsync(name?: string): Promise<PushKey> {
const ctx = await this.ensureAuthenticatedAsync();
return await createPushKeyAsync(ctx, name);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await createPushKeyAsync(userCtx, name);
}
public async revokePushKeyAsync(ids: string[]): Promise<void> {
const ctx = await this.ensureAuthenticatedAsync();
return await revokePushKeyAsync(ctx, ids);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await revokePushKeyAsync(userCtx, ids);
}
public async useExistingProvisioningProfileAsync(
@@ -144,22 +174,22 @@ export default class AppStoreApi {
}
public async listAscApiKeysAsync(): Promise<AscApiKeyInfo[]> {
const ctx = await this.ensureAuthenticatedAsync();
return await listAscApiKeysAsync(ctx);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await listAscApiKeysAsync(userCtx);
}
public async getAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo | null> {
const ctx = await this.ensureAuthenticatedAsync();
return await getAscApiKeyAsync(ctx, keyId);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await getAscApiKeyAsync(userCtx, keyId);
}
public async createAscApiKeyAsync({ nickname }: { nickname: string }): Promise<AscApiKey> {
const ctx = await this.ensureAuthenticatedAsync();
return await createAscApiKeyAsync(ctx, { nickname });
const userCtx = await this.ensureUserAuthenticatedAsync();
return await createAscApiKeyAsync(userCtx, { nickname });
}
public async revokeAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo> {
const ctx = await this.ensureAuthenticatedAsync();
return await revokeAscApiKeyAsync(ctx, keyId);
const userCtx = await this.ensureUserAuthenticatedAsync();
return await revokeAscApiKeyAsync(userCtx, keyId);
}
}
@@ -0,0 +1,92 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { AppleTeamType } from '../authenticateTypes';
import { resolveAppleTeamAsync, resolveAscApiKeyAsync } from '../resolveCredentials';
jest.mock('../../../../prompts');
jest.mock('fs');
const testAscApiKey = {
keyP8: 'test-key-p8',
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
};
beforeEach(() => {
vol.reset();
process.env = {};
asMock(promptAsync).mockReset();
});
describe(resolveAscApiKeyAsync, () => {
it(`uses option overrides over environment variables`, async () => {
process.env.EXPO_ASC_API_KEY_PATH = 'not supposed to be here';
process.env.EXPO_ASC_KEY_ID = 'not supposed to be here';
process.env.EXPO_ASC_ISSUER_ID = 'not supposed to be here';
const ascApiKey = await resolveAscApiKeyAsync(testAscApiKey);
expect(ascApiKey).toMatchObject(testAscApiKey);
});
it(`uses environment variables if no option overrides are provided`, async () => {
vol.fromJSON({
'/test-asc-key.p8': testAscApiKey.keyP8,
});
process.env.EXPO_ASC_API_KEY_PATH = '/test-asc-key.p8';
process.env.EXPO_ASC_KEY_ID = testAscApiKey.keyId;
process.env.EXPO_ASC_ISSUER_ID = testAscApiKey.issuerId;
const ascApiKey = await resolveAscApiKeyAsync();
expect(ascApiKey).toMatchObject(testAscApiKey);
});
it(`prompts the user if it can't find anything else`, async () => {
vol.fromJSON({
'/test-asc-key.p8': testAscApiKey.keyP8,
});
asMock(promptAsync).mockImplementation(() => ({
ascApiKeyPath: '/test-asc-key.p8',
ascIssuerId: testAscApiKey.issuerId,
ascApiKeyId: testAscApiKey.keyId,
}));
const ascApiKey = await resolveAscApiKeyAsync();
expect(ascApiKey).toMatchObject(testAscApiKey);
});
});
const testTeam = {
teamId: 'test-id',
teamName: 'test-name',
teamType: AppleTeamType.IN_HOUSE,
};
describe(resolveAppleTeamAsync, () => {
it(`uses option overrides over environment variables`, async () => {
process.env.EXPO_APPLE_TEAM_ID = 'not supposed to be here';
process.env.EXPO_APPLE_TEAM_TYPE = 'not supposed to be here';
const team = await resolveAppleTeamAsync(testTeam);
expect(team).toMatchObject({ id: testTeam.teamId, name: testTeam.teamName, inHouse: true });
});
it(`uses environment variables if no option overrides are provided`, async () => {
process.env.EXPO_APPLE_TEAM_ID = testTeam.teamId;
process.env.EXPO_APPLE_TEAM_TYPE = testTeam.teamType;
const team = await resolveAppleTeamAsync();
expect(team).toMatchObject({
id: testTeam.teamId,
name: undefined,
inHouse: true,
});
});
it(`prompts the user if it can't find anything else`, async () => {
asMock(promptAsync).mockImplementation(() => ({
appleTeamId: testTeam.teamId,
appleTeamType: testTeam.teamType,
}));
const team = await resolveAppleTeamAsync();
expect(team).toMatchObject({
id: testTeam.teamId,
name: undefined,
inHouse: true,
});
});
});
@@ -3,31 +3,40 @@ import { ApiKey, ApiKeyProps, ApiKeyType, UserRole } from '@expo/apple-utils';
import Log from '../../../log';
import { ora } from '../../../ora';
import { AscApiKey, AscApiKeyInfo } from './Credentials.types';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext } from './authenticate';
import { AuthCtx, UserAuthCtx } from './authenticateTypes';
export async function listAscApiKeysAsync(authCtx: AuthCtx): Promise<AscApiKeyInfo[]> {
/**
* List App Store Connect API Keys.
* **Does not support App Store Connect API (CI).**
*/
export async function listAscApiKeysAsync(userAuthCtx: UserAuthCtx): Promise<AscApiKeyInfo[]> {
const spinner = ora(`Fetching App Store Connect API Keys.`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const keys = await ApiKey.getAsync(context);
spinner.succeed(`Fetched App Store Connect API Keys.`);
return keys.map(key => getAscApiKeyInfo(key, authCtx));
return keys.map(key => getAscApiKeyInfo(key, userAuthCtx));
} catch (error) {
spinner.fail(`Failed to fetch App Store Connect API Keys.`);
throw error;
}
}
/**
* Get an App Store Connect API Key.
* **Does not support App Store Connect API (CI).**
*/
export async function getAscApiKeyAsync(
authCtx: AuthCtx,
userAuthCtx: UserAuthCtx,
keyId: string
): Promise<AscApiKeyInfo | null> {
const spinner = ora(`Fetching App Store Connect API Key.`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const apiKey = await ApiKey.infoAsync(context, { id: keyId });
spinner.succeed(`Fetched App Store Connect API Key (ID: ${keyId}).`);
return getAscApiKeyInfo(apiKey, authCtx);
return getAscApiKeyInfo(apiKey, userAuthCtx);
} catch (error: any) {
const message = error?.message ?? '';
if (message.includes("There is no resource of type 'apiKeys' with id")) {
@@ -40,8 +49,12 @@ export async function getAscApiKeyAsync(
}
}
/**
* Create an App Store Connect API Key.
* **Does not support App Store Connect API (CI).**
*/
export async function createAscApiKeyAsync(
authCtx: AuthCtx,
userAuthCtx: UserAuthCtx,
{
nickname,
allAppsVisible,
@@ -51,7 +64,7 @@ export async function createAscApiKeyAsync(
): Promise<AscApiKey> {
const spinner = ora(`Creating App Store Connect API Key.`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const key = await ApiKey.createAsync(context, {
nickname: nickname ?? `[expo] ${new Date().getTime()}`,
allAppsVisible: allAppsVisible ?? true,
@@ -76,7 +89,7 @@ export async function createAscApiKeyAsync(
const fullKey = await ApiKey.infoAsync(context, { id: key.id });
spinner.succeed(`Created App Store Connect API Key.`);
return {
...getAscApiKeyInfo(fullKey, authCtx),
...getAscApiKeyInfo(fullKey, userAuthCtx),
keyP8,
};
} catch (err: any) {
@@ -85,17 +98,21 @@ export async function createAscApiKeyAsync(
}
}
/**
* Revoke an App Store Connect API Key.
* **Does not support App Store Connect API (CI).**
*/
export async function revokeAscApiKeyAsync(
authCtx: AuthCtx,
userAuthCtx: UserAuthCtx,
keyId: string
): Promise<AscApiKeyInfo> {
const spinner = ora(`Revoking App Store Connect API Key.`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const apiKey = await ApiKey.infoAsync(context, { id: keyId });
const revokedKey = await apiKey.revokeAsync();
spinner.succeed(`Revoked App Store Connect API Key.`);
return getAscApiKeyInfo(revokedKey, authCtx);
return getAscApiKeyInfo(revokedKey, userAuthCtx);
} catch (error) {
Log.error(error);
spinner.fail(`Failed to revoke App Store Connect API Key.`);
@@ -4,16 +4,28 @@ import {
RequestContext,
Session,
Teams,
Token,
} from '@expo/apple-utils';
import assert from 'assert';
import chalk from 'chalk';
import Log from '../../../log';
import { toggleConfirmAsync } from '../../../prompts';
import { MinimalAscApiKey } from '../credentials';
import {
ApiKeyAuthCtx,
AppleTeamType,
AuthCtx,
AuthenticationMode,
Team,
UserAuthCtx,
} from './authenticateTypes';
import {
deletePasswordAsync,
promptPasswordAsync,
resolveCredentialsAsync,
resolveAppleTeamAsync,
resolveAscApiKeyAsync,
resolveUserCredentialsAsync,
} from './resolveCredentials';
const APPLE_IN_HOUSE_TEAM_TYPE = 'in-house';
@@ -21,31 +33,27 @@ const APPLE_IN_HOUSE_TEAM_TYPE = 'in-house';
export type Options = {
appleId?: string;
teamId?: string;
teamName?: string;
teamType?: AppleTeamType;
ascApiKey?: MinimalAscApiKey;
/**
* Can be used to restore the Apple auth state via apple-utils.
*/
cookies?: Session.AuthState['cookies'];
/** Indicates how Apple network requests will be made. */
mode?: AuthenticationMode;
};
export type Team = {
id: string;
name: string;
inHouse?: boolean;
};
export function isUserAuthCtx(authCtx: AuthCtx | undefined): authCtx is UserAuthCtx {
return !!authCtx && typeof (authCtx as UserAuthCtx).appleId === 'string';
}
export type AuthCtx = {
appleId: string;
appleIdPassword?: string;
team: Team;
/**
* Defined when using Fastlane
*/
fastlaneSession?: string;
/**
* Can be used to restore the Apple auth state via apple-utils.
*/
authState?: Session.AuthState;
};
export function assertUserAuthCtx(authCtx: AuthCtx | undefined): UserAuthCtx {
if (isUserAuthCtx(authCtx)) {
return authCtx;
}
throw new Error('Expected user authentication context (login/password).');
}
export function getRequestContext(authCtx: AuthCtx): RequestContext {
assert(authCtx.authState?.context, 'Apple request context must be defined');
@@ -67,7 +75,7 @@ async function loginAsync(
}
// Resolve the user credentials, optimizing for password-less login.
const { username, password } = await resolveCredentialsAsync(userCredentials);
const { username, password } = await resolveUserCredentialsAsync(userCredentials);
assert(username);
// Clear data
@@ -144,6 +152,35 @@ async function loginWithUserCredentialsAsync({
}
export async function authenticateAsync(options: Options = {}): Promise<AuthCtx> {
if (options.mode === AuthenticationMode.API_KEY) {
return await authenticateWithApiKeyAsync(options);
} else {
return await authenticateAsUserAsync(options);
}
}
async function authenticateWithApiKeyAsync(options: Options = {}): Promise<ApiKeyAuthCtx> {
// Resolve the user credentials, optimizing for password-less login.
const ascApiKey = await resolveAscApiKeyAsync(options.ascApiKey);
const team = await resolveAppleTeamAsync(options);
const jwtDurationSeconds = 1200; // 20 minutes
return {
team,
authState: {
context: {
token: new Token({
key: ascApiKey.keyP8,
issuerId: ascApiKey.issuerId,
keyId: ascApiKey.keyId,
duration: jwtDurationSeconds,
}),
},
},
ascApiKey,
};
}
async function authenticateAsUserAsync(options: Options = {}): Promise<AuthCtx> {
// help keep apple login visually apart from the other operations.
Log.addNewLineIfNone();
@@ -0,0 +1,48 @@
import { Session } from '@expo/apple-utils';
import { MinimalAscApiKey } from '../credentials';
export enum AuthenticationMode {
/** App Store API requests will be made using the official API via an API key, used for CI environments where 2FA cannot be performed. */
API_KEY,
/** Uses cookies based authentication and the unofficial App Store web API, this provides more functionality than the official API but cannot be reliably used in CI because it requires 2FA. */
USER,
}
export enum AppleTeamType {
IN_HOUSE = 'IN_HOUSE',
COMPANY_OR_ORGANIZATION = 'COMPANY_OR_ORGANIZATION',
INDIVIDUAL = 'INDIVIDUAL',
}
export type Team = {
id: string;
/** Name of the development team, this is undefined when ASC API keys are used instead of cookies for authentication. */
name?: string;
inHouse?: boolean;
};
export type UserAuthCtx = {
appleId: string;
appleIdPassword?: string;
team: Team;
/**
* Defined when using Fastlane
*/
fastlaneSession?: string;
/**
* Can be used to restore the Apple auth state via apple-utils.
*/
authState?: Session.AuthState;
};
export type ApiKeyAuthCtx = {
ascApiKey: MinimalAscApiKey;
team: Team;
/**
* Can be used to restore the Apple auth state via apple-utils.
*/
authState?: Partial<Session.AuthState>;
};
export type AuthCtx = UserAuthCtx | ApiKeyAuthCtx;
@@ -5,6 +5,9 @@ import Log from '../../../log';
import type { Ora } from '../../../ora';
import { convertHTMLToASCII } from '../utils/convertHTMLToASCII';
/**
* **Does not support App Store Connect API (CI).**
*/
async function getContractStatusAsync(
context: RequestContext
): Promise<ITCAgreements.ITCContractStatus | null> {
@@ -17,6 +20,9 @@ async function getContractStatusAsync(
}
}
/**
* **Does not support App Store Connect API (CI).**
*/
async function getContractMessagesAsync(
context: RequestContext
): Promise<ITCAgreements.ITCContractMessage[] | null> {
@@ -28,6 +34,9 @@ async function getContractMessagesAsync(
}
}
/**
* **Does not support App Store Connect API (CI).**
*/
async function getRequiredContractMessagesAsync(
context: RequestContext
): Promise<{ messages: ITCAgreements.ITCContractMessage[]; isFatal: boolean }> {
@@ -77,6 +86,9 @@ export function formatContractMessage(message: ITCAgreements.ITCContractMessage)
});
}
/**
* **Does not support App Store Connect API (CI).**
*/
export async function assertContractMessagesAsync(
context: RequestContext,
spinner?: Ora
@@ -7,7 +7,8 @@ import {
import { ora } from '../../../ora';
import { DistributionCertificate, DistributionCertificateStoreInfo } from './Credentials.types';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext } from './authenticate';
import { AuthCtx } from './authenticateTypes';
export class AppleTooManyCertsError extends Error {}
@@ -4,7 +4,8 @@ import chalk from 'chalk';
import Log from '../../../log';
import { ora } from '../../../ora';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext, isUserAuthCtx } from './authenticate';
import { AuthCtx, UserAuthCtx } from './authenticateTypes';
import { syncCapabilitiesForEntitlementsAsync } from './bundleIdCapabilities';
import { syncCapabilityIdentifiersForEntitlementsAsync } from './capabilityIdentifiers';
import { assertContractMessagesAsync } from './contractMessages';
@@ -72,7 +73,13 @@ export async function ensureBundleIdExistsWithNameAsync(
// Unable to process request - PLA Update available - You currently don't have access to this membership resource. To resolve this issue, agree to the latest Program License Agreement in your developer account.
err.message.match(/agree/)
) {
await assertContractMessagesAsync(context);
if (isUserAuthCtx(authCtx)) {
await assertContractMessagesAsync(context);
} else {
Log.warn(
`You currently don't have access to this membership resource. To resolve this issue, agree to the latest Program License Agreement in your developer account.`
);
}
}
}
@@ -150,7 +157,7 @@ export async function syncCapabilityIdentifiersAsync(
}
export async function ensureAppExistsAsync(
authCtx: AuthCtx,
userAuthCtx: UserAuthCtx,
{
name,
language,
@@ -165,7 +172,7 @@ export async function ensureAppExistsAsync(
sku?: string;
}
): Promise<App> {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const spinner = ora(`Linking to App Store Connect ${chalk.dim(bundleIdentifier)}`).start();
let app = await App.findAsync(context, { bundleId: bundleIdentifier });
@@ -176,7 +183,9 @@ export async function ensureAppExistsAsync(
try {
// Assert contract errors when the user needs to create an app.
await assertContractMessagesAsync(context, spinner);
/**
* **Does not support App Store Connect API (CI).**
*/
app = await App.createAsync(context, {
bundleId: bundleIdentifier,
name,
@@ -186,9 +195,8 @@ export async function ensureAppExistsAsync(
});
} catch (error: any) {
if (error.message.match(/An App ID with Identifier '(.*)' is not available/)) {
const providerName = authCtx.authState?.session.provider.name;
throw new Error(
`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${providerName}". Please change it in your app config and try again.\n`
`\nThe bundle identifier "${bundleIdentifier}" is not available to provider "${userAuthCtx.authState?.session.provider.name}. Please change it in your app config and try again.\n`
);
}
@@ -8,7 +8,8 @@ import {
ProvisioningProfile,
ProvisioningProfileStoreInfo,
} from './Credentials.types';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext } from './authenticate';
import { AuthCtx } from './authenticateTypes';
import { getBundleIdForIdentifierAsync, getProfilesForBundleIdAsync } from './bundleId';
import { getCertificateBySerialNumberAsync, transformCertificate } from './distributionCertificate';
@@ -3,7 +3,8 @@ import { Device, Profile, ProfileState, ProfileType, RequestContext } from '@exp
import { ora } from '../../../ora';
import { isAppStoreConnectTokenOnlyContext } from '../utils/authType';
import { ProvisioningProfile } from './Credentials.types';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext } from './authenticate';
import { AuthCtx } from './authenticateTypes';
import { getBundleIdForIdentifierAsync, getProfilesForBundleIdAsync } from './bundleId';
import { getDistributionCertificateAsync } from './distributionCertificate';
@@ -5,7 +5,8 @@ import dateformat from 'dateformat';
import Log from '../../../log';
import { ora } from '../../../ora';
import { PushKey, PushKeyStoreInfo } from './Credentials.types';
import { AuthCtx, getRequestContext } from './authenticate';
import { getRequestContext } from './authenticate';
import { UserAuthCtx } from './authenticateTypes';
const { MaxKeysCreatedError } = Keys;
@@ -19,10 +20,10 @@ Please remember that Apple Keys are not application specific!
* List all existing push keys on Apple servers.
* **Does not support App Store Connect API (CI).**
*/
export async function listPushKeysAsync(authCtx: AuthCtx): Promise<PushKeyStoreInfo[]> {
export async function listPushKeysAsync(userAuthCtx: UserAuthCtx): Promise<PushKeyStoreInfo[]> {
const spinner = ora(`Fetching Apple push keys`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const keys = await Keys.getKeysAsync(context);
spinner.succeed(`Fetched Apple push keys`);
return keys;
@@ -37,20 +38,20 @@ export async function listPushKeysAsync(authCtx: AuthCtx): Promise<PushKeyStoreI
* **Does not support App Store Connect API (CI).**
*/
export async function createPushKeyAsync(
authCtx: AuthCtx,
userAuthCtx: UserAuthCtx,
name: string = `Expo Push Notifications Key ${dateformat('yyyymmddHHMMss')}`
): Promise<PushKey> {
const spinner = ora(`Creating Apple push key`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
const key = await Keys.createKeyAsync(context, { name, isApns: true });
const apnsKeyP8 = await Keys.downloadKeyAsync(context, { id: key.id });
spinner.succeed(`Created Apple push key`);
return {
apnsKeyId: key.id,
apnsKeyP8,
teamId: authCtx.team.id,
teamName: authCtx.team.name,
teamId: userAuthCtx.team.id,
teamName: userAuthCtx.team.name,
};
} catch (err: any) {
spinner.fail('Failed to create Apple push key');
@@ -69,12 +70,12 @@ export async function createPushKeyAsync(
* Revoke an existing push key on Apple servers.
* **Does not support App Store Connect API (CI).**
*/
export async function revokePushKeyAsync(authCtx: AuthCtx, ids: string[]): Promise<void> {
export async function revokePushKeyAsync(userAuthCtx: UserAuthCtx, ids: string[]): Promise<void> {
const name = `Apple push key${ids?.length === 1 ? '' : 's'}`;
const spinner = ora(`Revoking ${name}`).start();
try {
const context = getRequestContext(authCtx);
const context = getRequestContext(userAuthCtx);
await Promise.all(ids.map(id => Keys.revokeKeyAsync(context, { id })));
spinner.succeed(`Revoked ${name}`);
@@ -5,6 +5,8 @@ import wrapAnsi from 'wrap-ansi';
import Log, { learnMore } from '../../../log';
import { promptAsync } from '../../../prompts';
import { MinimalAscApiKey } from '../credentials';
import { AppleTeamType, Team } from './authenticateTypes';
import * as Keychain from './keychain';
/**
@@ -13,7 +15,7 @@ import * as Keychain from './keychain';
*
* @param options
*/
export async function resolveCredentialsAsync(
export async function resolveUserCredentialsAsync(
options: Partial<Auth.UserCredentials>
): Promise<Partial<Auth.UserCredentials>> {
const credentials = getAppleIdFromEnvironmentOrOptions(options);
@@ -25,6 +27,162 @@ export async function resolveCredentialsAsync(
return credentials;
}
export function hasAscEnvVars(): boolean {
return (
!!process.env.EXPO_ASC_API_KEY_PATH ||
!!process.env.EXPO_ASC_KEY_ID ||
!!process.env.EXPO_ASC_ISSUER_ID
);
}
export async function resolveAscApiKeyAsync(
ascApiKey?: MinimalAscApiKey
): Promise<MinimalAscApiKey> {
const passedKeyP8 = await getAscKeyP8FromEnvironmentOrOptionsAsync(ascApiKey);
const passedKeyId = await getAscKeyIdFromEnvironmentOrOptionsAsync(ascApiKey);
const passedIssuerId = await getAscIssuerIdFromEnvironmentOrOptionsAsync(ascApiKey);
return {
keyP8: passedKeyP8,
keyId: passedKeyId,
issuerId: passedIssuerId,
};
}
async function getAscKeyP8FromEnvironmentOrOptionsAsync(
ascApiKey?: MinimalAscApiKey
): Promise<string> {
if (ascApiKey?.keyP8) {
return ascApiKey?.keyP8;
} else if (process.env.EXPO_ASC_API_KEY_PATH) {
return await fs.readFile(process.env.EXPO_ASC_API_KEY_PATH, 'utf-8');
}
const { ascApiKeyPath } = await promptAsync({
type: 'text',
name: 'ascApiKeyPath',
message: `Path to ASC Api Key Path (.p8):`,
validate: (val: string) => val !== '',
});
return await fs.readFile(ascApiKeyPath, 'utf-8');
}
async function getAscKeyIdFromEnvironmentOrOptionsAsync(
ascApiKey?: MinimalAscApiKey
): Promise<string> {
if (ascApiKey?.keyId) {
return ascApiKey?.keyId;
} else if (process.env.EXPO_ASC_KEY_ID) {
return process.env.EXPO_ASC_KEY_ID;
}
const { ascApiKeyId } = await promptAsync({
type: 'text',
name: 'ascApiKeyId',
message: `ASC Api Key ID:`,
validate: (val: string) => val !== '',
});
return ascApiKeyId;
}
async function getAscIssuerIdFromEnvironmentOrOptionsAsync(
ascApiKey?: MinimalAscApiKey
): Promise<string> {
if (ascApiKey?.issuerId) {
return ascApiKey?.issuerId;
} else if (process.env.EXPO_ASC_ISSUER_ID) {
return process.env.EXPO_ASC_ISSUER_ID;
}
const { ascIssuerId } = await promptAsync({
type: 'text',
name: 'ascIssuerId',
message: `ASC Issuer ID:`,
validate: (val: string) => val !== '',
});
return ascIssuerId;
}
function isAppleTeamType(maybeTeamType: any): maybeTeamType is AppleTeamType {
return maybeTeamType in AppleTeamType;
}
function assertAppleTeamType(maybeTeamType: any): AppleTeamType {
if (!isAppleTeamType(maybeTeamType)) {
throw new Error(
`Invalid Apple Team Type: ${maybeTeamType}. Must be one of ${Object.keys(AppleTeamType).join(
', '
)}`
);
}
return maybeTeamType;
}
function resolveAppleTeamTypeFromEnvironment(): AppleTeamType | undefined {
if (!process.env.EXPO_APPLE_TEAM_TYPE) {
return undefined;
}
return assertAppleTeamType(process.env.EXPO_APPLE_TEAM_TYPE);
}
async function getAppleTeamIdFromEnvironmentOrOptionsAsync(options: {
teamId?: string;
}): Promise<string> {
if (options.teamId) {
return options.teamId;
} else if (process.env.EXPO_APPLE_TEAM_ID) {
return process.env.EXPO_APPLE_TEAM_ID;
}
const { appleTeamId } = await promptAsync({
type: 'text',
name: 'appleTeamId',
message: `Apple Team ID:`,
validate: (val: string) => val !== '',
});
return appleTeamId;
}
async function getAppleTeamTypeFromEnvironmentOrOptionsAsync(options: {
teamType?: AppleTeamType;
}): Promise<string> {
if (options.teamType) {
return options.teamType;
}
const appleTeamTypeFromEnvironment = resolveAppleTeamTypeFromEnvironment();
if (appleTeamTypeFromEnvironment) {
return appleTeamTypeFromEnvironment;
}
const { appleTeamType } = await promptAsync({
type: 'select',
message: 'Select your Apple Team Type:',
name: 'appleTeamType',
choices: [
{ title: 'Enterprise', value: AppleTeamType.IN_HOUSE },
{ title: 'Company/Organization', value: AppleTeamType.COMPANY_OR_ORGANIZATION },
{ title: 'Individual', value: AppleTeamType.INDIVIDUAL },
],
});
return appleTeamType;
}
export async function resolveAppleTeamAsync(
options: {
teamId?: string;
teamName?: string;
teamType?: AppleTeamType;
} = {}
): Promise<Team> {
const passedTeamType = await getAppleTeamTypeFromEnvironmentOrOptionsAsync(options);
return {
id: await getAppleTeamIdFromEnvironmentOrOptionsAsync(options),
name: options.teamName,
inHouse: passedTeamType === AppleTeamType.IN_HOUSE,
};
}
function getAppleIdFromEnvironmentOrOptions({
username,
password,
+1 -1
View File
@@ -97,7 +97,7 @@ export class AccountResolver {
async function ensureAppleTeamExistsAsync(
accountId: string,
{ appleTeamIdentifier, appleTeamName }: { appleTeamIdentifier: string; appleTeamName: string }
{ appleTeamIdentifier, appleTeamName }: { appleTeamIdentifier: string; appleTeamName?: string }
): Promise<AppleTeamFragment> {
const appleTeam = await AppleTeamQuery.getByAppleTeamIdentifierAsync(
accountId,
+69 -5
View File
@@ -96,6 +96,7 @@ export type Account = {
availableBuilds?: Maybe<Scalars['Int']>;
/** Billing information */
billing?: Maybe<Billing>;
billingPeriod: BillingPeriod;
/** Build Jobs associated with this account */
buildJobs: Array<BuildJob>;
/**
@@ -129,6 +130,8 @@ export type Account = {
/** @deprecated See isCurrent */
unlimitedBuilds: Scalars['Boolean'];
updatedAt: Scalars['DateTime'];
/** Account query object for querying EAS usage metrics */
usageMetrics: AccountUsageMetrics;
/** Pending user invitations for this account */
userInvitations: Array<UserInvitation>;
/** Actors associated with this account and permissions they hold */
@@ -196,6 +199,15 @@ export type AccountAppsArgs = {
};
/**
* 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 AccountBillingPeriodArgs = {
date: Scalars['DateTime'];
};
/**
* An account is a container owning projects, credentials, billing and other organization
* data and settings. Actors may own and be members of accounts.
@@ -379,6 +391,26 @@ export type AccountQueryByNameArgs = {
accountName: Scalars['String'];
};
export type AccountUsageMetric = {
__typename?: 'AccountUsageMetric';
metric: EasServiceMetric;
metricType: UsageMetricType;
timestamp: Scalars['DateTime'];
value: Scalars['Float'];
};
export type AccountUsageMetrics = {
__typename?: 'AccountUsageMetrics';
metricsForServiceMetric: Array<AccountUsageMetric>;
};
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
granularity: UsageMetricsGranularity;
metric: EasServiceMetric;
timespan: UsageMetricsTimespan;
};
export type ActivityTimelineProjectActivity = {
activityTimestamp: Scalars['DateTime'];
actor?: Maybe<Actor>;
@@ -1482,6 +1514,13 @@ export type Billing = {
subscription?: Maybe<SubscriptionDetails>;
};
export type BillingPeriod = {
__typename?: 'BillingPeriod';
anchor: Scalars['DateTime'];
end: Scalars['DateTime'];
start: Scalars['DateTime'];
};
/** Represents an EAS Build */
export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
__typename?: 'Build';
@@ -1496,9 +1535,12 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
createdAt?: Maybe<Scalars['DateTime']>;
distribution?: Maybe<DistributionType>;
error?: Maybe<BuildError>;
estimatedWaitTimeLeftSeconds?: Maybe<Scalars['Int']>;
expirationDate?: Maybe<Scalars['DateTime']>;
gitCommitHash?: Maybe<Scalars['String']>;
id: Scalars['ID'];
/** Queue position is 1-indexed */
initialQueuePosition?: Maybe<Scalars['Int']>;
initiatingActor?: Maybe<Actor>;
/** @deprecated User type is deprecated */
initiatingUser?: Maybe<User>;
@@ -2014,6 +2056,10 @@ export enum EasBuildDeprecationInfoType {
UserFacing = 'USER_FACING'
}
export enum EasServiceMetric {
ManifestRequests = 'MANIFEST_REQUESTS'
}
export type EditUpdateBranchInput = {
appId?: InputMaybe<Scalars['ID']>;
id?: InputMaybe<Scalars['ID']>;
@@ -3349,6 +3395,22 @@ export enum UploadSessionType {
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE'
}
export enum UsageMetricType {
Request = 'REQUEST'
}
export enum UsageMetricsGranularity {
Day = 'DAY',
Hour = 'HOUR',
Minute = 'MINUTE',
Total = 'TOTAL'
}
export type UsageMetricsTimespan = {
end: Scalars['DateTime'];
start: Scalars['DateTime'];
};
/** Represents a human (not robot) actor. */
export type User = Actor & {
__typename?: 'User';
@@ -3470,6 +3532,7 @@ export type UserInvitation = {
created: Scalars['DateTime'];
/** Email to which this invitation was sent */
email: Scalars['String'];
expires: Scalars['DateTime'];
id: Scalars['ID'];
/** Account permissions to be granted upon acceptance of this invitation */
permissions: Array<Permission>;
@@ -3541,6 +3604,7 @@ export type UserInvitationPublicData = {
accountName: Scalars['String'];
created: Scalars['DateTime'];
email: Scalars['String'];
expires: Scalars['DateTime'];
/** Email to which this invitation was sent */
id: Scalars['ID'];
};
@@ -4193,7 +4257,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, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, 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, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type CreateIosBuildMutationVariables = Exact<{
appId: Scalars['ID'];
@@ -4202,7 +4266,7 @@ export type CreateIosBuildMutationVariables = Exact<{
}>;
export type CreateIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createIosBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, 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, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, 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, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }, deprecationInfo?: { __typename?: 'EASBuildDeprecationInfo', type: EasBuildDeprecationInfoType, message: string } | null } } };
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
input: CreateEnvironmentSecretInput;
@@ -4315,7 +4379,7 @@ 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, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type BuildsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type GetAllBuildsForAppQueryVariables = Exact<{
appId: Scalars['String'];
@@ -4325,7 +4389,7 @@ export type GetAllBuildsForAppQueryVariables = Exact<{
}>;
export type GetAllBuildsForAppQuery = { __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, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
export type GetAllBuildsForAppQuery = { __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, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } }> } } };
export type GetChannelByNameForAppQueryVariables = Exact<{
appId: Scalars['String'];
@@ -4421,7 +4485,7 @@ export type WebhookByIdQuery = { __typename?: 'RootQuery', webhook: { __typename
export type AppFragment = { __typename?: 'App', id: string, fullName: 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, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, releaseChannel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt?: any | null, updatedAt?: any | null, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, createdAt: any };
@@ -41,6 +41,10 @@ export const BuildFragmentNode = gql`
appBuildVersion
runtimeVersion
gitCommitHash
initialQueuePosition
queuePosition
estimatedWaitTimeLeftSeconds
priority
createdAt
updatedAt
}
+12 -1
View File
@@ -1 +1,12 @@
export { run } from '@oclif/core';
import { run } from '@oclif/core';
/**
* Invokes the `testcli-modern` CLI with args programmatically.
*
* @param {...string} args - args to pass to CLI.
*
* @returns {Promise<void>}
*/
export default async function eascliAsync(...args: any[]): Promise<unknown> {
return run(args, import.meta.url);
}
+4 -4
View File
@@ -54,7 +54,7 @@ export async function validateMetroConfigForManagedWorkflowAsync(
}
}
function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
async function importMetroConfigFromProjectAsync(projectDir: string): Promise<typeof MetroConfig> {
const resolvedPath = resolveFrom.silent(projectDir, 'metro-config');
if (!resolvedPath) {
throw new MetroConfigPackageMissingError(
@@ -64,12 +64,12 @@ function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
'and run `yarn` or `npm install`.'
);
}
return require(resolvedPath);
return await import(resolvedPath);
}
async function configExistsAsync(projectRoot: string): Promise<boolean> {
try {
const MetroConfig = importMetroConfigFromProject(projectRoot);
const MetroConfig = await importMetroConfigFromProjectAsync(projectRoot);
const result = await MetroConfig.resolveConfig(undefined, projectRoot);
return !result.isEmpty;
} catch (err) {
@@ -82,7 +82,7 @@ async function configExistsAsync(projectRoot: string): Promise<boolean> {
}
async function loadConfigAsync(projectDir: string): Promise<MetroConfig.ConfigT> {
const MetroConfig = importMetroConfigFromProject(projectDir);
const MetroConfig = await importMetroConfigFromProjectAsync(projectDir);
return await MetroConfig.loadConfig({ cwd: projectDir }, {});
}
+2 -1
View File
@@ -10,6 +10,7 @@ import { AssetMetadataStatus, PartialManifestAsset } from '../graphql/generated'
import { PublishMutation } from '../graphql/mutations/PublishMutation';
import { PresignedPost } from '../graphql/mutations/UploadSessionMutation';
import { PublishQuery } from '../graphql/queries/PublishQuery';
import Log from '../log';
import { uploadWithPresignedPostAsync } from '../uploads';
import { expoCommandAsync } from '../utils/expoCli';
import uniqBy from '../utils/expodash/uniqBy';
@@ -159,7 +160,7 @@ export async function buildBundlesAsync({
await expoCommandAsync(
projectDir,
['export', '--output-dir', inputDir, '--experimental-bundle'],
{ silent: true }
{ silent: !Log.isDebug }
);
}
@@ -169,7 +169,8 @@ async function askForServiceAccountPathAsync(): Promise<string> {
'A Google Service Account JSON key is required to upload your app to Google Play Store'
)}.\n` +
`If you're not sure what this is or how to create one, ${learnMore(
'https://expo.fyi/creating-google-service-account'
'https://expo.fyi/creating-google-service-account',
{ learnMoreMessage: 'learn more' }
)}`
);
const { filePath } = await promptAsync({
@@ -24,7 +24,6 @@ interface CreateAppOptions {
}
type AppStoreResult = {
appleIdUsername: string;
ascAppIdentifier: string;
};
@@ -67,29 +66,29 @@ async function createAppStoreConnectAppAsync(
sku,
} = options;
const authCtx = await ctx.credentialsCtx.appStore.ensureAuthenticatedAsync({
const userAuthCtx = await ctx.credentialsCtx.appStore.ensureUserAuthenticatedAsync({
appleId,
teamId: appleTeamId,
});
const requestCtx = getRequestContext(authCtx);
const requestCtx = getRequestContext(userAuthCtx);
Log.addNewLineIfNone();
if (await isProvisioningAvailableAsync(requestCtx)) {
await ensureBundleIdExistsWithNameAsync(authCtx, {
await ensureBundleIdExistsWithNameAsync(userAuthCtx, {
name: appName,
bundleIdentifier: bundleId,
});
} else {
Log.warn(
`Provisioning is not available for user "${authCtx.appleId}", skipping bundle identifier check.`
`Provisioning is not available for Apple User: ${userAuthCtx.appleId}, skipping bundle identifier check.`
);
}
let app: App | null = null;
try {
app = await ensureAppExistsAsync(authCtx, {
app = await ensureAppExistsAsync(userAuthCtx, {
name: appName,
language,
companyName,
@@ -120,7 +119,6 @@ async function createAppStoreConnectAppAsync(
}
return {
appleIdUsername: authCtx.appleId,
ascAppIdentifier: app.id,
};
}
@@ -1,6 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import getenv from 'getenv';
import { isUserAuthCtx } from '../../credentials/ios/appstore/authenticate';
import { promptAsync } from '../../prompts';
import UserSettings from '../../user/UserSettings';
import { SubmissionContext } from '../context';
@@ -51,7 +52,7 @@ export async function getAppleIdUsernameAsync(
return envAppleId;
}
if (ctx.credentialsCtx.appStore.authCtx?.appleId) {
if (isUserAuthCtx(ctx.credentialsCtx.appStore.authCtx)) {
return ctx.credentialsCtx.appStore.authCtx.appleId;
}
+7 -2
View File
@@ -17,13 +17,14 @@ enum SubmissionErrorCode {
IOS_INCORRECT_CREDENTIALS = 'SUBMISSION_SERVICE_IOS_INVALID_CREDENTIALS',
IOS_IPAD_INVALID_ORIENTATION = 'SUBMISSION_SERVICE_IOS_IPAD_INVALID_ORIENTATION',
IOS_APPLE_MAINTENANCE = 'SUBMISSION_SERVICE_IOS_APPLE_MAINTENANCE',
IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE = 'SUBMISSION_SERVICE_IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE',
}
const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
[SubmissionErrorCode.ARCHIVE_DOWNLOAD_NOT_FOUND_ERROR]:
"Failed to download the archive file (Response code: 404 Not Found). Please make sure the URL you've provided is correct.",
[SubmissionErrorCode.ARCHIVE_DOWNLOAD_FORBIDDEN_ERROR]:
'Failed to download the archive file (Response code: 403 Forbidden). This is most probably caused by trying to upload an expired build artifact. All Expo build artifacts expire after 30 days.',
'Failed to download the archive file (Response code: 403 Forbidden). This is most probably caused by trying to upload an expired build artifact. All EAS build artifacts expire after 30 days.',
[SubmissionErrorCode.ARCHIVE_EXTRACT_CORRUPT_ARCHIVE_ERROR]:
'The compressed archive is corrupt, in an unsupported format, or contains an invalid application format. Supported files include .apk, .aab, and .ipa files and one of these files compressed into a .tar.gz archive.',
[SubmissionErrorCode.ARCHIVE_EXTRACT_NO_FILES_FOUND_ERROR]:
@@ -37,7 +38,7 @@ const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
[SubmissionErrorCode.ANDROID_OLD_VERSION_CODE_ERROR]:
"You've already submitted this version of the app.\n" +
'Versions are identified by Android version code (expo.android.versionCode in app.json).\n' +
"If you're submitting a managed Expo project, increment the version code in app.json and build the project with expo build:android.\n" +
"If you're submitting a managed Expo project, increment the version code in app.json and build the project with eas build.\n" +
`${learnMore('https://expo.fyi/bumping-android-version-code')}.`,
[SubmissionErrorCode.ANDROID_MISSING_PRIVACY_POLICY]:
'The app has permissions that require a privacy policy set for the app.\n' +
@@ -65,6 +66,10 @@ const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
`${learnMore('https://expo.fyi/ipad-requires-fullscreen')}`,
[SubmissionErrorCode.IOS_APPLE_MAINTENANCE]:
'It looks like Apple servers are undergoing an unscheduled maintenance. Please try again later.',
[SubmissionErrorCode.IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE]:
'Invalid Provisioning Profile Signature (ITMS-90165)\n' +
"Some of Apple's certificates have expired.\n" +
'Please delete your Provisioning Profile from your account. Then rebuild the app interactively to generate a new one, and try submitting it to the App Store again.',
};
export function printSubmissionError(error: SubmissionError): boolean {
@@ -8,7 +8,6 @@ import {
getCodeSigningInfoAsync,
getKeyAndCertificateFromPathsAsync,
getManifestBodyAsync,
parseMultipartMixedResponseAsync,
} from '../code-signing';
function generateMultipartBody(stringifiedManifest: string): FormData {
@@ -37,7 +36,7 @@ describe(getCodeSigningInfoAsync, () => {
slug: 'test',
updates: {
codeSigningCertificate: 'wat',
} as any,
},
},
'test'
)
@@ -92,28 +91,13 @@ describe(getKeyAndCertificateFromPathsAsync, () => {
});
});
describe(parseMultipartMixedResponseAsync, () => {
it('parses multipart response', async () => {
const form = generateMultipartBody(JSON.stringify({ hello: 'world' }));
const parts = await parseMultipartMixedResponseAsync({
arrayBuffer: async () => form.getBuffer(),
headers: new Headers({
'content-type': `multipart/mixed; boundary=${form.getBoundary()}`,
}),
} as any as Response);
expect(parts).toHaveLength(2);
});
});
describe(getManifestBodyAsync, () => {
it('gets multipart manifest body', async () => {
const stringifiedManifest = JSON.stringify({ hello: 'world' });
const form = generateMultipartBody(stringifiedManifest);
const body = await getManifestBodyAsync({
arrayBuffer: async () => form.getBuffer(),
arrayBuffer: async () => new Uint8Array(form.getBuffer()).buffer,
headers: new Headers({
'content-type': `multipart/mixed; boundary=${form.getBoundary()}`,
}),
+21 -72
View File
@@ -5,14 +5,14 @@ import {
validateSelfSignedCertificate,
} from '@expo/code-signing-certificates';
import { ExpoConfig } from '@expo/config';
import Dicer from 'dicer';
import {
isMultipartPartWithName,
parseMultipartMixedResponseAsync,
} from '@expo/multipart-body-parser';
import isDeepEqual from 'fast-deep-equal';
import { promises as fs } from 'fs';
import { pki as PKI } from 'node-forge';
import nullthrows from 'nullthrows';
import path from 'path';
import { Stream } from 'stream';
import { parseItem } from 'structured-headers';
import { Response } from '../fetch';
import { PartialManifest, PartialManifestAsset } from '../graphql/generated';
@@ -33,7 +33,7 @@ export async function getCodeSigningInfoAsync(
}
if (!privateKeyPath) {
privateKeyPath = path.join(path.dirname(codeSigningCertificatePath), 'private-key.pem');
throw new Error('Must specify --private-key-path argument to sign update for code signing');
}
const codeSigningMetadata = config.updates?.codeSigningMetadata;
@@ -50,18 +50,16 @@ export async function getCodeSigningInfoAsync(
);
}
return codeSigningCertificatePath && privateKeyPath
? {
...(await getKeyAndCertificateFromPathsAsync({
codeSigningCertificatePath,
privateKeyPath,
})),
codeSigningMetadata: {
alg,
keyid,
},
}
: undefined;
return {
...(await getKeyAndCertificateFromPathsAsync({
codeSigningCertificatePath,
privateKeyPath,
})),
codeSigningMetadata: {
alg,
keyid,
},
};
}
async function readFileAsync(path: string, errorMessage: string): Promise<string> {
@@ -103,66 +101,17 @@ export async function getKeyAndCertificateFromPathsAsync({
};
}
export type MultipartPart = { headers: Map<string, string>; body: string };
export async function parseMultipartMixedResponseAsync(res: Response): Promise<MultipartPart[]> {
export async function getManifestBodyAsync(res: Response): Promise<string | null> {
const contentType = res.headers.get('content-type');
if (!contentType) {
throw new Error('The multipart manifest response is missing the content-type header');
}
const boundaryRegex = /^multipart\/.+?; boundary=(?:"([^"]+)"|([^\s;]+))/i;
const matches = boundaryRegex.exec(contentType);
if (!matches) {
throw new Error('The content-type header in the HTTP response is not a multipart media type');
}
const boundary = matches[1] ?? matches[2];
const bodyBuffer = await res.arrayBuffer();
const bufferStream = new Stream.PassThrough();
bufferStream.end(bodyBuffer);
return await new Promise((resolve, reject) => {
const parts: MultipartPart[] = [];
bufferStream.pipe(
new Dicer({ boundary })
.on('part', p => {
const part: MultipartPart = {
body: '',
headers: new Map(),
};
p.on('header', headers => {
for (const h in headers) {
part.headers.set(h, (headers as { [key: string]: string[] })[h][0]);
}
});
p.on('data', data => {
part.body += data.toString();
});
p.on('end', () => {
parts.push(part);
});
})
.on('finish', () => {
resolve(parts);
})
.on('error', error => {
reject(error);
})
);
});
}
function isManifestMultipartPart(multipartPart: MultipartPart): boolean {
const [, parameters] = parseItem(nullthrows(multipartPart.headers.get('content-disposition')));
const partName = parameters.get('name');
return partName === 'manifest';
}
export async function getManifestBodyAsync(res: Response): Promise<string | null> {
const multipartParts = await parseMultipartMixedResponseAsync(res);
const manifestPart = multipartParts.find(isManifestMultipartPart);
const multipartParts = await parseMultipartMixedResponseAsync(
contentType,
Buffer.from(bodyBuffer)
);
const manifestPart = multipartParts.find(part => isMultipartPartWithName(part, 'manifest'));
return manifestPart?.body ?? null;
}
+1 -1
View File
@@ -1,3 +1,3 @@
const packageJSON = require('../../package.json');
import packageJSON from '../../package.json';
export const easCliVersion: string = packageJSON.version;
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../nLine truncated
+7 -4
View File
@@ -2,20 +2,23 @@
"compilerOptions": {
"declaration": true,
"importHelpers": true,
"module": "commonjs",
"module": "es2020",
"moduleResolution": "node",
"composite": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noUnusedParameters":true,
"resolveJsonModule": true,
"noUnusedLocals":true,
"strict": true,
"target": "ES2019",
"target": "ES2020",
"outDir": "build",
"rootDir": "src",
// "rootDir": "src",
"typeRoots": [
"../../node_modules/@types",
"../../ts-declarations",
"./node_modules/@types"
]
},
"include": ["src/**/*"]
"include": ["src/**/*", "package.json"]
}
+1
View File
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../nLine truncated
+11 -2
View File
@@ -1,5 +1,14 @@
module.exports = {
...require('../../jest/jest.config.js'),
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import baseConfig from '../../jest/jest.config.js';
const filename = fileURLToPath(import.meta.url);
// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(filename);
export default {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
};
+4 -3
View File
@@ -1,9 +1,10 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "0.49.0",
"version": "0.51.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"type": "module",
"dependencies": {
"@expo/eas-build-job": "0.2.68",
"@expo/json-file": "8.2.34",
@@ -21,11 +22,11 @@
"typescript": "4.6.3"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.16.0"
},
"homepage": "https://github.com/expo/eas-cli",
"license": "MIT",
"main": "build/index.js",
"exports": "./build/index.js",
"types": "build/index.d.ts",
"repository": "expo/eas-cli",
"scripts": {
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","./src/errors.ts","../../node_modules/@expo/eas-build-job/node_modules/joi/lib/index.d.ts","../../node_modules/@expo/eas-build-job/dist/common.d.ts","../../node_modules/@expo/eas-build-job/dist/android.d.ts","../../node_modules/@expo/eas-build-job/dist/ios.d.ts","../../node_modules/@expo/eas-build-job/dist/job.d.ts","../../node_modules/@expo/eas-build-job/dist/metadata.d.ts","../../node_modules/@expo/eas-build-job/dist/logs.d.ts","../../node_modules/@expo/eas-build-job/dist/errors.d.ts","../../node_modules/@expo/eas-build-job/dist/index.d.ts","./src/submit/types.ts","./src/build/types.ts","./src/types.ts","../../node_modules/joi/lib/index.d.ts","./src/submit/schema.ts","./src/submit/resolver.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.tsLine truncated
+4 -2
View File
@@ -2,11 +2,13 @@
"compilerOptions": {
"declaration": true,
"importHelpers": true,
"module": "commonjs",
"module": "es2020",
"moduleResolution": "node",
"composite": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"strict": true,
"target": "ES2019",
"target": "ES2020",
"outDir": "build",
"rootDir": "src",
"typeRoots": [
+1
View File
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","./src/errors.ts","../../node_modules/@expo/eas-build-job/node_modules/joi/lib/index.d.ts","../../node_modules/@expo/eas-build-job/dist/common.d.ts","../../node_modules/@expo/eas-build-job/dist/android.d.ts","../../node_modules/@expo/eas-build-job/dist/ios.d.ts","../../node_modules/@expo/eas-build-job/dist/job.d.ts","../../node_modules/@expo/eas-build-job/dist/metadata.d.ts","../../node_modules/@expo/eas-build-job/dist/logs.d.ts","../../node_modules/@expo/eas-build-job/dist/errors.d.ts","../../node_modules/@expo/eas-build-job/dist/index.d.ts","./src/submit/types.ts","./src/build/types.ts","./src/types.ts","../../node_modules/joi/lib/index.d.ts","./src/submit/schema.ts","./src/submit/resolver.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.tsLine truncated
Generated
+29 -20
View File
@@ -1178,10 +1178,10 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@expo/[email protected]0":
version "0.0.0-alpha.30"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.30.tgz#5eb0ba660c1d099c6ea2b4d2ee25272ba041377e"
integrity sha512-ZlQCYBxHY931Tm3KWY6/v2EdqMsWWov3c+0Yq36i+yXvLpqTHiy7feMSb3s5QCDXEd1z0JKWbVPcpEKs/SAvXQ==
"@expo/[email protected]1":
version "0.0.0-alpha.31"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.31.tgz#ab18640c384ba4b088e4490977a12f6f41a1e2a0"
integrity sha512-lGJOS8eAPcZhaRl5GZFIg4ZNSRY1k10wYeYXjHUbHxbZGE9lkzrATY8OvrVpcu8qQh3lvPguel63V4mrnoAuOA==
"@expo/bunyan@^4.0.0":
version "4.0.0"
@@ -1279,6 +1279,15 @@
json5 "^1.0.1"
write-file-atomic "^2.3.0"
"@expo/[email protected]":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@expo/multipart-body-parser/-/multipart-body-parser-1.1.0.tgz#ba402b3047cf14ef17b4d785ec030dd6474ae6e4"
integrity sha512-XOaS79wFIJgx0J7oUzRb+kZsnZmFqGpisu0r8RPO3b0wjbW7xpWgiXmRR4RavKeGiVAPauZOi4vad7cJ3KCspg==
dependencies:
dicer "^0.3.1"
nullthrows "^1.1.1"
structured-headers "^0.4.1"
"@expo/[email protected]":
version "0.0.7"
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.0.7.tgz#4938b4e1f28e4910cd6a73b9b1674c751579025c"
@@ -3172,7 +3181,7 @@
debug "^4.0.0"
fs-extra "^9.0.1"
"@oclif/[email protected]", "@oclif/plugin-help@^5.1.11":
"@oclif/[email protected]", "@oclif/plugin-help@^5.1.12":
version "5.1.12"
resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-5.1.12.tgz#24a18631eb9b22cb55e1a3b8e4f6039fd42727e6"
integrity sha512-HvH/RubJxqCinP0vUWQLTOboT+SfjfL8h40s+PymkWaldIcXlpoRaJX50vz+SjZIs7uewZwEk8fzLqpF/BWXlg==
@@ -3493,6 +3502,13 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/[email protected]":
version "3.9.2"
resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.9.2.tgz#6ca355f96268af39bee9f9307f0ac96145639c26"
integrity sha512-VO5/X5Ij+oVgEVjg5u0IXVe3JQSKJX+Ev8C5x+0hPy0AuWyW+bF8tbajR7cPFnDGhs7pidztcac+ccrDtk5teA==
dependencies:
"@types/node" "*"
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@@ -3503,13 +3519,6 @@
resolved "https://registry.yarnpkg.com/@types/dateformat/-/dateformat-3.0.1.tgz#98d747a2e5e9a56070c6bf14e27bff56204e34cc"
integrity sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==
"@types/[email protected]":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@types/dicer/-/dicer-0.2.2.tgz#61e3a26ea4bf41cd003fd6f7adb9c78649f753de"
integrity sha512-UPLqCYey+jn5Mf57KFDwxD/7VZYDsbYUi3iyTehLFVjlbvl/JcUTPaot8uKNYLO0EoZpey+rC/s5AF3VxfeC2Q==
dependencies:
"@types/node" "*"
"@types/[email protected]":
version "7.8.1"
resolved "https://registry.yarnpkg.com/@types/envinfo/-/envinfo-7.8.1.tgz#1915df82c16d637e92146645c70db9360eb099c6"
@@ -4847,7 +4856,7 @@ cli-cursor@^3.1.0:
dependencies:
restore-cursor "^3.1.0"
cli-progress@^3.10.0:
[email protected], cli-progress@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.10.0.tgz#63fd9d6343c598c93542fdfa3563a8b59887d78a"
integrity sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw==
@@ -5627,7 +5636,7 @@ dezalgo@^1.0.0:
asap "^2.0.0"
wrappy "1"
[email protected]:
dicer@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.1.tgz#abf28921e3475bc5e801e74e0159fd94f927ba97"
integrity sha512-ObioMtXnmjYs3aRtpIJt9rgQSPCIhKVkFPip+E9GUDyWl8N435znUxK/JfNwGZJ2wnn5JKQ7Ly3vOK5Q5dylGA==
@@ -10021,13 +10030,13 @@ object.values@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
oclif@2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/oclif/-/oclif-2.6.3.tgz#77295d8342a41bfebcf13f7eba260d26ff5d92ef"
integrity sha512-t3w1GnDrZ9GnlO2WxdTRIlb5EmfLo7fkcpKo95q+03HoL4GVUNneraVymyUTYEBsvMyWcX8PbVkcMai/HhBydA==
oclif@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/oclif/-/oclif-3.0.1.tgz#f10bd702b4b2b22f849d57951882e0bd8aa9246c"
integrity sha512-DZZ53DFbDBai6doGTyVCkYWYBj9u+pXStZf8LMV5ttyr9vY9YL/d8kFpYk5OrUz8PuU81JU2Jo3ZV+4QzZFfug==
dependencies:
"@oclif/core" "^1.6.4"
"@oclif/plugin-help" "^5.1.11"
"@oclif/plugin-help" "^5.1.12"
"@oclif/plugin-not-found" "^2.3.1"
"@oclif/plugin-warn-if-update-available" "^2.0.4"
aws-sdk "^2.1069.0"
@@ -11833,7 +11842,7 @@ strong-log-transformer@^2.1.0:
minimist "^1.2.0"
through "^2.3.4"
[email protected]:
structured-headers@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/structured-headers/-/structured-headers-0.4.1.tgz#77abd9410622c6926261c09b9d16cf10592694d1"
integrity sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==