mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 03:38:08 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11c194a168 | ||
|
|
3c74c111a7 | ||
|
|
b69b8b87a7 | ||
|
|
825baa9f68 | ||
|
|
9f5736cfaa | ||
|
|
ae24ffbe86 | ||
|
|
487316370b | ||
|
|
8bc3565270 | ||
|
|
07450f6bf7 | ||
|
|
71d48c3cf2 | ||
|
|
7343e81065 | ||
|
|
1f1310bef6 | ||
|
|
27c69ec8e1 | ||
|
|
b81b896bc6 | ||
|
|
ef79db2ca5 | ||
|
|
88565eec62 | ||
|
|
e8a520e55f | ||
|
|
9a33f7bd93 | ||
|
|
23c70e237b | ||
|
|
7fcf41db78 | ||
|
|
36f4a4bd38 | ||
|
|
87541db80d | ||
|
|
80112c682f | ||
|
|
eb29d3dadb |
No files matched your search
+14
-11
@@ -1,15 +1,18 @@
|
||||
# Build directories
|
||||
packages/*/build
|
||||
packages/eas-cli/dist
|
||||
|
||||
# Code editors
|
||||
.idea
|
||||
.history
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
*-debug.log
|
||||
*-error.log
|
||||
|
||||
**/build/**
|
||||
!packages/eas-cli/src/commands/build/**
|
||||
!packages/eas-cli/src/build/**
|
||||
|
||||
dist
|
||||
/package-lock.json
|
||||
tmp
|
||||
node_modules
|
||||
# Tests
|
||||
coverage
|
||||
.history
|
||||
.DS_Store
|
||||
.idea
|
||||
+40
-1
@@ -6,6 +6,45 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for extending submit profiles. ([#794](https://github.com/expo/eas-cli/pull/794) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix reading app version and app build version of iOS projects. ([#798](https://github.com/expo/eas-cli/pull/798) by [@dsokal](https://github.com/dsokal))
|
||||
- Fix push notifications entitlements. ([#801](https://github.com/expo/eas-cli/pull/801) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [0.38.1](https://github.com/expo/eas-cli/releases/tag/v0.38.1) - 2021-11-25
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix building iOS projects with whitespace in the project name. ([#792](https://github.com/expo/eas-cli/pull/792) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [0.38.0](https://github.com/expo/eas-cli/releases/tag/v0.38.0) - 2021-11-24
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Improve unknown capability syncing error message. ([#775](https://github.com/expo/eas-cli/pulls/775) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Add submit webhooks. ([#777](https://github.com/expo/eas-cli/pull/777) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Compute runtime version policies. ([#783](https://github.com/expo/eas-cli/pull/783), [#785](https://github.com/expo/eas-cli/pull/785) by [@jkhales](https://github.com/jkhales))
|
||||
- Fix local builds with npm < 7. ([#787](https://github.com/expo/eas-cli/pull/787) by [@dsokal](https://github.com/dsokal))
|
||||
- Override `applicationId`/`bundleIdentifier` when auto-submitting after build. ([#780](https://github.com/expo/eas-cli/pull/780) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade `typescript` to 4.5.2. Upgrade oclif dependencies. ([#781](https://github.com/expo/eas-cli/pull/781) by [@dsokal](https://github.com/dsokal))
|
||||
- Make missing `git` command error message more descriptive. ([#784](https://github.com/expo/eas-cli/pull/784) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [0.37.0](https://github.com/expo/eas-cli/releases/tag/v0.37.0) - 2021-11-18
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Require explicitly defined applicationId/bundleIdentifier for EAS Submit in case of bare projects consisting of multiple product flavors on Android or multiple schemes/targets on iOS. ([#765](https://github.com/expo/eas-cli/pull/765) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🎉 New features
|
||||
@@ -68,7 +107,7 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
### 🧹 Chores
|
||||
|
||||
- Clean up credentials prompt method. ([#728](https://github.com/expo/eas-cli/pull/728) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Bump @expo/apple-utils to 0.0.0-alpha.26. ([#723](https://github.com/expo/eas-cli/pull/723) by [@brentvatne](https://github.com/brentvatne))
|
||||
- Bump `@expo/apple-utils` to 0.0.0-alpha.26. ([#723](https://github.com/expo/eas-cli/pull/723) by [@brentvatne](https://github.com/brentvatne))
|
||||
- Grammar: replace setup with set up. ([#735](https://github.com/expo/eas-cli/pull/735) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Improve VCS workflow migration experience. ([#732](https://github.com/expo/eas-cli/pull/732) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.37.0"
|
||||
"version": "0.38.2"
|
||||
}
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"private": true,
|
||||
"workspaces": ["packages/*"],
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "lerna run build",
|
||||
"build:tarballs:linux": "yarn build && yarn workspace eas-cli oclif-dev pack --targets linux-x64",
|
||||
@@ -26,7 +28,7 @@
|
||||
"lerna": "4.0.0",
|
||||
"prettier": "2.4.1",
|
||||
"ts-jest": "27.0.5",
|
||||
"typescript": "4.4.2"
|
||||
"typescript": "4.5.2"
|
||||
},
|
||||
"volta": {
|
||||
"node": "16.10.0",
|
||||
|
||||
+13
-64
@@ -73,8 +73,6 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/account/login.js)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
log out
|
||||
@@ -87,8 +85,6 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/account/logout.js)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
show the username you are logged in as
|
||||
@@ -101,8 +97,6 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/account/view.js)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
view or change analytics settings
|
||||
@@ -112,8 +106,6 @@ USAGE
|
||||
$ eas analytics [STATUS]
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/analytics.js)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
start a build
|
||||
@@ -146,8 +138,6 @@ OPTIONS
|
||||
--[no-]wait Wait for build(s) to complete
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/build/index.js)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
Cancel a build.
|
||||
@@ -157,8 +147,6 @@ USAGE
|
||||
$ eas build:cancel [BUILD_ID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/build/cancel.js)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
Configure the project to support EAS Build.
|
||||
@@ -171,8 +159,6 @@ OPTIONS
|
||||
-p, --platform=(android|ios|all) Platform to configure
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/build/configure.js)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
list all builds for your project
|
||||
@@ -204,8 +190,6 @@ OPTIONS
|
||||
--status=(new|in-queue|in-progress|errored|finished|canceled)
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/build/list.js)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
view a build for your project
|
||||
@@ -218,8 +202,6 @@ OPTIONS
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/build/view.js)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
show the eas.json config
|
||||
@@ -233,8 +215,6 @@ OPTIONS
|
||||
--profile=profile
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/config.js)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
manage your credentials
|
||||
@@ -244,8 +224,6 @@ USAGE
|
||||
$ eas credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/credentials.js)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
register new Apple Devices to use for internal distribution
|
||||
@@ -255,8 +233,6 @@ USAGE
|
||||
$ eas device:create
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/device/create.js)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
list all registered devices for your account
|
||||
@@ -269,8 +245,6 @@ OPTIONS
|
||||
--apple-team-id=apple-team-id
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/device/list.js)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
view a device for your project
|
||||
@@ -280,8 +254,6 @@ USAGE
|
||||
$ eas device:view [UDID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/device/view.js)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
log environment info to the console
|
||||
@@ -291,8 +263,6 @@ USAGE
|
||||
$ eas diagnostics
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/diagnostics.js)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
display help for eas
|
||||
@@ -308,7 +278,7 @@ OPTIONS
|
||||
--all see all commands in CLI
|
||||
```
|
||||
|
||||
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.3/src/commands/help.ts)_
|
||||
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.5/src/commands/help.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -319,8 +289,6 @@ USAGE
|
||||
$ eas project:info
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/project/info.js)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
create or link an EAS project
|
||||
@@ -333,8 +301,6 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/project/init.js)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
Create an environment secret on the current project or owner account.
|
||||
@@ -350,8 +316,6 @@ OPTIONS
|
||||
--value=value Value of the secret
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/secret/create.js)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
Delete an environment secret by ID.
|
||||
@@ -367,8 +331,6 @@ DESCRIPTION
|
||||
Unsure where to find the secret's ID? Run eas secret:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/secret/delete.js)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
Lists environment secrets available for your current app
|
||||
@@ -378,8 +340,6 @@ USAGE
|
||||
$ eas secret:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/secret/list.js)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
submit build archive to app store
|
||||
@@ -405,14 +365,13 @@ OPTIONS
|
||||
--[no-]wait Wait for submission to complete
|
||||
|
||||
DESCRIPTION
|
||||
See how to configure submits with eas.json: https://docs.expo.dev/submit/eas-json/
|
||||
See how to configure submits with eas.json:
|
||||
https://docs.expo.dev/submit/eas-json/
|
||||
|
||||
ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/submit.js)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
Create a webhook on the current project.
|
||||
@@ -422,16 +381,14 @@ USAGE
|
||||
$ eas webhook:create
|
||||
|
||||
OPTIONS
|
||||
--event=(BUILD) [default: BUILD] Event type that triggers the webhook
|
||||
--event=(BUILD|SUBMIT) Event type that triggers the webhook
|
||||
|
||||
--secret=secret Secret used to create a hash signature of the request payload, provided in the 'Expo-Signature'
|
||||
header.
|
||||
--secret=secret Secret used to create a hash signature of the request payload, provided in the
|
||||
'Expo-Signature' header.
|
||||
|
||||
--url=url Webhook URL
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/webhook/create.js)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
Delete a webhook on the current project.
|
||||
@@ -444,8 +401,6 @@ ARGUMENTS
|
||||
ID ID of the webhook to delete
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/webhook/delete.js)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
List webhooks on the current project.
|
||||
@@ -455,11 +410,9 @@ USAGE
|
||||
$ eas webhook:list
|
||||
|
||||
OPTIONS
|
||||
--event=(BUILD) Event type that triggers the webhook
|
||||
--event=(BUILD|SUBMIT) Event type that triggers the webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/webhook/list.js)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
Create a webhook on the current project.
|
||||
@@ -469,17 +422,15 @@ USAGE
|
||||
$ eas webhook:update
|
||||
|
||||
OPTIONS
|
||||
--event=(BUILD) [default: BUILD] Event type that triggers the webhook
|
||||
--id=id (required) Webhook ID
|
||||
--event=(BUILD|SUBMIT) Event type that triggers the webhook
|
||||
--id=id (required) Webhook ID
|
||||
|
||||
--secret=secret Secret used to create a hash signature of the request payload, provided in the 'Expo-Signature'
|
||||
header.
|
||||
--secret=secret Secret used to create a hash signature of the request payload, provided in the
|
||||
'Expo-Signature' header.
|
||||
|
||||
--url=url Webhook URL
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/webhook/update.js)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
View a webhook on the current project.
|
||||
@@ -491,6 +442,4 @@ USAGE
|
||||
ARGUMENTS
|
||||
ID ID of the webhook to view
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.js](https://github.com/expo/eas-cli/blob/v0.37.0/packages/eas-cli/src/commands/webhook/view.js)_
|
||||
<!-- commandsstop -->
|
||||
Generated
+6
@@ -10674,6 +10674,12 @@
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SUBMIT",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "0.37.0",
|
||||
"version": "0.38.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
@@ -9,24 +9,23 @@
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "0.0.0-alpha.26",
|
||||
"@expo/config": "6.0.3",
|
||||
"@expo/config-plugins": "4.0.3",
|
||||
"@expo/config": "6.0.9",
|
||||
"@expo/config-plugins": "4.0.9",
|
||||
"@expo/eas-build-job": "0.2.57",
|
||||
"@expo/eas-json": "^0.37.0",
|
||||
"@expo/json-file": "8.2.33",
|
||||
"@expo/pkcs12": "0.0.4",
|
||||
"@expo/plist": "0.0.14",
|
||||
"@expo/eas-json": "^0.38.2",
|
||||
"@expo/json-file": "8.2.34",
|
||||
"@expo/pkcs12": "0.0.6",
|
||||
"@expo/plist": "0.0.16",
|
||||
"@expo/plugin-warn-if-update-available": "1.7.0",
|
||||
"@expo/prebuild-config": "3.0.3",
|
||||
"@expo/prebuild-config": "3.0.9",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.0",
|
||||
"@expo/sdk-runtime-versions": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.5.0",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/command": "1.8.0",
|
||||
"@oclif/config": "1.17.0",
|
||||
"@oclif/command": "1.8.3",
|
||||
"@oclif/config": "1.17.1",
|
||||
"@oclif/errors": "1.3.5",
|
||||
"@oclif/plugin-help": "3.2.3",
|
||||
"@oclif/plugin-help": "3.2.5",
|
||||
"@urql/core": "2.3.1",
|
||||
"@urql/exchange-retry": "0.3.0",
|
||||
"chalk": "4.1.2",
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync,
|
||||
getApplicationIdAsync,
|
||||
} from '../../project/android/applicationId';
|
||||
import { GradleBuildContext, resolveGradleBuildContextAsync } from '../../project/android/gradle';
|
||||
import { resolveGradleBuildContextAsync } from '../../project/android/gradle';
|
||||
import { toggleConfirmAsync } from '../../prompts';
|
||||
import { findAccountByName } from '../../user/Account';
|
||||
import {
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
JobData,
|
||||
prepareBuildRequestForPlatformAsync,
|
||||
} from '../build';
|
||||
import { BuildContext } from '../context';
|
||||
import { AndroidBuildContext, BuildContext, CommonContext } from '../context';
|
||||
import { transformMetadata } from '../graphql';
|
||||
import { logCredentialsSource } from '../utils/credentials';
|
||||
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
|
||||
@@ -28,9 +28,9 @@ import { validateAndSyncProjectConfigurationAsync } from './configure';
|
||||
import { transformJob } from './graphql';
|
||||
import { prepareJobAsync } from './prepareJob';
|
||||
|
||||
export async function prepareAndroidBuildAsync(
|
||||
ctx: BuildContext<Platform.ANDROID>
|
||||
): Promise<BuildRequestSender> {
|
||||
export async function createAndroidContextAsync(
|
||||
ctx: CommonContext<Platform.ANDROID>
|
||||
): Promise<AndroidBuildContext> {
|
||||
const { buildProfile } = ctx;
|
||||
|
||||
if (buildProfile.distribution === 'internal' && buildProfile.gradleCommand?.match(/bundle/)) {
|
||||
@@ -58,21 +58,26 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp);
|
||||
}
|
||||
|
||||
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
|
||||
|
||||
return { applicationId, gradleContext };
|
||||
}
|
||||
|
||||
export async function prepareAndroidBuildAsync(
|
||||
ctx: BuildContext<Platform.ANDROID>
|
||||
): Promise<BuildRequestSender> {
|
||||
return await prepareBuildRequestForPlatformAsync({
|
||||
ctx,
|
||||
ensureCredentialsAsync: async (ctx: BuildContext<Platform.ANDROID>) => {
|
||||
return await ensureAndroidCredentialsAsync(ctx, gradleContext);
|
||||
return await ensureAndroidCredentialsAsync(ctx);
|
||||
},
|
||||
ensureProjectConfiguredAsync: async () => {
|
||||
await validateAndSyncProjectConfigurationAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
buildProfile,
|
||||
buildProfile: ctx.buildProfile,
|
||||
});
|
||||
},
|
||||
getMetadataContext: () => ({
|
||||
gradleContext,
|
||||
}),
|
||||
prepareJobAsync: async (
|
||||
ctx: BuildContext<Platform.ANDROID>,
|
||||
jobData: JobData<AndroidCredentials>
|
||||
@@ -100,8 +105,7 @@ function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean
|
||||
}
|
||||
|
||||
async function ensureAndroidCredentialsAsync(
|
||||
ctx: BuildContext<Platform.ANDROID>,
|
||||
gradleContext?: GradleBuildContext
|
||||
ctx: BuildContext<Platform.ANDROID>
|
||||
): Promise<CredentialsResult<AndroidCredentials> | undefined> {
|
||||
if (!shouldProvideCredentials(ctx)) {
|
||||
return;
|
||||
@@ -109,7 +113,7 @@ async function ensureAndroidCredentialsAsync(
|
||||
const androidApplicationIdentifier = await getApplicationIdAsync(
|
||||
ctx.projectDir,
|
||||
ctx.exp,
|
||||
gradleContext
|
||||
ctx.android.gradleContext
|
||||
);
|
||||
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
|
||||
app: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { AndroidConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AndroidBuildProfile, AndroidVersionAutoIncrement } from '@expo/eas-json';
|
||||
import { AndroidVersionAutoIncrement, BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import Log from '../../log';
|
||||
import {
|
||||
@@ -41,7 +41,7 @@ export async function validateAndSyncProjectConfigurationAsync({
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
buildProfile: AndroidBuildProfile;
|
||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||
}): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
const { autoIncrement } = buildProfile;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Android, ArchiveSource, Job, Platform, sanitizeJob } from '@expo/eas-build-job';
|
||||
import { AndroidBuildProfile } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import path from 'path';
|
||||
import slash from 'slash';
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function prepareJobAsync(
|
||||
jobData: JobData
|
||||
): Promise<Job> {
|
||||
const username = getUsername(ctx.exp, await ensureLoggedInAsync());
|
||||
const buildProfile: AndroidBuildProfile = ctx.buildProfile;
|
||||
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
|
||||
const projectRootDirectory =
|
||||
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
|
||||
const { credentials } = jobData;
|
||||
|
||||
@@ -18,7 +18,7 @@ import { sleepAsync } from '../utils/promise';
|
||||
import { getVcsClient } from '../vcs';
|
||||
import { BuildContext } from './context';
|
||||
import { runLocalBuildAsync } from './local';
|
||||
import { MetadataContext, collectMetadataAsync } from './metadata';
|
||||
import { collectMetadataAsync } from './metadata';
|
||||
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
||||
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
||||
|
||||
@@ -39,7 +39,6 @@ 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>;
|
||||
}
|
||||
@@ -88,8 +87,7 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
bucketKey: await uploadProjectAsync(ctx),
|
||||
} as const);
|
||||
|
||||
const metadataContext = builder.getMetadataContext();
|
||||
const metadata = await collectMetadataAsync(ctx, metadataContext);
|
||||
const metadata = await collectMetadataAsync(ctx);
|
||||
const job = await builder.prepareJobAsync(ctx, {
|
||||
projectArchive,
|
||||
credentials: credentialsResult?.credentials,
|
||||
|
||||
@@ -58,7 +58,6 @@ export async function configureAsync(options: {
|
||||
platform: RequestedPlatform;
|
||||
projectDir: string;
|
||||
}): Promise<void> {
|
||||
await getVcsClient().ensureRepoExistsAsync();
|
||||
await maybeBailOnRepoStatusAsync();
|
||||
|
||||
const { exp } = getConfig(options.projectDir, { skipSDKVersionRequirement: true });
|
||||
@@ -144,7 +143,7 @@ export async function ensureEasJsonExistsAsync(ctx: ConfigureContext): Promise<v
|
||||
|
||||
if (await fs.pathExists(easJsonPath)) {
|
||||
const reader = new EasJsonReader(ctx.projectDir);
|
||||
await reader.readAndValidateAsync();
|
||||
await reader.readAsync();
|
||||
|
||||
Log.withTick('Validated eas.json');
|
||||
return;
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
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';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { TrackingContext } from '../analytics/common';
|
||||
import { Analytics, BuildEvent } from '../analytics/events';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import { Target } from '../credentials/ios/types';
|
||||
import { RequestedPlatform } from '../platform';
|
||||
import { getExpoConfig } from '../project/expoConfig';
|
||||
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../project/workflow';
|
||||
import { findAccountByName } from '../user/Account';
|
||||
import { GradleBuildContext } from '../project/android/gradle';
|
||||
import { XcodeBuildContext } from '../project/ios/scheme';
|
||||
import { Actor } from '../user/User';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
|
||||
export interface ConfigureContext {
|
||||
user: Actor;
|
||||
@@ -27,6 +22,21 @@ export interface ConfigureContext {
|
||||
hasIosNativeProject: boolean;
|
||||
}
|
||||
|
||||
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
|
||||
|
||||
export interface AndroidBuildContext {
|
||||
applicationId: string;
|
||||
gradleContext?: GradleBuildContext;
|
||||
}
|
||||
|
||||
export interface IosBuildContext {
|
||||
bundleIdentifier: string;
|
||||
applicationTargetBuildSettings: XCBuildConfiguration['buildSettings'];
|
||||
applicationTarget: Target;
|
||||
targets: Target[];
|
||||
xcodeBuildContext: XcodeBuildContext;
|
||||
}
|
||||
|
||||
export interface BuildContext<T extends Platform> {
|
||||
accountName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
@@ -44,112 +54,6 @@ export interface BuildContext<T extends Platform> {
|
||||
trackingCtx: TrackingContext;
|
||||
user: Actor;
|
||||
workflow: Workflow;
|
||||
}
|
||||
|
||||
export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfileName,
|
||||
buildProfile,
|
||||
clearCache = false,
|
||||
local,
|
||||
nonInteractive = false,
|
||||
platform,
|
||||
projectDir,
|
||||
skipProjectConfiguration = false,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
clearCache: boolean;
|
||||
local: boolean;
|
||||
nonInteractive: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
skipProjectConfiguration: boolean;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
|
||||
|
||||
const user = await ensureLoggedInAsync();
|
||||
const accountName = getProjectAccountName(exp, user);
|
||||
const projectName = exp.slug;
|
||||
const projectId = await getProjectIdAsync(exp, { env: buildProfile.env });
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
const accountId = findAccountByName(user.accounts, accountName)?.id;
|
||||
|
||||
const credentialsCtx = new CredentialsContext({
|
||||
exp,
|
||||
nonInteractive,
|
||||
projectDir,
|
||||
user,
|
||||
});
|
||||
|
||||
const devClientProperties = getDevClientEventProperties({
|
||||
platform,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
});
|
||||
const trackingCtx = {
|
||||
tracking_id: uuidv4(),
|
||||
platform,
|
||||
...(accountId && { account_id: accountId }),
|
||||
account_name: accountName,
|
||||
project_id: projectId,
|
||||
project_type: workflow,
|
||||
...devClientProperties,
|
||||
};
|
||||
Analytics.logEvent(BuildEvent.BUILD_COMMAND, trackingCtx);
|
||||
|
||||
return {
|
||||
accountName,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
clearCache,
|
||||
credentialsCtx,
|
||||
exp,
|
||||
local,
|
||||
nonInteractive,
|
||||
platform,
|
||||
projectDir,
|
||||
projectId,
|
||||
projectName,
|
||||
skipProjectConfiguration,
|
||||
trackingCtx,
|
||||
user,
|
||||
workflow,
|
||||
};
|
||||
}
|
||||
|
||||
function getDevClientEventProperties({
|
||||
platform,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
}: {
|
||||
platform: Platform;
|
||||
projectDir: string;
|
||||
buildProfile: AndroidBuildProfile | IosBuildProfile;
|
||||
}): Partial<TrackingContext> {
|
||||
let includesDevClient;
|
||||
const version = tryGetDevClientVersion(projectDir);
|
||||
if (platform === Platform.ANDROID && 'gradleCommand' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.gradleCommand?.includes('Debug'));
|
||||
} else if (platform === Platform.IOS && 'buildConfiguration' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.buildConfiguration === 'Debug');
|
||||
} else if (buildProfile.developmentClient) {
|
||||
includesDevClient = true;
|
||||
} else {
|
||||
includesDevClient = false;
|
||||
}
|
||||
|
||||
if (version) {
|
||||
return { dev_client: includesDevClient, dev_client_version: version };
|
||||
} else {
|
||||
return { dev_client: includesDevClient };
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetDevClientVersion(projectDir: string): string | null {
|
||||
try {
|
||||
const pkg = JsonFile.read(resolveFrom(projectDir, 'expo-dev-client/package.json'));
|
||||
return pkg.version?.toString() ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
||||
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { TrackingContext } from '../analytics/common';
|
||||
import { Analytics, BuildEvent } from '../analytics/events';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import { getExpoConfig } from '../project/expoConfig';
|
||||
import { getProjectAccountName, getProjectIdAsync } from '../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../project/workflow';
|
||||
import { findAccountByName } from '../user/Account';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
import { createAndroidContextAsync } from './android/build';
|
||||
import { BuildContext, CommonContext } from './context';
|
||||
import { createIosContextAsync } from './ios/build';
|
||||
|
||||
export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfileName,
|
||||
buildProfile,
|
||||
clearCache = false,
|
||||
local,
|
||||
nonInteractive = false,
|
||||
platform,
|
||||
projectDir,
|
||||
skipProjectConfiguration = false,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
clearCache: boolean;
|
||||
local: boolean;
|
||||
nonInteractive: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
skipProjectConfiguration: boolean;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
|
||||
|
||||
const user = await ensureLoggedInAsync();
|
||||
const accountName = getProjectAccountName(exp, user);
|
||||
const projectName = exp.slug;
|
||||
const projectId = await getProjectIdAsync(exp, { env: buildProfile.env });
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
const accountId = findAccountByName(user.accounts, accountName)?.id;
|
||||
|
||||
const credentialsCtx = new CredentialsContext({
|
||||
exp,
|
||||
nonInteractive,
|
||||
projectDir,
|
||||
user,
|
||||
});
|
||||
|
||||
const devClientProperties = getDevClientEventProperties({
|
||||
platform,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
});
|
||||
const trackingCtx = {
|
||||
tracking_id: uuidv4(),
|
||||
platform,
|
||||
...(accountId && { account_id: accountId }),
|
||||
account_name: accountName,
|
||||
project_id: projectId,
|
||||
project_type: workflow,
|
||||
...devClientProperties,
|
||||
};
|
||||
Analytics.logEvent(BuildEvent.BUILD_COMMAND, trackingCtx);
|
||||
|
||||
const commonContext: CommonContext<T> = {
|
||||
accountName,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
clearCache,
|
||||
credentialsCtx,
|
||||
exp,
|
||||
local,
|
||||
nonInteractive,
|
||||
platform,
|
||||
projectDir,
|
||||
projectId,
|
||||
projectName,
|
||||
skipProjectConfiguration,
|
||||
trackingCtx,
|
||||
user,
|
||||
workflow,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||
return {
|
||||
...common,
|
||||
android: await createAndroidContextAsync(common),
|
||||
} as BuildContext<T>;
|
||||
} else {
|
||||
const common = commonContext as CommonContext<Platform.IOS>;
|
||||
return {
|
||||
...common,
|
||||
ios: await createIosContextAsync(common),
|
||||
} as BuildContext<T>;
|
||||
}
|
||||
}
|
||||
|
||||
function getDevClientEventProperties({
|
||||
platform,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
}: {
|
||||
platform: Platform;
|
||||
projectDir: string;
|
||||
buildProfile: BuildProfile;
|
||||
}): Partial<TrackingContext> {
|
||||
let includesDevClient;
|
||||
const version = tryGetDevClientVersion(projectDir);
|
||||
if (platform === Platform.ANDROID && 'gradleCommand' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.gradleCommand?.includes('Debug'));
|
||||
} else if (platform === Platform.IOS && 'buildConfiguration' in buildProfile) {
|
||||
includesDevClient = Boolean(version && buildProfile.buildConfiguration === 'Debug');
|
||||
} else if (buildProfile.developmentClient) {
|
||||
includesDevClient = true;
|
||||
} else {
|
||||
includesDevClient = false;
|
||||
}
|
||||
|
||||
if (version) {
|
||||
return { dev_client: includesDevClient, dev_client_version: version };
|
||||
} else {
|
||||
return { dev_client: includesDevClient };
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetDevClientVersion(projectDir: string): string | null {
|
||||
try {
|
||||
const pkg = JsonFile.read(resolveFrom(projectDir, 'expo-dev-client/package.json'));
|
||||
return pkg.version?.toString() ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -8,24 +8,17 @@ import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../pro
|
||||
import { resolveXcodeBuildContextAsync } from '../../project/ios/scheme';
|
||||
import { findApplicationTarget, resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { BuildRequestSender, JobData, prepareBuildRequestForPlatformAsync } from '../build';
|
||||
import { BuildContext } from '../context';
|
||||
import { BuildContext, CommonContext, IosBuildContext } from '../context';
|
||||
import { transformMetadata } from '../graphql';
|
||||
import { IosMetadataContext } from '../metadata';
|
||||
import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate';
|
||||
import { validateAndSyncProjectConfigurationAsync } from './configure';
|
||||
import { ensureIosCredentialsAsync } from './credentials';
|
||||
import { transformJob } from './graphql';
|
||||
import { prepareJobAsync } from './prepareJob';
|
||||
|
||||
interface BuildConfiguration {
|
||||
targetName?: string;
|
||||
buildConfiguration?: string;
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}
|
||||
|
||||
export async function prepareIosBuildAsync(
|
||||
ctx: BuildContext<Platform.IOS>
|
||||
): Promise<BuildRequestSender> {
|
||||
export async function createIosContextAsync(
|
||||
ctx: CommonContext<Platform.IOS>
|
||||
): Promise<IosBuildContext> {
|
||||
const { buildProfile } = ctx;
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
@@ -50,29 +43,42 @@ export async function prepareIosBuildAsync(
|
||||
},
|
||||
xcodeBuildContext
|
||||
);
|
||||
const buildConfiguration = resolveBuildConfiguration(ctx, targets);
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
const applicationTargetBuildSettings = resolveBuildSettings(ctx, applicationTarget);
|
||||
|
||||
return {
|
||||
bundleIdentifier: applicationTarget.bundleIdentifier,
|
||||
applicationTarget,
|
||||
applicationTargetBuildSettings,
|
||||
targets,
|
||||
xcodeBuildContext,
|
||||
};
|
||||
}
|
||||
|
||||
export async function prepareIosBuildAsync(
|
||||
ctx: BuildContext<Platform.IOS>
|
||||
): Promise<BuildRequestSender> {
|
||||
return await prepareBuildRequestForPlatformAsync({
|
||||
ctx,
|
||||
ensureCredentialsAsync: async (ctx: BuildContext<Platform.IOS>) => {
|
||||
return ensureIosCredentialsAsync(ctx, targets);
|
||||
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
|
||||
},
|
||||
ensureProjectConfiguredAsync: async () => {
|
||||
await validateAndSyncProjectConfigurationAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
buildProfile,
|
||||
buildSettings: buildConfiguration.buildSettings,
|
||||
buildProfile: ctx.buildProfile,
|
||||
buildSettings: ctx.ios.applicationTargetBuildSettings,
|
||||
});
|
||||
},
|
||||
getMetadataContext: (): IosMetadataContext => {
|
||||
return buildConfiguration;
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData<IosCredentials>
|
||||
): Promise<Job> => {
|
||||
return await prepareJobAsync(ctx, { ...jobData, buildScheme: xcodeBuildContext.buildScheme });
|
||||
return await prepareJobAsync(ctx, {
|
||||
...jobData,
|
||||
buildScheme: ctx.ios.xcodeBuildContext.buildScheme,
|
||||
});
|
||||
},
|
||||
sendBuildRequestAsync: async (
|
||||
appId: string,
|
||||
@@ -90,23 +96,17 @@ export async function prepareIosBuildAsync(
|
||||
});
|
||||
}
|
||||
|
||||
function resolveBuildConfiguration(
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
targets: Target[]
|
||||
): BuildConfiguration {
|
||||
function resolveBuildSettings(
|
||||
ctx: CommonContext<Platform.IOS>,
|
||||
applicationTarget: Target
|
||||
): XCBuildConfiguration['buildSettings'] {
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
return { buildSettings: {} };
|
||||
return {};
|
||||
}
|
||||
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,
|
||||
targetName: applicationTarget.targetName,
|
||||
buildConfiguration: applicationTarget.buildConfiguration,
|
||||
};
|
||||
return xcBuildConfiguration?.buildSettings ?? {};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { IosBuildProfile, IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
import { BuildProfile, IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import Log from '../../log';
|
||||
@@ -36,7 +36,7 @@ export async function validateAndSyncProjectConfigurationAsync({
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
buildProfile: IosBuildProfile;
|
||||
buildProfile: BuildProfile<Platform.IOS>;
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
|
||||
@@ -130,6 +130,7 @@ export async function maybeResolveVersionsAsync(
|
||||
};
|
||||
} catch (err: any) {
|
||||
Log.warn('Failed to read app versions.');
|
||||
Log.debug(err);
|
||||
Log.warn(err.message);
|
||||
Log.warn('Proceeding anyway...');
|
||||
return {};
|
||||
@@ -200,7 +201,12 @@ function ensureStaticConfigExists(projectDir: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateTemplateString(s: string, vars: Record<string, any>): string {
|
||||
export function evaluateTemplateString(
|
||||
s: string,
|
||||
buildSettings: XCBuildConfiguration['buildSettings']
|
||||
): string {
|
||||
// necessary because XCBuildConfiguration['buildSettings'] is not a plain object
|
||||
const vars = { ...buildSettings };
|
||||
return s.replace(/\$\((\w+)\)/g, (match, key) => {
|
||||
if (vars.hasOwnProperty(key)) {
|
||||
const value = String(vars[key]);
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Job } from '@expo/eas-build-job';
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import semver from 'semver';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '0.0.50';
|
||||
|
||||
export async function runLocalBuildAsync(job: Job): Promise<void> {
|
||||
const { command, args } = getCommandAndArgs(job);
|
||||
const { command, args } = await getCommandAndArgsAsync(job);
|
||||
await spawnAsync(command, args, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
}
|
||||
|
||||
function getCommandAndArgs(job: Job): { command: string; args: string[] } {
|
||||
async function getCommandAndArgsAsync(job: Job): Promise<{ command: string; args: string[] }> {
|
||||
const jobBase64 = Buffer.from(JSON.stringify({ job })).toString('base64');
|
||||
if (process.env.EAS_LOCAL_BUILD_PLUGIN_PATH) {
|
||||
return {
|
||||
@@ -19,9 +20,20 @@ function getCommandAndArgs(job: Job): { command: string; args: string[] } {
|
||||
args: [jobBase64],
|
||||
};
|
||||
} else {
|
||||
const args = [`${PLUGIN_PACKAGE_NAME}@${PLUGIN_PACKAGE_VERSION}`, jobBase64];
|
||||
if (await isAtLeastNpm7Async()) {
|
||||
// npx shipped with npm >= 7.0.0 requires the "-y" flag to run commands without
|
||||
// prompting the user to install a package that is used for the first time
|
||||
args.unshift('-y');
|
||||
}
|
||||
return {
|
||||
command: 'npx',
|
||||
args: ['-y', `${PLUGIN_PACKAGE_NAME}@${PLUGIN_PACKAGE_VERSION}`, jobBase64],
|
||||
args,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function isAtLeastNpm7Async(): Promise<boolean> {
|
||||
const version = (await spawnAsync('npm', ['--version'])).stdout.trim();
|
||||
return semver.gte(version, '7.0.0');
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { getApplicationIdAsync } from '../project/android/applicationId';
|
||||
import { GradleBuildContext } from '../project/android/gradle';
|
||||
import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier';
|
||||
import Log from '../log';
|
||||
import { getUsername } from '../project/projectUtils';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
import { easCliVersion } from '../utils/easCli';
|
||||
@@ -22,22 +20,20 @@ import {
|
||||
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
||||
import { isExpoUpdatesInstalled } from './utils/updates';
|
||||
|
||||
export type MetadataContext<T extends Platform> = T extends Platform.ANDROID
|
||||
? AndroidMetadataContext
|
||||
: IosMetadataContext;
|
||||
|
||||
export interface AndroidMetadataContext {
|
||||
gradleContext?: GradleBuildContext;
|
||||
}
|
||||
export interface IosMetadataContext {
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
targetName?: string;
|
||||
buildConfiguration?: string;
|
||||
// TODO(JJ): Replace this with the getRuntimeVersionNullable function in @expo/config-plugins
|
||||
function getRuntimeVersionNullable(
|
||||
...[config, platform]: Parameters<typeof Updates.getRuntimeVersion>
|
||||
): string | null {
|
||||
try {
|
||||
return Updates.getRuntimeVersion(config, platform);
|
||||
} catch (e) {
|
||||
Log.debug(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function collectMetadataAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>,
|
||||
platformContext: MetadataContext<T>
|
||||
ctx: BuildContext<T>
|
||||
): Promise<Metadata> {
|
||||
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
|
||||
const distribution =
|
||||
@@ -46,16 +42,16 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
: ctx.buildProfile.distribution) ?? 'store';
|
||||
const metadata = {
|
||||
trackingContext: ctx.trackingCtx,
|
||||
...(await maybeResolveVersionsAsync(ctx, platformContext)),
|
||||
...(await maybeResolveVersionsAsync(ctx)),
|
||||
cliVersion: easCliVersion,
|
||||
workflow: ctx.workflow,
|
||||
credentialsSource: ctx.buildProfile.credentialsSource,
|
||||
sdkVersion: ctx.exp.sdkVersion,
|
||||
runtimeVersion: ctx.exp.runtimeVersion,
|
||||
runtimeVersion: getRuntimeVersionNullable(ctx.exp, ctx.platform) ?? undefined,
|
||||
...channelOrReleaseChannel,
|
||||
distribution,
|
||||
appName: ctx.exp.name,
|
||||
appIdentifier: await resolveAppIdentifierAsync(ctx, platformContext),
|
||||
appIdentifier: resolveAppIdentifier(ctx),
|
||||
buildProfile: ctx.buildProfileName,
|
||||
gitCommitHash: await getVcsClient().getCommitHashAsync(),
|
||||
isGitWorkingTreeDirty: await getVcsClient().hasUncommittedChangesAsync(),
|
||||
@@ -70,15 +66,14 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
}
|
||||
|
||||
async function maybeResolveVersionsAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>,
|
||||
platformContext: MetadataContext<T>
|
||||
ctx: BuildContext<T>
|
||||
): Promise<{ appBuildVersion?: string; appVersion?: string }> {
|
||||
if (ctx.platform === Platform.IOS) {
|
||||
const iosContext = platformContext as IosMetadataContext;
|
||||
const iosContext = ctx as BuildContext<Platform.IOS>;
|
||||
return await maybeResolveIosVersionsAsync(
|
||||
ctx.projectDir,
|
||||
ctx.exp,
|
||||
iosContext?.buildSettings ?? {}
|
||||
iosContext.ios.applicationTargetBuildSettings
|
||||
);
|
||||
} else if (ctx.platform === Platform.ANDROID) {
|
||||
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
|
||||
@@ -88,19 +83,11 @@ async function maybeResolveVersionsAsync<T extends Platform>(
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveAppIdentifierAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>,
|
||||
platformContext: MetadataContext<T>
|
||||
): Promise<string> {
|
||||
function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string {
|
||||
if (ctx.platform === Platform.IOS) {
|
||||
const iosContext = platformContext as IosMetadataContext;
|
||||
return await getBundleIdentifierAsync(ctx.projectDir, ctx.exp, {
|
||||
targetName: iosContext.targetName,
|
||||
buildConfiguration: iosContext.buildConfiguration,
|
||||
});
|
||||
return (ctx as BuildContext<Platform.IOS>).ios.bundleIdentifier;
|
||||
} else {
|
||||
const androidContext = platformContext as AndroidMetadataContext;
|
||||
return await getApplicationIdAsync(ctx.projectDir, ctx.exp, androidContext.gradleContext);
|
||||
return (ctx as BuildContext<Platform.ANDROID>).android.applicationId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import path from 'path';
|
||||
|
||||
import Log, { learnMore } from '../log';
|
||||
import { getVcsClient } from '../vcs';
|
||||
import { BuildContext } from './context';
|
||||
import { CommonContext } from './context';
|
||||
|
||||
export function checkNodeEnvVariable(ctx: BuildContext<Platform>): void {
|
||||
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
|
||||
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
|
||||
Log.warn(
|
||||
'You set NODE_ENV=production in the build profile. Remember that it will be available during the entire build process. In particular, it will make yarn/npm install only production packages.'
|
||||
@@ -16,7 +16,7 @@ export function checkNodeEnvVariable(ctx: BuildContext<Platform>): void {
|
||||
}
|
||||
|
||||
export async function checkGoogleServicesFileAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
ctx: CommonContext<T>
|
||||
): Promise<void> {
|
||||
if (ctx.workflow === Workflow.GENERIC || ctx.buildProfile?.env?.GOOGLE_SERVICES_FILE) {
|
||||
return;
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
initAsync as initAnalyticsAsync,
|
||||
logEvent,
|
||||
} from '../analytics/rudderstackClient';
|
||||
import Log from '../log';
|
||||
import { findProjectRootAsync } from '../project/projectUtils';
|
||||
import { getUserAsync } from '../user/User';
|
||||
import { ensureLoggedInAsync } from '../user/actions';
|
||||
@@ -27,8 +26,6 @@ export default abstract class EasCommand extends Command {
|
||||
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
async run(): Promise<any> {
|
||||
EasJsonReader.setLog(Log);
|
||||
|
||||
await initAnalyticsAsync();
|
||||
await this.applyCliConfigAsync();
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ import { createUpdateBranchOnAppAsync } from './create';
|
||||
import { listBranchesAsync } from './list';
|
||||
import { viewUpdateBranchAsync } from './view';
|
||||
|
||||
const EAS_UPDATE_URL = 'https://u.expo.dev';
|
||||
|
||||
export const defaultPublishPlatforms: PublishPlatform[] = ['android', 'ios'];
|
||||
type PlatformFlag = PublishPlatform | 'all';
|
||||
|
||||
@@ -204,8 +206,8 @@ export default class BranchPublish extends EasCommand {
|
||||
});
|
||||
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, platformFlag, projectDir);
|
||||
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
await checkEASUpdateURLIsSetAsync(exp);
|
||||
|
||||
if (!branchName && autoFlag) {
|
||||
branchName =
|
||||
@@ -473,3 +475,19 @@ function formatUpdateTitle(
|
||||
'mmm dd HH:MM'
|
||||
)} by ${actorName}, runtimeVersion: ${runtimeVersion}] ${message}`;
|
||||
}
|
||||
|
||||
async function getEASUpdateURLAsync(exp: ExpoConfig): Promise<string> {
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
return new URL(projectId, EAS_UPDATE_URL).href;
|
||||
}
|
||||
|
||||
async function checkEASUpdateURLIsSetAsync(exp: ExpoConfig): Promise<void> {
|
||||
const configuredURL = exp.updates?.url;
|
||||
const expectedURL = await getEASUpdateURLAsync(exp);
|
||||
|
||||
if (configuredURL !== expectedURL) {
|
||||
throw new Error(
|
||||
`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import Log, { learnMore } from '../../log';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { findProjectRootAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
|
||||
export default class BuildConfigure extends EasCommand {
|
||||
static description = 'Configure the project to support EAS Build.';
|
||||
@@ -21,11 +22,14 @@ export default class BuildConfigure extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = this.parse(BuildConfigure);
|
||||
|
||||
Log.log(
|
||||
'💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.'
|
||||
);
|
||||
Log.newLine();
|
||||
|
||||
await getVcsClient().ensureRepoExistsAsync();
|
||||
|
||||
const platform =
|
||||
(flags.platform as RequestedPlatform | undefined) ?? (await promptForPlatformAsync());
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { EasJsonReader } from '@expo/eas-json';
|
||||
import { BuildProfile, EasJsonReader, SubmitProfile } from '@expo/eas-json';
|
||||
import { flags } from '@oclif/command';
|
||||
import { error } from '@oclif/errors';
|
||||
import chalk from 'chalk';
|
||||
@@ -10,13 +10,13 @@ import nullthrows from 'nullthrows';
|
||||
import { prepareAndroidBuildAsync } from '../../build/android/build';
|
||||
import { BuildRequestSender, waitForBuildEndAsync } from '../../build/build';
|
||||
import { ensureProjectConfiguredAsync } from '../../build/configure';
|
||||
import { BuildContext, createBuildContextAsync } from '../../build/context';
|
||||
import { BuildContext } from '../../build/context';
|
||||
import { createBuildContextAsync } from '../../build/createContext';
|
||||
import { prepareIosBuildAsync } from '../../build/ios/build';
|
||||
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from '../../build/utils/devClient';
|
||||
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
|
||||
import { ensureRepoIsCleanAsync, reviewAndCommitChangesAsync } from '../../build/utils/repository';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { CredentialsContext } from '../../credentials/context';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
@@ -64,7 +64,7 @@ interface RawBuildFlags {
|
||||
interface BuildFlags {
|
||||
requestedPlatform: RequestedPlatform;
|
||||
skipProjectConfiguration: boolean;
|
||||
profile: string | null;
|
||||
profile?: string;
|
||||
nonInteractive: boolean;
|
||||
local: boolean;
|
||||
wait: boolean;
|
||||
@@ -152,7 +152,7 @@ export default class Build extends EasCommand {
|
||||
type: 'build',
|
||||
projectDir,
|
||||
platforms,
|
||||
profileName: flags.profile,
|
||||
profileName: flags.profile ?? undefined,
|
||||
});
|
||||
|
||||
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
||||
@@ -190,16 +190,26 @@ export default class Build extends EasCommand {
|
||||
|
||||
const submissions: SubmissionFragment[] = [];
|
||||
if (flags.autoSubmit) {
|
||||
const submitProfiles = await getProfilesAsync({
|
||||
projectDir,
|
||||
platforms,
|
||||
profileName: flags.submitProfile,
|
||||
type: 'submit',
|
||||
});
|
||||
for (const startedBuild of startedBuilds) {
|
||||
const submitProfile = nullthrows(
|
||||
submitProfiles.find(
|
||||
({ platform }) => toAppPlatform(platform) === startedBuild.build.platform
|
||||
)
|
||||
).profile;
|
||||
const submission = await this.prepareAndStartSubmissionAsync({
|
||||
build: startedBuild.build,
|
||||
credentialsCtx: nullthrows(
|
||||
buildCtxByPlatform[startedBuild.build.platform]?.credentialsCtx
|
||||
),
|
||||
flags,
|
||||
buildCtx: nullthrows(buildCtxByPlatform[startedBuild.build.platform]),
|
||||
moreBuilds: startedBuilds.length > 1,
|
||||
projectDir,
|
||||
buildProfile: startedBuild.buildProfile,
|
||||
buildProfile: startedBuild.buildProfile.profile,
|
||||
submitProfile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
submissions.push(submission);
|
||||
}
|
||||
@@ -262,7 +272,7 @@ export default class Build extends EasCommand {
|
||||
return {
|
||||
requestedPlatform,
|
||||
skipProjectConfiguration: flags['skip-project-configuration'],
|
||||
profile: profile ?? null,
|
||||
profile,
|
||||
nonInteractive,
|
||||
local: flags['local'],
|
||||
wait: flags['wait'],
|
||||
@@ -329,31 +339,32 @@ export default class Build extends EasCommand {
|
||||
|
||||
private async prepareAndStartSubmissionAsync({
|
||||
build,
|
||||
credentialsCtx,
|
||||
flags,
|
||||
buildCtx,
|
||||
moreBuilds,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
submitProfile,
|
||||
nonInteractive,
|
||||
}: {
|
||||
build: BuildFragment;
|
||||
credentialsCtx: CredentialsContext;
|
||||
flags: BuildFlags;
|
||||
buildCtx: BuildContext<Platform>;
|
||||
moreBuilds: boolean;
|
||||
projectDir: string;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
buildProfile: BuildProfile;
|
||||
submitProfile: SubmitProfile;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<SubmissionFragment> {
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
const platform = toPlatform(build.platform);
|
||||
const submitProfile = await easJsonReader.readSubmitProfileAsync(platform, flags.submitProfile);
|
||||
const submissionCtx = await createSubmissionContextAsync({
|
||||
platform,
|
||||
projectDir,
|
||||
projectId: build.project.id,
|
||||
profile: submitProfile,
|
||||
archiveFlags: { id: build.id },
|
||||
nonInteractive: flags.nonInteractive,
|
||||
env: buildProfile.profile.env,
|
||||
credentialsCtx,
|
||||
nonInteractive,
|
||||
env: buildProfile.env,
|
||||
credentialsCtx: buildCtx.credentialsCtx,
|
||||
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
|
||||
});
|
||||
|
||||
if (moreBuilds) {
|
||||
@@ -387,7 +398,7 @@ export async function handleDeprecatedEasJsonAsync(
|
||||
return;
|
||||
}
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
const rawEasJson = await easJsonReader.readRawAsync();
|
||||
const rawEasJson = await easJsonReader.readAsync();
|
||||
if (rawEasJson?.cli) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export default class Config extends EasCommand {
|
||||
},
|
||||
]));
|
||||
|
||||
const profile = await reader.readBuildProfileAsync(platform, profileName);
|
||||
const profile = await reader.getBuildProfileAsync(platform, profileName);
|
||||
const config = getExpoConfig(projectDir, { env: profile.env, isPublicConfig: true });
|
||||
|
||||
Log.log(getProjectConfigDescription(projectDir));
|
||||
|
||||
@@ -14,8 +14,7 @@ export default class WebhookCreate extends EasCommand {
|
||||
static flags = {
|
||||
event: flags.enum({
|
||||
description: 'Event type that triggers the webhook',
|
||||
options: [WebhookType.Build],
|
||||
default: WebhookType.Build,
|
||||
options: [WebhookType.Build, WebhookType.Submit],
|
||||
}),
|
||||
url: flags.string({
|
||||
description: 'Webhook URL',
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class WebhookList extends EasCommand {
|
||||
static flags = {
|
||||
event: flags.enum({
|
||||
description: 'Event type that triggers the webhook',
|
||||
options: [WebhookType.Build],
|
||||
options: [WebhookType.Build, WebhookType.Submit],
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ export default class WebhookUpdate extends EasCommand {
|
||||
}),
|
||||
event: flags.enum({
|
||||
description: 'Event type that triggers the webhook',
|
||||
options: [WebhookType.Build],
|
||||
default: WebhookType.Build,
|
||||
options: [WebhookType.Build, WebhookType.Submit],
|
||||
}),
|
||||
url: flags.string({
|
||||
description: 'Webhook URL',
|
||||
|
||||
@@ -150,6 +150,30 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
expect(results.disabled).toStrictEqual([]);
|
||||
});
|
||||
|
||||
// It's unclear what is happening in this issue, so for now we can provide a more
|
||||
// helpful error message until we can get a reproducible example.
|
||||
// https://github.com/expo/eas-cli/issues/740
|
||||
it('throw useful error message on unexpected error', async () => {
|
||||
const bundleId = {
|
||||
getBundleIdCapabilitiesAsync: jest.fn(() => []),
|
||||
updateBundleIdCapabilityAsync: jest.fn(() => {
|
||||
throw new Error(`bundle 'XXX333' cannot be deleted. Delete all the Apps`);
|
||||
}),
|
||||
attributes: {
|
||||
identifier: 'dev.expo.app',
|
||||
},
|
||||
id: 'XXX333',
|
||||
} as any;
|
||||
|
||||
await expect(
|
||||
syncCapabilitiesForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.healthkit': true,
|
||||
})
|
||||
).rejects.toThrowError(
|
||||
`https://developer.apple.com/account/resources/identifiers/bundleId/edit/XXX333`
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot skip complex duplicates', async () => {
|
||||
const bundleId = {
|
||||
getBundleIdCapabilitiesAsync: jest.fn(() => [
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@expo/apple-utils';
|
||||
import { JSONObject, JSONValue } from '@expo/json-file';
|
||||
import getenv from 'getenv';
|
||||
import { inspect } from 'util';
|
||||
|
||||
import Log from '../../../log';
|
||||
|
||||
@@ -100,7 +101,20 @@ export async function syncCapabilitiesForEntitlementsAsync(
|
||||
);
|
||||
|
||||
if (modifiedRequest.length) {
|
||||
await bundleId.updateBundleIdCapabilityAsync(modifiedRequest);
|
||||
Log.debug(`Patch Request:`, inspect(modifiedRequest, { depth: null, colors: true }));
|
||||
try {
|
||||
await bundleId.updateBundleIdCapabilityAsync(modifiedRequest);
|
||||
} catch (error: any) {
|
||||
if (error.message.match(/bundle '[\w\d]+' cannot be deleted. Delete all the Apps/)) {
|
||||
Log.error(
|
||||
'Failed to patch capabilities:',
|
||||
inspect(modifiedRequest, { depth: null, colors: true })
|
||||
);
|
||||
throw new Error(
|
||||
`Unexpected error occurred while attempting to update capabilities for app "${bundleId.attributes.identifier}".\nCapabilities can be modified manually in the Apple developer console at https://developer.apple.com/account/resources/identifiers/bundleId/edit/${bundleId.id}.\nAuto capability syncing can be disabled with the environment variable \`EXPO_NO_CAPABILITY_SYNC=1\`.\n${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { enabled: enabledCapabilityNames, disabled: disabledCapabilityNames };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IOSConfig, compileModsAsync } from '@expo/config-plugins';
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { JSONObject } from '@expo/json-file';
|
||||
import { getPrebuildConfig } from '@expo/prebuild-config';
|
||||
import { getPrebuildConfigAsync } from '@expo/prebuild-config';
|
||||
|
||||
import { readPlistAsync } from '../../../utils/plist';
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function getManagedEntitlementsJsonAsync(
|
||||
...process.env,
|
||||
...env,
|
||||
};
|
||||
const { exp } = getPrebuildConfig(projectDir, { platforms: ['ios'] });
|
||||
const { exp } = await getPrebuildConfigAsync(projectDir, { platforms: ['ios'] });
|
||||
|
||||
const expWithMods = await compileModsAsync(exp, {
|
||||
projectRoot: projectDir,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { AndroidBuildProfile } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import assert from 'assert';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
@@ -142,7 +142,7 @@ export class ManageAndroid {
|
||||
|
||||
private async createProjectContextAsync(ctx: CredentialsContext): Promise<{
|
||||
gradleContext?: GradleBuildContext;
|
||||
buildProfile?: AndroidBuildProfile;
|
||||
buildProfile?: BuildProfile<Platform.ANDROID>;
|
||||
}> {
|
||||
if (!ctx.hasProjectContext) {
|
||||
return {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { IosBuildProfile } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import assert from 'assert';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -155,7 +155,11 @@ export class ManageIos {
|
||||
private async createProjectContextAsync(
|
||||
ctx: CredentialsContext,
|
||||
account: Account
|
||||
): Promise<{ app: App | null; targets: Target[] | null; buildProfile: IosBuildProfile | null }> {
|
||||
): Promise<{
|
||||
app: App | null;
|
||||
targets: Target[] | null;
|
||||
buildProfile: BuildProfile<Platform.IOS> | null;
|
||||
}> {
|
||||
if (!ctx.hasProjectContext) {
|
||||
return {
|
||||
app: null,
|
||||
@@ -219,7 +223,7 @@ export class ManageIos {
|
||||
ctx: CredentialsContext,
|
||||
app: App,
|
||||
targets: Target[],
|
||||
buildProfile: IosBuildProfile,
|
||||
buildProfile: BuildProfile<Platform.IOS>,
|
||||
action: IosActionType
|
||||
): Promise<void> {
|
||||
if (action === IosActionType.SetUpBuildCredentials) {
|
||||
|
||||
@@ -14,7 +14,7 @@ export class SelectBuildProfileFromEasJson<T extends Platform> {
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<BuildProfile<T>> {
|
||||
const profileName = await this.getProfileNameFromEasConfigAsync(ctx);
|
||||
const easConfig = await this.easJsonReader.readBuildProfileAsync<T>(this.platform, profileName);
|
||||
const easConfig = await this.easJsonReader.getBuildProfileAsync<T>(this.platform, profileName);
|
||||
Log.succeed(`Using build profile: ${profileName}`);
|
||||
return easConfig;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,11 +1,12 @@
|
||||
import { IosBuildProfile } from '@expo/eas-json';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { IosDistributionType as IosDistributionTypeGraphql } from '../../graphql/generated';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { CredentialsContext } from '../context';
|
||||
|
||||
export class SelectIosDistributionTypeGraphqlFromBuildProfile {
|
||||
constructor(private buildProfile: IosBuildProfile) {}
|
||||
constructor(private buildProfile: BuildProfile<Platform.IOS>) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosDistributionTypeGraphql> {
|
||||
const { distribution, simulator } = this.buildProfile;
|
||||
|
||||
+6
-5
@@ -1,4 +1,5 @@
|
||||
import { IosBuildProfile } from '@expo/eas-json';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { IosDistributionType as IosDistributionTypeGraphql } from '../../../graphql/generated';
|
||||
import { createCtxMock } from '../../__tests__/fixtures-context';
|
||||
@@ -8,7 +9,7 @@ describe('SelectIosDistributionTypeGraphqlFromBuildProfile', () => {
|
||||
it('errors with a simulator distribution', async () => {
|
||||
const buildProfile = {
|
||||
simulator: true,
|
||||
} as IosBuildProfile;
|
||||
} as BuildProfile<Platform.IOS>;
|
||||
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
@@ -22,7 +23,7 @@ describe('SelectIosDistributionTypeGraphqlFromBuildProfile', () => {
|
||||
it('returns APP_STORE type with a store distribution', async () => {
|
||||
const buildProfile = {
|
||||
distribution: 'store',
|
||||
} as IosBuildProfile;
|
||||
} as BuildProfile<Platform.IOS>;
|
||||
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
@@ -37,7 +38,7 @@ describe('SelectIosDistributionTypeGraphqlFromBuildProfile', () => {
|
||||
const buildProfile = {
|
||||
distribution: 'internal',
|
||||
enterpriseProvisioning: 'universal',
|
||||
} as IosBuildProfile;
|
||||
} as BuildProfile<Platform.IOS>;
|
||||
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
@@ -52,7 +53,7 @@ describe('SelectIosDistributionTypeGraphqlFromBuildProfile', () => {
|
||||
const buildProfile = {
|
||||
distribution: 'internal',
|
||||
enterpriseProvisioning: 'adhoc',
|
||||
} as IosBuildProfile;
|
||||
} as BuildProfile<Platform.IOS>;
|
||||
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
|
||||
+2
-1
@@ -1411,7 +1411,8 @@ export type WebhookFilter = {
|
||||
};
|
||||
|
||||
export enum WebhookType {
|
||||
Build = 'BUILD'
|
||||
Build = 'BUILD',
|
||||
Submit = 'SUBMIT'
|
||||
}
|
||||
|
||||
export type Webhook = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { IosBuildProfile } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Log from '../../log';
|
||||
@@ -20,7 +20,7 @@ export async function resolveXcodeBuildContextAsync(
|
||||
projectDir,
|
||||
nonInteractive,
|
||||
}: { exp: ExpoConfig; projectDir: string; nonInteractive: boolean },
|
||||
buildProfile: IosBuildProfile
|
||||
buildProfile: BuildProfile<Platform.IOS>
|
||||
): Promise<XcodeBuildContext> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
|
||||
@@ -128,7 +128,8 @@ export default class AndroidSubmitCommand {
|
||||
path: serviceAccountKeyPath,
|
||||
});
|
||||
}
|
||||
let androidApplicationIdentifier: string | undefined = this.ctx.profile.applicationId;
|
||||
let androidApplicationIdentifier: string | undefined =
|
||||
this.ctx.applicationIdentifierOverride ?? this.ctx.profile.applicationId;
|
||||
if (!androidApplicationIdentifier) {
|
||||
const androidApplicationIdentifierResult =
|
||||
await this.maybeGetAndroidPackageFromCurrentProjectAsync();
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface SubmissionContext<T extends Platform> {
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
user: Actor;
|
||||
applicationIdentifierOverride?: string;
|
||||
}
|
||||
|
||||
export interface SubmitArchiveFlags {
|
||||
@@ -43,8 +44,9 @@ export async function createSubmissionContextAsync<T extends Platform>(params: {
|
||||
profile: SubmitProfile<T>;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
applicationIdentifier?: string;
|
||||
}): Promise<SubmissionContext<T>> {
|
||||
const { projectDir, nonInteractive } = params;
|
||||
const { applicationIdentifier, projectDir, nonInteractive } = params;
|
||||
const exp = getExpoConfig(projectDir, { env: params.env });
|
||||
const { env, ...rest } = params;
|
||||
const user = await ensureLoggedInAsync();
|
||||
@@ -74,5 +76,6 @@ export async function createSubmissionContextAsync<T extends Platform>(params: {
|
||||
projectName,
|
||||
user,
|
||||
trackingCtx,
|
||||
applicationIdentifierOverride: applicationIdentifier,
|
||||
};
|
||||
}
|
||||
@@ -119,7 +119,8 @@ export default class IosSubmitCommand {
|
||||
return { appSpecificPasswordSource: this.resolveAppSpecificPasswordSource() };
|
||||
}
|
||||
|
||||
let bundleIdentifier = this.ctx.profile.bundleIdentifier;
|
||||
let bundleIdentifier =
|
||||
this.ctx.applicationIdentifierOverride ?? this.ctx.profile.bundleIdentifier;
|
||||
if (!bundleIdentifier) {
|
||||
const bundleIdentifierResult = await this.maybeGetIosBundleIdentifierAsync();
|
||||
if (!bundleIdentifierResult.ok) {
|
||||
|
||||
@@ -8,8 +8,8 @@ jest.mock('@expo/eas-json', () => {
|
||||
|
||||
const EasJsonReaderMock = jest.fn();
|
||||
EasJsonReaderMock.prototype = {
|
||||
readBuildProfileAsync: jest.fn(),
|
||||
readSubmitProfileAsync: jest.fn(),
|
||||
getBuildProfileAsync: jest.fn(),
|
||||
getSubmitProfileAsync: jest.fn(),
|
||||
};
|
||||
return {
|
||||
...actual,
|
||||
@@ -17,11 +17,11 @@ jest.mock('@expo/eas-json', () => {
|
||||
};
|
||||
});
|
||||
|
||||
const readBuildProfileAsync = jest.spyOn(EasJsonReader.prototype, 'readBuildProfileAsync');
|
||||
const getBuildProfileAsync = jest.spyOn(EasJsonReader.prototype, 'getBuildProfileAsync');
|
||||
|
||||
describe(getProfilesAsync, () => {
|
||||
afterEach(() => {
|
||||
readBuildProfileAsync.mockReset();
|
||||
getBuildProfileAsync.mockReset();
|
||||
});
|
||||
|
||||
it('defaults to production profile', async () => {
|
||||
@@ -34,17 +34,17 @@ describe(getProfilesAsync, () => {
|
||||
|
||||
expect(result[0].profileName).toBe('production');
|
||||
expect(result[1].profileName).toBe('production');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'production');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.IOS, 'production');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'production');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.IOS, 'production');
|
||||
});
|
||||
|
||||
it('defaults to release profile when production profile is non-existent', async () => {
|
||||
readBuildProfileAsync
|
||||
.mockRejectedValueOnce(() => {
|
||||
throw new Error();
|
||||
getBuildProfileAsync
|
||||
.mockImplementationOnce(() => {
|
||||
throw new errors.MissingProfileError();
|
||||
})
|
||||
.mockRejectedValueOnce(() => {
|
||||
throw new Error();
|
||||
.mockImplementationOnce(() => {
|
||||
throw new errors.MissingProfileError();
|
||||
});
|
||||
|
||||
const result = await getProfilesAsync({
|
||||
@@ -56,20 +56,16 @@ describe(getProfilesAsync, () => {
|
||||
|
||||
expect(result[0].profileName).toBe('release');
|
||||
expect(result[1].profileName).toBe('release');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'production');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.IOS, 'production');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'release');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.IOS, 'release');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'production');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.IOS, 'production');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'release');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.IOS, 'release');
|
||||
});
|
||||
|
||||
it('fails when neither production or release profiles are present', async () => {
|
||||
readBuildProfileAsync
|
||||
.mockRejectedValueOnce(() => {
|
||||
throw new Error();
|
||||
})
|
||||
.mockRejectedValueOnce(() => {
|
||||
throw new Error();
|
||||
});
|
||||
getBuildProfileAsync.mockImplementation(() => {
|
||||
throw new errors.MissingProfileError();
|
||||
});
|
||||
|
||||
await expect(
|
||||
getProfilesAsync({
|
||||
@@ -78,7 +74,7 @@ describe(getProfilesAsync, () => {
|
||||
profileName: undefined,
|
||||
type: 'build',
|
||||
})
|
||||
).rejects.toThrowError(/There is no build profile named "production" in eas.json/);
|
||||
).rejects.toThrowError(errors.MissingProfileError);
|
||||
});
|
||||
|
||||
it('gets a specific profile', async () => {
|
||||
@@ -91,12 +87,12 @@ describe(getProfilesAsync, () => {
|
||||
|
||||
expect(result[0].profileName).toBe('custom-profile');
|
||||
expect(result[1].profileName).toBe('custom-profile');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'custom-profile');
|
||||
expect(readBuildProfileAsync).toBeCalledWith(Platform.IOS, 'custom-profile');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.ANDROID, 'custom-profile');
|
||||
expect(getBuildProfileAsync).toBeCalledWith(Platform.IOS, 'custom-profile');
|
||||
});
|
||||
|
||||
it('throws validation error if eas.json is invalid', async () => {
|
||||
readBuildProfileAsync.mockImplementation(() => {
|
||||
getBuildProfileAsync.mockImplementation(() => {
|
||||
throw new errors.InvalidEasJsonError('eas.json is not valid');
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonReader, SubmitProfile, errors } from '@expo/eas-json';
|
||||
import {
|
||||
BuildProfile,
|
||||
EasJsonReader,
|
||||
ProfileType,
|
||||
SubmitProfile,
|
||||
errors,
|
||||
getDefaultSubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
|
||||
import Log from '../log';
|
||||
|
||||
type ProfileType = 'build' | 'submit';
|
||||
|
||||
type EasProfile<T extends ProfileType> = T extends 'build'
|
||||
? BuildProfile<Platform>
|
||||
: SubmitProfile<Platform>;
|
||||
@@ -23,7 +28,7 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
}: {
|
||||
projectDir: string;
|
||||
platforms: Platform[];
|
||||
profileName?: string | null;
|
||||
profileName?: string;
|
||||
type: T;
|
||||
}): Promise<ProfileData<T>[]> {
|
||||
const results = platforms.map(async function (platform) {
|
||||
@@ -32,23 +37,28 @@ export async function getProfilesAsync<T extends ProfileType>({
|
||||
|
||||
if (!profileName) {
|
||||
try {
|
||||
profile = await readProfileAsync({ projectDir, platform, type, profileName: 'production' });
|
||||
profileName = 'production';
|
||||
profile = await readProfileAsync({ projectDir, platform, type, profileName });
|
||||
} catch (errorOuter) {
|
||||
if (errorOuter instanceof errors.InvalidEasJsonError) {
|
||||
if (!(errorOuter instanceof errors.MissingProfileError)) {
|
||||
throw errorOuter;
|
||||
}
|
||||
try {
|
||||
profile = await readProfileAsync({ projectDir, platform, type, profileName: 'release' });
|
||||
profileName = 'release';
|
||||
profile = await readProfileAsync({ projectDir, platform, type, profileName });
|
||||
Log.warn(
|
||||
'The default profile changed from "release" to "production". We detected that you still have a "release" build profile, so we are using it. Update eas.json to have a profile named "production" under the `build` key, or specify which profile you\'d like to use with the --profile flag. This fallback behavior will be removed in the next major version of EAS CLI.'
|
||||
);
|
||||
} catch (errorInner) {
|
||||
if (errorInner instanceof errors.InvalidEasJsonError) {
|
||||
if (!(errorInner instanceof errors.MissingProfileError)) {
|
||||
throw errorInner;
|
||||
}
|
||||
throw new Error(`There is no ${type} profile named "production" in eas.json`);
|
||||
const defaultProfile = getDefaultProfile({ platform, type });
|
||||
if (!defaultProfile) {
|
||||
throw errorInner;
|
||||
}
|
||||
profileName = '__default__';
|
||||
profile = defaultProfile;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -73,13 +83,27 @@ async function readProfileAsync<T extends ProfileType>({
|
||||
}: {
|
||||
projectDir: string;
|
||||
platform: Platform;
|
||||
type: ProfileType;
|
||||
type: T;
|
||||
profileName: string;
|
||||
}): Promise<EasProfile<T>> {
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
if (type === 'build') {
|
||||
return (await easJsonReader.readBuildProfileAsync(platform, profileName)) as EasProfile<T>;
|
||||
return (await easJsonReader.getBuildProfileAsync(platform, profileName)) as EasProfile<T>;
|
||||
} else {
|
||||
return (await easJsonReader.readSubmitProfileAsync(platform, profileName)) as EasProfile<T>;
|
||||
return (await easJsonReader.getSubmitProfileAsync(platform, profileName)) as EasProfile<T>;
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultProfile<T extends ProfileType>({
|
||||
platform,
|
||||
type,
|
||||
}: {
|
||||
platform: Platform;
|
||||
type: T;
|
||||
}): EasProfile<T> | null {
|
||||
if (type === 'build') {
|
||||
return null;
|
||||
} else {
|
||||
return getDefaultSubmitProfile(platform) as EasProfile<T>;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import { exit } from '@oclif/errors';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
@@ -16,7 +17,13 @@ import { Client } from '../vcs';
|
||||
export default class GitClient extends Client {
|
||||
public async ensureRepoExistsAsync(): Promise<void> {
|
||||
if (!(await isGitInstalledAsync())) {
|
||||
throw new Error('git command not found, install it before proceeding');
|
||||
Log.error(
|
||||
`${chalk.bold('git')} command not found. Install it before proceeding or set ${chalk.bold(
|
||||
'EAS_NO_VCS=1'
|
||||
)} to use EAS CLI without Git (or any other version control system).`
|
||||
);
|
||||
Log.error(learnMore('https://expo.fyi/eas-vcs-workflow'));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (await doesGitRepoExistAsync()) {
|
||||
|
||||
@@ -7,19 +7,36 @@ import { promptAsync } from '../prompts';
|
||||
|
||||
export async function prepareInputParamsAsync(
|
||||
{
|
||||
event,
|
||||
event: maybeEvent,
|
||||
url: maybeUrl,
|
||||
secret: maybeSecret,
|
||||
}: {
|
||||
event: WebhookType;
|
||||
event?: WebhookType;
|
||||
url?: string;
|
||||
secret?: string;
|
||||
},
|
||||
existingWebhook?: WebhookFragment
|
||||
): Promise<WebhookInput> {
|
||||
let event: WebhookType | undefined = maybeEvent;
|
||||
let url: string | undefined = maybeUrl;
|
||||
let secret: string | undefined = maybeSecret;
|
||||
|
||||
if (!event) {
|
||||
const choices = [
|
||||
{ title: 'Build', value: WebhookType.Build },
|
||||
{ title: 'Submit', value: WebhookType.Submit },
|
||||
];
|
||||
({ event } = await promptAsync({
|
||||
type: 'select',
|
||||
name: 'event',
|
||||
message: 'Webhook event type:',
|
||||
choices,
|
||||
initial: existingWebhook?.event
|
||||
? choices.findIndex(choice => choice.value === existingWebhook.event)
|
||||
: undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
if (!url || !validateURL(url)) {
|
||||
const urlValidationMessage =
|
||||
'The provided webhook URL is invalid and must be an absolute URL, including a scheme.';
|
||||
@@ -50,7 +67,7 @@ export async function prepareInputParamsAsync(
|
||||
}
|
||||
|
||||
return {
|
||||
event,
|
||||
event: nullthrows(event),
|
||||
url: nullthrows(url),
|
||||
secret: nullthrows(secret),
|
||||
};
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "0.37.0",
|
||||
"version": "0.38.2",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
|
||||
import { AndroidBuildProfile, CommonBuildProfile, IosBuildProfile } from './EasBuild.types';
|
||||
import { AndroidSubmitProfile, IosSubmitProfile } from './EasSubmit.types';
|
||||
|
||||
export enum CredentialsSource {
|
||||
LOCAL = 'local',
|
||||
REMOTE = 'remote',
|
||||
}
|
||||
|
||||
export interface RawBuildProfile extends Partial<CommonBuildProfile> {
|
||||
extends?: string;
|
||||
[Platform.ANDROID]?: Partial<AndroidBuildProfile>;
|
||||
[Platform.IOS]?: Partial<IosBuildProfile>;
|
||||
}
|
||||
|
||||
export interface EasSubmitConfiguration {
|
||||
[Platform.ANDROID]?: AndroidSubmitProfile;
|
||||
[Platform.IOS]?: IosSubmitProfile;
|
||||
}
|
||||
|
||||
export interface CliConfig {
|
||||
version?: string;
|
||||
requireCommit?: boolean;
|
||||
}
|
||||
|
||||
export interface EasJson {
|
||||
cli?: CliConfig;
|
||||
build: { [profile: string]: RawBuildProfile };
|
||||
submit?: { [profile: string]: EasSubmitConfiguration };
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import envString from 'env-string';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { BuildProfile } from './EasBuild.types';
|
||||
import { CliConfig, CredentialsSource, EasJson, RawBuildProfile } from './EasJson.types';
|
||||
import {
|
||||
AndroidSubmitProfileSchema,
|
||||
CliConfigSchema,
|
||||
EasJsonSchema,
|
||||
IosSubmitProfileSchema,
|
||||
MinimalEasJsonSchema,
|
||||
} from './EasJsonSchema';
|
||||
import {
|
||||
AndroidSubmitProfileFieldsToEvaluate,
|
||||
IosSubmitProfileFieldsToEvaluate,
|
||||
SubmitProfile,
|
||||
} from './EasSubmit.types';
|
||||
import { InvalidEasJsonError, MissingEasJsonError } from './errors';
|
||||
|
||||
interface EasJsonPreValidation {
|
||||
cli?: object;
|
||||
build: { [profile: string]: object };
|
||||
submit?: { [profile: string]: object };
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
distribution: 'store',
|
||||
credentialsSource: CredentialsSource.REMOTE,
|
||||
} as const;
|
||||
|
||||
type LoggerFn = (...args: any[]) => void;
|
||||
|
||||
interface Logger {
|
||||
log: LoggerFn;
|
||||
warn: LoggerFn;
|
||||
}
|
||||
|
||||
export class EasJsonReader {
|
||||
private static log?: Logger;
|
||||
|
||||
public static formatEasJsonPath(projectDir: string): string {
|
||||
return path.join(projectDir, 'eas.json');
|
||||
}
|
||||
|
||||
public static setLog(log: Logger): void {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
constructor(private projectDir: string) {}
|
||||
|
||||
public async getBuildProfileNamesAsync(): Promise<string[]> {
|
||||
const easJson = await this.readRawAsync();
|
||||
return Object.keys(easJson?.build ?? {});
|
||||
}
|
||||
|
||||
public async getCliConfigAsync(): Promise<CliConfig | null> {
|
||||
try {
|
||||
const easJson = await this.readRawAsync();
|
||||
if (!easJson.cli) {
|
||||
return null;
|
||||
}
|
||||
const { value, error } = CliConfigSchema.validate(easJson.cli, {
|
||||
allowUnknown: false,
|
||||
convert: true,
|
||||
abortEarly: false,
|
||||
});
|
||||
if (error) {
|
||||
throw new Error(`"cli" field in eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
return value as CliConfig;
|
||||
} catch (err: any) {
|
||||
if (err instanceof MissingEasJsonError) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public async getSubmitProfileNamesAsync({ throwIfEasJsonDoesNotExist = true } = {}): Promise<
|
||||
string[]
|
||||
> {
|
||||
try {
|
||||
const easJson = await this.readRawAsync();
|
||||
return Object.keys(easJson?.submit ?? {});
|
||||
} catch (err: any) {
|
||||
if (!throwIfEasJsonDoesNotExist && err.code === 'ENOENT') {
|
||||
return [];
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async readBuildProfileAsync<T extends Platform>(
|
||||
platform: T,
|
||||
profileName: string
|
||||
): Promise<BuildProfile<T>> {
|
||||
const easJson = await this.readAndValidateAsync();
|
||||
this.ensureBuildProfileExists(easJson, profileName);
|
||||
const {
|
||||
android: resolvedAndroidSpecificValues,
|
||||
ios: resolvedIosSpecificValues,
|
||||
...resolvedProfile
|
||||
} = this.resolveBuildProfile(easJson, profileName);
|
||||
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 {
|
||||
throw new Error(`Unknown platform ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
public async readSubmitProfileAsync<T extends Platform>(
|
||||
platform: T,
|
||||
profileNameArg?: string
|
||||
): Promise<SubmitProfile<T>> {
|
||||
let profileName = profileNameArg;
|
||||
|
||||
if (!profileName) {
|
||||
const profileNames = await this.getSubmitProfileNamesAsync({
|
||||
throwIfEasJsonDoesNotExist: false,
|
||||
});
|
||||
|
||||
if (profileNames.includes('production')) {
|
||||
profileName = 'production';
|
||||
} else if (profileNames.includes('release')) {
|
||||
profileName = 'release';
|
||||
} else {
|
||||
return getDefaultSubmitProfile(platform);
|
||||
}
|
||||
}
|
||||
|
||||
const easJson = await this.readAndValidateAsync();
|
||||
const profile = easJson?.submit?.[profileName];
|
||||
if (!profile) {
|
||||
throw new Error(`There is no submit profile named ${profileName} in eas.json`);
|
||||
}
|
||||
const platformProfile = profile[platform];
|
||||
if (platformProfile) {
|
||||
return this.evaluateFields(platform, platformProfile as SubmitProfile<T>);
|
||||
} else {
|
||||
return getDefaultSubmitProfile(platform);
|
||||
}
|
||||
}
|
||||
|
||||
public async readAndValidateAsync(): Promise<EasJson> {
|
||||
const easJson = await this.readRawAsync();
|
||||
const { value, error } = EasJsonSchema.validate(easJson, {
|
||||
allowUnknown: false,
|
||||
convert: true,
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new InvalidEasJsonError(`eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
return value as EasJson;
|
||||
}
|
||||
|
||||
public async readRawAsync(): Promise<EasJsonPreValidation> {
|
||||
try {
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(this.projectDir);
|
||||
if (!(await fs.pathExists(easJsonPath))) {
|
||||
throw new MissingEasJsonError(
|
||||
`An eas.json file could not be found at ${easJsonPath}. You must make one in order to proceed. Learn more at https://expo.fyi/eas-json`
|
||||
);
|
||||
}
|
||||
const rawEasJson = JsonFile.read(easJsonPath);
|
||||
const { value, error } = MinimalEasJsonSchema.validate(rawEasJson, { abortEarly: false });
|
||||
if (error) {
|
||||
throw new InvalidEasJsonError(`eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
return value;
|
||||
} catch (err: any) {
|
||||
if (err.code === 'EJSONPARSE') {
|
||||
err.message = `Found invalid JSON in eas.json. ${err.message}`;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private resolveBuildProfile(
|
||||
easJson: EasJson,
|
||||
profileName: string,
|
||||
depth: number = 0
|
||||
): 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 = easJson.build[profileName];
|
||||
if (!buildProfile) {
|
||||
throw new Error(`There is no build profile named ${profileName} in eas.json`);
|
||||
}
|
||||
const { extends: baseProfileName, ...buildProfileRest } = buildProfile;
|
||||
if (baseProfileName) {
|
||||
return profileMerge(
|
||||
this.resolveBuildProfile(easJson, baseProfileName, depth + 1),
|
||||
buildProfileRest
|
||||
);
|
||||
} else {
|
||||
return buildProfileRest;
|
||||
}
|
||||
}
|
||||
|
||||
private ensureBuildProfileExists(easJson: EasJson, profileName: string): void {
|
||||
if (!easJson.build || !easJson.build[profileName]) {
|
||||
throw new Error(`There is no build profile named ${profileName} in eas.json`);
|
||||
}
|
||||
}
|
||||
|
||||
private evaluateFields<T extends Platform>(
|
||||
platform: T,
|
||||
profile: SubmitProfile<T>
|
||||
): SubmitProfile<T> {
|
||||
const fields =
|
||||
platform === Platform.ANDROID
|
||||
? AndroidSubmitProfileFieldsToEvaluate
|
||||
: IosSubmitProfileFieldsToEvaluate;
|
||||
const evaluatedProfile = { ...profile };
|
||||
for (const field of fields) {
|
||||
if (field in evaluatedProfile) {
|
||||
// @ts-ignore
|
||||
evaluatedProfile[field] = envString(evaluatedProfile[field], process.env);
|
||||
}
|
||||
}
|
||||
return evaluatedProfile;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function getDefaultSubmitProfile<T extends Platform>(platform: T): SubmitProfile<T> {
|
||||
const Schema =
|
||||
platform === Platform.ANDROID ? AndroidSubmitProfileSchema : IosSubmitProfileSchema;
|
||||
return Schema.validate({}, { convert: true }).value;
|
||||
}
|
||||
+49
-51
@@ -2,7 +2,7 @@ import { Platform } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import { EasJsonReader } from '../EasJsonReader';
|
||||
import { EasJsonReader } from '../reader';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
@@ -19,18 +19,18 @@ test('minimal valid eas.json for both platforms', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readBuildProfileAsync(Platform.IOS, 'production');
|
||||
const androidProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
const iosProfile = await reader.getBuildProfileAsync(Platform.IOS, 'production');
|
||||
const androidProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
|
||||
expect({
|
||||
expect(androidProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(androidProfile);
|
||||
});
|
||||
|
||||
expect({
|
||||
expect(iosProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(iosProfile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid eas.json for development client builds', async () => {
|
||||
@@ -47,20 +47,20 @@ test('valid eas.json for development client builds', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readBuildProfileAsync(Platform.IOS, 'debug');
|
||||
const androidProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'debug');
|
||||
expect({
|
||||
const iosProfile = await reader.getBuildProfileAsync(Platform.IOS, 'debug');
|
||||
const androidProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'debug');
|
||||
expect(androidProfile).toEqual({
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
developmentClient: true,
|
||||
withoutCredentials: true,
|
||||
}).toEqual(androidProfile);
|
||||
});
|
||||
|
||||
expect({
|
||||
expect(iosProfile).toEqual({
|
||||
credentialsSource: 'remote',
|
||||
distribution: 'store',
|
||||
developmentClient: true,
|
||||
}).toEqual(iosProfile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile for internal distribution on Android', async () => {
|
||||
@@ -73,11 +73,11 @@ test('valid profile for internal distribution on Android', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const profile = await reader.readBuildProfileAsync(Platform.ANDROID, 'internal');
|
||||
expect({
|
||||
const profile = await reader.getBuildProfileAsync(Platform.ANDROID, 'internal');
|
||||
expect(profile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
}).toEqual(profile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile extending other profile', async () => {
|
||||
@@ -95,18 +95,18 @@ test('valid profile extending other profile', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
expect({
|
||||
const baseProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
expect(baseProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
node: '12.0.0',
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
});
|
||||
expect(extendedProfile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
node: '13.0.0',
|
||||
}).toEqual(extendedProfile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile extending other profile with platform specific envs', async () => {
|
||||
@@ -134,33 +134,33 @@ test('valid profile extending other profile with platform specific envs', async
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
const extendedIosProfile = await reader.readBuildProfileAsync(Platform.IOS, 'extension');
|
||||
expect({
|
||||
const baseProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
const extendedIosProfile = await reader.getBuildProfileAsync(Platform.IOS, 'extension');
|
||||
expect(baseProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'base',
|
||||
},
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
});
|
||||
expect(extendedAndroidProfile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'extension:android',
|
||||
},
|
||||
}).toEqual(extendedAndroidProfile);
|
||||
expect({
|
||||
});
|
||||
expect(extendedIosProfile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
env: {
|
||||
BASE_ENV: '1',
|
||||
PROFILE: 'extension',
|
||||
},
|
||||
}).toEqual(extendedIosProfile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid profile extending other profile with platform specific caching', async () => {
|
||||
@@ -188,32 +188,32 @@ test('valid profile extending other profile with platform specific caching', asy
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await reader.readBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
const extendedIosProfile = await reader.readBuildProfileAsync(Platform.IOS, 'extension');
|
||||
expect({
|
||||
const baseProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'base');
|
||||
const extendedAndroidProfile = await reader.getBuildProfileAsync(Platform.ANDROID, 'extension');
|
||||
const extendedIosProfile = await reader.getBuildProfileAsync(Platform.IOS, 'extension');
|
||||
expect(baseProfile).toEqual({
|
||||
distribution: 'store',
|
||||
credentialsSource: 'remote',
|
||||
cache: {
|
||||
disabled: true,
|
||||
},
|
||||
}).toEqual(baseProfile);
|
||||
expect({
|
||||
});
|
||||
expect(extendedAndroidProfile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
cache: {
|
||||
cacheDefaultPaths: false,
|
||||
customPaths: ['somefakepath'],
|
||||
},
|
||||
}).toEqual(extendedAndroidProfile);
|
||||
expect({
|
||||
});
|
||||
expect(extendedIosProfile).toEqual({
|
||||
distribution: 'internal',
|
||||
credentialsSource: 'remote',
|
||||
|
||||
cache: {
|
||||
key: 'extend-key',
|
||||
},
|
||||
}).toEqual(extendedIosProfile);
|
||||
});
|
||||
});
|
||||
|
||||
test('valid eas.json with missing profile', async () => {
|
||||
@@ -224,8 +224,8 @@ test('valid eas.json with missing profile', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync(Platform.ANDROID, 'debug');
|
||||
await expect(promise).rejects.toThrowError('There is no build profile named debug in eas.json');
|
||||
const promise = reader.getBuildProfileAsync(Platform.ANDROID, 'debug');
|
||||
await expect(promise).rejects.toThrowError('Missing build profile in eas.json: debug');
|
||||
});
|
||||
|
||||
test('invalid eas.json when using wrong buildType', async () => {
|
||||
@@ -236,7 +236,7 @@ test('invalid eas.json when using wrong buildType', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
const promise = reader.getBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'eas.json is not valid [ValidationError: "build.production.android.buildType" must be one of [apk, app-bundle]]'
|
||||
);
|
||||
@@ -246,10 +246,8 @@ test('empty json', async () => {
|
||||
await fs.writeJson('/project/eas.json', {});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'There is no build profile named production in eas.json'
|
||||
);
|
||||
const promise = reader.getBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError('Missing build profile in eas.json: production');
|
||||
});
|
||||
|
||||
test('invalid semver value', async () => {
|
||||
@@ -260,7 +258,7 @@ test('invalid semver value', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
const promise = reader.getBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(
|
||||
'eas.json is not valid [ValidationError: "build.production.node" failed custom validation because 12.0.0-alpha is not a valid version]'
|
||||
);
|
||||
@@ -274,15 +272,15 @@ test('invalid release channel', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const promise = reader.readBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
const promise = reader.getBuildProfileAsync(Platform.ANDROID, 'production');
|
||||
await expect(promise).rejects.toThrowError(/fails to match the required pattern/);
|
||||
});
|
||||
|
||||
test('get profile names', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
build: {
|
||||
production: { node: '12.0.0-alpha' },
|
||||
blah: { node: '12.0.0-alpha' },
|
||||
production: { node: '12.0.0' },
|
||||
blah: { node: '12.0.0' },
|
||||
},
|
||||
});
|
||||
|
||||
+49
-7
@@ -2,7 +2,7 @@ import { Platform } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import { EasJsonReader } from '../EasJsonReader';
|
||||
import { EasJsonReader } from '../reader';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
@@ -19,8 +19,8 @@ test('minimal allowed eas.json for both platforms', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readSubmitProfileAsync(Platform.IOS, 'production');
|
||||
const androidProfile = await reader.readSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
const iosProfile = await reader.getSubmitProfileAsync(Platform.IOS, 'production');
|
||||
const androidProfile = await reader.getSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
|
||||
expect(androidProfile).toEqual({
|
||||
changesNotSentForReview: false,
|
||||
@@ -46,7 +46,7 @@ test('android config with all required values', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const androidProfile = await reader.readSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
const androidProfile = await reader.getSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
|
||||
expect(androidProfile).toEqual({
|
||||
serviceAccountKeyPath: './path.json',
|
||||
@@ -72,7 +72,7 @@ test('android config with serviceAccountKeyPath set to env var', async () => {
|
||||
try {
|
||||
process.env.GOOGLE_SERVICE_ACCOUNT = './path.json';
|
||||
const reader = new EasJsonReader('/project');
|
||||
const androidProfile = await reader.readSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
const androidProfile = await reader.getSubmitProfileAsync(Platform.ANDROID, 'production');
|
||||
|
||||
expect(androidProfile).toEqual({
|
||||
serviceAccountKeyPath: './path.json',
|
||||
@@ -102,7 +102,7 @@ test('ios config with all required values', async () => {
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readSubmitProfileAsync(Platform.IOS, 'production');
|
||||
const iosProfile = await reader.getSubmitProfileAsync(Platform.IOS, 'production');
|
||||
|
||||
expect(iosProfile).toEqual({
|
||||
appleId: '[email protected]',
|
||||
@@ -136,7 +136,7 @@ test('ios config with ascApiKey fields set to env var', async () => {
|
||||
process.env.ASC_API_KEY_ISSUER_ID = 'abc-123-def-456';
|
||||
process.env.ASC_API_KEY_ID = 'ABCD';
|
||||
const reader = new EasJsonReader('/project');
|
||||
const iosProfile = await reader.readSubmitProfileAsync(Platform.IOS, 'release');
|
||||
const iosProfile = await reader.getSubmitProfileAsync(Platform.IOS, 'release');
|
||||
|
||||
expect(iosProfile).toEqual({
|
||||
appleId: '[email protected]',
|
||||
@@ -153,3 +153,45 @@ test('ios config with ascApiKey fields set to env var', async () => {
|
||||
process.env.ASC_API_KEY_ID = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
test('valid profile extending other profile', async () => {
|
||||
await fs.writeJson('/project/eas.json', {
|
||||
submit: {
|
||||
base: {
|
||||
ios: {
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'QWERTY',
|
||||
},
|
||||
},
|
||||
extension: {
|
||||
extends: 'base',
|
||||
ios: {
|
||||
appleTeamId: 'ABCDEF',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: 'abc-123-def-456',
|
||||
ascApiKeyId: 'ABCD',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const reader = new EasJsonReader('/project');
|
||||
const baseProfile = await reader.getSubmitProfileAsync(Platform.IOS, 'base');
|
||||
const extendedProfile = await reader.getSubmitProfileAsync(Platform.IOS, 'extension');
|
||||
expect(baseProfile).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'QWERTY',
|
||||
});
|
||||
expect(extendedProfile).toEqual({
|
||||
language: 'en-US',
|
||||
appleId: '[email protected]',
|
||||
ascAppId: '1223423523',
|
||||
appleTeamId: 'ABCDEF',
|
||||
ascApiKeyPath: './path-ABCD.p8',
|
||||
ascApiKeyIssuerId: 'abc-123-def-456',
|
||||
ascApiKeyId: 'ABCD',
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
|
||||
import { MissingParentProfileError, MissingProfileError } from '../errors';
|
||||
import { EasJson } from '../types';
|
||||
import { BuildProfileSchema } from './schema';
|
||||
import { BuildProfile, EasJsonBuildProfile } from './types';
|
||||
|
||||
type EasJsonBuildProfileResolved = Omit<EasJsonBuildProfile, 'extends'>;
|
||||
|
||||
export function resolveBuildProfile<T extends Platform>({
|
||||
easJson,
|
||||
platform,
|
||||
profileName,
|
||||
}: {
|
||||
easJson: EasJson;
|
||||
platform: T;
|
||||
profileName: string;
|
||||
}): BuildProfile<T> {
|
||||
const easJsonProfile = resolveProfile({
|
||||
easJson,
|
||||
profileName,
|
||||
});
|
||||
const { android, ios, ...base } = easJsonProfile;
|
||||
const withoutDefaults = mergeProfiles(base, easJsonProfile[platform] ?? {});
|
||||
return mergeProfiles(getDefaultProfile(platform), withoutDefaults) as BuildProfile<T>;
|
||||
}
|
||||
|
||||
function resolveProfile({
|
||||
easJson,
|
||||
profileName,
|
||||
depth = 0,
|
||||
}: {
|
||||
easJson: EasJson;
|
||||
profileName: string;
|
||||
depth?: number;
|
||||
}): EasJsonBuildProfileResolved {
|
||||
if (depth >= 2) {
|
||||
throw new Error(
|
||||
'Too long chain of profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
}
|
||||
|
||||
const profile = easJson.build?.[profileName];
|
||||
if (!profile) {
|
||||
if (depth === 0) {
|
||||
throw new MissingProfileError(`Missing build profile in eas.json: ${profileName}`);
|
||||
} else {
|
||||
throw new MissingParentProfileError(
|
||||
`Extending non-existent build profile in eas.json: ${profileName}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { extends: baseProfileName, ...rest } = profile;
|
||||
if (baseProfileName) {
|
||||
const baseProfile = resolveProfile({
|
||||
easJson,
|
||||
profileName: baseProfileName,
|
||||
depth: depth + 1,
|
||||
});
|
||||
return mergeProfiles(baseProfile, rest);
|
||||
} else {
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeProfiles(
|
||||
base: EasJsonBuildProfileResolved,
|
||||
update: EasJsonBuildProfileResolved
|
||||
): EasJsonBuildProfileResolved {
|
||||
const result = {
|
||||
...base,
|
||||
...update,
|
||||
};
|
||||
if (base.env && update.env) {
|
||||
result.env = {
|
||||
...base.env,
|
||||
...update.env,
|
||||
};
|
||||
}
|
||||
if (base.android && update.android) {
|
||||
result.android = mergeProfiles(base.android, update.android);
|
||||
}
|
||||
if (base.ios && update.ios) {
|
||||
result.ios = mergeProfiles(base.ios, update.ios);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getDefaultProfile<T extends Platform>(platform: T): EasJsonBuildProfile {
|
||||
const defaultProfile = BuildProfileSchema.validate(
|
||||
{},
|
||||
{ allowUnknown: false, abortEarly: false, convert: true }
|
||||
).value;
|
||||
const { android, ios, ...base } = defaultProfile;
|
||||
return mergeProfiles(base, defaultProfile[platform]);
|
||||
}
|
||||
@@ -1,16 +1,6 @@
|
||||
import { Android, Ios } from '@expo/eas-build-job';
|
||||
import Joi from 'joi';
|
||||
|
||||
import { AndroidReleaseStatus, AndroidReleaseTrack } from './EasSubmit.types';
|
||||
|
||||
const semverSchemaCheck = (value: any): any => {
|
||||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) {
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`${value} is not a valid version`);
|
||||
}
|
||||
};
|
||||
|
||||
const CacheSchema = Joi.object({
|
||||
disabled: Joi.boolean(),
|
||||
key: Joi.string().max(128),
|
||||
@@ -19,8 +9,8 @@ const CacheSchema = Joi.object({
|
||||
});
|
||||
|
||||
const CommonBuildProfileSchema = Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
credentialsSource: Joi.string().valid('local', 'remote').default('remote'),
|
||||
distribution: Joi.string().valid('store', 'internal').default('store'),
|
||||
cache: CacheSchema,
|
||||
releaseChannel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
channel: Joi.string().regex(/^[a-z\d][a-z\d._-]*$/),
|
||||
@@ -34,6 +24,8 @@ const CommonBuildProfileSchema = Joi.object({
|
||||
|
||||
const AndroidBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
withoutCredentials: Joi.boolean(),
|
||||
|
||||
image: Joi.string().valid(...Android.builderBaseImages),
|
||||
@@ -52,6 +44,8 @@ const AndroidBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
|
||||
const IosBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
credentialsSource: Joi.string().valid('local', 'remote'),
|
||||
distribution: Joi.string().valid('store', 'internal'),
|
||||
enterpriseProvisioning: Joi.string().valid('adhoc', 'universal'),
|
||||
autoIncrement: Joi.alternatives().try(
|
||||
Joi.boolean(),
|
||||
@@ -70,7 +64,7 @@ const IosBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
})
|
||||
);
|
||||
|
||||
const EasJsonBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
export const BuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
Joi.object({
|
||||
extends: Joi.string(),
|
||||
android: AndroidBuildProfileSchema,
|
||||
@@ -78,50 +72,10 @@ const EasJsonBuildProfileSchema = CommonBuildProfileSchema.concat(
|
||||
})
|
||||
);
|
||||
|
||||
export const AndroidSubmitProfileSchema = Joi.object({
|
||||
serviceAccountKeyPath: Joi.string(),
|
||||
track: Joi.string()
|
||||
.valid(...Object.values(AndroidReleaseTrack))
|
||||
.default(AndroidReleaseTrack.internal),
|
||||
releaseStatus: Joi.string()
|
||||
.valid(...Object.values(AndroidReleaseStatus))
|
||||
.default(AndroidReleaseStatus.completed),
|
||||
changesNotSentForReview: Joi.boolean().default(false),
|
||||
applicationId: Joi.string(),
|
||||
});
|
||||
|
||||
export const IosSubmitProfileSchema = Joi.object({
|
||||
ascApiKeyPath: Joi.string(),
|
||||
ascApiKeyId: Joi.string(),
|
||||
ascApiKeyIssuerId: Joi.string(),
|
||||
appleId: Joi.string(),
|
||||
ascAppId: Joi.string(),
|
||||
appleTeamId: Joi.string(),
|
||||
sku: Joi.string(),
|
||||
language: Joi.string().default('en-US'),
|
||||
companyName: Joi.string(),
|
||||
appName: Joi.string(),
|
||||
bundleIdentifier: Joi.string(),
|
||||
});
|
||||
|
||||
const EasJsonSubmitConfigurationSchema = Joi.object({
|
||||
android: AndroidSubmitProfileSchema,
|
||||
ios: IosSubmitProfileSchema,
|
||||
});
|
||||
|
||||
export const CliConfigSchema = Joi.object({
|
||||
version: Joi.string(),
|
||||
requireCommit: Joi.boolean(),
|
||||
});
|
||||
|
||||
export const MinimalEasJsonSchema = Joi.object({
|
||||
cli: Joi.object(),
|
||||
build: Joi.object().pattern(Joi.string(), Joi.object()),
|
||||
submit: Joi.object().pattern(Joi.string(), Joi.object()),
|
||||
});
|
||||
|
||||
export const EasJsonSchema = Joi.object({
|
||||
cli: CliConfigSchema,
|
||||
build: Joi.object().pattern(Joi.string(), EasJsonBuildProfileSchema),
|
||||
submit: Joi.object().pattern(Joi.string(), EasJsonSubmitConfigurationSchema),
|
||||
});
|
||||
function semverSchemaCheck(value: any): any {
|
||||
if (/^[0-9]+\.[0-9]+\.[0-9]+$/.test(value)) {
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(`${value} is not a valid version`);
|
||||
}
|
||||
}
|
||||
@@ -55,8 +55,10 @@ export interface IosBuildProfile extends CommonBuildProfile {
|
||||
|
||||
export type BuildProfile<TPlatform extends Platform = Platform> = TPlatform extends Platform.ANDROID
|
||||
? AndroidBuildProfile
|
||||
: TPlatform extends Platform.IOS
|
||||
? IosBuildProfile
|
||||
: TPlatform extends Platform
|
||||
? AndroidBuildProfile | IosBuildProfile
|
||||
: never;
|
||||
: IosBuildProfile;
|
||||
|
||||
export interface EasJsonBuildProfile extends Partial<CommonBuildProfile> {
|
||||
extends?: string;
|
||||
[Platform.ANDROID]?: Partial<AndroidBuildProfile>;
|
||||
[Platform.IOS]?: Partial<IosBuildProfile>;
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
export class InvalidEasJsonError extends Error {}
|
||||
|
||||
export class MissingEasJsonError extends Error {}
|
||||
|
||||
export class MissingProfileError extends Error {}
|
||||
|
||||
export class MissingParentProfileError extends Error {}
|
||||
@@ -1,20 +1,13 @@
|
||||
export { AndroidReleaseStatus, AndroidReleaseTrack, SubmitProfile } from './submit/types';
|
||||
export { getDefaultProfile as getDefaultSubmitProfile } from './submit/resolver';
|
||||
export { EasJson, ProfileType } from './types';
|
||||
export {
|
||||
AndroidSubmitProfile,
|
||||
AndroidReleaseStatus,
|
||||
AndroidReleaseTrack,
|
||||
IosSubmitProfile,
|
||||
SubmitProfile,
|
||||
} from './EasSubmit.types';
|
||||
export { EasJson } from './EasJson.types';
|
||||
export {
|
||||
AndroidBuildProfile,
|
||||
AndroidVersionAutoIncrement,
|
||||
BuildProfile,
|
||||
CredentialsSource,
|
||||
DistributionType,
|
||||
IosBuildProfile,
|
||||
IosEnterpriseProvisioning,
|
||||
IosVersionAutoIncrement,
|
||||
AndroidVersionAutoIncrement,
|
||||
} from './EasBuild.types';
|
||||
export { EasJsonReader } from './EasJsonReader';
|
||||
} from './build/types';
|
||||
export { EasJsonReader } from './reader';
|
||||
export * as errors from './errors';
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { resolveBuildProfile } from './build/resolver';
|
||||
import { BuildProfile } from './build/types';
|
||||
import { InvalidEasJsonError, MissingEasJsonError } from './errors';
|
||||
import { EasJsonSchema } from './schema';
|
||||
import { resolveSubmitProfile } from './submit/resolver';
|
||||
import { SubmitProfile } from './submit/types';
|
||||
import { EasJson } from './types';
|
||||
|
||||
export class EasJsonReader {
|
||||
private easJson: EasJson | undefined;
|
||||
|
||||
constructor(private projectDir: string) {}
|
||||
|
||||
public static formatEasJsonPath(projectDir: string): string {
|
||||
return path.join(projectDir, 'eas.json');
|
||||
}
|
||||
|
||||
public async readAsync(): Promise<EasJson> {
|
||||
if (this.easJson) {
|
||||
return this.easJson;
|
||||
}
|
||||
|
||||
try {
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(this.projectDir);
|
||||
if (!(await fs.pathExists(easJsonPath))) {
|
||||
throw new MissingEasJsonError(
|
||||
`eas.json could not be found at ${easJsonPath}. Learn more at https://expo.fyi/eas-json`
|
||||
);
|
||||
}
|
||||
const contents = JsonFile.read(easJsonPath);
|
||||
const { value, error } = EasJsonSchema.validate(contents, {
|
||||
allowUnknown: false,
|
||||
abortEarly: false,
|
||||
convert: true,
|
||||
noDefaults: true,
|
||||
});
|
||||
if (error) {
|
||||
throw new InvalidEasJsonError(`eas.json is not valid [${error.toString()}]`);
|
||||
}
|
||||
this.easJson = value;
|
||||
return value;
|
||||
} catch (err: any) {
|
||||
if (err.code === 'EJSONPARSE') {
|
||||
err.message = `Found invalid JSON in eas.json. ${err.message}`;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public async getBuildProfileNamesAsync(): Promise<string[]> {
|
||||
const easJson = await this.readAsync();
|
||||
return Object.keys(easJson?.build ?? {});
|
||||
}
|
||||
|
||||
public async getBuildProfileAsync<T extends Platform>(
|
||||
platform: T,
|
||||
profileName: string
|
||||
): Promise<BuildProfile<T>> {
|
||||
const easJson = await this.readAsync();
|
||||
return resolveBuildProfile({ easJson, platform, profileName });
|
||||
}
|
||||
|
||||
public async getCliConfigAsync(): Promise<EasJson['cli'] | null> {
|
||||
try {
|
||||
const easJson = await this.readAsync();
|
||||
return easJson.cli ?? null;
|
||||
} catch (err: any) {
|
||||
if (err instanceof MissingEasJsonError) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
public async getSubmitProfileAsync<T extends Platform>(
|
||||
platform: T,
|
||||
profileName: string
|
||||
): Promise<SubmitProfile<T>> {
|
||||
const easJson = await this.readAsync();
|
||||
return resolveSubmitProfile({ easJson, platform, profileName });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Joi from 'joi';
|
||||
|
||||
import { BuildProfileSchema } from './build/schema';
|
||||
import { SubmitProfileSchema } from './submit/schema';
|
||||
|
||||
export const EasJsonSchema = Joi.object({
|
||||
cli: Joi.object({
|
||||
version: Joi.string(),
|
||||
requireCommit: Joi.boolean(),
|
||||
}),
|
||||
build: Joi.object().pattern(Joi.string(), BuildProfileSchema),
|
||||
submit: Joi.object().pattern(Joi.string(), SubmitProfileSchema),
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import envString from 'env-string';
|
||||
|
||||
import { MissingParentProfileError, MissingProfileError } from '../errors';
|
||||
import { EasJson } from '../types';
|
||||
import { AndroidSubmitProfileSchema, IosSubmitProfileSchema } from './schema';
|
||||
import {
|
||||
AndroidSubmitProfileFieldsToEvaluate,
|
||||
IosSubmitProfileFieldsToEvaluate,
|
||||
SubmitProfile,
|
||||
} from './types';
|
||||
|
||||
export function resolveSubmitProfile<T extends Platform>({
|
||||
easJson,
|
||||
platform,
|
||||
profileName,
|
||||
}: {
|
||||
easJson: EasJson;
|
||||
platform: T;
|
||||
profileName: string;
|
||||
}): SubmitProfile<T> {
|
||||
const submitProfile = resolveProfile({
|
||||
easJson,
|
||||
platform,
|
||||
profileName,
|
||||
});
|
||||
const unevaluatedProfile = mergeProfiles(getDefaultProfile(platform), submitProfile);
|
||||
return evaluateFields(platform, unevaluatedProfile);
|
||||
}
|
||||
|
||||
function resolveProfile<T extends Platform>({
|
||||
easJson,
|
||||
profileName,
|
||||
depth = 0,
|
||||
platform,
|
||||
}: {
|
||||
platform: T;
|
||||
easJson: EasJson;
|
||||
profileName: string;
|
||||
depth?: number;
|
||||
}): SubmitProfile<T> | undefined {
|
||||
if (depth >= 2) {
|
||||
throw new Error(
|
||||
'Too long chain of profile extensions, make sure "extends" keys do not make a cycle'
|
||||
);
|
||||
}
|
||||
|
||||
const profile = easJson.submit?.[profileName];
|
||||
if (!profile) {
|
||||
if (depth === 0) {
|
||||
throw new MissingProfileError(`There is no submit profile named ${profileName} in eas.json`);
|
||||
} else {
|
||||
throw new MissingParentProfileError(
|
||||
`Extending non-existent submit profile in eas.json: ${profileName}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { extends: baseProfileName, ...rest } = profile;
|
||||
const platformProfile = rest[platform] as SubmitProfile<T> | undefined;
|
||||
if (baseProfileName) {
|
||||
const baseProfile = resolveProfile({
|
||||
easJson,
|
||||
platform,
|
||||
profileName: baseProfileName,
|
||||
depth: depth + 1,
|
||||
});
|
||||
return mergeProfiles(baseProfile, platformProfile);
|
||||
} else {
|
||||
return platformProfile;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeProfiles<T extends Platform>(
|
||||
base: SubmitProfile<T>,
|
||||
update?: SubmitProfile<T>
|
||||
): SubmitProfile<T>;
|
||||
function mergeProfiles<T extends Platform>(
|
||||
base?: SubmitProfile<T>,
|
||||
update?: SubmitProfile<T>
|
||||
): SubmitProfile<T> | undefined;
|
||||
function mergeProfiles<T extends Platform>(
|
||||
base?: SubmitProfile<T>,
|
||||
update?: SubmitProfile<T>
|
||||
): SubmitProfile<T> | undefined {
|
||||
if (!update) {
|
||||
return base;
|
||||
}
|
||||
return { ...base, ...update };
|
||||
}
|
||||
|
||||
export function getDefaultProfile<T extends Platform>(platform: T): SubmitProfile<T> {
|
||||
const Schema =
|
||||
platform === Platform.ANDROID ? AndroidSubmitProfileSchema : IosSubmitProfileSchema;
|
||||
return Schema.validate({}, { allowUnknown: false, abortEarly: false, convert: true }).value;
|
||||
}
|
||||
|
||||
function evaluateFields<T extends Platform>(
|
||||
platform: T,
|
||||
profile: SubmitProfile<T>
|
||||
): SubmitProfile<T> {
|
||||
const fields =
|
||||
platform === Platform.ANDROID
|
||||
? AndroidSubmitProfileFieldsToEvaluate
|
||||
: IosSubmitProfileFieldsToEvaluate;
|
||||
const evaluatedProfile = { ...profile };
|
||||
for (const field of fields) {
|
||||
if (field in evaluatedProfile) {
|
||||
// @ts-ignore
|
||||
evaluatedProfile[field] = envString(evaluatedProfile[field], process.env);
|
||||
}
|
||||
}
|
||||
return evaluatedProfile;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Joi from 'joi';
|
||||
|
||||
import { AndroidReleaseStatus, AndroidReleaseTrack } from './types';
|
||||
|
||||
export const AndroidSubmitProfileSchema = Joi.object({
|
||||
serviceAccountKeyPath: Joi.string(),
|
||||
track: Joi.string()
|
||||
.valid(...Object.values(AndroidReleaseTrack))
|
||||
.default(AndroidReleaseTrack.internal),
|
||||
releaseStatus: Joi.string()
|
||||
.valid(...Object.values(AndroidReleaseStatus))
|
||||
.default(AndroidReleaseStatus.completed),
|
||||
changesNotSentForReview: Joi.boolean().default(false),
|
||||
applicationId: Joi.string(),
|
||||
});
|
||||
|
||||
export const IosSubmitProfileSchema = Joi.object({
|
||||
ascApiKeyPath: Joi.string(),
|
||||
ascApiKeyId: Joi.string(),
|
||||
ascApiKeyIssuerId: Joi.string(),
|
||||
appleId: Joi.string(),
|
||||
ascAppId: Joi.string(),
|
||||
appleTeamId: Joi.string(),
|
||||
sku: Joi.string(),
|
||||
language: Joi.string().default('en-US'),
|
||||
companyName: Joi.string(),
|
||||
appName: Joi.string(),
|
||||
bundleIdentifier: Joi.string(),
|
||||
});
|
||||
|
||||
export const SubmitProfileSchema = Joi.object({
|
||||
extends: Joi.string(),
|
||||
android: AndroidSubmitProfileSchema,
|
||||
ios: IosSubmitProfileSchema,
|
||||
});
|
||||
@@ -47,10 +47,10 @@ export const IosSubmitProfileFieldsToEvaluate: (keyof IosSubmitProfile)[] = [
|
||||
];
|
||||
|
||||
export type SubmitProfile<TPlatform extends Platform = Platform> =
|
||||
TPlatform extends Platform.ANDROID
|
||||
? AndroidSubmitProfile
|
||||
: TPlatform extends Platform.IOS
|
||||
? IosSubmitProfile
|
||||
: TPlatform extends Platform
|
||||
? AndroidSubmitProfile | IosSubmitProfile
|
||||
: never;
|
||||
TPlatform extends Platform.ANDROID ? AndroidSubmitProfile : IosSubmitProfile;
|
||||
|
||||
export interface EasJsonSubmitProfile {
|
||||
extends?: string;
|
||||
[Platform.ANDROID]?: AndroidSubmitProfile;
|
||||
[Platform.IOS]?: IosSubmitProfile;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { EasJsonBuildProfile } from './build/types';
|
||||
import { EasJsonSubmitProfile } from './submit/types';
|
||||
|
||||
export type ProfileType = 'build' | 'submit';
|
||||
export type EasJsonProfile<T extends ProfileType> = T extends 'build'
|
||||
? EasJsonBuildProfile
|
||||
: EasJsonSubmitProfile;
|
||||
|
||||
export enum CredentialsSource {
|
||||
LOCAL = 'local',
|
||||
REMOTE = 'remote',
|
||||
}
|
||||
|
||||
export interface EasJson {
|
||||
cli?: {
|
||||
version?: string;
|
||||
requireCommit?: boolean;
|
||||
};
|
||||
build?: { [profileName: string]: EasJsonBuildProfile };
|
||||
submit?: { [profileName: string]: EasJsonSubmitProfile };
|
||||
}
|
||||
@@ -28,6 +28,6 @@
|
||||
"@types/node": "16.7.8",
|
||||
"@types/semver": "7.3.8",
|
||||
"ts-node": "10.2.1",
|
||||
"typescript": "4.4.2"
|
||||
"typescript": "4.5.2"
|
||||
}
|
||||
}
|
||||
Generated
+4
-4
@@ -176,10 +176,10 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
typescript@4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
|
||||
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
|
||||
typescript@4.5.2:
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998"
|
||||
integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
||||
@@ -848,14 +848,14 @@
|
||||
mv "~2"
|
||||
safe-json-stringify "~1"
|
||||
|
||||
"@expo/[email protected].3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-4.0.3.tgz#8680dea2709c70f3a9c9e77ca6057335758341a8"
|
||||
integrity sha512-vJ+4YOXxAjznpIG0WQHNsbeeJQXS/ZxzVPHhOO3le8Lkl+WJ2eH7XypulPRalBI4E0WLFtzDzLIdnqYJyjzvaw==
|
||||
"@expo/[email protected].9":
|
||||
version "4.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-4.0.9.tgz#52c5bee8411185fc4a340072fbed3584d0409234"
|
||||
integrity sha512-3wv0uhG5U5O4BGX2qm4gjFK3jvYVTy0raFsR6B/2zoBNgD3lXF8Ae5osVN118aKNZb2B7SMx3V7McunZhf7RXg==
|
||||
dependencies:
|
||||
"@expo/config-types" "^43.0.0"
|
||||
"@expo/json-file" "8.2.33"
|
||||
"@expo/plist" "0.0.15"
|
||||
"@expo/config-types" "^43.0.1"
|
||||
"@expo/json-file" "8.2.34"
|
||||
"@expo/plist" "0.0.16"
|
||||
"@react-native/normalize-color" "^2.0.0"
|
||||
chalk "^4.1.2"
|
||||
debug "^4.3.1"
|
||||
@@ -869,20 +869,20 @@
|
||||
xcode "^3.0.1"
|
||||
xml2js "0.4.23"
|
||||
|
||||
"@expo/config-types@^43.0.0":
|
||||
version "43.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-43.0.0.tgz#e283a4a1b72a655a6a2a745bd277caef58c8edb0"
|
||||
integrity sha512-GBbEtLdGX40DJ1wTixWtOHseupA5xWclV57KNZ58ebauWAOyK3Fyni7GD2OCOzxTIFi1nSjqgO9JlMp3ayP4Tw==
|
||||
"@expo/config-types@^43.0.1":
|
||||
version "43.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-43.0.1.tgz#3e047dccb371741a540980eaff26fb0c95039c30"
|
||||
integrity sha512-EtllpCGDdB/UdwAIs5YXJwBLpbFQNdlLLrxIvoILA9cXrpQMWkeDCT9lQPJzFRMFcLUaMuGvkzX2tR4tx5EQFQ==
|
||||
|
||||
"@expo/[email protected].3":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.3.tgz#48e5d453ae00ca090ea47799f4a9f352d5136a89"
|
||||
integrity sha512-Qo2OVDfKbCyLiG3g2vqrPQXBBGRB2I3ADIB/8nZ3Ypy7QhqOA1L7WYhTQQr3T1zknHTMzy7u61ArHweiAnEEpw==
|
||||
"@expo/[email protected].9":
|
||||
version "6.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-6.0.9.tgz#d2f700e067581f7fb088c4e811b38351ac57e151"
|
||||
integrity sha512-UJs8b5SSU9dl4VrX+MiSJcSwUVt7u6oUfNQDJ6VIWu03lDkNKtrk64GeSmUqHKsFd6iFXaMxtvkTBVmWWM6NaA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
"@expo/config-plugins" "4.0.3"
|
||||
"@expo/config-types" "^43.0.0"
|
||||
"@expo/json-file" "8.2.33"
|
||||
"@expo/config-plugins" "4.0.9"
|
||||
"@expo/config-types" "^43.0.1"
|
||||
"@expo/json-file" "8.2.34"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
require-from-string "^2.0.2"
|
||||
@@ -899,10 +899,10 @@
|
||||
joi "^17.4.2"
|
||||
semver "^7.3.5"
|
||||
|
||||
"@expo/[email protected]7":
|
||||
version "0.3.17"
|
||||
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.3.17.tgz#75e2606749ffa1284de570245f668503e4a06c3f"
|
||||
integrity sha512-zaOj24JK5F+pPGpULP8D9aAbApcR7ixV2GjT43YzZqjHuyxQ5knI+EsbmZ1TNnA1vNJJKMYrigB5+chcUGwkpw==
|
||||
"@expo/[email protected]8":
|
||||
version "0.3.18"
|
||||
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.3.18.tgz#36a41ab2925974ac5707e02cb0d3694349140497"
|
||||
integrity sha512-77/ub2aGuf7SYfaFhvCHE54Hs/jRuU5j+pemS5seLfVHNwHbJSse91TMhsTLLNz3GwwqTxFVe3KMycSccJ73nA==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "1.5.0"
|
||||
chalk "^4.0.0"
|
||||
@@ -925,6 +925,15 @@
|
||||
json5 "^1.0.1"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "8.2.34"
|
||||
resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.34.tgz#2f24e90a677195f7a81e167115460eb2902c3130"
|
||||
integrity sha512-ZxtBodAZGxdLtgKzmsC+8ViUxt1mhFW642Clu2OuG3f6PAyAFsU/SqEGag9wKFaD3x3Wt8VhL+3y5fMJmUFgPw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
json5 "^1.0.1"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.24.0-expo"
|
||||
resolved "https://registry.yarnpkg.com/@expo/oclif-dev-cli/-/oclif-dev-cli-1.24.0-expo.tgz#4e1f766e79b4d99e3da29318a577952793b143bf"
|
||||
@@ -945,26 +954,17 @@
|
||||
qqjs "^0.3.10"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@expo/[email protected].4":
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.0.4.tgz#d4ba48bfcb5dcae979e96644a0ca157c0d70f075"
|
||||
integrity sha512-dYNecU+r4RIy8dPzu1SCUi+EqPhfSic6ODH6IkKLCf9m7unxEO0SvhU+rJqlgpoQ49PThOmk4EFX2KCClyrKnw==
|
||||
"@expo/[email protected].6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.0.6.tgz#ef9e4691bfced9318d4a3736260ced27401fc752"
|
||||
integrity sha512-glYvPd2fv9tAQbXWprhnUYmKGmOtFJzGxhBef90bkZL8N4yNhcF1T/+l1Nrtzrf/ZSAjCnek4oXezpwOUYBnWw==
|
||||
dependencies:
|
||||
node-forge "^0.10.0"
|
||||
|
||||
"@expo/[email protected]4":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.0.14.tgz#a756903bd28aabe0a961222df2e7858a39a218c9"
|
||||
integrity sha512-bb4Ua1M/OdNgS8KiGdSDUjZ/bbPfv3xdPY/lz8Ctp/adlj/QgB8xA7tVPeqSSfJPZqFRwU0qLCnRhpUOnP51VQ==
|
||||
dependencies:
|
||||
"@xmldom/xmldom" "~0.7.0"
|
||||
base64-js "^1.2.3"
|
||||
xmlbuilder "^14.0.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.0.15.tgz#41ef37b7bbe6b81c48bf4a5c359661c766bb9e90"
|
||||
integrity sha512-LDxiS0KNZAGJu4fIJhbEKczmb+zeftl1NU0LE0tj0mozoMI5HSKdMUchgvnBm35bwBl8ekKkAfJJ0ONxljWQjQ==
|
||||
"@expo/[email protected]6":
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.0.16.tgz#c7d294a774196cfa1d8caf89a75ba923f5d1d53c"
|
||||
integrity sha512-yQMt2CTm2VNLiHv2/EqHYrHvfflI2mFJd+DZIQQy569hvpZle0CxMPGH1p2KatbrO8htxJNvwrlR/4TIwhQJ5w==
|
||||
dependencies:
|
||||
"@xmldom/xmldom" "~0.7.0"
|
||||
base64-js "^1.2.3"
|
||||
@@ -985,30 +985,31 @@
|
||||
lodash.template "^4.4.0"
|
||||
semver "^7.3.2"
|
||||
|
||||
"@expo/[email protected].3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-3.0.3.tgz#448f48aab2a28af6b12dc1a95696dbd08f5668f3"
|
||||
integrity sha512-7r/Wfzs2QNm8RvnbT1LFZ+Xkb3vrcLVGwkRt1BHPxmRFwq4SNwhkMyLPopQEjUhJU3AqLn6EKItS39+9c2f8vg==
|
||||
"@expo/[email protected].9":
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-3.0.9.tgz#c9a7034d4299bd7b3bf36c17efbdf1772899c7fe"
|
||||
integrity sha512-j4cHyZe04WijAyH2sJS2pH8DltqPDVQczN6brqFiuRktyIAroqh4ZMY1fgpSweX18LTXmGF9d1fG1aXC+N64YQ==
|
||||
dependencies:
|
||||
"@expo/config" "6.0.3"
|
||||
"@expo/config-plugins" "4.0.3"
|
||||
"@expo/config-types" "^43.0.0"
|
||||
"@expo/image-utils" "0.3.17"
|
||||
"@expo/json-file" "8.2.33"
|
||||
"@expo/config" "6.0.9"
|
||||
"@expo/config-plugins" "4.0.9"
|
||||
"@expo/config-types" "^43.0.1"
|
||||
"@expo/image-utils" "0.3.18"
|
||||
"@expo/json-file" "8.2.34"
|
||||
debug "^4.3.1"
|
||||
fs-extra "^9.0.0"
|
||||
resolve-from "^5.0.0"
|
||||
semver "7.3.2"
|
||||
xml2js "0.4.23"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/results/-/results-1.0.0.tgz#fd4b22f936ceafce23b04799f54b87fe2a9e18d1"
|
||||
integrity sha512-qECzzXX5oJot3m2Gu9pfRDz50USdBieQVwYAzeAtQRUTD3PVeTK1tlRUoDcrK8PSruDLuVYdKkLebX4w/o55VA==
|
||||
|
||||
"@expo/[email protected].0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.0.tgz#90c6ef615fbf34473180aa439e3daac7b5af9eed"
|
||||
integrity sha512-XUxJi2xUEi3RNdKc78emywcl8jQ+iGzpBq0gC1Gpu2/3gAuIxIfp/vGWp1vWo58cZtabhbQx/AMmbOfsHK35Qw==
|
||||
"@expo/[email protected].1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz#6aa575f346833eb6290282118766d4919c808c6a"
|
||||
integrity sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==
|
||||
dependencies:
|
||||
"@expo/bunyan" "^4.0.0"
|
||||
"@segment/loosely-validate-event" "^2.0.0"
|
||||
@@ -1018,11 +1019,6 @@
|
||||
remove-trailing-slash "^0.1.0"
|
||||
uuid "^8.3.2"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c"
|
||||
integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.5.0.tgz#799827edd8c10ef07eb1a2ff9dcfe081d596a395"
|
||||
@@ -2383,7 +2379,19 @@
|
||||
node-gyp "^7.1.0"
|
||||
read-package-json-fast "^2.0.1"
|
||||
|
||||
"@oclif/[email protected].0", "@oclif/command@^1.5.13", "@oclif/command@^1.5.20", "@oclif/command@^1.6.0":
|
||||
"@oclif/[email protected].3", "@oclif/command@^1.8.3":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.8.3.tgz#1f8bc2c4ecf94b6659a5134d95da179e1dffad9e"
|
||||
integrity sha512-OGjrhdVgTT2TAAj/2RrdXjwxaDoTm16c2LfAzrta1xIFe6/XhgQIYDmeRN/RptQoZQBX8e9Vv2JoQq+TbghJmw==
|
||||
dependencies:
|
||||
"@oclif/config" "^1.15.1"
|
||||
"@oclif/errors" "^1.3.5"
|
||||
"@oclif/parser" "^3.8.5"
|
||||
"@oclif/plugin-help" "^3.2.4"
|
||||
debug "^4.1.1"
|
||||
semver "^7.3.2"
|
||||
|
||||
"@oclif/command@^1.5.13", "@oclif/command@^1.5.20", "@oclif/command@^1.6.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.8.0.tgz#c1a499b10d26e9d1a611190a81005589accbb339"
|
||||
integrity sha512-5vwpq6kbvwkQwKqAoOU3L72GZ3Ta8RRrewKj9OJRolx28KLJJ8Dg9Rf7obRwt5jQA9bkYd8gqzMTrI7H3xLfaw==
|
||||
@@ -2395,7 +2403,19 @@
|
||||
debug "^4.1.1"
|
||||
semver "^7.3.2"
|
||||
|
||||
"@oclif/[email protected].0", "@oclif/config@^1.12.12", "@oclif/config@^1.12.8", "@oclif/config@^1.15.1":
|
||||
"@oclif/[email protected].1", "@oclif/config@^1.17.1":
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.17.1.tgz#383515f6715b91d8df5db8108214e93bb46e86ca"
|
||||
integrity sha512-UqV5qsN2np96TNlJspSNlRl7CpFmxYSrB0iLe3XV9NDkbFEE5prGP++h6w6xOR/FL3QV7BoqrbwGuJdJdFbidw==
|
||||
dependencies:
|
||||
"@oclif/errors" "^1.3.3"
|
||||
"@oclif/parser" "^3.8.6"
|
||||
debug "^4.1.1"
|
||||
globby "^11.0.1"
|
||||
is-wsl "^2.1.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@oclif/config@^1.12.12", "@oclif/config@^1.12.8", "@oclif/config@^1.15.1":
|
||||
version "1.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.17.0.tgz#ba8639118633102a7e481760c50054623d09fcab"
|
||||
integrity sha512-Lmfuf6ubjQ4ifC/9bz1fSCHc6F6E653oyaRXxg+lgT4+bYf9bk+nqrUpAbrXyABkCqgIBiFr3J4zR/kiFdE1PA==
|
||||
@@ -2407,7 +2427,7 @@
|
||||
is-wsl "^2.1.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@oclif/[email protected]":
|
||||
"@oclif/[email protected]", "@oclif/errors@^1.3.5":
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/errors/-/errors-1.3.5.tgz#a1e9694dbeccab10fe2fe15acb7113991bed636c"
|
||||
integrity sha512-OivucXPH/eLLlOT7FkCMoZXiaVYf8I/w1eTAM1+gKzfhALwWTusxEx7wBmW0uzvkSg/9ovWLycPaBgJbM3LOCQ==
|
||||
@@ -2444,17 +2464,27 @@
|
||||
chalk "^2.4.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@oclif/p[email protected]":
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.2.3.tgz#cd24010e7eb326782843d3aa6d6b5a4affebb2c3"
|
||||
integrity sha512-l2Pd0lbOMq4u/7xsl9hqISFqyR9gWEz/8+05xmrXFr67jXyS6EUCQB+mFBa0wepltrmJu0sAFg9AvA2mLaMMqQ==
|
||||
"@oclif/parser@^3.8.5", "@oclif/parser@^3.8.6":
|
||||
version "3.8.6"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.6.tgz#d5a108af9c708a051cc6b1d27d47359d75f41236"
|
||||
integrity sha512-tXb0NKgSgNxmf6baN6naK+CCwOueaFk93FG9u202U7mTBHUKsioOUlw1SG/iPi9aJM3WE4pHLXmty59pci0OEw==
|
||||
dependencies:
|
||||
"@oclif/command" "^1.5.20"
|
||||
"@oclif/config" "^1.15.1"
|
||||
"@oclif/errors" "^1.2.2"
|
||||
"@oclif/linewrap" "^1.0.0"
|
||||
chalk "^4.1.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@oclif/[email protected]", "@oclif/plugin-help@^3.2.4":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-3.2.5.tgz#dab0f5e655971b2c49dd80ee472f97b6e69a1297"
|
||||
integrity sha512-fjkZTstvacCPicF2oaa3Lc+Yw3ocKEaW6x6O7doVqMLuoMUX6wBOQ+f1a3VFzO1fErqNeFPDlUlVUhwq9yMzQg==
|
||||
dependencies:
|
||||
"@oclif/command" "^1.8.3"
|
||||
"@oclif/config" "^1.17.1"
|
||||
"@oclif/errors" "^1.3.5"
|
||||
chalk "^4.1.0"
|
||||
indent-string "^4.0.0"
|
||||
lodash.template "^4.4.0"
|
||||
lodash "^4.17.21"
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
widest-line "^3.1.0"
|
||||
@@ -7543,7 +7573,7 @@ lodash.without@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
|
||||
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
|
||||
|
||||
[email protected], lodash@^4.7.0:
|
||||
[email protected], lodash@^4.17.21, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -10460,10 +10490,10 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
|
||||
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
|
||||
typescript@4.5.2:
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998"
|
||||
integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==
|
||||
|
||||
ua-parser-js@^0.7.18:
|
||||
version "0.7.22"
|
||||
|
||||
Reference in new issue
Block a user