mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 13:08:11 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20b091fbfa | ||
|
|
168b6afdfa | ||
|
|
3c6320b929 | ||
|
|
4f1dc582bd | ||
|
|
daf2ef1e30 | ||
|
|
f703bd8b5a | ||
|
|
997cd7058a | ||
|
|
e896c2f6c5 | ||
|
|
3d723ca426 | ||
|
|
c17e1b3fa2 | ||
|
|
d70fc36485 |
No files matched your search
@@ -6,6 +6,23 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Introduce new format of eas.json (old eas.json will be migrated automatically). ([#537](https://github.com/expo/eas-cli/pull/537) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Validate metro config for managed workflow projects. ([#534](https://github.com/expo/eas-cli/pull/534) by [@dsokal](https://github.com/dsokal))
|
||||
- Add more filter params to `build:list`. ([#540](https://github.com/expo/eas-cli/pull/540) by [@dsokal](https://github.com/dsokal))
|
||||
- Evaluate interpolated iOS version strings for metadata. ([#541](https://github.com/expo/eas-cli/pull/541) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Validate metadata client side to print better errors. ([#542](https://github.com/expo/eas-cli/pull/542) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Fix `new` build status when building both paltforms. ([#543](https://github.com/expo/eas-cli/pull/543) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [0.22.1](https://github.com/expo/eas-cli/releases/tag/v0.22.1) - 2021-07-28
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Use env variables from `eas.json` when evaluating `app.config.js`. ([#532](https://github.com/expo/eas-cli/pull/532) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.22.1"
|
||||
"version": "0.22.2"
|
||||
}
|
||||
+49
-24
@@ -42,6 +42,7 @@ eas --help COMMAND
|
||||
* [`eas build:configure`](#eas-buildconfigure)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
||||
* [`eas config`](#eas-config)
|
||||
* [`eas credentials`](#eas-credentials)
|
||||
* [`eas device:create`](#eas-devicecreate)
|
||||
* [`eas device:list`](#eas-devicelist)
|
||||
@@ -71,7 +72,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -85,7 +86,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -99,7 +100,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -110,7 +111,7 @@ USAGE
|
||||
$ eas analytics [STATUS]
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -130,7 +131,7 @@ OPTIONS
|
||||
--[no-]wait Wait for build(s) to complete
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -141,7 +142,7 @@ USAGE
|
||||
$ eas build:cancel [BUILD_ID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -155,7 +156,7 @@ OPTIONS
|
||||
-p, --platform=(android|ios|all) Platform to configure
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -166,12 +167,21 @@ USAGE
|
||||
$ eas build:list
|
||||
|
||||
OPTIONS
|
||||
--appBuildVersion=appBuildVersion
|
||||
--appIdentifier=appIdentifier
|
||||
--appVersion=appVersion
|
||||
--buildProfile=buildProfile
|
||||
--channel=channel
|
||||
--distribution=(store|internal|simulator)
|
||||
--gitCommitHash=gitCommitHash
|
||||
--limit=limit
|
||||
--platform=(all|android|ios)
|
||||
--runtimeVersion=runtimeVersion
|
||||
--sdkVersion=sdkVersion
|
||||
--status=(new|in-queue|in-progress|errored|finished|canceled)
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -182,7 +192,22 @@ USAGE
|
||||
$ eas build:view [BUILD_ID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
Show the eas.json config
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas config
|
||||
|
||||
OPTIONS
|
||||
-p, --platform=(android|ios)
|
||||
--profile=profile
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -193,7 +218,7 @@ USAGE
|
||||
$ eas credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -204,7 +229,7 @@ USAGE
|
||||
$ eas device:create
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -218,7 +243,7 @@ OPTIONS
|
||||
--apple-team-id=apple-team-id
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -229,7 +254,7 @@ USAGE
|
||||
$ eas device:view [UDID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -257,7 +282,7 @@ USAGE
|
||||
$ eas project:info
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -271,7 +296,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -288,7 +313,7 @@ OPTIONS
|
||||
--value=value Value of the secret
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -305,7 +330,7 @@ DESCRIPTION
|
||||
Unsure where to find the secret's ID? Run eas secrets:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -316,7 +341,7 @@ USAGE
|
||||
$ eas secret:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas submit --platform=(android|ios)`
|
||||
|
||||
@@ -396,7 +421,7 @@ EXAMPLES
|
||||
and provide its App ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -415,7 +440,7 @@ OPTIONS
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -429,7 +454,7 @@ ARGUMENTS
|
||||
ID ID of the webhook to delete
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -443,7 +468,7 @@ OPTIONS
|
||||
--event=(BUILD) Event type that triggers the webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -463,7 +488,7 @@ OPTIONS
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -477,5 +502,5 @@ ARGUMENTS
|
||||
ID ID of the webhook to view
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.22.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.22.2/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
<!-- commandsstop -->
|
||||
Generated
+522
-328
@@ -2771,9 +2771,19 @@
|
||||
"name": "builds",
|
||||
"description": "(EAS Build) Builds associated with this app",
|
||||
"args": [
|
||||
{
|
||||
"name": "filter",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "BuildFilter",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"description": null,
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
@@ -2787,7 +2797,7 @@
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"description": null,
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
@@ -2801,7 +2811,7 @@
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"description": null,
|
||||
"description": "Deprecated, use filter instead",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "BuildStatus",
|
||||
@@ -2811,7 +2821,7 @@
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"description": null,
|
||||
"description": "Deprecated, use filter instead",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
@@ -3019,6 +3029,69 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "deployments",
|
||||
"description": "Deployments associated with this app",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "buildListMaxSize",
|
||||
"description": "Max number of associated builds to return",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "mostRecentlyUpdatedAt",
|
||||
"description": "Offset the query",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Deployment",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iosAppCredentials",
|
||||
"description": "iOS app credentials for the project",
|
||||
@@ -3810,6 +3883,150 @@
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "BuildFilter",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "platform",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "BuildStatus",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "distribution",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "DistributionType",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "appVersion",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "appBuildVersion",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "sdkVersion",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "runtimeVersion",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "appIdentifier",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "buildProfile",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "gitCommitHash",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "IOS",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "ANDROID",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "BuildStatus",
|
||||
@@ -3859,20 +4076,26 @@
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
"name": "DistributionType",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "IOS",
|
||||
"name": "STORE",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "ANDROID",
|
||||
"name": "INTERNAL",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SIMULATOR",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
@@ -5647,35 +5870,6 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "DistributionType",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "STORE",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "INTERNAL",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SIMULATOR",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "BuildIosEnterpriseProvisioning",
|
||||
@@ -6788,6 +6982,298 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Deployment",
|
||||
"description": "Represents a Deployment - a set of Builds with the same Runtime Version and Channel",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "runtimeVersion",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "recentBuilds",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Build",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "branchToReceive",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "UpdateBranch",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "UpdateBranch",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "appId",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updatedAt",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updates",
|
||||
"description": "",
|
||||
"args": [
|
||||
{
|
||||
"name": "offset",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "filter",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UpdatesFilter",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "mostRecentUpdateForEachPlatform",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UpdatesFilter",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "platform",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "runtimeVersions",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "IosAppCredentialsFilter",
|
||||
@@ -9141,223 +9627,6 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "UpdateBranch",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "appId",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "createdAt",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updatedAt",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updates",
|
||||
"description": "",
|
||||
"args": [
|
||||
{
|
||||
"name": "offset",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "filter",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UpdatesFilter",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "mostRecentUpdateForEachPlatform",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UpdatesFilter",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "platform",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "AppPlatform",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "runtimeVersions",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EnvironmentSecret",
|
||||
@@ -21728,81 +21997,6 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Deployment",
|
||||
"description": "Represents a Deployment - a set of Builds with the same Runtime Version and Channel",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "runtimeVersion",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "recentBuilds",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Build",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "branchToReceive",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "UpdateBranch",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "CacheControlScope",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "0.22.1",
|
||||
"version": "0.22.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
@@ -13,8 +13,8 @@
|
||||
"@expo/apple-utils": "0.0.0-alpha.24",
|
||||
"@expo/config": "3.3.42",
|
||||
"@expo/config-plugins": "3.0.3",
|
||||
"@expo/eas-build-job": "0.2.44",
|
||||
"@expo/eas-json": "^0.22.1",
|
||||
"@expo/eas-build-job": "0.2.45",
|
||||
"@expo/eas-json": "^0.22.2",
|
||||
"@expo/json-file": "8.2.25",
|
||||
"@expo/pkcs12": "0.0.4",
|
||||
"@expo/plist": "0.0.11",
|
||||
|
||||
@@ -60,6 +60,7 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
await validateAndSyncProjectConfigurationAsync(ctx.projectDir, ctx.exp);
|
||||
}
|
||||
},
|
||||
getMetadataContext: () => ({}),
|
||||
prepareJobAsync,
|
||||
sendBuildRequestAsync: async (
|
||||
appId: string,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Android, ArchiveSource, Job, sanitizeJob } from '@expo/eas-build-job';
|
||||
import { AndroidBuildProfile } from '@expo/eas-json';
|
||||
import path from 'path';
|
||||
|
||||
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
||||
@@ -13,11 +14,18 @@ interface JobData {
|
||||
credentials?: AndroidCredentials;
|
||||
}
|
||||
|
||||
const cacheDefaults = {
|
||||
disabled: false,
|
||||
customPaths: [],
|
||||
cacheDefaultPaths: true,
|
||||
};
|
||||
|
||||
export async function prepareJobAsync(
|
||||
ctx: BuildContext<Platform.ANDROID>,
|
||||
jobData: JobData
|
||||
): Promise<Job> {
|
||||
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
|
||||
const buildProfile: AndroidBuildProfile = ctx.buildProfile;
|
||||
const projectRootDirectory = path.relative(await vcs.getRootPathAsync(), ctx.projectDir) || '.';
|
||||
const { credentials } = jobData;
|
||||
const buildCredentials = credentials
|
||||
@@ -33,27 +41,27 @@ export async function prepareJobAsync(
|
||||
}
|
||||
: {};
|
||||
|
||||
const { gradleCommand } = ctx.buildProfile;
|
||||
let buildType: Android.BuildType | undefined = ctx.buildProfile.buildType;
|
||||
if (!buildType && !gradleCommand && ctx.buildProfile.distribution === 'internal') {
|
||||
let buildType = ctx.buildProfile.buildType;
|
||||
if (!buildType && !buildProfile.gradleCommand && ctx.buildProfile.distribution === 'internal') {
|
||||
buildType = Android.BuildType.APK;
|
||||
}
|
||||
|
||||
const job = {
|
||||
const job: Android.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.ANDROID,
|
||||
projectRootDirectory,
|
||||
projectArchive: jobData.projectArchive,
|
||||
builderEnvironment: {
|
||||
image: ctx.buildProfile.image,
|
||||
node: ctx.buildProfile.node,
|
||||
yarn: ctx.buildProfile.yarn,
|
||||
ndk: ctx.buildProfile.ndk,
|
||||
expoCli: ctx.buildProfile.expoCli,
|
||||
env: ctx.buildProfile.env,
|
||||
image: buildProfile.image ?? 'default',
|
||||
node: buildProfile.node,
|
||||
yarn: buildProfile.yarn,
|
||||
ndk: buildProfile.ndk,
|
||||
expoCli: buildProfile.expoCli,
|
||||
env: buildProfile.env,
|
||||
},
|
||||
cache: {
|
||||
...ctx.buildProfile.cache,
|
||||
...cacheDefaults,
|
||||
...buildProfile.cache,
|
||||
clear: ctx.clearCache,
|
||||
},
|
||||
secrets: {
|
||||
@@ -62,11 +70,11 @@ export async function prepareJobAsync(
|
||||
releaseChannel: ctx.buildProfile.releaseChannel,
|
||||
updates: { channel: ctx.buildProfile.channel },
|
||||
|
||||
gradleCommand,
|
||||
artifactPath: ctx.buildProfile.artifactPath,
|
||||
gradleCommand: buildProfile.gradleCommand,
|
||||
artifactPath: buildProfile.artifactPath,
|
||||
|
||||
username,
|
||||
buildType,
|
||||
buildType: buildProfile.developmentClient ? Android.BuildType.DEVELOPMENT_CLIENT : buildType,
|
||||
};
|
||||
|
||||
return sanitizeJob(job);
|
||||
|
||||
@@ -18,7 +18,7 @@ import vcs from '../vcs';
|
||||
import { requestedPlatformDisplayNames } from './constants';
|
||||
import { BuildContext } from './context';
|
||||
import { runLocalBuildAsync } from './local';
|
||||
import { collectMetadata } from './metadata';
|
||||
import { MetadataContext, collectMetadata } from './metadata';
|
||||
import { Platform, TrackingContext } from './types';
|
||||
import Analytics, { Event } from './utils/analytics';
|
||||
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
||||
@@ -41,6 +41,7 @@ interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
|
||||
ctx: BuildContext<TPlatform>
|
||||
): Promise<CredentialsResult<Credentials> | undefined>;
|
||||
ensureProjectConfiguredAsync(ctx: BuildContext<TPlatform>): Promise<void>;
|
||||
getMetadataContext: () => MetadataContext<TPlatform>;
|
||||
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
|
||||
sendBuildRequestAsync(appId: string, job: TJob, metadata: Metadata): Promise<BuildResult>;
|
||||
}
|
||||
@@ -52,54 +53,50 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
Credentials,
|
||||
TJob extends Job
|
||||
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
|
||||
const { ctx } = builder;
|
||||
const credentialsResult = await withAnalyticsAsync(
|
||||
async () => await builder.ensureCredentialsAsync(builder.ctx),
|
||||
async () => await builder.ensureCredentialsAsync(ctx),
|
||||
{
|
||||
successEvent: Event.GATHER_CREDENTIALS_SUCCESS,
|
||||
failureEvent: Event.GATHER_CREDENTIALS_FAIL,
|
||||
trackingCtx: builder.ctx.trackingCtx,
|
||||
trackingCtx: ctx.trackingCtx,
|
||||
}
|
||||
);
|
||||
if (!builder.ctx.skipProjectConfiguration) {
|
||||
await withAnalyticsAsync(async () => await builder.ensureProjectConfiguredAsync(builder.ctx), {
|
||||
if (!ctx.skipProjectConfiguration) {
|
||||
await withAnalyticsAsync(async () => await builder.ensureProjectConfiguredAsync(ctx), {
|
||||
successEvent: Event.CONFIGURE_PROJECT_SUCCESS,
|
||||
failureEvent: Event.CONFIGURE_PROJECT_FAIL,
|
||||
trackingCtx: builder.ctx.trackingCtx,
|
||||
trackingCtx: ctx.trackingCtx,
|
||||
});
|
||||
}
|
||||
|
||||
if (await vcs.hasUncommittedChangesAsync()) {
|
||||
Log.addNewLineIfNone();
|
||||
await reviewAndCommitChangesAsync(
|
||||
`[EAS Build] Run EAS Build for ${
|
||||
requestedPlatformDisplayNames[builder.ctx.platform as Platform]
|
||||
}`,
|
||||
{
|
||||
nonInteractive: builder.ctx.nonInteractive,
|
||||
}
|
||||
`[EAS Build] Run EAS Build for ${requestedPlatformDisplayNames[ctx.platform as Platform]}`,
|
||||
{ nonInteractive: ctx.nonInteractive }
|
||||
);
|
||||
}
|
||||
|
||||
const projectArchive = builder.ctx.local
|
||||
const projectArchive = ctx.local
|
||||
? ({
|
||||
type: ArchiveSourceType.PATH,
|
||||
path: (await makeProjectTarballAsync()).path,
|
||||
} as const)
|
||||
: ({
|
||||
type: ArchiveSourceType.S3,
|
||||
bucketKey: await uploadProjectAsync(builder.ctx),
|
||||
bucketKey: await uploadProjectAsync(ctx),
|
||||
} as const);
|
||||
|
||||
const metadata = await collectMetadata(builder.ctx, {
|
||||
credentialsSource: credentialsResult?.source,
|
||||
});
|
||||
const job = await builder.prepareJobAsync(builder.ctx, {
|
||||
const metadataContext = builder.getMetadataContext();
|
||||
const metadata = await collectMetadata(ctx, metadataContext);
|
||||
const job = await builder.prepareJobAsync(ctx, {
|
||||
projectArchive,
|
||||
credentials: credentialsResult?.credentials,
|
||||
});
|
||||
|
||||
return async () => {
|
||||
if (builder.ctx.local) {
|
||||
if (ctx.local) {
|
||||
await runLocalBuildAsync(job);
|
||||
return undefined;
|
||||
} else {
|
||||
@@ -354,7 +351,7 @@ export async function waitForBuildEndAsync(
|
||||
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 unknownState = builds.length - inQueue - inProgress - errored - finished;
|
||||
const unknownState = builds.length - newBuilds - inQueue - inProgress - errored - finished;
|
||||
spinner.text = [
|
||||
newBuilds && `Builds created: ${newBuilds}`,
|
||||
inQueue && `Builds in queue: ${inQueue}`,
|
||||
|
||||
@@ -26,24 +26,16 @@ export async function ensureProjectConfiguredAsync(
|
||||
projectDir: string,
|
||||
requestedPlatform: RequestedPlatform
|
||||
): Promise<void> {
|
||||
const platformToConfigure = await getPlatformToConfigureAsync(projectDir, requestedPlatform);
|
||||
if (!platformToConfigure) {
|
||||
if (await fs.pathExists(EasJsonReader.formatEasJsonPath(projectDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the prompt is consistent with the platforms we need to configure
|
||||
let message = 'This project is not configured to build with EAS. Set it up now?';
|
||||
if (platformToConfigure === RequestedPlatform.Ios) {
|
||||
message = 'Your iOS project is not configured to build with EAS. Set it up now?';
|
||||
} else if (platformToConfigure === RequestedPlatform.Android) {
|
||||
message = 'Your Android project is not configured to build with EAS. Set it up now?';
|
||||
}
|
||||
|
||||
const message = 'This project is not configured to build with EAS. Set it up now?';
|
||||
const confirm = await confirmAsync({ message });
|
||||
if (confirm) {
|
||||
await configureAsync({
|
||||
projectDir,
|
||||
platform: platformToConfigure,
|
||||
platform: requestedPlatform,
|
||||
});
|
||||
if (await vcs.hasUncommittedChangesAsync()) {
|
||||
error(
|
||||
@@ -103,95 +95,46 @@ export async function configureAsync(options: {
|
||||
}
|
||||
}
|
||||
|
||||
const ANDROID_MANAGED_DEFAULTS = {
|
||||
release: {
|
||||
buildType: 'app-bundle',
|
||||
},
|
||||
const MANAGED_DEFAULTS = {
|
||||
release: {},
|
||||
development: {
|
||||
buildType: 'development-client',
|
||||
distribution: 'internal',
|
||||
},
|
||||
};
|
||||
const ANDROID_GENERIC_DEFAULTS = {
|
||||
release: {
|
||||
gradleCommand: ':app:bundleRelease',
|
||||
},
|
||||
development: {
|
||||
gradleCommand: ':app:assembleDebug',
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
},
|
||||
};
|
||||
|
||||
const IOS_MANAGED_DEFAULTS = {
|
||||
release: {
|
||||
buildType: 'release',
|
||||
},
|
||||
const GENERIC_DEFAULTS = {
|
||||
release: {},
|
||||
development: {
|
||||
buildType: 'development-client',
|
||||
distribution: 'internal',
|
||||
},
|
||||
};
|
||||
|
||||
const IOS_GENERIC_DEFAULTS = {
|
||||
release: {
|
||||
schemeBuildConfiguration: 'Release',
|
||||
},
|
||||
development: {
|
||||
schemeBuildConfiguration: 'Debug',
|
||||
distribution: 'internal',
|
||||
android: {
|
||||
gradleCommand: ':app:assembleDebug',
|
||||
},
|
||||
ios: {
|
||||
buildConfiguration: 'Debug',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export async function ensureEasJsonExistsAsync(ctx: ConfigureContext): Promise<void> {
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(ctx.projectDir);
|
||||
let existingEasJson;
|
||||
|
||||
if (await fs.pathExists(easJsonPath)) {
|
||||
const reader = new EasJsonReader(ctx.projectDir, ctx.requestedPlatform);
|
||||
await reader.validateAsync();
|
||||
const reader = new EasJsonReader(ctx.projectDir);
|
||||
await reader.readAndValidateAsync();
|
||||
|
||||
existingEasJson = await reader.readRawAsync();
|
||||
Log.withTick('Validated eas.json');
|
||||
|
||||
// If we have already populated eas.json with the default fields for the
|
||||
// platform then proceed
|
||||
if (ctx.requestedPlatform !== 'all' && existingEasJson.builds[ctx.requestedPlatform]) {
|
||||
return;
|
||||
} else if (
|
||||
ctx.requestedPlatform === 'all' &&
|
||||
existingEasJson.builds.ios &&
|
||||
existingEasJson.builds.android
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldInitIOS =
|
||||
['all', 'ios'].includes(ctx.requestedPlatform) && !existingEasJson?.builds.ios;
|
||||
const shouldInitAndroid =
|
||||
['all', 'android'].includes(ctx.requestedPlatform) && !existingEasJson?.builds.android;
|
||||
|
||||
const easJson = {
|
||||
builds: {
|
||||
...existingEasJson?.builds,
|
||||
...(shouldInitAndroid
|
||||
? {
|
||||
android: ctx.hasAndroidNativeProject
|
||||
? ANDROID_GENERIC_DEFAULTS
|
||||
: ANDROID_MANAGED_DEFAULTS,
|
||||
}
|
||||
: null),
|
||||
...(shouldInitIOS
|
||||
? {
|
||||
ios: ctx.hasIosNativeProject ? IOS_GENERIC_DEFAULTS : IOS_MANAGED_DEFAULTS,
|
||||
}
|
||||
: null),
|
||||
},
|
||||
};
|
||||
const easJson =
|
||||
ctx.hasAndroidNativeProject && ctx.hasIosNativeProject
|
||||
? { build: GENERIC_DEFAULTS }
|
||||
: { build: MANAGED_DEFAULTS };
|
||||
|
||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
||||
await vcs.trackFileAsync(easJsonPath);
|
||||
Log.withTick(`${existingEasJson ? 'Updated' : 'Generated'} eas.json`);
|
||||
Log.withTick('Generated eas.json');
|
||||
}
|
||||
|
||||
enum ShouldCommitChanges {
|
||||
@@ -228,30 +171,3 @@ async function reviewAndCommitChangesAsync(
|
||||
await reviewAndCommitChangesAsync(initialCommitMessage, false);
|
||||
}
|
||||
}
|
||||
|
||||
async function getPlatformToConfigureAsync(
|
||||
projectDir: string,
|
||||
platform: RequestedPlatform
|
||||
): Promise<RequestedPlatform | null> {
|
||||
if (!(await fs.pathExists(EasJsonReader.formatEasJsonPath(projectDir)))) {
|
||||
return platform;
|
||||
}
|
||||
|
||||
const easConfig = await new EasJsonReader(projectDir, platform).readRawAsync();
|
||||
if (platform === RequestedPlatform.All) {
|
||||
if (easConfig.builds?.android && easConfig.builds?.ios) {
|
||||
return null;
|
||||
} else if (easConfig.builds?.ios) {
|
||||
return RequestedPlatform.Android;
|
||||
} else if (easConfig.builds?.android) {
|
||||
return RequestedPlatform.Ios;
|
||||
}
|
||||
} else if (
|
||||
(platform === RequestedPlatform.Android || platform === RequestedPlatform.Ios) &&
|
||||
easConfig.builds?.[platform]
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return platform;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { AndroidBuildProfile, EasConfig, IosBuildProfile } from '@expo/eas-json';
|
||||
import { AndroidBuildProfile, BuildProfile, IosBuildProfile } from '@expo/eas-json';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
@@ -13,6 +13,7 @@ import { Actor } from '../user/User';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
import { Platform, RequestedPlatform, TrackingContext } from './types';
|
||||
import Analytics, { Event } from './utils/analytics';
|
||||
|
||||
export interface ConfigureContext {
|
||||
user: Actor;
|
||||
projectDir: string;
|
||||
@@ -24,13 +25,9 @@ export interface ConfigureContext {
|
||||
hasIosNativeProject: boolean;
|
||||
}
|
||||
|
||||
type PlatformBuildProfile<T extends Platform> = T extends Platform.ANDROID
|
||||
? AndroidBuildProfile
|
||||
: IosBuildProfile;
|
||||
|
||||
export interface BuildContext<T extends Platform> {
|
||||
accountName: string;
|
||||
buildProfile: PlatformBuildProfile<T>;
|
||||
buildProfile: BuildProfile<T>;
|
||||
buildProfileName: string;
|
||||
clearCache: boolean;
|
||||
exp: ExpoConfig;
|
||||
@@ -48,8 +45,8 @@ export interface BuildContext<T extends Platform> {
|
||||
|
||||
export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfileName,
|
||||
buildProfile,
|
||||
clearCache = false,
|
||||
easConfig,
|
||||
local,
|
||||
nonInteractive = false,
|
||||
platform,
|
||||
@@ -57,20 +54,15 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
skipProjectConfiguration = false,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
clearCache: boolean;
|
||||
easConfig: EasConfig;
|
||||
local: boolean;
|
||||
nonInteractive: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
skipProjectConfiguration: boolean;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const buildProfile = easConfig.builds[platform] as PlatformBuildProfile<T> | undefined;
|
||||
if (!buildProfile) {
|
||||
throw new Error(`${platform} build profile does not exist`);
|
||||
}
|
||||
|
||||
const exp = getExpoConfig(projectDir, buildProfile.env);
|
||||
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
|
||||
|
||||
const user = await ensureLoggedInAsync();
|
||||
const accountName = getProjectAccountName(exp, user);
|
||||
@@ -126,10 +118,10 @@ function getDevClientEventProperties({
|
||||
const version = tryGetDevClientVersion(projectDir);
|
||||
if (platform === Platform.ANDROID && 'gradleCommand' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.gradleCommand?.includes('Debug'));
|
||||
} else if (platform === Platform.IOS && 'schemeBuildConfiguration' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.schemeBuildConfiguration === 'Debug');
|
||||
} else if ('buildType' in buildProfile) {
|
||||
includesDevClient = buildProfile.buildType === 'development-client';
|
||||
} else if (platform === Platform.IOS && 'buildConfiguration' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.buildConfiguration === 'Debug');
|
||||
} else if (buildProfile.developmentClient) {
|
||||
includesDevClient = true;
|
||||
} else {
|
||||
includesDevClient = false;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ describe(readBuildNumberAsync, () => {
|
||||
describe('generic project', () => {
|
||||
it('reads the build number from native code', async () => {
|
||||
const exp = initGenericProject();
|
||||
const buildNumber = await readBuildNumberAsync('/repo', exp);
|
||||
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
|
||||
expect(buildNumber).toBe('1');
|
||||
});
|
||||
});
|
||||
@@ -157,7 +157,7 @@ describe(readBuildNumberAsync, () => {
|
||||
describe('managed project', () => {
|
||||
it('reads the build number from expo config', async () => {
|
||||
const exp = initManagedProject();
|
||||
const buildNumber = await readBuildNumberAsync('/repo', exp);
|
||||
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
|
||||
expect(buildNumber).toBe('1');
|
||||
});
|
||||
});
|
||||
@@ -167,21 +167,33 @@ describe(readShortVersionAsync, () => {
|
||||
describe('generic project', () => {
|
||||
it('reads the short version from native code', async () => {
|
||||
const exp = initGenericProject();
|
||||
const shortVersion = await readShortVersionAsync('/repo', exp);
|
||||
const shortVersion = await readShortVersionAsync('/repo', exp, {});
|
||||
expect(shortVersion).toBe('1.0.0');
|
||||
});
|
||||
it('evaluates interpolated build number', async () => {
|
||||
const exp = initGenericProject({
|
||||
shortVersion: '$(CURRENT_PROJECT_VERSION)',
|
||||
});
|
||||
const buildNumber = await readShortVersionAsync('/repo', exp, {
|
||||
CURRENT_PROJECT_VERSION: '1.0.0',
|
||||
});
|
||||
expect(buildNumber).toBe('1.0.0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('managed project', () => {
|
||||
it('reads the version from app config', async () => {
|
||||
const exp = initGenericProject();
|
||||
const shortVersion = await readShortVersionAsync('/repo', exp);
|
||||
const shortVersion = await readShortVersionAsync('/repo', exp, {});
|
||||
expect(shortVersion).toBe('1.0.0');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function initGenericProject(): ExpoConfig {
|
||||
function initGenericProject({
|
||||
shortVersion = '1.0.0',
|
||||
version = '1',
|
||||
}: { shortVersion?: string; version?: string } = {}): ExpoConfig {
|
||||
vol.fromJSON(
|
||||
{
|
||||
'./app.json': JSON.stringify({
|
||||
@@ -197,9 +209,9 @@ function initGenericProject(): ExpoConfig {
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<string>${shortVersion}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>${version}</string>
|
||||
</dict>
|
||||
</plist>`,
|
||||
},
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { Ios, Job, Metadata, Workflow } from '@expo/eas-build-job';
|
||||
|
||||
import { IosCredentials } from '../../credentials/ios/types';
|
||||
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
|
||||
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier';
|
||||
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
|
||||
import { resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
|
||||
import { BuildContext } from '../context';
|
||||
import { transformMetadata } from '../graphql';
|
||||
import { IosMetadataContext } from '../metadata';
|
||||
import { Platform } from '../types';
|
||||
import { validateAndSyncProjectConfigurationAsync } from './configure';
|
||||
import { ensureIosCredentialsAsync } from './credentials';
|
||||
@@ -51,6 +53,19 @@ export async function prepareIosBuildAsync(
|
||||
buildProfile,
|
||||
});
|
||||
},
|
||||
getMetadataContext: (): IosMetadataContext => {
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
return { buildSettings: {} };
|
||||
}
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
const project = IOSConfig.XcodeUtils.getPbxproj(ctx.projectDir);
|
||||
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
|
||||
targetName: applicationTarget.targetName,
|
||||
buildConfiguration: applicationTarget.buildConfiguration,
|
||||
});
|
||||
const buildSettings = xcBuildConfiguration?.buildSettings ?? {};
|
||||
return { buildSettings };
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData<IosCredentials>
|
||||
|
||||
@@ -38,7 +38,7 @@ export async function validateAndSyncProjectConfigurationAsync({
|
||||
}): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
const { autoIncrement } = buildProfile;
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
|
||||
@@ -41,5 +41,5 @@ export async function ensureIosCredentialsAsync(
|
||||
}
|
||||
|
||||
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
|
||||
return buildCtx.buildProfile.distribution !== 'simulator';
|
||||
return !buildCtx.buildProfile.simulator;
|
||||
}
|
||||
@@ -15,6 +15,12 @@ interface JobData {
|
||||
buildScheme: string;
|
||||
}
|
||||
|
||||
const cacheDefaults = {
|
||||
disabled: false,
|
||||
customPaths: [],
|
||||
cacheDefaultPaths: true,
|
||||
};
|
||||
|
||||
export async function prepareJobAsync(
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData
|
||||
@@ -29,12 +35,12 @@ export async function prepareJobAsync(
|
||||
}
|
||||
}
|
||||
|
||||
const job = {
|
||||
const job: Ios.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.IOS,
|
||||
projectArchive: jobData.projectArchive,
|
||||
projectRootDirectory,
|
||||
distribution: ctx.buildProfile.distribution,
|
||||
distribution: ctx.buildProfile.scheme ? 'simulator' : ctx.buildProfile.distribution,
|
||||
builderEnvironment: {
|
||||
image: resolveImage(ctx),
|
||||
node: ctx.buildProfile.node,
|
||||
@@ -46,6 +52,7 @@ export async function prepareJobAsync(
|
||||
env: ctx.buildProfile.env,
|
||||
},
|
||||
cache: {
|
||||
...cacheDefaults,
|
||||
...ctx.buildProfile.cache,
|
||||
clear: ctx.clearCache,
|
||||
},
|
||||
@@ -56,13 +63,14 @@ export async function prepareJobAsync(
|
||||
updates: { channel: ctx.buildProfile.channel },
|
||||
|
||||
scheme: jobData.buildScheme,
|
||||
buildConfiguration: ctx.buildProfile.schemeBuildConfiguration,
|
||||
buildConfiguration: ctx.buildProfile.buildConfiguration,
|
||||
artifactPath: ctx.buildProfile.artifactPath,
|
||||
|
||||
buildType: ctx.buildProfile.buildType,
|
||||
buildType: ctx.buildProfile.developmentClient
|
||||
? Ios.BuildType.DEVELOPMENT_CLIENT
|
||||
: Ios.BuildType.RELEASE,
|
||||
username,
|
||||
};
|
||||
|
||||
return sanitizeJob(job);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
import semver from 'semver';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import Log from '../../log';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||
import { evaluateString } from '../utils/template';
|
||||
import { readPlistAsync, writePlistAsync } from './plist';
|
||||
|
||||
export enum BumpStrategy {
|
||||
@@ -104,12 +106,16 @@ export async function bumpVersionInAppJsonAsync({
|
||||
|
||||
export async function readShortVersionAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig
|
||||
exp: ExpoConfig,
|
||||
buildSettings: XCBuildConfiguration['buildSettings']
|
||||
): Promise<string | undefined> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
const infoPlist = await readInfoPlistAsync(projectDir);
|
||||
return infoPlist.CFBundleShortVersionString;
|
||||
return (
|
||||
infoPlist.CFBundleShortVersionString &&
|
||||
evaluateString(infoPlist.CFBundleShortVersionString, buildSettings)
|
||||
);
|
||||
} else {
|
||||
return exp.version;
|
||||
}
|
||||
@@ -117,12 +123,13 @@ export async function readShortVersionAsync(
|
||||
|
||||
export async function readBuildNumberAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig
|
||||
exp: ExpoConfig,
|
||||
buildSettings: XCBuildConfiguration['buildSettings']
|
||||
): Promise<string | undefined> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
const infoPlist = await readInfoPlistAsync(projectDir);
|
||||
return infoPlist.CFBundleVersion;
|
||||
return infoPlist.CFBundleVersion && evaluateString(infoPlist.CFBundleVersion, buildSettings);
|
||||
} else {
|
||||
return IOSConfig.Version.getBuildNumber(exp);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Metadata } from '@expo/eas-build-job';
|
||||
import { CredentialsSource, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import { Metadata, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { getApplicationIdAsync } from '../project/android/applicationId';
|
||||
import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier';
|
||||
@@ -27,22 +28,26 @@ import { isExpoUpdatesInstalled } from './utils/updates';
|
||||
*/
|
||||
const packageJSON = require('../../package.json');
|
||||
|
||||
export type MetadataContext<T extends Platform> = T extends Platform.ANDROID
|
||||
? object
|
||||
: IosMetadataContext;
|
||||
|
||||
export interface IosMetadataContext {
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}
|
||||
|
||||
export async function collectMetadata<T extends Platform>(
|
||||
ctx: BuildContext<T>,
|
||||
{
|
||||
credentialsSource,
|
||||
}: {
|
||||
credentialsSource?: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
|
||||
}
|
||||
platformContext?: MetadataContext<T>
|
||||
): Promise<Metadata> {
|
||||
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
|
||||
return {
|
||||
const metadata = {
|
||||
trackingContext: ctx.trackingCtx,
|
||||
appVersion: await resolveAppVersionAsync(ctx),
|
||||
appBuildVersion: await resolveAppBuildVersionAsync(ctx),
|
||||
appBuildVersion: await resolveAppBuildVersionAsync(ctx, platformContext),
|
||||
cliVersion: packageJSON.version,
|
||||
workflow: ctx.workflow,
|
||||
credentialsSource,
|
||||
credentialsSource: ctx.buildProfile.credentialsSource,
|
||||
sdkVersion: ctx.exp.sdkVersion,
|
||||
runtimeVersion: ctx.exp.runtimeVersion,
|
||||
...channelOrReleaseChannel,
|
||||
@@ -58,23 +63,28 @@ export async function collectMetadata<T extends Platform>(
|
||||
),
|
||||
}),
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
}
|
||||
|
||||
async function resolveAppVersionAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
ctx: BuildContext<T>,
|
||||
platformContext?: MetadataContext<T>
|
||||
): Promise<string | undefined> {
|
||||
if (ctx.platform === Platform.IOS) {
|
||||
return await readShortVersionAsync(ctx.projectDir, ctx.exp);
|
||||
const iosContext = platformContext as IosMetadataContext | undefined;
|
||||
return await readShortVersionAsync(ctx.projectDir, ctx.exp, iosContext?.buildSettings ?? {});
|
||||
} else {
|
||||
return await readVersionNameAsync(ctx.projectDir, ctx.exp);
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveAppBuildVersionAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
ctx: BuildContext<T>,
|
||||
platformContext?: MetadataContext<T>
|
||||
): Promise<string | undefined> {
|
||||
if (ctx.platform === Platform.IOS) {
|
||||
return await readBuildNumberAsync(ctx.projectDir, ctx.exp);
|
||||
const iosContext = platformContext as IosMetadataContext | undefined;
|
||||
return await readBuildNumberAsync(ctx.projectDir, ctx.exp, iosContext?.buildSettings ?? {});
|
||||
} else {
|
||||
const versionCode = await readVersionCodeAsync(ctx.projectDir, ctx.exp);
|
||||
return versionCode !== undefined ? String(versionCode) : undefined;
|
||||
|
||||
@@ -15,4 +15,10 @@ export enum BuildStatus {
|
||||
CANCELED = 'canceled',
|
||||
}
|
||||
|
||||
export enum BuildDistributionType {
|
||||
STORE = 'store',
|
||||
INTERNAL = 'internal',
|
||||
SIMULATOR = 'simulator',
|
||||
}
|
||||
|
||||
export type TrackingContext = Record<string, string | number | boolean>;
|
||||
@@ -0,0 +1,5 @@
|
||||
import template from 'lodash/template';
|
||||
|
||||
export function evaluateString(s: string, vars: Record<string, any>): string {
|
||||
return template(s, { interpolate: /\$\(([\s\S]+?)\)/g })(vars);
|
||||
}
|
||||
@@ -67,9 +67,9 @@ async function selectBuildToCancelAsync(
|
||||
let builds;
|
||||
try {
|
||||
const [newBuilds, inQueueBuilds, inProgressBuilds] = await Promise.all([
|
||||
BuildQuery.allForAppAsync(projectId, { status: BuildStatus.New }),
|
||||
BuildQuery.allForAppAsync(projectId, { status: BuildStatus.InQueue }),
|
||||
BuildQuery.allForAppAsync(projectId, { status: BuildStatus.InProgress }),
|
||||
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.New } }),
|
||||
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.InQueue } }),
|
||||
BuildQuery.allForAppAsync(projectId, { filter: { status: BuildStatus.InProgress } }),
|
||||
]);
|
||||
spinner.stop();
|
||||
builds = [...newBuilds, ...inQueueBuilds, ...inProgressBuilds];
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { EasJsonReader } from '@expo/eas-json';
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
EasJsonReader,
|
||||
hasMismatchedExtendsAsync,
|
||||
isUsingDeprecatedFormatAsync,
|
||||
migrateAsync,
|
||||
} from '@expo/eas-json';
|
||||
import { flags } from '@oclif/command';
|
||||
import { error, exit } from '@oclif/errors';
|
||||
|
||||
@@ -17,8 +23,9 @@ import {
|
||||
EAS_UNAVAILABLE_MESSAGE,
|
||||
isEasEnabledForProjectAsync,
|
||||
} from '../../project/isEasEnabledForProject';
|
||||
import { validateMetroConfigForManagedWorkflowAsync } from '../../project/metroConfig';
|
||||
import { findProjectRootAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { confirmAsync, promptAsync } from '../../prompts';
|
||||
import vcs from '../../vcs';
|
||||
|
||||
interface RawBuildFlags {
|
||||
@@ -80,27 +87,27 @@ export default class Build extends EasCommand {
|
||||
|
||||
async run(): Promise<void> {
|
||||
const { flags: rawFlags } = this.parse(Build);
|
||||
|
||||
const flags = await this.sanitizeFlagsAsync(rawFlags);
|
||||
const { requestedPlatform } = flags;
|
||||
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await vcs.ensureRepoExistsAsync();
|
||||
await ensureRepoIsCleanAsync(flags.nonInteractive);
|
||||
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
await ensureProjectConfiguredAsync(projectDir, requestedPlatform);
|
||||
|
||||
const easConfig = await new EasJsonReader(projectDir, requestedPlatform).readAsync(
|
||||
flags.profile
|
||||
);
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
const platformsToBuild = this.getPlatformsToBuild(requestedPlatform);
|
||||
|
||||
const startedBuilds: BuildFragment[] = [];
|
||||
let metroConfigValidated = false;
|
||||
for (const platform of platformsToBuild) {
|
||||
const ctx = await createBuildContextAsync({
|
||||
buildProfileName: flags.profile,
|
||||
clearCache: flags.clearCache,
|
||||
easConfig,
|
||||
buildProfile: await easJsonReader.readBuildProfileAsync(flags.profile, platform),
|
||||
local: flags.local,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
platform,
|
||||
@@ -108,6 +115,11 @@ export default class Build extends EasCommand {
|
||||
skipProjectConfiguration: flags.skipProjectConfiguration,
|
||||
});
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED && !metroConfigValidated) {
|
||||
await validateMetroConfigForManagedWorkflowAsync(ctx);
|
||||
metroConfigValidated = true;
|
||||
}
|
||||
|
||||
if (!ctx.local && !(await isEasEnabledForProjectAsync(ctx.projectId))) {
|
||||
error(EAS_UNAVAILABLE_MESSAGE, { exit: 1 });
|
||||
}
|
||||
@@ -210,3 +222,41 @@ export default class Build extends EasCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleDeprecatedEasJsonAsync(
|
||||
projectDir: string,
|
||||
nonInteractive: boolean
|
||||
): Promise<void> {
|
||||
if (await isUsingDeprecatedFormatAsync(projectDir)) {
|
||||
const hasMismatchedExtendsKeys = await hasMismatchedExtendsAsync(projectDir);
|
||||
if (nonInteractive) {
|
||||
Log.error('We detected that your eas.json is using a deprecated format.');
|
||||
Log.error(
|
||||
'We will convert it automatically if run this command without --non-interactive flag. Alternatively, you can update eas.json manually according to https://docs.expo.io/build/eas-json'
|
||||
);
|
||||
error('Unsupported eas.json format', { exit: 1 });
|
||||
}
|
||||
|
||||
const confirm = await confirmAsync({
|
||||
message:
|
||||
'We detected that your eas.json is using a deprecated format, do you want to migrate to the new format?',
|
||||
});
|
||||
if (confirm) {
|
||||
await migrateAsync(projectDir);
|
||||
if (hasMismatchedExtendsKeys) {
|
||||
Log.warn(
|
||||
'"extends" keyword can only be migrated automatically to the new format if both Android and iOS profiles extend base profiles with the same names for both platforms'
|
||||
);
|
||||
Log.warn(
|
||||
'Migration was successful, but you need to manually adjust the extend rules for your profiles'
|
||||
);
|
||||
error('Fix eas.json manually', { exit: 1 });
|
||||
}
|
||||
} else {
|
||||
Log.error(
|
||||
"Aborting, update your eas.json according to https://docs.expo.io/build/eas-json and run 'eas build' again"
|
||||
);
|
||||
error('Unsupported eas.json format', { exit: 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,13 @@ import { Command, flags } from '@oclif/command';
|
||||
import chalk from 'chalk';
|
||||
import ora from 'ora';
|
||||
|
||||
import { BuildStatus, RequestedPlatform } from '../../build/types';
|
||||
import { BuildDistributionType, BuildStatus, RequestedPlatform } from '../../build/types';
|
||||
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
|
||||
import { AppPlatform, BuildStatus as GraphQLBuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
DistributionType,
|
||||
BuildStatus as GraphQLBuildStatus,
|
||||
} from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import Log from '../../log';
|
||||
import {
|
||||
@@ -31,18 +35,36 @@ export default class BuildList extends Command {
|
||||
BuildStatus.CANCELED,
|
||||
],
|
||||
}),
|
||||
distribution: flags.enum({
|
||||
options: [
|
||||
BuildDistributionType.STORE,
|
||||
BuildDistributionType.INTERNAL,
|
||||
BuildDistributionType.SIMULATOR,
|
||||
],
|
||||
}),
|
||||
channel: flags.string(),
|
||||
appVersion: flags.string(),
|
||||
appBuildVersion: flags.string(),
|
||||
sdkVersion: flags.string(),
|
||||
runtimeVersion: flags.string(),
|
||||
appIdentifier: flags.string(),
|
||||
buildProfile: flags.string(),
|
||||
gitCommitHash: flags.string(),
|
||||
limit: flags.integer(),
|
||||
};
|
||||
|
||||
async run() {
|
||||
const { flags } = this.parse(BuildList);
|
||||
const {
|
||||
platform: requestedPlatform,
|
||||
status: buildStatus,
|
||||
distribution: buildDistribution,
|
||||
limit = 10,
|
||||
} = this.parse(BuildList).flags;
|
||||
} = flags;
|
||||
|
||||
const platform = toAppPlatform(requestedPlatform);
|
||||
const graphqlBuildStatus = toGraphQLBuildStatus(buildStatus);
|
||||
const graphqlBuildDistribution = toGraphQLBuildDistribution(buildDistribution);
|
||||
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
|
||||
@@ -54,8 +76,19 @@ export default class BuildList extends Command {
|
||||
try {
|
||||
const builds = await BuildQuery.allForAppAsync(projectId, {
|
||||
limit,
|
||||
platform,
|
||||
status: graphqlBuildStatus,
|
||||
filter: {
|
||||
platform,
|
||||
status: graphqlBuildStatus,
|
||||
distribution: graphqlBuildDistribution,
|
||||
channel: flags.channel,
|
||||
appVersion: flags.appVersion,
|
||||
appBuildVersion: flags.appBuildVersion,
|
||||
sdkVersion: flags.sdkVersion,
|
||||
runtimeVersion: flags.runtimeVersion,
|
||||
appIdentifier: flags.appIdentifier,
|
||||
buildProfile: flags.buildProfile,
|
||||
gitCommitHash: flags.gitCommitHash,
|
||||
},
|
||||
});
|
||||
|
||||
if (builds.length) {
|
||||
@@ -92,7 +125,7 @@ const toAppPlatform = (requestedPlatform?: RequestedPlatform): AppPlatform | und
|
||||
}
|
||||
};
|
||||
|
||||
const toGraphQLBuildStatus = (buildStatus: BuildStatus): GraphQLBuildStatus | undefined => {
|
||||
const toGraphQLBuildStatus = (buildStatus?: BuildStatus): GraphQLBuildStatus | undefined => {
|
||||
if (!buildStatus) {
|
||||
return undefined;
|
||||
} else if (buildStatus === BuildStatus.NEW) {
|
||||
@@ -109,3 +142,17 @@ const toGraphQLBuildStatus = (buildStatus: BuildStatus): GraphQLBuildStatus | un
|
||||
return GraphQLBuildStatus.Canceled;
|
||||
}
|
||||
};
|
||||
|
||||
const toGraphQLBuildDistribution = (
|
||||
buildDistribution?: BuildDistributionType
|
||||
): DistributionType | undefined => {
|
||||
if (buildDistribution === BuildDistributionType.STORE) {
|
||||
return DistributionType.Store;
|
||||
} else if (buildDistribution === BuildDistributionType.INTERNAL) {
|
||||
return DistributionType.Internal;
|
||||
} else if (buildDistribution === BuildDistributionType.SIMULATOR) {
|
||||
return DistributionType.Simulator;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
import { getProjectConfigDescription } from '@expo/config';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJsonReader } from '@expo/eas-json';
|
||||
import { Command, flags } from '@oclif/command';
|
||||
|
||||
import Log from '../log';
|
||||
import { getExpoConfig } from '../project/expoConfig';
|
||||
import { findProjectRootAsync } from '../project/projectUtils';
|
||||
import { selectAsync } from '../prompts';
|
||||
import { handleDeprecatedEasJsonAsync } from './build';
|
||||
export { Platform } from '@expo/eas-build-job';
|
||||
|
||||
export default class Config extends Command {
|
||||
static description = 'Show the eas.json config';
|
||||
|
||||
static flags = {
|
||||
platform: flags.enum({ char: 'p', options: ['android', 'ios'] }),
|
||||
profile: flags.string(),
|
||||
};
|
||||
|
||||
async run() {
|
||||
const { flags } = this.parse(Config);
|
||||
const { platform: maybePlatform, profile: maybeProfile } = flags as {
|
||||
platform?: Platform;
|
||||
profile?: string;
|
||||
};
|
||||
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
await handleDeprecatedEasJsonAsync(projectDir, false);
|
||||
|
||||
const reader = new EasJsonReader(projectDir);
|
||||
const profileName =
|
||||
maybeProfile ??
|
||||
(await selectAsync(
|
||||
'Select build profile',
|
||||
(
|
||||
await reader.getBuildProfileNamesAsync()
|
||||
).map(profileName => ({
|
||||
title: profileName,
|
||||
value: profileName,
|
||||
}))
|
||||
));
|
||||
const platform =
|
||||
maybePlatform ??
|
||||
(await selectAsync('Select platform', [
|
||||
{
|
||||
title: 'Android',
|
||||
value: Platform.ANDROID,
|
||||
},
|
||||
{
|
||||
title: 'iOS',
|
||||
value: Platform.IOS,
|
||||
},
|
||||
]));
|
||||
|
||||
const profile = await reader.readBuildProfileAsync(profileName, platform);
|
||||
const config = getExpoConfig(projectDir, { env: profile.env, isPublicConfig: true });
|
||||
|
||||
Log.log(getProjectConfigDescription(projectDir));
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(config, null, 2));
|
||||
Log.newLine();
|
||||
Log.newLine();
|
||||
Log.log(`Build profile "${profileName}" from eas.json for platform ${platform}`);
|
||||
Log.newLine();
|
||||
Log.log(JSON.stringify(profile, null, 2));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { CredentialsSource, IosDistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import { CredentialsSource, DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
|
||||
import { CommonIosAppCredentialsFragment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
@@ -18,7 +18,7 @@ import { isAdHocProfile } from './utils/provisioningProfile';
|
||||
interface Options {
|
||||
app: App;
|
||||
targets: Target[];
|
||||
distribution: IosDistributionType;
|
||||
distribution: DistributionType;
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
iosCapabilitiesOptions?: IosCapabilitiesOptions;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IosDistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import { DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SetupTargetBuildCredentials } from './SetupTargetBuildCredentials';
|
||||
interface Options {
|
||||
app: App;
|
||||
targets: Target[];
|
||||
distribution: IosDistributionType;
|
||||
distribution: DistributionType;
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
iosCapabilitiesOptions?: IosCapabilitiesOptions;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IosDistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import { DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
|
||||
import {
|
||||
IosDistributionType as GraphQLIosDistributionType,
|
||||
@@ -14,7 +14,7 @@ import { SetupProvisioningProfile } from './SetupProvisioningProfile';
|
||||
|
||||
interface Options {
|
||||
app: GraphQLAppLookupParams;
|
||||
distribution: IosDistributionType;
|
||||
distribution: DistributionType;
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
iosCapabilitiesOptions?: IosCapabilitiesOptions;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface App {
|
||||
|
||||
export interface Target {
|
||||
targetName: string;
|
||||
buildConfiguration?: string;
|
||||
bundleIdentifier: string;
|
||||
parentBundleIdentifier?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EasJsonReader, IosBuildProfile } from '@expo/eas-json';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { IosBuildProfile } from '@expo/eas-json';
|
||||
import assert from 'assert';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -284,9 +285,10 @@ export class ManageIos {
|
||||
}
|
||||
|
||||
const app = { account, projectName: ctx.exp.slug };
|
||||
const easJsonReader = new EasJsonReader(ctx.projectDir, 'ios');
|
||||
const easConfig = await new SelectBuildProfileFromEasJson(easJsonReader).runAsync(ctx);
|
||||
const buildProfile = nullthrows(easConfig.builds.ios, 'iOS build profile must be defined');
|
||||
const buildProfile = await new SelectBuildProfileFromEasJson(
|
||||
ctx.projectDir,
|
||||
Platform.IOS
|
||||
).runAsync(ctx);
|
||||
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
|
||||
{
|
||||
projectDir: ctx.projectDir,
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { EasConfig, EasJsonReader } from '@expo/eas-json';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
|
||||
|
||||
import Log from '../../log';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { Context } from '../context';
|
||||
|
||||
export class SelectBuildProfileFromEasJson {
|
||||
constructor(private easJsonReader: EasJsonReader) {}
|
||||
async runAsync(ctx: Context): Promise<EasConfig> {
|
||||
export class SelectBuildProfileFromEasJson<T extends Platform> {
|
||||
private easJsonReader: EasJsonReader;
|
||||
|
||||
constructor(private projectDir: string, private platform: T) {
|
||||
this.easJsonReader = new EasJsonReader(projectDir);
|
||||
}
|
||||
|
||||
async runAsync(ctx: Context): Promise<BuildProfile<T>> {
|
||||
const profileName = await this.getProfileNameFromEasConfigAsync(ctx);
|
||||
const easConfig = await this.easJsonReader.readAsync(profileName);
|
||||
const easConfig = await this.easJsonReader.readBuildProfileAsync<T>(profileName, this.platform);
|
||||
Log.succeed(`Using build profile: ${profileName}`);
|
||||
return easConfig;
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ export class SelectIosDistributionTypeGraphqlFromBuildProfile {
|
||||
constructor(private buildProfile: IosBuildProfile) {}
|
||||
|
||||
async runAsync(ctx: Context): Promise<IosDistributionTypeGraphql> {
|
||||
const { distribution } = this.buildProfile;
|
||||
if (distribution === 'simulator') {
|
||||
const { distribution, simulator } = this.buildProfile;
|
||||
if (simulator) {
|
||||
throw new Error('A simulator distribution does not require credentials to be configured.');
|
||||
} else if (distribution === 'store') {
|
||||
return IosDistributionTypeGraphql.AppStore;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import { SelectIosDistributionTypeGraphqlFromBuildProfile } from '../SelectIosDi
|
||||
describe('SelectIosDistributionTypeGraphqlFromBuildProfile', () => {
|
||||
it('errors with a simulator distribution', async () => {
|
||||
const buildProfile = {
|
||||
distribution: 'simulator',
|
||||
simulator: true,
|
||||
} as IosBuildProfile;
|
||||
|
||||
const ctx = createCtxMock({
|
||||
|
||||
+68
-44
@@ -436,6 +436,8 @@ export type App = Project & {
|
||||
buildOrBuildJobs: Array<BuildOrBuildJob>;
|
||||
/** EAS Submissions associated with this app */
|
||||
submissions: Array<Submission>;
|
||||
/** Deployments associated with this app */
|
||||
deployments: Array<Deployment>;
|
||||
/** iOS app credentials for the project */
|
||||
iosAppCredentials: Array<IosAppCredentials>;
|
||||
/** Android app credentials for the project */
|
||||
@@ -487,6 +489,7 @@ export type App = Project & {
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppBuildsArgs = {
|
||||
filter?: Maybe<BuildFilter>;
|
||||
offset: Scalars['Int'];
|
||||
limit: Scalars['Int'];
|
||||
status?: Maybe<BuildStatus>;
|
||||
@@ -517,6 +520,14 @@ export type AppSubmissionsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppDeploymentsArgs = {
|
||||
limit: Scalars['Int'];
|
||||
buildListMaxSize: Scalars['Int'];
|
||||
mostRecentlyUpdatedAt?: Maybe<Scalars['DateTime']>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppIosAppCredentialsArgs = {
|
||||
filter?: Maybe<IosAppCredentialsFilter>;
|
||||
@@ -605,6 +616,25 @@ export enum AppPrivacy {
|
||||
Hidden = 'HIDDEN'
|
||||
}
|
||||
|
||||
export type BuildFilter = {
|
||||
platform?: Maybe<AppPlatform>;
|
||||
status?: Maybe<BuildStatus>;
|
||||
distribution?: Maybe<DistributionType>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
appVersion?: Maybe<Scalars['String']>;
|
||||
appBuildVersion?: Maybe<Scalars['String']>;
|
||||
sdkVersion?: Maybe<Scalars['String']>;
|
||||
runtimeVersion?: Maybe<Scalars['String']>;
|
||||
appIdentifier?: Maybe<Scalars['String']>;
|
||||
buildProfile?: Maybe<Scalars['String']>;
|
||||
gitCommitHash?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum AppPlatform {
|
||||
Ios = 'IOS',
|
||||
Android = 'ANDROID'
|
||||
}
|
||||
|
||||
export enum BuildStatus {
|
||||
New = 'NEW',
|
||||
InQueue = 'IN_QUEUE',
|
||||
@@ -614,9 +644,10 @@ export enum BuildStatus {
|
||||
Canceled = 'CANCELED'
|
||||
}
|
||||
|
||||
export enum AppPlatform {
|
||||
Ios = 'IOS',
|
||||
Android = 'ANDROID'
|
||||
export enum DistributionType {
|
||||
Store = 'STORE',
|
||||
Internal = 'INTERNAL',
|
||||
Simulator = 'SIMULATOR'
|
||||
}
|
||||
|
||||
/** Represents an EAS Build */
|
||||
@@ -835,12 +866,6 @@ export type BuildMetrics = {
|
||||
buildDuration?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export enum DistributionType {
|
||||
Store = 'STORE',
|
||||
Internal = 'INTERNAL',
|
||||
Simulator = 'SIMULATOR'
|
||||
}
|
||||
|
||||
export enum BuildIosEnterpriseProvisioning {
|
||||
Adhoc = 'ADHOC',
|
||||
Universal = 'UNIVERSAL'
|
||||
@@ -985,6 +1010,39 @@ export type SubmissionFilter = {
|
||||
status?: Maybe<SubmissionStatus>;
|
||||
};
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type Deployment = {
|
||||
__typename?: 'Deployment';
|
||||
id?: Maybe<Scalars['String']>;
|
||||
runtimeVersion?: Maybe<Scalars['String']>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
recentBuilds?: Maybe<Array<Maybe<Build>>>;
|
||||
branchToReceive?: Maybe<UpdateBranch>;
|
||||
};
|
||||
|
||||
export type UpdateBranch = {
|
||||
__typename?: 'UpdateBranch';
|
||||
id: Scalars['ID'];
|
||||
appId: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
updates: Array<Update>;
|
||||
mostRecentUpdateForEachPlatform: Array<Update>;
|
||||
};
|
||||
|
||||
|
||||
export type UpdateBranchUpdatesArgs = {
|
||||
offset: Scalars['Int'];
|
||||
limit: Scalars['Int'];
|
||||
filter?: Maybe<UpdatesFilter>;
|
||||
};
|
||||
|
||||
export type UpdatesFilter = {
|
||||
platform?: Maybe<AppPlatform>;
|
||||
runtimeVersions?: Maybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
export type IosAppCredentialsFilter = {
|
||||
appleAppIdentifierId?: Maybe<Scalars['String']>;
|
||||
};
|
||||
@@ -1240,29 +1298,6 @@ export type UpdateChannelUpdateBranchesArgs = {
|
||||
limit: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type UpdateBranch = {
|
||||
__typename?: 'UpdateBranch';
|
||||
id: Scalars['ID'];
|
||||
appId: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
updates: Array<Update>;
|
||||
mostRecentUpdateForEachPlatform: Array<Update>;
|
||||
};
|
||||
|
||||
|
||||
export type UpdateBranchUpdatesArgs = {
|
||||
offset: Scalars['Int'];
|
||||
limit: Scalars['Int'];
|
||||
filter?: Maybe<UpdatesFilter>;
|
||||
};
|
||||
|
||||
export type UpdatesFilter = {
|
||||
platform?: Maybe<AppPlatform>;
|
||||
runtimeVersions?: Maybe<Array<Scalars['String']>>;
|
||||
};
|
||||
|
||||
export type EnvironmentSecret = {
|
||||
__typename?: 'EnvironmentSecret';
|
||||
id: Scalars['ID'];
|
||||
@@ -3424,16 +3459,6 @@ export type DeleteWebhookResult = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||
export type Deployment = {
|
||||
__typename?: 'Deployment';
|
||||
id?: Maybe<Scalars['String']>;
|
||||
runtimeVersion?: Maybe<Scalars['String']>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
recentBuilds?: Maybe<Array<Maybe<Build>>>;
|
||||
branchToReceive?: Maybe<UpdateBranch>;
|
||||
};
|
||||
|
||||
export enum CacheControlScope {
|
||||
Public = 'PUBLIC',
|
||||
Private = 'PRIVATE'
|
||||
@@ -4868,8 +4893,7 @@ export type GetAllBuildsForAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
offset: Scalars['Int'];
|
||||
limit: Scalars['Int'];
|
||||
status?: Maybe<BuildStatus>;
|
||||
platform?: Maybe<AppPlatform>;
|
||||
filter?: Maybe<BuildFilter>;
|
||||
}>;
|
||||
|
||||
|
||||
|
||||
@@ -8,16 +8,28 @@ import {
|
||||
BuildStatus,
|
||||
BuildsByIdQuery,
|
||||
BuildsByIdQueryVariables,
|
||||
DistributionType,
|
||||
GetAllBuildsForAppQuery,
|
||||
GetAllBuildsForAppQueryVariables,
|
||||
} from '../generated';
|
||||
import { BuildFragmentNode } from '../types/Build';
|
||||
|
||||
type Filters = {
|
||||
platform?: AppPlatform;
|
||||
status?: BuildStatus;
|
||||
type BuildsQuery = {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
filter?: {
|
||||
platform?: AppPlatform;
|
||||
status?: BuildStatus;
|
||||
distribution?: DistributionType;
|
||||
channel?: string;
|
||||
appVersion?: string;
|
||||
appBuildVersion?: string;
|
||||
sdkVersion?: string;
|
||||
runtimeVersion?: string;
|
||||
appIdentifier?: string;
|
||||
buildProfile?: string;
|
||||
gitCommitHash?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const BuildQuery = {
|
||||
@@ -52,7 +64,7 @@ export const BuildQuery = {
|
||||
|
||||
async allForAppAsync(
|
||||
appId: string,
|
||||
{ limit = 10, offset = 0, status, platform }: Filters
|
||||
{ limit = 10, offset = 0, filter }: BuildsQuery
|
||||
): Promise<BuildFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
@@ -63,13 +75,12 @@ export const BuildQuery = {
|
||||
$appId: String!
|
||||
$offset: Int!
|
||||
$limit: Int!
|
||||
$status: BuildStatus
|
||||
$platform: AppPlatform
|
||||
$filter: BuildFilter
|
||||
) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
builds(offset: $offset, limit: $limit, status: $status, platform: $platform) {
|
||||
builds(offset: $offset, limit: $limit, filter: $filter) {
|
||||
id
|
||||
...BuildFragment
|
||||
}
|
||||
@@ -78,7 +89,7 @@ export const BuildQuery = {
|
||||
}
|
||||
${print(BuildFragmentNode)}
|
||||
`,
|
||||
{ appId, offset, limit, status, platform }
|
||||
{ appId, offset, limit, filter }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { ExpoConfig, getConfig } from '@expo/config';
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
|
||||
export function getExpoConfig(projectDir: string, env: Env = {}): ExpoConfig {
|
||||
interface Options {
|
||||
env?: Env;
|
||||
isPublicConfig?: boolean;
|
||||
}
|
||||
|
||||
export function getExpoConfig(projectDir: string, opts: Options = {}): ExpoConfig {
|
||||
const originalProcessEnv: NodeJS.ProcessEnv = process.env;
|
||||
try {
|
||||
process.env = {
|
||||
...process.env,
|
||||
...env,
|
||||
...opts.env,
|
||||
};
|
||||
const { exp } = getConfig(projectDir, {
|
||||
skipSDKVersionRequirement: true,
|
||||
...(opts.isPublicConfig ? { isPublicConfig: true } : {}),
|
||||
});
|
||||
return exp;
|
||||
} finally {
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function resolveXcodeBuildContextAsync(
|
||||
return {
|
||||
buildScheme,
|
||||
buildConfiguration:
|
||||
buildProfile.schemeBuildConfiguration ??
|
||||
buildProfile.buildConfiguration ??
|
||||
(await IOSConfig.BuildScheme.getArchiveBuildConfigurationForSchemeAsync(
|
||||
projectDir,
|
||||
buildScheme
|
||||
|
||||
@@ -21,12 +21,14 @@ export async function resolveTargetsAsync(
|
||||
result.push({
|
||||
targetName: applicationTarget.name,
|
||||
bundleIdentifier,
|
||||
buildConfiguration,
|
||||
});
|
||||
|
||||
if (applicationTarget.dependencies && applicationTarget.dependencies.length > 0) {
|
||||
for (const dependency of applicationTarget.dependencies) {
|
||||
result.push({
|
||||
targetName: dependency.name,
|
||||
buildConfiguration,
|
||||
bundleIdentifier: await getBundleIdentifierAsync(projectDir, exp, {
|
||||
targetName: dependency.name,
|
||||
buildConfiguration,
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { exit } from '@oclif/errors';
|
||||
import chalk from 'chalk';
|
||||
import type MetroConfig from 'metro-config';
|
||||
import resolveFrom from 'resolve-from';
|
||||
|
||||
import { BuildContext } from '../build/context';
|
||||
import { Platform } from '../build/types';
|
||||
import Log, { learnMore } from '../log';
|
||||
import { confirmAsync } from '../prompts';
|
||||
|
||||
export async function validateMetroConfigForManagedWorkflowAsync(ctx: BuildContext<Platform>) {
|
||||
if (!(await configExistsAsync(ctx.projectDir))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metroConfig = await loadConfigAsync(ctx.projectDir);
|
||||
const hasHashAssetFilesPlugin = metroConfig.transformer?.assetPlugins?.find((plugin: string) =>
|
||||
plugin.match(/expo-asset\/tools\/hashAssetFiles/)
|
||||
);
|
||||
if (!hasHashAssetFilesPlugin) {
|
||||
Log.warn(
|
||||
`It looks like that you are using a custom ${chalk.bold(
|
||||
'metro.config.js'
|
||||
)} that does not extend ${chalk.bold('@expo/metro-config')}.`
|
||||
);
|
||||
Log.warn(
|
||||
'This can result in unexpected and hard to debug issues, like missing assets in the production bundle.'
|
||||
);
|
||||
Log.warn(`We recommend you to abort, fix the ${chalk.bold('metro.config.js')}, and try again.`);
|
||||
Log.warn(
|
||||
learnMore('https://docs.expo.io/guides/customizing-metro/', {
|
||||
learnMoreMessage: 'Learn more on customizing Metro',
|
||||
})
|
||||
);
|
||||
|
||||
const shouldAbort = await confirmAsync({
|
||||
message: 'Would you like to abort?',
|
||||
});
|
||||
if (shouldAbort) {
|
||||
Log.log('Aborting...');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
|
||||
const resolvedPath = resolveFrom.silent(projectDir, 'metro-config');
|
||||
if (!resolvedPath) {
|
||||
throw new MetroConfigPackageMissingError(
|
||||
'Missing package "metro-config" in the project. ' +
|
||||
'This usually means `react-native` is not installed. ' +
|
||||
'Please verify that dependencies in package.json include "react-native" ' +
|
||||
'and run `yarn` or `npm install`.'
|
||||
);
|
||||
}
|
||||
return require(resolvedPath);
|
||||
}
|
||||
|
||||
async function configExistsAsync(projectRoot: string): Promise<boolean> {
|
||||
try {
|
||||
const MetroConfig = importMetroConfigFromProject(projectRoot);
|
||||
const result = await MetroConfig.resolveConfig(undefined, projectRoot);
|
||||
return !result.isEmpty;
|
||||
} catch (err) {
|
||||
if (err instanceof MetroConfigPackageMissingError) {
|
||||
return false;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConfigAsync(projectDir: string): Promise<MetroConfig.ConfigT> {
|
||||
const MetroConfig = importMetroConfigFromProject(projectDir);
|
||||
return await MetroConfig.loadConfig({ cwd: projectDir }, {});
|
||||
}
|
||||
|
||||
class MetroConfigPackageMissingError extends Error {}
|
||||
@@ -61,7 +61,7 @@ export async function setProjectIdAsync(
|
||||
projectDir: string,
|
||||
options: { env?: Env } = {}
|
||||
): Promise<ExpoConfig | undefined> {
|
||||
const exp = getExpoConfig(projectDir, options.env);
|
||||
const exp = getExpoConfig(projectDir, options);
|
||||
|
||||
const privacy = toAppPrivacy(exp.privacy);
|
||||
const projectId = await ensureProjectExistsAsync({
|
||||
|
||||
@@ -31,9 +31,11 @@ export async function getLatestBuildForSubmissionAsync(
|
||||
appId: string
|
||||
): Promise<BuildFragmentWithArtifact | null> {
|
||||
const builds = await BuildQuery.allForAppAsync(appId, {
|
||||
platform,
|
||||
status: BuildStatus.Finished,
|
||||
limit: 1,
|
||||
filter: {
|
||||
platform,
|
||||
status: BuildStatus.Finished,
|
||||
},
|
||||
});
|
||||
|
||||
if (builds.length < 1) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with the eas.json",
|
||||
"version": "0.22.1",
|
||||
"version": "0.22.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/eas-build-job": "0.2.44",
|
||||
"@expo/eas-build-job": "0.2.45",
|
||||
"@hapi/joi": "17.1.1",
|
||||
"fs-extra": "9.0.1",
|
||||
"tslib": "2.3.0"
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,190 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
AndroidBuildProfile,
|
||||
BuildProfile,
|
||||
EasConfig,
|
||||
IosBuildProfile,
|
||||
} from './DeprecatedConfig.types';
|
||||
import { EasJsonSchema, schemaBuildProfileMap } from './DeprecatedEasJsonSchema';
|
||||
|
||||
export interface EasJson {
|
||||
builds: {
|
||||
android?: { [key: string]: BuildProfilePreValidation };
|
||||
ios?: { [key: string]: BuildProfilePreValidation };
|
||||
};
|
||||
}
|
||||
|
||||
interface BuildProfilePreValidation {
|
||||
workflow?: Workflow;
|
||||
extends?: string;
|
||||
}
|
||||
|
||||
function intersect<T>(setA: Set<T>, setB: Set<T>): Set<T> {
|
||||
return new Set([...setA].filter(i => setB.has(i)));
|
||||
}
|
||||
|
||||
export class EasJsonReader {
|
||||
constructor(private projectDir: string, private platform: 'android' | 'ios' | 'all') {}
|
||||
|
||||
/**
|
||||
* Return build profile names for a particular platform.
|
||||
* If platform is 'all', return common build profiles for all platforms
|
||||
*/
|
||||
public async getBuildProfileNamesAsync(): Promise<string[]> {
|
||||
const easJson = await this.readRawAsync();
|
||||
if (this.platform === 'android') {
|
||||
return Object.keys(easJson?.builds?.android ?? {});
|
||||
} else if (this.platform === 'ios') {
|
||||
return Object.keys(easJson?.builds?.ios ?? {});
|
||||
} else {
|
||||
const intersectingProfileNames = intersect(
|
||||
new Set(Object.keys(easJson?.builds?.ios ?? {})),
|
||||
new Set(Object.keys(easJson?.builds?.android ?? {}))
|
||||
);
|
||||
return Array.from(intersectingProfileNames);
|
||||
}
|
||||
}
|
||||
|
||||
public async readAsync(buildProfileName: string): Promise<EasConfig> {
|
||||
const easJson = await this.readRawAsync();
|
||||
|
||||
let androidConfig;
|
||||
if (['android', 'all'].includes(this.platform)) {
|
||||
androidConfig = this.validateBuildProfile<AndroidBuildProfile>(
|
||||
Platform.ANDROID,
|
||||
buildProfileName,
|
||||
easJson.builds?.android || {}
|
||||
);
|
||||
}
|
||||
let iosConfig;
|
||||
if (['ios', 'all'].includes(this.platform)) {
|
||||
iosConfig = this.validateBuildProfile<IosBuildProfile>(
|
||||
Platform.IOS,
|
||||
buildProfileName,
|
||||
easJson.builds?.ios || {}
|
||||
);
|
||||
}
|
||||
return {
|
||||
builds: {
|
||||
...(androidConfig ? { android: androidConfig } : {}),
|
||||
...(iosConfig ? { ios: iosConfig } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public async validateAsync(): Promise<void> {
|
||||
const easJson = await this.readRawAsync();
|
||||
|
||||
const androidProfiles = easJson.builds?.android ?? {};
|
||||
for (const name of Object.keys(androidProfiles)) {
|
||||
try {
|
||||
this.validateBuildProfile(Platform.ANDROID, name, androidProfiles);
|
||||
} catch (err) {
|
||||
err.msg = `Failed to validate Android build profile "${name}"\n${err.msg}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const iosProfiles = easJson.builds?.ios ?? {};
|
||||
for (const name of Object.keys(iosProfiles)) {
|
||||
try {
|
||||
this.validateBuildProfile(Platform.IOS, name, iosProfiles);
|
||||
} catch (err) {
|
||||
err.msg = `Failed to validate iOS build profile "${name}"\n${err.msg}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async readRawAsync(): Promise<EasJson> {
|
||||
const rawFile = await fs.readFile(path.join(this.projectDir, 'eas.json'), 'utf8');
|
||||
const json = JSON.parse(rawFile);
|
||||
|
||||
const { value, error } = EasJsonSchema.validate(json, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(`eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private validateBuildProfile<T extends BuildProfile>(
|
||||
platform: Platform,
|
||||
buildProfileName: string,
|
||||
buildProfiles: Record<string, BuildProfilePreValidation>
|
||||
): T {
|
||||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles);
|
||||
const schema = schemaBuildProfileMap[platform];
|
||||
const { value, error } = schema.validate(buildProfile, {
|
||||
stripUnknown: true,
|
||||
convert: true,
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(
|
||||
`Object "${platform}.${buildProfileName}" in eas.json is not valid [${error.toString()}]`
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private resolveBuildProfile(
|
||||
platform: Platform,
|
||||
buildProfileName: string,
|
||||
buildProfiles: Record<string, BuildProfilePreValidation>,
|
||||
depth: number = 0
|
||||
): Record<string, any> {
|
||||
if (depth >= 2) {
|
||||
throw new Error(
|
||||
'Too long chain of build profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
}
|
||||
const buildProfile = buildProfiles[buildProfileName];
|
||||
if (!buildProfile) {
|
||||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`);
|
||||
}
|
||||
const { extends: baseProfileName, ...buildProfileRest } = buildProfile;
|
||||
if (baseProfileName) {
|
||||
return deepMerge(
|
||||
this.resolveBuildProfile(platform, baseProfileName, buildProfiles, depth + 1),
|
||||
buildProfileRest
|
||||
);
|
||||
} else {
|
||||
return buildProfileRest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isObject(value: any): boolean {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
export function deepMerge(
|
||||
base: Record<string, any>,
|
||||
update: Record<string, any>
|
||||
): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
Object.keys(base).forEach(key => {
|
||||
const oldValue = base[key];
|
||||
const newValue = update[key];
|
||||
if (isObject(newValue) && isObject(oldValue)) {
|
||||
result[key] = deepMerge(oldValue, newValue);
|
||||
} else if (newValue !== undefined) {
|
||||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue;
|
||||
} else {
|
||||
result[key] = isObject(oldValue) ? deepMerge({}, oldValue) : oldValue;
|
||||
}
|
||||
});
|
||||
Object.keys(update).forEach(key => {
|
||||
const newValue = update[key];
|
||||
if (result[key] === undefined) {
|
||||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Android, Ios } from '@expo/eas-build-job';
|
||||
import Joi, { CustomHelpers } from '@hapi/joi';
|
||||
|
||||
const semverSchemaCheck = (value: any, helpers: CustomHelpers) => {
|
||||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) {
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`${value} is not a valid version`);
|
||||
}
|
||||
};
|
||||
|
||||
const AndroidBuilderEnvironmentSchema = Joi.object({
|
||||
image: Joi.string()
|
||||
.valid(...Android.builderBaseImages)
|
||||
.default('default'),
|
||||
node: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
ndk: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)).default({}),
|
||||
});
|
||||
|
||||
const IosBuilderEnvironmentSchema = Joi.object({
|
||||
image: Joi.string().valid(...Ios.builderBaseImages),
|
||||
node: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
bundler: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
fastlane: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
cocoapods: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)).default({}),
|
||||
});
|
||||
|
||||
const CacheSchema = Joi.object({
|
||||
disabled: Joi.boolean().default(false),
|
||||
key: Joi.string().max(128),
|
||||
cacheDefaultPaths: Joi.boolean().default(true),
|
||||
customPaths: Joi.array().items(Joi.string()).default([]),
|
||||
});
|
||||
|
||||
const AndroidSchema = Joi.object({
|
||||
workflow: Joi.string(),
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
releaseChannel: Joi.string(),
|
||||
channel: Joi.string(),
|
||||
distribution: Joi.string().valid('store', 'internal').default('store'),
|
||||
cache: CacheSchema.default(),
|
||||
withoutCredentials: Joi.boolean().default(false),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
gradleCommand: Joi.string(),
|
||||
|
||||
buildType: Joi.alternatives().conditional('distribution', {
|
||||
is: 'internal',
|
||||
then: Joi.string().valid('apk', 'development-client'),
|
||||
otherwise: Joi.string().valid('apk', 'app-bundle', 'development-client'),
|
||||
}),
|
||||
}).concat(AndroidBuilderEnvironmentSchema);
|
||||
|
||||
const IosSchema = Joi.object({
|
||||
workflow: Joi.string(),
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
releaseChannel: Joi.string(),
|
||||
channel: Joi.string(),
|
||||
distribution: Joi.string().valid('store', 'internal', 'simulator').default('store'),
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
autoIncrement: Joi.alternatives()
|
||||
.try(Joi.boolean(), Joi.string().valid('version', 'buildNumber'))
|
||||
.default(false),
|
||||
cache: CacheSchema.default(),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
scheme: Joi.string(),
|
||||
schemeBuildConfiguration: Joi.string(),
|
||||
|
||||
buildType: Joi.string().valid('release', 'development-client'),
|
||||
}).concat(IosBuilderEnvironmentSchema);
|
||||
|
||||
export const schemaBuildProfileMap: Record<string, Joi.Schema> = {
|
||||
android: AndroidSchema,
|
||||
ios: IosSchema,
|
||||
};
|
||||
|
||||
export const EasJsonSchema = Joi.object({
|
||||
builds: Joi.object({
|
||||
android: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.object({}).unknown(true) // profile is validated further only if build is for that platform
|
||||
),
|
||||
ios: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.object({}).unknown(true) // profile is validated further only if build is for that platform
|
||||
),
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Android, Cache, Ios, Platform } from '@expo/eas-build-job';
|
||||
|
||||
export enum CredentialsSource {
|
||||
LOCAL = 'local',
|
||||
REMOTE = 'remote',
|
||||
}
|
||||
|
||||
export type DistributionType = 'store' | 'internal';
|
||||
|
||||
export type IosEnterpriseProvisioning = 'adhoc' | 'universal';
|
||||
|
||||
export type VersionAutoIncrement = boolean | 'version' | 'buildNumber';
|
||||
|
||||
export interface CommonBuildProfile {
|
||||
credentialsSource: CredentialsSource;
|
||||
distribution: DistributionType;
|
||||
cache?: Omit<Cache, 'clear'>;
|
||||
releaseChannel?: string;
|
||||
channel?: string;
|
||||
developmentClient?: boolean;
|
||||
|
||||
node?: string;
|
||||
yarn?: string;
|
||||
expoCli?: string;
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface AndroidBuildProfile extends CommonBuildProfile {
|
||||
withoutCredentials?: boolean;
|
||||
image?: Android.BuilderEnvironment['image'];
|
||||
ndk?: string;
|
||||
|
||||
buildType?: Android.BuildType.APK | Android.BuildType.APP_BUNDLE;
|
||||
|
||||
gradleCommand?: string;
|
||||
artifactPath?: string;
|
||||
}
|
||||
|
||||
export interface IosBuildProfile extends CommonBuildProfile {
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
autoIncrement?: VersionAutoIncrement;
|
||||
simulator?: boolean;
|
||||
image?: Ios.BuilderEnvironment['image'];
|
||||
bundler?: string;
|
||||
fastlane?: string;
|
||||
cocoapods?: string;
|
||||
|
||||
artifactPath?: string;
|
||||
scheme?: string;
|
||||
buildConfiguration?: string;
|
||||
}
|
||||
|
||||
export type BuildProfile<TPlatform extends Platform = Platform> = TPlatform extends Platform.ANDROID
|
||||
? AndroidBuildProfile
|
||||
: TPlatform extends Platform.IOS
|
||||
? IosBuildProfile
|
||||
: TPlatform extends Platform
|
||||
? AndroidBuildProfile | IosBuildProfile
|
||||
: never;
|
||||
|
||||
export interface RawBuildProfile extends Partial<CommonBuildProfile> {
|
||||
extends?: string;
|
||||
android?: Partial<AndroidBuildProfile>;
|
||||
ios?: Partial<IosBuildProfile>;
|
||||
}
|
||||
|
||||
export interface EasJson {
|
||||
build: {
|
||||
[profile: string]: RawBuildProfile;
|
||||
};
|
||||
}
|
||||
@@ -1,107 +1,75 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { AndroidBuildProfile, BuildProfile, EasConfig, IosBuildProfile } from './Config.types';
|
||||
import { EasJsonSchema, schemaBuildProfileMap } from './EasJsonSchema';
|
||||
import { BuildProfile, CredentialsSource, EasJson, RawBuildProfile } from './EasJson.types';
|
||||
import { EasJsonSchema, MinimalEasJsonSchema } from './EasJsonSchema';
|
||||
|
||||
interface EasJson {
|
||||
builds: {
|
||||
android?: { [key: string]: BuildProfilePreValidation };
|
||||
ios?: { [key: string]: BuildProfilePreValidation };
|
||||
};
|
||||
interface EasJsonPreValidation {
|
||||
build: { [profile: string]: object };
|
||||
}
|
||||
|
||||
interface BuildProfilePreValidation {
|
||||
workflow?: Workflow;
|
||||
extends?: string;
|
||||
}
|
||||
|
||||
function intersect<T>(setA: Set<T>, setB: Set<T>): Set<T> {
|
||||
return new Set([...setA].filter(i => setB.has(i)));
|
||||
}
|
||||
const defaults = {
|
||||
distribution: 'store',
|
||||
credentialsSource: CredentialsSource.REMOTE,
|
||||
} as const;
|
||||
|
||||
export class EasJsonReader {
|
||||
public static formatEasJsonPath(projectDir: string) {
|
||||
return path.join(projectDir, 'eas.json');
|
||||
}
|
||||
|
||||
constructor(private projectDir: string, private platform: 'android' | 'ios' | 'all') {}
|
||||
constructor(private projectDir: string) {}
|
||||
|
||||
/**
|
||||
* Return build profile names for a particular platform.
|
||||
* If platform is 'all', return common build profiles for all platforms
|
||||
*/
|
||||
public async getBuildProfileNamesAsync(): Promise<string[]> {
|
||||
const easJson = await this.readRawAsync();
|
||||
if (this.platform === 'android') {
|
||||
return Object.keys(easJson?.builds?.android ?? {});
|
||||
} else if (this.platform === 'ios') {
|
||||
return Object.keys(easJson?.builds?.ios ?? {});
|
||||
return Object.keys(easJson?.build ?? {});
|
||||
}
|
||||
|
||||
public async readBuildProfileAsync<T extends Platform>(
|
||||
buildProfileName: string,
|
||||
platform: T
|
||||
): Promise<BuildProfile<T>> {
|
||||
const easJson = await this.readAndValidateAsync();
|
||||
this.ensureProfileExists(easJson, buildProfileName);
|
||||
const {
|
||||
android: resolvedAndroidSpecificValues,
|
||||
ios: resolvedIosSpecificValues,
|
||||
...resolvedProfile
|
||||
} = this.resolveBuildProfile(easJson, buildProfileName);
|
||||
if (platform === Platform.ANDROID) {
|
||||
const profileWithoutDefaults = profileMerge(
|
||||
resolvedProfile,
|
||||
resolvedAndroidSpecificValues ?? {}
|
||||
);
|
||||
return profileMerge(defaults, profileWithoutDefaults) as BuildProfile<T>;
|
||||
} else if (platform === Platform.IOS) {
|
||||
const profileWithoutDefaults = profileMerge(resolvedProfile, resolvedIosSpecificValues ?? {});
|
||||
return profileMerge(defaults, profileWithoutDefaults) as BuildProfile<T>;
|
||||
} else {
|
||||
const intersectingProfileNames = intersect(
|
||||
new Set(Object.keys(easJson?.builds?.ios ?? {})),
|
||||
new Set(Object.keys(easJson?.builds?.android ?? {}))
|
||||
);
|
||||
return Array.from(intersectingProfileNames);
|
||||
throw new Error(`Unknown platform ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async readAsync(buildProfileName: string): Promise<EasConfig> {
|
||||
public async readAndValidateAsync(): Promise<EasJson> {
|
||||
const easJson = await this.readRawAsync();
|
||||
const { value, error } = EasJsonSchema.validate(easJson, {
|
||||
allowUnknown: false,
|
||||
convert: true,
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
let androidConfig;
|
||||
if (['android', 'all'].includes(this.platform)) {
|
||||
androidConfig = this.validateBuildProfile<AndroidBuildProfile>(
|
||||
Platform.ANDROID,
|
||||
buildProfileName,
|
||||
easJson.builds?.android || {}
|
||||
);
|
||||
if (error) {
|
||||
throw new Error(`eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
let iosConfig;
|
||||
if (['ios', 'all'].includes(this.platform)) {
|
||||
iosConfig = this.validateBuildProfile<IosBuildProfile>(
|
||||
Platform.IOS,
|
||||
buildProfileName,
|
||||
easJson.builds?.ios || {}
|
||||
);
|
||||
}
|
||||
return {
|
||||
builds: {
|
||||
...(androidConfig ? { android: androidConfig } : {}),
|
||||
...(iosConfig ? { ios: iosConfig } : {}),
|
||||
},
|
||||
};
|
||||
return value as EasJson;
|
||||
}
|
||||
|
||||
public async validateAsync(): Promise<void> {
|
||||
const easJson = await this.readRawAsync();
|
||||
|
||||
const androidProfiles = easJson.builds?.android ?? {};
|
||||
for (const name of Object.keys(androidProfiles)) {
|
||||
try {
|
||||
this.validateBuildProfile(Platform.ANDROID, name, androidProfiles);
|
||||
} catch (err) {
|
||||
err.msg = `Failed to validate Android build profile "${name}"\n${err.msg}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
const iosProfiles = easJson.builds?.ios ?? {};
|
||||
for (const name of Object.keys(iosProfiles)) {
|
||||
try {
|
||||
this.validateBuildProfile(Platform.IOS, name, iosProfiles);
|
||||
} catch (err) {
|
||||
err.msg = `Failed to validate iOS build profile "${name}"\n${err.msg}`;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async readRawAsync(): Promise<EasJson> {
|
||||
public async readRawAsync(): Promise<EasJsonPreValidation> {
|
||||
const rawFile = await fs.readFile(EasJsonReader.formatEasJsonPath(this.projectDir), 'utf8');
|
||||
const json = JSON.parse(rawFile);
|
||||
|
||||
const { value, error } = EasJsonSchema.validate(json, {
|
||||
const { value, error } = MinimalEasJsonSchema.validate(json, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
@@ -111,79 +79,54 @@ export class EasJsonReader {
|
||||
return value;
|
||||
}
|
||||
|
||||
private validateBuildProfile<T extends BuildProfile>(
|
||||
platform: Platform,
|
||||
buildProfileName: string,
|
||||
buildProfiles: Record<string, BuildProfilePreValidation>
|
||||
): T {
|
||||
const buildProfile = this.resolveBuildProfile(platform, buildProfileName, buildProfiles);
|
||||
const schema = schemaBuildProfileMap[platform];
|
||||
const { value, error } = schema.validate(buildProfile, {
|
||||
stripUnknown: true,
|
||||
convert: true,
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(
|
||||
`Object "${platform}.${buildProfileName}" in eas.json is not valid [${error.toString()}]`
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private resolveBuildProfile(
|
||||
platform: Platform,
|
||||
buildProfileName: string,
|
||||
buildProfiles: Record<string, BuildProfilePreValidation>,
|
||||
easJson: EasJson,
|
||||
profileName: string,
|
||||
depth: number = 0
|
||||
): Record<string, any> {
|
||||
): RawBuildProfile {
|
||||
if (depth >= 2) {
|
||||
throw new Error(
|
||||
'Too long chain of build profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
}
|
||||
const buildProfile = buildProfiles[buildProfileName];
|
||||
const buildProfile = easJson.build[profileName];
|
||||
if (!buildProfile) {
|
||||
throw new Error(`There is no profile named ${buildProfileName} for platform ${platform}`);
|
||||
throw new Error(`There is no profile named ${profileName}`);
|
||||
}
|
||||
const { extends: baseProfileName, ...buildProfileRest } = buildProfile;
|
||||
if (baseProfileName) {
|
||||
return deepMerge(
|
||||
this.resolveBuildProfile(platform, baseProfileName, buildProfiles, depth + 1),
|
||||
return profileMerge(
|
||||
this.resolveBuildProfile(easJson, baseProfileName, depth + 1),
|
||||
buildProfileRest
|
||||
);
|
||||
} else {
|
||||
return buildProfileRest;
|
||||
}
|
||||
}
|
||||
|
||||
private ensureProfileExists(easJson: EasJson, profileName: string) {
|
||||
if (!easJson.build || !easJson.build[profileName]) {
|
||||
throw new Error(`There is no profile named ${profileName} in eas.json.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isObject(value: any): boolean {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
|
||||
export function deepMerge(
|
||||
base: Record<string, any>,
|
||||
update: Record<string, any>
|
||||
): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
Object.keys(base).forEach(key => {
|
||||
const oldValue = base[key];
|
||||
const newValue = update[key];
|
||||
if (isObject(newValue) && isObject(oldValue)) {
|
||||
result[key] = deepMerge(oldValue, newValue);
|
||||
} else if (newValue !== undefined) {
|
||||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue;
|
||||
} else {
|
||||
result[key] = isObject(oldValue) ? deepMerge({}, oldValue) : oldValue;
|
||||
}
|
||||
});
|
||||
Object.keys(update).forEach(key => {
|
||||
const newValue = update[key];
|
||||
if (result[key] === undefined) {
|
||||
result[key] = isObject(newValue) ? deepMerge({}, newValue) : newValue;
|
||||
}
|
||||
});
|
||||
export function profileMerge(base: RawBuildProfile, update: RawBuildProfile): RawBuildProfile {
|
||||
const result = {
|
||||
...base,
|
||||
...update,
|
||||
};
|
||||
if (base.env && update.env) {
|
||||
result.env = {
|
||||
...base.env,
|
||||
...update.env,
|
||||
};
|
||||
}
|
||||
if (base.android && update.android) {
|
||||
result.android = profileMerge(base.android, update.android);
|
||||
}
|
||||
if (base.ios && update.ios) {
|
||||
result.ios = profileMerge(base.ios, update.ios);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Android, Ios } from '@expo/eas-build-job';
|
||||
import Joi, { CustomHelpers } from '@hapi/joi';
|
||||
import Joi from '@hapi/joi';
|
||||
|
||||
const semverSchemaCheck = (value: any, helpers: CustomHelpers) => {
|
||||
const semverSchemaCheck = (value: any) => {
|
||||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) {
|
||||
return value;
|
||||
} else {
|
||||
@@ -9,87 +9,72 @@ const semverSchemaCheck = (value: any, helpers: CustomHelpers) => {
|
||||
}
|
||||
};
|
||||
|
||||
const AndroidBuilderEnvironmentSchema = Joi.object({
|
||||
image: Joi.string()
|
||||
.valid(...Android.builderBaseImages)
|
||||
.default('default'),
|
||||
node: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
ndk: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)).default({}),
|
||||
});
|
||||
|
||||
const IosBuilderEnvironmentSchema = Joi.object({
|
||||
image: Joi.string().valid(...Ios.builderBaseImages),
|
||||
node: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
bundler: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
fastlane: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
cocoapods: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)).default({}),
|
||||
});
|
||||
|
||||
const CacheSchema = Joi.object({
|
||||
disabled: Joi.boolean().default(false),
|
||||
disabled: Joi.boolean(),
|
||||
key: Joi.string().max(128),
|
||||
cacheDefaultPaths: Joi.boolean().default(true),
|
||||
customPaths: Joi.array().items(Joi.string()).default([]),
|
||||
cacheDefaultPaths: Joi.boolean(),
|
||||
customPaths: Joi.array().items(Joi.string()),
|
||||
});
|
||||
|
||||
const AndroidSchema = Joi.object({
|
||||
workflow: Joi.string(),
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
const CommonBuildProfileSchema = Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
cache: CacheSchema,
|
||||
releaseChannel: Joi.string(),
|
||||
channel: Joi.string(),
|
||||
distribution: Joi.string().valid('store', 'internal').default('store'),
|
||||
cache: CacheSchema.default(),
|
||||
withoutCredentials: Joi.boolean().default(false),
|
||||
developmentClient: Joi.boolean(),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
gradleCommand: Joi.string(),
|
||||
node: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
expoCli: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
|
||||
});
|
||||
|
||||
buildType: Joi.alternatives().conditional('distribution', {
|
||||
is: 'internal',
|
||||
then: Joi.string().valid('apk', 'development-client'),
|
||||
otherwise: Joi.string().valid('apk', 'app-bundle', 'development-client'),
|
||||
}),
|
||||
}).concat(AndroidBuilderEnvironmentSchema);
|
||||
const AndroidBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
withoutCredentials: Joi.boolean(),
|
||||
|
||||
const IosSchema = Joi.object({
|
||||
workflow: Joi.string(),
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
releaseChannel: Joi.string(),
|
||||
channel: Joi.string(),
|
||||
distribution: Joi.string().valid('store', 'internal', 'simulator').default('store'),
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
autoIncrement: Joi.alternatives()
|
||||
.try(Joi.boolean(), Joi.string().valid('version', 'buildNumber'))
|
||||
.default(false),
|
||||
cache: CacheSchema.default(),
|
||||
image: Joi.string().valid(...Android.builderBaseImages),
|
||||
ndk: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
scheme: Joi.string(),
|
||||
schemeBuildConfiguration: Joi.string(),
|
||||
artifactPath: Joi.string(),
|
||||
gradleCommand: Joi.string(),
|
||||
|
||||
buildType: Joi.string().valid('release', 'development-client'),
|
||||
}).concat(IosBuilderEnvironmentSchema);
|
||||
buildType: Joi.string().valid('apk', 'app-bundle'),
|
||||
})
|
||||
);
|
||||
|
||||
export const schemaBuildProfileMap: Record<string, Joi.Schema> = {
|
||||
android: AndroidSchema,
|
||||
ios: IosSchema,
|
||||
};
|
||||
const IosBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
autoIncrement: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
Joi.string().valid('version', 'buildNumber')
|
||||
),
|
||||
|
||||
image: Joi.string().valid(...Ios.builderBaseImages),
|
||||
bundler: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
fastlane: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
cocoapods: Joi.string().empty(null).custom(semverSchemaCheck),
|
||||
|
||||
artifactPath: Joi.string(),
|
||||
scheme: Joi.string(),
|
||||
buildConfiguration: Joi.string(),
|
||||
})
|
||||
);
|
||||
|
||||
const EasJsonBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
extends: Joi.string(),
|
||||
android: AndroidBuildProfileSchema,
|
||||
ios: IosBuildProfileSchema,
|
||||
})
|
||||
);
|
||||
|
||||
export const MinimalEasJsonSchema = Joi.object({
|
||||
build: Joi.object().pattern(Joi.string(), Joi.object()),
|
||||
});
|
||||
|
||||
export const EasJsonSchema = Joi.object({
|
||||
builds: Joi.object({
|
||||
android: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.object({}).unknown(true) // profile is validated further only if build is for that platform
|
||||
),
|
||||
ios: Joi.object().pattern(
|
||||
Joi.string(),
|
||||
Joi.object({}).unknown(true) // profile is validated further only if build is for that platform
|
||||
),
|
||||
}),
|
||||
build: Joi.object().pattern(Joi.string(), EasJsonBuildProfileSchema),
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
@@ -10,314 +11,268 @@ beforeEach(async () => {
|
||||
await fs.mkdirp('/project');
|
||||
});
|
||||
|
||||
test('minimal valid android eas.json', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const easJson = await reader.readAsync('release');
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
env: {},
|
||||
withoutCredentials: false,
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
image: 'default',
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
});
|
||||
|
||||
test('minimal valid ios eas.json', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
ios: {
|
||||
release: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'ios');
|
||||
const easJson = await reader.readAsync('release');
|
||||
expect({
|
||||
builds: {
|
||||
ios: {
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
autoIncrement: false,
|
||||
env: {},
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
});
|
||||
|
||||
test('minimal valid eas.json for both platforms', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: {},
|
||||
},
|
||||
ios: {
|
||||
release: {},
|
||||
},
|
||||
build: {
|
||||
release: {},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'all');
|
||||
const easJson = await reader.readAsync('release');
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
env: {},
|
||||
withoutCredentials: false,
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
image: 'default',
|
||||
},
|
||||
ios: {
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
autoIncrement: false,
|
||||
env: {},
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
});
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readBuildProfileAsync('release', Platform.IOS);
|
||||
const androidProfile = await reader.readBuildProfileAsync('release', Platform.ANDROID);
|
||||
|
||||
test('valid eas.json with both platform, but reading only android', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
ios: {
|
||||
release: {},
|
||||
},
|
||||
android: {
|
||||
release: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const easJson = await reader.readAsync('release');
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
env: {},
|
||||
withoutCredentials: false,
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
image: 'default',
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(androidProfile);
|
||||
|
||||
expect({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(iosProfile);
|
||||
});
|
||||
|
||||
test('valid eas.json for development client builds', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
ios: {
|
||||
release: {},
|
||||
debug: { buildType: 'development-client' },
|
||||
},
|
||||
android: {
|
||||
release: {},
|
||||
debug: {
|
||||
withoutCredentials: false,
|
||||
buildType: 'development-client',
|
||||
build: {
|
||||
release: {},
|
||||
debug: {
|
||||
developmentClient: true,
|
||||
android: {
|
||||
withoutCredentials: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'all');
|
||||
const easJson = await reader.readAsync('debug');
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readBuildProfileAsync('debug', Platform.IOS);
|
||||
const androidProfile = await reader.readBuildProfileAsync('debug', Platform.ANDROID);
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
env: {},
|
||||
image: 'default',
|
||||
withoutCredentials: false,
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
buildType: 'development-client',
|
||||
},
|
||||
ios: {
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
autoIncrement: false,
|
||||
env: {},
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
buildType: 'development-client',
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
developmentClient: true,
|
||||
withoutCredentials: true,
|
||||
}).toEqual(androidProfile);
|
||||
|
||||
expect({
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
developmentClient: true,
|
||||
}).toEqual(iosProfile);
|
||||
});
|
||||
|
||||
test('valid generic profile for internal distribution on Android', async () => {
|
||||
test('valid profile for internal distribution on Android', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
internal: {
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const easJson = await reader.readAsync('internal');
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
build: {
|
||||
internal: {
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
env: {},
|
||||
withoutCredentials: false,
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
image: 'default',
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
});
|
||||
|
||||
test('valid managed profile for internal distribution on Android', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
internal: {
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const easJson = await reader.readAsync('internal');
|
||||
const reader = new EasJsonReader('/project');
|
||||
const profile = await reader.readBuildProfileAsync('internal', Platform.ANDROID);
|
||||
expect({
|
||||
builds: {
|
||||
android: {
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
withoutCredentials: false,
|
||||
env: {},
|
||||
cache: { disabled: false, cacheDefaultPaths: true, customPaths: [] },
|
||||
image: 'default',
|
||||
},
|
||||
},
|
||||
}).toEqual(easJson);
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(profile);
|
||||
});
|
||||
|
||||
test('invalid managed profile for internal distribution on Android', async () => {
|
||||
test('valid profile extending other profile', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
internal: {
|
||||
buildType: 'aab',
|
||||
distribution: 'internal',
|
||||
build: {
|
||||
base: {
|
||||
node: '12.0.0',
|
||||
},
|
||||
extension: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
node: '13.0.0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync('base', Platform.ANDROID);
|
||||
const extendedProfile = await reader.readBuildProfileAsync('extension', Platform.ANDROID);
|
||||
expect({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
node: '12.0.0',
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '13.0.0',
|
||||
}).toEqual(extendedProfile);
|
||||
});
|
||||
|
||||
test('valid profile extending other profile with platform specific envs', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
base: {
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'base',
|
||||
},
|
||||
},
|
||||
extension: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
env: {
|
||||
PROFILE: 'extension',
|
||||
},
|
||||
android: {
|
||||
env: {
|
||||
PROFILE: 'extension:android',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const promise = reader.readAsync('internal');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'Object "android.internal" in eas.json is not valid [ValidationError: "buildType" must be one of [apk, development-client]]'
|
||||
);
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync('base', Platform.ANDROID);
|
||||
const extendedAndroidProfile = await reader.readBuildProfileAsync('extension', Platform.ANDROID);
|
||||
const extendedIosProfile = await reader.readBuildProfileAsync('extension', Platform.IOS);
|
||||
expect({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'base',
|
||||
},
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'extension:android',
|
||||
},
|
||||
}).toEqual(extendedAndroidProfile);
|
||||
expect({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'extension',
|
||||
},
|
||||
}).toEqual(extendedIosProfile);
|
||||
});
|
||||
|
||||
test('invalid eas.json with missing preset', async () => {
|
||||
test('valid profile extending other profile with platform specific caching', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: {},
|
||||
build: {
|
||||
base: {
|
||||
cache: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
extension: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
cache: {
|
||||
key: 'extend-key',
|
||||
},
|
||||
android: {
|
||||
cache: {
|
||||
cacheDefaultPaths: false,
|
||||
customPaths: ['somefakepath'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const promise = reader.readAsync('debug');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'There is no profile named debug for platform android'
|
||||
);
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync('base', Platform.ANDROID);
|
||||
const extendedAndroidProfile = await reader.readBuildProfileAsync('extension', Platform.ANDROID);
|
||||
const extendedIosProfile = await reader.readBuildProfileAsync('extension', Platform.IOS);
|
||||
expect({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
cache: {
|
||||
disabled: true,
|
||||
},
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
cache: {
|
||||
cacheDefaultPaths: false,
|
||||
customPaths: ['somefakepath'],
|
||||
},
|
||||
}).toEqual(extendedAndroidProfile);
|
||||
expect({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
|
||||
cache: {
|
||||
key: 'extend-key',
|
||||
},
|
||||
}).toEqual(extendedIosProfile);
|
||||
});
|
||||
|
||||
test('invalid eas.json when using buildType for wrong platform', async () => {
|
||||
test('valid eas.json with missing profile', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: { buildType: 'archive' },
|
||||
},
|
||||
build: {
|
||||
release: {},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const promise = reader.readAsync('release');
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync('debug', Platform.ANDROID);
|
||||
await expect(promise).rejects.toThrowError('There is no profile named debug in eas.json.');
|
||||
});
|
||||
|
||||
test('invalid eas.json when using wrong buildType', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
release: { android: { buildType: 'archive' } },
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync('release', Platform.ANDROID);
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'Object "android.release" in eas.json is not valid [ValidationError: "buildType" must be one of [apk, app-bundle, development-client]]'
|
||||
'eas.json is not valid [ValidationError: "build.release.android.buildType" must be one of [apk, app-bundle]]'
|
||||
);
|
||||
});
|
||||
|
||||
test('empty json', async () => {
|
||||
await fs.writeJson('/project/eas.json', {});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const promise = reader.readAsync('release');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'There is no profile named release for platform android'
|
||||
);
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync('release', Platform.ANDROID);
|
||||
await expect(promise).rejects.toThrowError('There is no profile named release in eas.json.');
|
||||
});
|
||||
|
||||
test('invalid semver value', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: { node: '12.0.0-alpha' },
|
||||
},
|
||||
build: {
|
||||
release: { node: '12.0.0-alpha' },
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project', 'android');
|
||||
const promise = reader.readAsync('release');
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync('release', Platform.ANDROID);
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'Object "android.release" in eas.json is not valid [ValidationError: "node" failed custom validation because 12.0.0-alpha is not a valid version]'
|
||||
'eas.json is not valid [ValidationError: "build.release.node" failed custom validation because 12.0.0-alpha is not a valid version]'
|
||||
);
|
||||
});
|
||||
|
||||
test('get profile names', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
builds: {
|
||||
android: {
|
||||
release: { node: '12.0.0-alpha' },
|
||||
blah: { node: '12.0.0-alpha' },
|
||||
},
|
||||
ios: {
|
||||
test: { node: '12.0.0-alpha' },
|
||||
blah: { node: '12.0.0-alpha' },
|
||||
},
|
||||
build: {
|
||||
release: { node: '12.0.0-alpha' },
|
||||
blah: { node: '12.0.0-alpha' },
|
||||
},
|
||||
});
|
||||
|
||||
const androidReader = new EasJsonReader('/project', 'android');
|
||||
const androidProfileNames = await androidReader.getBuildProfileNamesAsync();
|
||||
expect(androidProfileNames.sort()).toEqual(['release', 'blah'].sort());
|
||||
|
||||
const iosReader = new EasJsonReader('/project', 'ios');
|
||||
const iosProfileNames = await iosReader.getBuildProfileNamesAsync();
|
||||
expect(iosProfileNames.sort()).toEqual(['test', 'blah'].sort());
|
||||
|
||||
const allReader = new EasJsonReader('/project', 'all');
|
||||
const allProfileNames = await allReader.getBuildProfileNamesAsync();
|
||||
expect(allProfileNames.sort()).toEqual(['blah'].sort());
|
||||
const reader = new EasJsonReader('/project');
|
||||
const allProfileNames = await reader.getBuildProfileNamesAsync();
|
||||
expect(allProfileNames.sort()).toEqual(['blah', 'release'].sort());
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
import { deepMerge } from '../EasJsonReader';
|
||||
|
||||
test('deepMerge: case 1', async () => {
|
||||
const result = deepMerge({ b: 'test', c: 'test2' }, { b: 'test1' });
|
||||
expect(result).toEqual({ b: 'test1', c: 'test2' });
|
||||
});
|
||||
|
||||
test('deepMerge: case 2', async () => {
|
||||
const result = deepMerge({ a: { b: 'test', c: 'test2' } }, { a: { b: 'test1' } });
|
||||
expect(result).toEqual({ a: { b: 'test1', c: 'test2' } });
|
||||
});
|
||||
|
||||
test('deepMerge: case 3', async () => {
|
||||
const result = deepMerge({ a: { b: 'test' } }, { a: { b: 'test1', d: 'test3' } });
|
||||
expect(result).toEqual({ a: { b: 'test1', d: 'test3' } });
|
||||
});
|
||||
|
||||
test('deepMerge: case 4', async () => {
|
||||
const result = deepMerge({ a: 'test' }, { a: { b: 'test1', d: 'test3' } });
|
||||
expect(result).toEqual({ a: { b: 'test1', d: 'test3' } });
|
||||
});
|
||||
|
||||
test('deepMerge: case 5', async () => {
|
||||
const result = deepMerge({ a: { b: 'test1', d: 'test3' } }, { a: 'test' });
|
||||
expect(result).toEqual({ a: 'test' });
|
||||
});
|
||||
|
||||
test('deepMerge: case 5', async () => {
|
||||
const result = deepMerge({ a: { b: 'test1', d: 'test3' } }, { a: 0 });
|
||||
expect(result).toEqual({ a: 0 });
|
||||
});
|
||||
|
||||
test('deepMerge: case 6', async () => {
|
||||
const result = deepMerge({ a: { b: 'test1', d: 'test3' } }, { a: null });
|
||||
expect(result).toEqual({ a: null });
|
||||
});
|
||||
|
||||
test('deepMerge: case 7', async () => {
|
||||
const result = deepMerge({ a: { b: 'test1', d: 'test3' } }, { a: undefined });
|
||||
expect(result).toEqual({ a: { b: 'test1', d: 'test3' } });
|
||||
});
|
||||
|
||||
test('deepMerge: case 8', async () => {
|
||||
const result = deepMerge({ a: 'test' }, { a: 0 });
|
||||
expect(result).toEqual({ a: 0 });
|
||||
});
|
||||
|
||||
test('deepMerge: case 9', async () => {
|
||||
const result = deepMerge({ a: 'test' }, { a: null });
|
||||
expect(result).toEqual({ a: null });
|
||||
});
|
||||
|
||||
test('deepMerge: case 10', async () => {
|
||||
const result = deepMerge({ a: 'test' }, { a: undefined });
|
||||
expect(result).toEqual({ a: 'test' });
|
||||
});
|
||||
@@ -0,0 +1,297 @@
|
||||
import { EasJson as DeprecatedEasJson } from '../DeprecatedEasJsonReader';
|
||||
import { migrateProfile } from '../migrate';
|
||||
|
||||
test('migration for default manged eas.json', async () => {
|
||||
const easJson = {
|
||||
builds: {
|
||||
android: {
|
||||
release: {
|
||||
buildType: 'app-bundle',
|
||||
},
|
||||
development: {
|
||||
buildType: 'development-client',
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
release: {
|
||||
buildType: 'release',
|
||||
},
|
||||
development: {
|
||||
buildType: 'development-client',
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as DeprecatedEasJson;
|
||||
expect(migrateProfile(easJson, 'release')).toEqual({
|
||||
android: {
|
||||
buildType: 'app-bundle',
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'development')).toEqual({
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
});
|
||||
});
|
||||
|
||||
test('migration for default generic eas.json', async () => {
|
||||
const easJson = {
|
||||
builds: {
|
||||
android: {
|
||||
release: {
|
||||
gradleCommand: ':app:bundleRelease',
|
||||
},
|
||||
development: {
|
||||
gradleCommand: ':app:assembleDebug',
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
release: {
|
||||
schemeBuildConfiguration: 'Release',
|
||||
},
|
||||
development: {
|
||||
schemeBuildConfiguration: 'Debug',
|
||||
distribution: 'internal',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as DeprecatedEasJson;
|
||||
expect(migrateProfile(easJson, 'release')).toEqual({
|
||||
android: {
|
||||
gradleCommand: ':app:bundleRelease',
|
||||
},
|
||||
ios: {
|
||||
buildConfiguration: 'Release',
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'development')).toEqual({
|
||||
distribution: 'internal',
|
||||
android: {
|
||||
gradleCommand: ':app:assembleDebug',
|
||||
},
|
||||
ios: {
|
||||
buildConfiguration: 'Debug',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('migration for example manged eas.json', async () => {
|
||||
const easJson = {
|
||||
builds: {
|
||||
android: {
|
||||
base: {
|
||||
image: 'default',
|
||||
env: {
|
||||
EXAMPLE_ENV: 'example value',
|
||||
},
|
||||
},
|
||||
release: {
|
||||
extends: 'base',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
buildType: 'app-bundle',
|
||||
},
|
||||
staging: {
|
||||
extends: 'base',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
distribution: 'internal',
|
||||
buildType: 'apk',
|
||||
},
|
||||
debug: {
|
||||
extends: 'base',
|
||||
withoutCredentials: true,
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
distribution: 'internal',
|
||||
buildType: 'development-client',
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
base: {
|
||||
image: 'latest',
|
||||
node: '12.13.0',
|
||||
yarn: '1.22.5',
|
||||
},
|
||||
release: {
|
||||
extends: 'base',
|
||||
buildType: 'release',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
},
|
||||
inhouse: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
enterpriseProvisioning: 'universal',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
adhoc: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
client: {
|
||||
extends: 'adhoc',
|
||||
buildType: 'development-client',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as DeprecatedEasJson;
|
||||
expect(migrateProfile(easJson, 'base')).toEqual({
|
||||
android: {
|
||||
env: {
|
||||
EXAMPLE_ENV: 'example value',
|
||||
},
|
||||
image: 'default',
|
||||
},
|
||||
ios: {
|
||||
image: 'latest',
|
||||
node: '12.13.0',
|
||||
yarn: '1.22.5',
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'release')).toEqual({
|
||||
extends: 'base',
|
||||
android: {
|
||||
buildType: 'app-bundle',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'staging')).toEqual({
|
||||
android: {
|
||||
buildType: 'apk',
|
||||
distribution: 'internal',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'inhouse')).toEqual({
|
||||
ios: {
|
||||
distribution: 'internal',
|
||||
enterpriseProvisioning: 'universal',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('migration for example generic eas.json', async () => {
|
||||
const easJson = {
|
||||
builds: {
|
||||
android: {
|
||||
base: {
|
||||
image: 'ubuntu-18.04-android-30-ndk-r19c',
|
||||
ndk: '21.4.7075529',
|
||||
env: {
|
||||
EXAMPLE_ENV: 'example value',
|
||||
},
|
||||
},
|
||||
release: {
|
||||
extends: 'base',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
gradleCommand: ':app:bundleRelease',
|
||||
},
|
||||
staging: {
|
||||
extends: 'base',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
distribution: 'internal',
|
||||
gradleCommand: ':app:assembleRelease',
|
||||
},
|
||||
debug: {
|
||||
extends: 'base',
|
||||
withoutCredentials: true,
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
distribution: 'internal',
|
||||
gradleCommand: ':app:assembleDebug',
|
||||
},
|
||||
},
|
||||
ios: {
|
||||
base: {
|
||||
image: 'latest',
|
||||
node: '12.13.0',
|
||||
yarn: '1.22.5',
|
||||
},
|
||||
release: {
|
||||
extends: 'base',
|
||||
schemeBuildConfiguration: 'Release',
|
||||
scheme: 'testapp',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
},
|
||||
inhouse: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
enterpriseProvisioning: 'universal',
|
||||
scheme: 'testapp-enterprise',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
adhoc: {
|
||||
extends: 'base',
|
||||
distribution: 'internal',
|
||||
scheme: 'testapp',
|
||||
env: {
|
||||
ENVIRONMENT: 'staging',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as DeprecatedEasJson;
|
||||
expect(migrateProfile(easJson, 'base')).toEqual({
|
||||
android: {
|
||||
env: {
|
||||
EXAMPLE_ENV: 'example value',
|
||||
},
|
||||
image: 'ubuntu-18.04-android-30-ndk-r19c',
|
||||
ndk: '21.4.7075529',
|
||||
},
|
||||
ios: {
|
||||
image: 'latest',
|
||||
node: '12.13.0',
|
||||
yarn: '1.22.5',
|
||||
},
|
||||
});
|
||||
expect(migrateProfile(easJson, 'release')).toEqual({
|
||||
extends: 'base',
|
||||
android: {
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
gradleCommand: ':app:bundleRelease',
|
||||
},
|
||||
ios: {
|
||||
buildConfiguration: 'Release',
|
||||
env: {
|
||||
ENVIRONMENT: 'production',
|
||||
},
|
||||
scheme: 'testapp',
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,12 @@
|
||||
export {
|
||||
CredentialsSource,
|
||||
AndroidDistributionType,
|
||||
AndroidBuildProfile,
|
||||
BuildProfile,
|
||||
IosDistributionType,
|
||||
CredentialsSource,
|
||||
DistributionType,
|
||||
EasJson,
|
||||
IosBuildProfile,
|
||||
IosEnterpriseProvisioning,
|
||||
DistributionType,
|
||||
EasConfig,
|
||||
VersionAutoIncrement,
|
||||
} from './Config.types';
|
||||
} from './EasJson.types';
|
||||
export { EasJsonReader } from './EasJsonReader';
|
||||
export { hasMismatchedExtendsAsync, isUsingDeprecatedFormatAsync, migrateAsync } from './migrate';
|
||||
@@ -0,0 +1,172 @@
|
||||
import { Android, Ios } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { AndroidBuildProfile, IosBuildProfile } from './DeprecatedConfig.types';
|
||||
import { EasJson as DeprecatedEasJson, EasJsonReader } from './DeprecatedEasJsonReader';
|
||||
import { EasJson, RawBuildProfile } from './EasJson.types';
|
||||
|
||||
export async function isUsingDeprecatedFormatAsync(projectDir: string): Promise<boolean> {
|
||||
const easJsonPath = path.join(projectDir, 'eas.json');
|
||||
if (!(await fs.pathExists(easJsonPath))) {
|
||||
return false;
|
||||
}
|
||||
const rawFile = await fs.readFile(easJsonPath, 'utf8');
|
||||
const json = JSON.parse(rawFile);
|
||||
return !!json?.builds;
|
||||
}
|
||||
|
||||
export async function hasMismatchedExtendsAsync(projectDir: string): Promise<boolean> {
|
||||
const rawFile = await fs.readFile(path.join(projectDir, 'eas.json'), 'utf8');
|
||||
const rawEasJson = JSON.parse(rawFile) as DeprecatedEasJson;
|
||||
const profiles = new Set<string>();
|
||||
Object.keys(rawEasJson.builds.android ?? {}).forEach(profile => profiles.add(profile));
|
||||
Object.keys(rawEasJson.builds.ios ?? {}).forEach(profile => profiles.add(profile));
|
||||
|
||||
let hasMismatchedExtendsKeys = false;
|
||||
profiles.forEach(profileName => {
|
||||
if (
|
||||
rawEasJson?.builds?.ios?.[profileName]?.extends !==
|
||||
rawEasJson?.builds?.android?.[profileName]?.extends
|
||||
) {
|
||||
hasMismatchedExtendsKeys = true;
|
||||
}
|
||||
});
|
||||
return hasMismatchedExtendsKeys;
|
||||
}
|
||||
|
||||
export async function migrateAsync(projectDir: string): Promise<void> {
|
||||
const reader = new EasJsonReader(projectDir, 'all');
|
||||
try {
|
||||
await reader.validateAsync();
|
||||
} catch (err) {
|
||||
throw new Error(`Valid eas.json is required to migrate to the new format\n${err.msg}`);
|
||||
}
|
||||
const rawFile = await fs.readFile(path.join(projectDir, 'eas.json'), 'utf8');
|
||||
const rawEasJson = JSON.parse(rawFile) as DeprecatedEasJson;
|
||||
const profiles = new Set<string>();
|
||||
Object.keys(rawEasJson.builds.android ?? {}).forEach(profile => profiles.add(profile));
|
||||
Object.keys(rawEasJson.builds.ios ?? {}).forEach(profile => profiles.add(profile));
|
||||
|
||||
const result: EasJson = {
|
||||
build: {},
|
||||
};
|
||||
profiles.forEach(profileName => {
|
||||
result.build[profileName] = migrateProfile(rawEasJson, profileName);
|
||||
});
|
||||
await fs.writeFile(path.join(projectDir, 'eas.json'), `${JSON.stringify(result, null, 2)}\n`);
|
||||
}
|
||||
|
||||
interface MigrateContext {
|
||||
androidProfile?: Partial<AndroidBuildProfile>;
|
||||
iosProfile?: Partial<IosBuildProfile>;
|
||||
}
|
||||
|
||||
export function migrateProfile(
|
||||
rawEasJson: DeprecatedEasJson,
|
||||
profileName: string
|
||||
): RawBuildProfile {
|
||||
const androidProfile = (rawEasJson?.builds?.android?.[profileName] ??
|
||||
{}) as AndroidBuildProfile & { extends?: string };
|
||||
const iosProfile = (rawEasJson?.builds?.ios?.[profileName] ?? {}) as IosBuildProfile & {
|
||||
extends?: string;
|
||||
};
|
||||
let profile: RawBuildProfile = {
|
||||
android: {},
|
||||
ios: {},
|
||||
};
|
||||
const ctx = { androidProfile, iosProfile };
|
||||
const androidCtx = { androidProfile };
|
||||
const iosCtx = { iosProfile };
|
||||
|
||||
// simple common values
|
||||
profile = migrateProperty('credentialsSource', profile, ctx);
|
||||
profile = migrateProperty('releaseChannel', profile, ctx);
|
||||
profile = migrateProperty('channel', profile, ctx);
|
||||
profile = migrateProperty('node', profile, ctx);
|
||||
profile = migrateProperty('yarn', profile, ctx);
|
||||
profile = migrateProperty('expoCli', profile, ctx);
|
||||
if (androidProfile.extends && androidProfile.extends === iosProfile.extends) {
|
||||
profile = migrateProperty('extends', profile, ctx);
|
||||
}
|
||||
|
||||
// android
|
||||
profile = migrateProperty('image', profile, androidCtx);
|
||||
profile = migrateProperty('ndk', profile, androidCtx);
|
||||
profile = migrateProperty('gradleCommand', profile, androidCtx);
|
||||
profile = migrateProperty('artifactPath', profile, androidCtx);
|
||||
profile = migrateProperty('env', profile, androidCtx);
|
||||
profile = migrateProperty('cache', profile, androidCtx);
|
||||
profile = migrateProperty('withoutCredentials', profile, androidCtx);
|
||||
|
||||
// ios
|
||||
profile = migrateProperty('enterpriseProvisioning', profile, iosCtx);
|
||||
profile = migrateProperty('autoIncrement', profile, iosCtx);
|
||||
profile = migrateProperty('image', profile, iosCtx);
|
||||
profile = migrateProperty('bundler', profile, iosCtx);
|
||||
profile = migrateProperty('fastlane', profile, iosCtx);
|
||||
profile = migrateProperty('cocoapods', profile, iosCtx);
|
||||
profile = migrateProperty('artifactPath', profile, iosCtx);
|
||||
profile = migrateProperty('scheme', profile, iosCtx);
|
||||
profile = migrateProperty('env', profile, iosCtx);
|
||||
profile = migrateProperty('cache', profile, iosCtx);
|
||||
|
||||
profile = migrateProperty('developmentClient', profile, {
|
||||
androidProfile: {
|
||||
...androidProfile,
|
||||
developmentClient:
|
||||
androidProfile.buildType === Android.BuildType.DEVELOPMENT_CLIENT || undefined,
|
||||
} as any,
|
||||
iosProfile: {
|
||||
...iosProfile,
|
||||
developmentClient: iosProfile.buildType === Ios.BuildType.DEVELOPMENT_CLIENT || undefined,
|
||||
} as any,
|
||||
});
|
||||
if (
|
||||
androidProfile.buildType &&
|
||||
androidProfile.buildType !== Android.BuildType.DEVELOPMENT_CLIENT
|
||||
) {
|
||||
profile.android!.buildType = androidProfile.buildType;
|
||||
}
|
||||
if (iosProfile.distribution === 'simulator') {
|
||||
profile.ios!.simulator = true;
|
||||
delete (iosProfile as any).distribution;
|
||||
}
|
||||
profile = migrateProperty('distribution', profile, ctx);
|
||||
|
||||
if (iosProfile.schemeBuildConfiguration) {
|
||||
profile.ios!.buildConfiguration = iosProfile.schemeBuildConfiguration;
|
||||
}
|
||||
if (Object.keys(profile.android ?? {}).length === 0) {
|
||||
delete profile.android;
|
||||
}
|
||||
if (Object.keys(profile.ios ?? {}).length === 0) {
|
||||
delete profile.ios;
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
function migrateProperty(
|
||||
key: string,
|
||||
profile: RawBuildProfile,
|
||||
ctx: MigrateContext
|
||||
): RawBuildProfile {
|
||||
const androidProperty = (ctx.androidProfile as any)?.[key];
|
||||
const iosProperty = (ctx.iosProfile as any)?.[key];
|
||||
|
||||
if (androidProperty && iosProperty && androidProperty === iosProperty) {
|
||||
return { ...profile, [key]: androidProperty };
|
||||
} else {
|
||||
return {
|
||||
...profile,
|
||||
android: {
|
||||
...profile.android,
|
||||
...(androidProperty ? { [key]: androidProperty } : {}),
|
||||
},
|
||||
ios: {
|
||||
...profile.ios,
|
||||
...(iosProperty ? { [key]: iosProperty } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
declare module 'metro-config' {
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import type {
|
||||
DeltaResult,
|
||||
Graph,
|
||||
JsTransformerConfig,
|
||||
Module,
|
||||
Reporter,
|
||||
SerializerOptions,
|
||||
Server,
|
||||
TransformVariants,
|
||||
} from 'metro';
|
||||
|
||||
type CacheStore = unknown;
|
||||
type CustomResolver = unknown;
|
||||
|
||||
import type { BasicSourceMap, MixedSourceMap } from 'metro-source-map';
|
||||
|
||||
//#region metro/packages/metro-config/src/configTypes.flow.js
|
||||
|
||||
export type PostMinifyProcess = (arg: {
|
||||
code: string;
|
||||
map: BasicSourceMap | null | undefined;
|
||||
}) => {
|
||||
code: string;
|
||||
map: BasicSourceMap | null | undefined;
|
||||
};
|
||||
|
||||
export type PostProcessBundleSourcemap = (arg: {
|
||||
code: Buffer | string;
|
||||
map: MixedSourceMap;
|
||||
outFileName: string;
|
||||
}) => {
|
||||
code: Buffer | string;
|
||||
map: MixedSourceMap | string;
|
||||
};
|
||||
|
||||
interface ExtraTransformOptions {
|
||||
readonly preloadedModules:
|
||||
| {
|
||||
[path: string]: true;
|
||||
}
|
||||
| false;
|
||||
readonly ramGroups: string[];
|
||||
readonly transform: {
|
||||
readonly experimentalImportSupport: boolean;
|
||||
readonly inlineRequires:
|
||||
| {
|
||||
readonly blacklist: {
|
||||
[key: string]: true;
|
||||
};
|
||||
}
|
||||
| boolean;
|
||||
readonly nonInlinedRequires?: string[];
|
||||
readonly unstable_disableES6Transforms?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetTransformOptionsOpts {
|
||||
dev: boolean;
|
||||
hot: boolean;
|
||||
platform: string | null | undefined;
|
||||
}
|
||||
|
||||
export type GetTransformOptions = (
|
||||
entryPoints: readonly string[],
|
||||
options: GetTransformOptionsOpts,
|
||||
getDependenciesOf: (path: string) => Promise<string[]>
|
||||
) => Promise<ExtraTransformOptions>;
|
||||
|
||||
export type Middleware = (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
next: (e: Error | null | undefined) => unknown
|
||||
) => unknown;
|
||||
|
||||
interface ResolverConfigT {
|
||||
assetExts: readonly string[];
|
||||
assetResolutions: readonly string[];
|
||||
blacklistRE: RegExp;
|
||||
dependencyExtractor: string | null | undefined;
|
||||
extraNodeModules: {
|
||||
[name: string]: string;
|
||||
};
|
||||
hasteImplModulePath: string | null | undefined;
|
||||
platforms: readonly string[];
|
||||
resolverMainFields: readonly string[];
|
||||
resolveRequest: CustomResolver | null | undefined;
|
||||
sourceExts: readonly string[];
|
||||
useWatchman: boolean;
|
||||
}
|
||||
|
||||
interface SerializerConfigT {
|
||||
createModuleIdFactory: () => (path: string) => number;
|
||||
customSerializer: (
|
||||
entryPoint: string,
|
||||
preModules: Module[],
|
||||
graph: Graph,
|
||||
options: SerializerOptions
|
||||
) => string | { code: string; map: string } | null | undefined;
|
||||
experimentalSerializerHook: (graph: Graph, delta: DeltaResult) => unknown;
|
||||
getModulesRunBeforeMainModule: (entryFilePath: string) => string[];
|
||||
getPolyfills: (arg: { platform: string | null | undefined }) => readonly string[];
|
||||
getRunModuleStatement: (moduleId: number | string) => string;
|
||||
polyfillModuleNames: readonly string[];
|
||||
postProcessBundleSourcemap: PostProcessBundleSourcemap;
|
||||
processModuleFilter: (modules: Module) => boolean;
|
||||
}
|
||||
|
||||
type TransformerConfigT = JsTransformerConfig & {
|
||||
getTransformOptions: GetTransformOptions;
|
||||
postMinifyProcess: PostMinifyProcess;
|
||||
transformVariants: TransformVariants;
|
||||
workerPath: string;
|
||||
publicPath: string;
|
||||
experimentalImportBundleSupport: false;
|
||||
};
|
||||
|
||||
interface MetalConfigT {
|
||||
cacheStores: readonly any[];
|
||||
cacheVersion: string;
|
||||
hasteMapCacheDirectory?: string;
|
||||
maxWorkers: number;
|
||||
projectRoot: string;
|
||||
stickyWorkers: boolean;
|
||||
transformerPath: string;
|
||||
reporter: Reporter;
|
||||
resetCache: boolean;
|
||||
watchFolders: readonly string[];
|
||||
}
|
||||
|
||||
interface ServerConfigT {
|
||||
enhanceMiddleware: (middleware: Middleware, server: Server) => Middleware;
|
||||
useGlobalHotkey: boolean;
|
||||
port: number;
|
||||
runInspectorProxy: boolean;
|
||||
verifyConnections: boolean;
|
||||
}
|
||||
|
||||
type StackFrameCustomizations = undefined | { collapse?: boolean };
|
||||
|
||||
interface SymbolicatorConfigT {
|
||||
customizeFrame: (
|
||||
frame: Readonly<{
|
||||
file: string | null;
|
||||
lineNumber: number | null;
|
||||
column: number | null;
|
||||
methodName: string | null;
|
||||
}>
|
||||
) => StackFrameCustomizations | Promise<StackFrameCustomizations>;
|
||||
}
|
||||
|
||||
export type InputConfigT = Partial<
|
||||
MetalConfigT &
|
||||
Readonly<{
|
||||
resolver: Partial<ResolverConfigT>;
|
||||
server: Partial<ServerConfigT>;
|
||||
serializer: Partial<SerializerConfigT>;
|
||||
symbolicator: Partial<SymbolicatorConfigT>;
|
||||
transformer: Partial<TransformerConfigT>;
|
||||
}>
|
||||
>;
|
||||
|
||||
export type IntermediateConfigT = MetalConfigT & {
|
||||
resolver: ResolverConfigT;
|
||||
server: ServerConfigT;
|
||||
serializer: SerializerConfigT;
|
||||
symbolicator: SymbolicatorConfigT;
|
||||
transformer: TransformerConfigT;
|
||||
};
|
||||
|
||||
export type ConfigT = Readonly<
|
||||
Readonly<MetalConfigT> &
|
||||
Readonly<{
|
||||
resolver: Readonly<ResolverConfigT>;
|
||||
server: Readonly<ServerConfigT>;
|
||||
serializer: Readonly<SerializerConfigT>;
|
||||
symbolicator: Readonly<SymbolicatorConfigT>;
|
||||
transformer: Readonly<TransformerConfigT>;
|
||||
}>
|
||||
>;
|
||||
|
||||
//#endregion
|
||||
//#region metro/packages/metro-config/src/index.js
|
||||
|
||||
interface CosmiConfigResult {
|
||||
filepath: string;
|
||||
isEmpty: boolean;
|
||||
config: ((arg: ConfigT) => Promise<ConfigT>) | ((arg: ConfigT) => ConfigT) | InputConfigT;
|
||||
}
|
||||
|
||||
interface YargArguments {
|
||||
config?: string;
|
||||
cwd?: string;
|
||||
port?: string | number;
|
||||
host?: string;
|
||||
projectRoot?: string;
|
||||
watchFolders?: string[];
|
||||
assetExts?: string[];
|
||||
sourceExts?: string[];
|
||||
platforms?: string[];
|
||||
'max-workers'?: string | number;
|
||||
maxWorkers?: string | number;
|
||||
transformer?: string;
|
||||
'reset-cache'?: boolean;
|
||||
resetCache?: boolean;
|
||||
runInspectorProxy?: boolean;
|
||||
verbose?: boolean;
|
||||
}
|
||||
|
||||
export function resolveConfig(path?: string, cwd?: string): Promise<CosmiConfigResult>;
|
||||
|
||||
export function mergeConfig<T extends InputConfigT>(
|
||||
defaultConfig: T,
|
||||
...configs: InputConfigT[]
|
||||
): T;
|
||||
|
||||
export function loadConfig(
|
||||
argv: YargArguments,
|
||||
defaultConfigOverrides: InputConfigT
|
||||
): Promise<ConfigT>;
|
||||
|
||||
export function getDefaultConfig(rootPath?: string): Promise<ConfigT>;
|
||||
|
||||
namespace getDefaultConfig {
|
||||
function getDefaultValues(rootPath?: string): ConfigT;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
declare module 'metro-source-map' {
|
||||
//#region metro-source-map/src/source-map.js.flow
|
||||
|
||||
type GeneratedCodeMapping = [number, number];
|
||||
type SourceMapping = [number, number, number, number];
|
||||
type SourceMappingWithName = [number, number, number, number, string];
|
||||
|
||||
type HermesFunctionOffsets = { [key: number]: Readonly<number[]> };
|
||||
|
||||
type FBSourcesArray = Readonly<(FBSourceMetadata | null)[]>;
|
||||
type FBSourceMetadata = [FBSourceFunctionMap | null];
|
||||
type FBSourceFunctionMap = {
|
||||
names: Readonly<string[]>;
|
||||
mappings: string;
|
||||
};
|
||||
|
||||
type FBSegmentMap = { [id: string]: MixedSourceMap };
|
||||
|
||||
interface BasicSourceMap {
|
||||
file?: string;
|
||||
mappings: string;
|
||||
names: string[];
|
||||
sourceRoot?: string;
|
||||
sources: string[];
|
||||
sourcesContent?: (string | null)[];
|
||||
version: number;
|
||||
x_facebook_offsets?: number[];
|
||||
x_metro_module_paths?: string[];
|
||||
x_facebook_sources?: FBSourcesArray;
|
||||
x_facebook_segments?: FBSegmentMap;
|
||||
x_hermes_function_offsets?: HermesFunctionOffsets;
|
||||
}
|
||||
|
||||
interface IndexMapSection {
|
||||
map: IndexMap | BasicSourceMap;
|
||||
offset: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface IndexMap {
|
||||
file?: string;
|
||||
mappings?: void; // avoids SourceMap being a disjoint union
|
||||
sections: IndexMapSection[];
|
||||
version: number;
|
||||
x_facebook_offsets?: number[];
|
||||
x_metro_module_paths?: string[];
|
||||
x_facebook_sources?: FBSourcesArray;
|
||||
x_facebook_segments?: FBSegmentMap;
|
||||
x_hermes_function_offsets?: HermesFunctionOffsets;
|
||||
}
|
||||
|
||||
type MixedSourceMap = IndexMap | BasicSourceMap;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region metro-source-map/src/composeSourceMaps.js.flow
|
||||
|
||||
export function composeSourceMaps(maps: Readonly<MixedSourceMap[]>): MixedSourceMap;
|
||||
|
||||
//#endregion
|
||||
}
|
||||
Vendored
+486
@@ -0,0 +1,486 @@
|
||||
declare module 'metro' {
|
||||
//#region metro/src/Assets.js
|
||||
|
||||
type AssetDataWithoutFiles = {
|
||||
readonly __packager_asset: true;
|
||||
readonly fileSystemLocation: string;
|
||||
readonly hash: string;
|
||||
readonly height: number | null | undefined;
|
||||
readonly httpServerLocation: string;
|
||||
readonly name: string;
|
||||
readonly scales: number[];
|
||||
readonly type: string;
|
||||
readonly width: number | null | undefined;
|
||||
};
|
||||
|
||||
export type AssetData = AssetDataWithoutFiles & { readonly files: string[] };
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/DeltaBundler/types.flow.js
|
||||
|
||||
type DynamicRequiresBehavior = unknown;
|
||||
type MinifierConfig = unknown;
|
||||
type BundleDetails = unknown;
|
||||
type GlobalCacheDisabledReason = unknown;
|
||||
type MixedSourceMap = unknown;
|
||||
type MetroSourceMapSegmentTuple = unknown;
|
||||
|
||||
interface MixedOutput {
|
||||
readonly data: unknown;
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
interface BabelSourceLocation {
|
||||
start: { line: number; column: number };
|
||||
end: { line: number; column: number };
|
||||
identifierName?: string;
|
||||
}
|
||||
|
||||
interface TransformResultDependency {
|
||||
/**
|
||||
* The literal name provided to a require or import call. For example 'foo' in
|
||||
* case of `require('foo')`.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* Extra data returned by the dependency extractor. Whatever is added here is
|
||||
* blindly piped by Metro to the serializers.
|
||||
*/
|
||||
readonly data: {
|
||||
/**
|
||||
* If `true` this dependency is due to a dynamic `import()` call. If `false`,
|
||||
* this dependency was pulled using a synchronous `require()` call.
|
||||
*/
|
||||
readonly isAsync: boolean;
|
||||
|
||||
/**
|
||||
* The dependency is actually a `__prefetchImport()` call.
|
||||
*/
|
||||
readonly isPrefetchOnly?: true;
|
||||
|
||||
/**
|
||||
* The condition for splitting on this dependency edge.
|
||||
*/
|
||||
readonly splitCondition?: {
|
||||
readonly mobileConfigName: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dependency is enclosed in a try/catch block.
|
||||
*/
|
||||
readonly isOptional?: boolean;
|
||||
|
||||
readonly locs: readonly BabelSourceLocation[];
|
||||
};
|
||||
}
|
||||
|
||||
interface Dependency {
|
||||
readonly absolutePath: string;
|
||||
readonly data: TransformResultDependency;
|
||||
}
|
||||
|
||||
export interface Module<T = MixedOutput> {
|
||||
readonly dependencies: Map<string, Dependency>;
|
||||
readonly inverseDependencies: Set<string>;
|
||||
readonly output: readonly T[];
|
||||
readonly path: string;
|
||||
readonly getSource: () => Buffer;
|
||||
}
|
||||
|
||||
export interface Graph<T = MixedOutput> {
|
||||
dependencies: Map<string, Module<T>>;
|
||||
importBundleNames: Set<string>;
|
||||
readonly entryPoints: readonly string[];
|
||||
}
|
||||
|
||||
export type TransformResult<T = MixedOutput> = Readonly<{
|
||||
dependencies: readonly TransformResultDependency[];
|
||||
output: readonly T[];
|
||||
}>;
|
||||
|
||||
interface AllowOptionalDependenciesWithOptions {
|
||||
readonly exclude: string[];
|
||||
}
|
||||
type AllowOptionalDependencies = boolean | AllowOptionalDependenciesWithOptions;
|
||||
|
||||
export interface DeltaResult<T = MixedOutput> {
|
||||
readonly added: Map<string, Module<T>>;
|
||||
readonly modified: Map<string, Module<T>>;
|
||||
readonly deleted: Set<string>;
|
||||
readonly reset: boolean;
|
||||
}
|
||||
|
||||
export interface SerializerOptions {
|
||||
readonly asyncRequireModulePath: string;
|
||||
readonly createModuleId: (arg0: string) => number;
|
||||
readonly dev: boolean;
|
||||
readonly getRunModuleStatement: (arg0: number | string) => string;
|
||||
readonly inlineSourceMap: boolean | null | undefined;
|
||||
readonly modulesOnly: boolean;
|
||||
readonly processModuleFilter: (module: Module) => boolean;
|
||||
readonly projectRoot: string;
|
||||
readonly runBeforeMainModule: readonly string[];
|
||||
readonly runModule: boolean;
|
||||
readonly sourceMapUrl: string | null | undefined;
|
||||
readonly sourceUrl: string | null | undefined;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/DeltaBundler/Serializers/getRamBundleInfo.js
|
||||
|
||||
interface RamBundleInfo {
|
||||
getDependencies: (filePath: string) => Set<string>;
|
||||
startupModules: readonly ModuleTransportLike[];
|
||||
lazyModules: readonly ModuleTransportLike[];
|
||||
groups: Map<number, Set<number>>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/index.js
|
||||
|
||||
import { Server as HttpServer, IncomingMessage, ServerResponse } from 'http';
|
||||
import { Server as HttpsServer } from 'https';
|
||||
import { ConfigT, InputConfigT, Middleware, loadConfig } from 'metro-config';
|
||||
|
||||
type MetroMiddleWare = {
|
||||
attachHmrServer: (httpServer: HttpServer | HttpsServer) => void;
|
||||
end: () => void;
|
||||
metroServer: Server;
|
||||
middleware: Middleware;
|
||||
};
|
||||
|
||||
type RunServerOptions = {
|
||||
hasReducedPerformance?: boolean;
|
||||
hmrEnabled?: boolean;
|
||||
host?: string;
|
||||
onError?: (arg0: Error & { code?: string }) => void;
|
||||
onReady?: (server: HttpServer | HttpsServer) => void;
|
||||
runInspectorProxy?: boolean;
|
||||
secure?: boolean;
|
||||
secureCert?: string;
|
||||
secureKey?: string;
|
||||
};
|
||||
|
||||
type BuildGraphOptions = {
|
||||
entries: readonly string[];
|
||||
customTransformOptions?: CustomTransformOptions;
|
||||
dev?: boolean;
|
||||
minify?: boolean;
|
||||
onProgress?: (transformedFileCount: number, totalFileCount: number) => void;
|
||||
platform?: string;
|
||||
type?: 'module' | 'script';
|
||||
};
|
||||
|
||||
type RunBuildOptions = {
|
||||
entry: string;
|
||||
dev?: boolean;
|
||||
out?: string;
|
||||
onBegin?: () => void;
|
||||
onComplete?: () => void;
|
||||
onProgress?: (transformedFileCount: number, totalFileCount: number) => void;
|
||||
minify?: boolean;
|
||||
output?: {
|
||||
build: (
|
||||
arg0: Server,
|
||||
arg1: RequestOptions
|
||||
) => Promise<{
|
||||
code: string;
|
||||
map: string;
|
||||
}>;
|
||||
save: (
|
||||
arg0: {
|
||||
code: string;
|
||||
map: string;
|
||||
},
|
||||
arg1: OutputOptions,
|
||||
arg2: (...args: string[]) => void
|
||||
) => Promise<unknown>;
|
||||
};
|
||||
platform?: string;
|
||||
sourceMap?: boolean;
|
||||
sourceMapUrl?: string;
|
||||
};
|
||||
|
||||
export function runMetro(config: InputConfigT, options?: ServerOptions): Promise<Server>;
|
||||
|
||||
export { loadConfig };
|
||||
|
||||
export function createConnectMiddleware(
|
||||
config: ConfigT,
|
||||
options?: ServerOptions
|
||||
): Promise<MetroMiddleWare>;
|
||||
|
||||
export function runServer(
|
||||
config: ConfigT,
|
||||
options: RunServerOptions
|
||||
): Promise<HttpServer | HttpsServer>;
|
||||
|
||||
export function runBuild(
|
||||
config: ConfigT,
|
||||
options: RunBuildOptions
|
||||
): Promise<{
|
||||
code: string;
|
||||
map: string;
|
||||
}>;
|
||||
|
||||
export function buildGraph(config: InputConfigT, options: BuildGraphOptions): Promise<Graph>;
|
||||
//#endregion
|
||||
//#region metro/src/JSTransformer/worker.js
|
||||
|
||||
type CustomTransformOptions = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type JsTransformerConfig = Readonly<{
|
||||
assetPlugins: readonly string[];
|
||||
assetRegistryPath: string;
|
||||
asyncRequireModulePath: string;
|
||||
babelTransformerPath: string;
|
||||
dynamicDepsInPackages: DynamicRequiresBehavior;
|
||||
enableBabelRCLookup: boolean;
|
||||
enableBabelRuntime: boolean;
|
||||
experimentalImportBundleSupport: boolean;
|
||||
minifierConfig: MinifierConfig;
|
||||
minifierPath: string;
|
||||
optimizationSizeLimit: number;
|
||||
publicPath: string;
|
||||
allowOptionalDependencies: AllowOptionalDependencies;
|
||||
}>;
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/lib/reporting.js
|
||||
|
||||
/**
|
||||
* A tagged union of all the actions that may happen and we may want to
|
||||
* report to the tool user.
|
||||
*/
|
||||
export type ReportableEvent =
|
||||
| {
|
||||
port: number;
|
||||
hasReducedPerformance: boolean;
|
||||
type: 'initialize_started';
|
||||
}
|
||||
| {
|
||||
type: 'initialize_failed';
|
||||
port: number;
|
||||
error: Error;
|
||||
}
|
||||
| {
|
||||
buildID: string;
|
||||
type: 'bundle_build_done';
|
||||
}
|
||||
| {
|
||||
buildID: string;
|
||||
type: 'bundle_build_failed';
|
||||
}
|
||||
| {
|
||||
buildID: string;
|
||||
bundleDetails: BundleDetails;
|
||||
type: 'bundle_build_started';
|
||||
}
|
||||
| {
|
||||
error: Error;
|
||||
type: 'bundling_error';
|
||||
}
|
||||
| {
|
||||
type: 'dep_graph_loading';
|
||||
hasReducedPerformance: boolean;
|
||||
}
|
||||
| { type: 'dep_graph_loaded' }
|
||||
| {
|
||||
buildID: string;
|
||||
type: 'bundle_transform_progressed';
|
||||
transformedFileCount: number;
|
||||
totalFileCount: number;
|
||||
}
|
||||
| {
|
||||
type: 'global_cache_error';
|
||||
error: Error;
|
||||
}
|
||||
| {
|
||||
type: 'global_cache_disabled';
|
||||
reason: GlobalCacheDisabledReason;
|
||||
}
|
||||
| { type: 'transform_cache_reset' }
|
||||
| {
|
||||
type: 'worker_stdout_chunk';
|
||||
chunk: string;
|
||||
}
|
||||
| {
|
||||
type: 'worker_stderr_chunk';
|
||||
chunk: string;
|
||||
}
|
||||
| {
|
||||
type: 'hmr_client_error';
|
||||
error: Error;
|
||||
}
|
||||
| {
|
||||
type: 'client_log';
|
||||
level:
|
||||
| 'trace'
|
||||
| 'info'
|
||||
| 'warn'
|
||||
| 'log'
|
||||
| 'group'
|
||||
| 'groupCollapsed'
|
||||
| 'groupEnd'
|
||||
| 'debug';
|
||||
data: unknown[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Code across the application takes a reporter as an option and calls the
|
||||
* update whenever one of the ReportableEvent happens. Code does not directly
|
||||
* write to the standard output, because a build would be:
|
||||
*
|
||||
* 1. ad-hoc, embedded into another tool, in which case we do not want to
|
||||
* pollute that tool's own output. The tool is free to present the
|
||||
* warnings/progress we generate any way they want, by specifing a custom
|
||||
* reporter.
|
||||
* 2. run as a background process from another tool, in which case we want
|
||||
* to expose updates in a way that is easily machine-readable, for example
|
||||
* a JSON-stream. We don't want to pollute it with textual messages.
|
||||
*
|
||||
* We centralize terminal reporting into a single place because we want the
|
||||
* output to be robust and consistent. The most common reporter is
|
||||
* TerminalReporter, that should be the only place in the application should
|
||||
* access the `terminal` module (nor the `console`).
|
||||
*/
|
||||
export interface Reporter {
|
||||
update(event: ReportableEvent): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/ModuleGraph/types.flow.js
|
||||
|
||||
export type TransformVariants = {
|
||||
readonly [name: string]: object;
|
||||
};
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/Server.js
|
||||
|
||||
type ServerOptions = Readonly<{
|
||||
watch?: boolean;
|
||||
}>;
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/Server/index.js
|
||||
|
||||
type IncrementalBundler = unknown;
|
||||
|
||||
export class Server {
|
||||
constructor(config: ConfigT, options?: ServerOptions);
|
||||
|
||||
end(): void;
|
||||
|
||||
getBundler(): IncrementalBundler;
|
||||
|
||||
getCreateModuleId(): (path: string) => number;
|
||||
|
||||
build(options: BundleOptions): Promise<{
|
||||
code: string;
|
||||
map: string;
|
||||
}>;
|
||||
|
||||
getRamBundleInfo(options: BundleOptions): Promise<RamBundleInfo>;
|
||||
|
||||
getAssets(options: BundleOptions): Promise<readonly AssetData[]>;
|
||||
|
||||
getOrderedDependencyPaths(options: {
|
||||
readonly dev: boolean;
|
||||
readonly entryFile: string;
|
||||
readonly minify: boolean;
|
||||
readonly platform: string;
|
||||
}): Promise<string[]>;
|
||||
|
||||
processRequest(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
next: (arg0: Error | null | undefined) => unknown
|
||||
): void;
|
||||
|
||||
getNewBuildID(): string;
|
||||
|
||||
getPlatforms(): readonly string[];
|
||||
|
||||
getWatchFolders(): readonly string[];
|
||||
|
||||
static DEFAULT_GRAPH_OPTIONS: {
|
||||
customTransformOptions: any;
|
||||
dev: boolean;
|
||||
hot: boolean;
|
||||
minify: boolean;
|
||||
};
|
||||
|
||||
static DEFAULT_BUNDLE_OPTIONS: typeof Server.DEFAULT_GRAPH_OPTIONS & {
|
||||
excludeSource: false;
|
||||
inlineSourceMap: false;
|
||||
modulesOnly: false;
|
||||
onProgress: null;
|
||||
runModule: true;
|
||||
shallow: false;
|
||||
sourceMapUrl: null;
|
||||
sourceUrl: null;
|
||||
};
|
||||
}
|
||||
|
||||
//#endregion
|
||||
//#region metro/src/shared/types.flow.js
|
||||
|
||||
type BundleType = 'bundle' | 'delta' | 'meta' | 'map' | 'ram' | 'cli' | 'hmr' | 'todo' | 'graph';
|
||||
|
||||
type MetroSourceMapOrMappings = MixedSourceMap | MetroSourceMapSegmentTuple[];
|
||||
|
||||
export interface BundleOptions {
|
||||
bundleType: BundleType;
|
||||
customTransformOptions: CustomTransformOptions;
|
||||
dev: boolean;
|
||||
entryFile: string;
|
||||
readonly excludeSource: boolean;
|
||||
readonly hot: boolean;
|
||||
readonly inlineSourceMap: boolean;
|
||||
minify: boolean;
|
||||
readonly modulesOnly: boolean;
|
||||
onProgress: (doneCont: number, totalCount: number) => unknown | null | undefined;
|
||||
readonly platform: string | null | undefined;
|
||||
readonly runModule: boolean;
|
||||
readonly shallow: boolean;
|
||||
sourceMapUrl: string | null | undefined;
|
||||
sourceUrl: string | null | undefined;
|
||||
createModuleIdFactory?: () => (path: string) => number;
|
||||
}
|
||||
|
||||
type ModuleTransportLike = {
|
||||
readonly code: string;
|
||||
readonly id: number;
|
||||
readonly map: MetroSourceMapOrMappings | null | undefined;
|
||||
readonly name?: string;
|
||||
readonly sourcePath: string;
|
||||
};
|
||||
|
||||
export interface OutputOptions {
|
||||
bundleOutput: string;
|
||||
bundleEncoding?: 'utf8' | 'utf16le' | 'ascii';
|
||||
dev?: boolean;
|
||||
indexedRamBundle?: boolean;
|
||||
platform: string;
|
||||
sourcemapOutput?: string;
|
||||
sourcemapSourcesRoot?: string;
|
||||
sourcemapUseAbsolutePath?: boolean;
|
||||
}
|
||||
|
||||
export interface RequestOptions {
|
||||
entryFile: string;
|
||||
inlineSourceMap?: boolean;
|
||||
sourceMapUrl?: string;
|
||||
dev?: boolean;
|
||||
minify: boolean;
|
||||
platform: string;
|
||||
createModuleIdFactory?: () => (path: string) => number;
|
||||
onProgress?: (transformedFileCount: number, totalFileCount: number) => void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -1956,10 +1956,10 @@
|
||||
xcode "^3.0.0"
|
||||
xml-js "^1.6.11"
|
||||
|
||||
"@expo/[email protected]4":
|
||||
version "0.2.44"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.44.tgz#11f442cf9c1ac8402afbb06742bc99d4c1c6b6d9"
|
||||
integrity sha512-4/wtOyAFLa0aILfco0a/slL96CBUppSeXfPdLzcHkBIc+AUB/pUSGf/M+Z3Pn7QoA3BHCnSIW8DosqqBBOppRg==
|
||||
"@expo/[email protected]5":
|
||||
version "0.2.45"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.45.tgz#1404767e815b08ebdce8af073dd0f0e6911af1a5"
|
||||
integrity sha512-ysidetne3cqg6H1zsFUCJhfHwDZEGbUhZKQk12a2NtC3P8QYALkOzwc+JbqhPX6oQd9zm9WqF1w7rc7tPK6plA==
|
||||
dependencies:
|
||||
"@hapi/joi" "^17.1.1"
|
||||
|
||||
|
||||
Reference in new issue
Block a user