mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 03:38:08 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
878028271a | ||
|
|
d4e15bbcbf | ||
|
|
13bb056df1 | ||
|
|
14d56904d2 | ||
|
|
c2ffd05763 | ||
|
|
46e394fbef | ||
|
|
2613f0c2f9 | ||
|
|
e5fd7c01f4 | ||
|
|
73a0dd6e23 | ||
|
|
a9dc7f9eeb | ||
|
|
c3836f23d1 | ||
|
|
bfd73cbb19 | ||
|
|
6a1753edaa | ||
|
|
558a73086d | ||
|
|
15966fbd26 | ||
|
|
1e8b55383e | ||
|
|
6920ebb0ba | ||
|
|
55f35949e1 | ||
|
|
ccb998a221 | ||
|
|
30bc27b4c4 | ||
|
|
da3c95ccaf | ||
|
|
bbcbe1dceb |
No files matched your search
@@ -19,6 +19,7 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
'flowtype/no-types-missing-file-annotation': 'off',
|
||||
'curly': 'warn',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
**/yarn.lock linguist-generated
|
||||
@@ -11,3 +11,4 @@ tmp
|
||||
node_modules
|
||||
coverage
|
||||
.history
|
||||
.DS_Store
|
||||
+27
-3
@@ -6,17 +6,41 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Unifify generic and managed workflow, deprecate `workflow` field. ([#497](https://github.com/expo/eas-cli/pull/497) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
### 🎉 New features
|
||||
|
||||
- Added support for automatically creating and linking iOS capability identifiers (Apple Pay, iCloud Containers, App Groups). ([#524](https://github.com/expo/eas-cli/pull/524) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Add `secret:create --force` command to overwrite existing secrets. ([#513](https://github.com/expo/eas-cli/pull/513) by [@bycedric](https://github.com/bycedric))
|
||||
- Fall back to APK when building for internal distribution. ([#527](https://github.com/expo/eas-cli/pull/527) by [@dsokal](https://github.com/dsokal))
|
||||
- Add `iosEnterpriseProvisioning` to build metadata. ([#522](https://github.com/expo/eas-cli/pull/522) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix iOS capability syncing on build. ([#521](https://github.com/expo/eas-cli/pull/521) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Fix unhandled error when amplitude domains are blocked. ([#512](https://github.com/expo/eas-cli/pull/512) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Use default value for `appBuildVersion` in build metadata when building an Android managed project. ([#526](https://github.com/expo/eas-cli/pull/526) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [0.21.0](https://github.com/expo/eas-cli/releases/tag/v0.21.0) - 2021-07-12
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `project:*` commands. ([#500](https://github.com/expo/eas-cli/pull/500) by [@jkhales](https://github.com/jkhales))
|
||||
- Added support for iOS 15 capabilities: Communication Notifications, Time Sensitive Notifications, Group Activities, and Family Controls. ([#499](https://github.com/expo/eas-cli/pull/499) by [@EvanBacon](https://github.com/EvanBacon))
|
||||
- Show more build metadata in `build:view` and `build:list`. ([#504](https://github.com/expo/eas-cli/pull/504) and [#508](https://github.com/expo/eas-cli/pull/508) by [@dsokal](https://github.com/dsokal))
|
||||
- Add runtime version to build metadata. ([#493](https://github.com/expo/eas-cli/pull/493) by [@dsokal](https://github.com/dsokal))
|
||||
|
||||
## [0.20.0](https://github.com/expo/eas-cli/releases/tag/v0.20.0) - 2021-07-09
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Unifify generic and managed workflow, deprecate `workflow` field. ([#497](https://github.com/expo/eas-cli/pull/497) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix runtime version checks. ([#495](https://github.com/expo/eas-cli/pull/495) by [@dsokal](https://github.com/dsokal))
|
||||
- Resolve `--android-package` correctly in `eas submit` command. ([#494](https://github.com/expo/eas-cli/pull/494) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [0.19.1](https://github.com/expo/eas-cli/releases/tag/v0.19.1) - 2021-07-02
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.20.0"
|
||||
"version": "0.22.0"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
graphql.schema.json linguist-generated
|
||||
src/graphql/generated.ts linguist-generated
|
||||
+50
-22
@@ -47,6 +47,8 @@ eas --help COMMAND
|
||||
* [`eas device:list`](#eas-devicelist)
|
||||
* [`eas device:view [UDID]`](#eas-deviceview-udid)
|
||||
* [`eas help [COMMAND]`](#eas-help-command)
|
||||
* [`eas project:info`](#eas-projectinfo)
|
||||
* [`eas project:init`](#eas-projectinit)
|
||||
* [`eas secret:create`](#eas-secretcreate)
|
||||
* [`eas secret:delete`](#eas-secretdelete)
|
||||
* [`eas secret:list`](#eas-secretlist)
|
||||
@@ -69,7 +71,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -83,7 +85,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -97,7 +99,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -108,7 +110,7 @@ USAGE
|
||||
$ eas analytics [STATUS]
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -128,7 +130,7 @@ OPTIONS
|
||||
--[no-]wait Wait for build(s) to complete
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -139,7 +141,7 @@ USAGE
|
||||
$ eas build:cancel [BUILD_ID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -153,7 +155,7 @@ OPTIONS
|
||||
-p, --platform=(android|ios|all) Platform to configure
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -169,7 +171,7 @@ OPTIONS
|
||||
--status=(new|in-queue|in-progress|errored|finished|canceled)
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -180,7 +182,7 @@ USAGE
|
||||
$ eas build:view [BUILD_ID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -191,7 +193,7 @@ USAGE
|
||||
$ eas credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -202,7 +204,7 @@ USAGE
|
||||
$ eas device:create
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -216,7 +218,7 @@ OPTIONS
|
||||
--apple-team-id=apple-team-id
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -227,7 +229,7 @@ USAGE
|
||||
$ eas device:view [UDID]
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -246,6 +248,31 @@ OPTIONS
|
||||
|
||||
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.2.0/src/commands/help.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
information about the current project
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas project:info
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
create or link an EAS project
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas project:init
|
||||
|
||||
ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
Create an environment secret on the current project or owner account.
|
||||
@@ -255,12 +282,13 @@ USAGE
|
||||
$ eas secret:create
|
||||
|
||||
OPTIONS
|
||||
--force Delete and recreate existing secrets
|
||||
--name=name Name of the secret
|
||||
--scope=(account|project) [default: project] Scope for the secret
|
||||
--value=value Value of the secret
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -277,7 +305,7 @@ DESCRIPTION
|
||||
Unsure where to find the secret's ID? Run eas secrets:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -288,7 +316,7 @@ USAGE
|
||||
$ eas secret:list
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas submit --platform=(android|ios)`
|
||||
|
||||
@@ -368,7 +396,7 @@ EXAMPLES
|
||||
and provide its App ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -387,7 +415,7 @@ OPTIONS
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -401,7 +429,7 @@ ARGUMENTS
|
||||
ID ID of the webhook to delete
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -415,7 +443,7 @@ OPTIONS
|
||||
--event=(BUILD) Event type that triggers the webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -435,7 +463,7 @@ OPTIONS
|
||||
--url=url Webhook URL
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -449,5 +477,5 @@ ARGUMENTS
|
||||
ID ID of the webhook to view
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.20.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.22.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
<!-- commandsstop -->
|
||||
Generated
+572
-11
@@ -312,6 +312,22 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "easBuild",
|
||||
"description": "Top-level query object for querying EAS Build configuration.",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildQuery",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "experimentation",
|
||||
"description": "Top-level query object for querying Experimentation configuration.",
|
||||
@@ -1419,7 +1435,7 @@
|
||||
},
|
||||
{
|
||||
"name": "buildOrBuildJobs",
|
||||
"description": "Coalesced Build (EAS) or BuildJob (Classic) items for this account. Use \"createdBefore\" to offset a query.",
|
||||
"description": "Coalesced Build (EAS) or BuildJob (Classic) for all apps belonging to this account.",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
@@ -1437,7 +1453,7 @@
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"description": " Use a Date to offset queries ",
|
||||
"description": " Offset the query ",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
@@ -1463,6 +1479,73 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use activityTimelineProjectActivities with filterTypes instead"
|
||||
},
|
||||
{
|
||||
"name": "activityTimelineProjectActivities",
|
||||
"description": "Coalesced project activity for all apps belonging to this account.",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"description": " Offset the query ",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "filterTypes",
|
||||
"description": " Types of objects to filter ",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "ActivityTimelineProjectActivityType",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INTERFACE",
|
||||
"name": "ActivityTimelineProjectActivity",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
@@ -2822,7 +2905,7 @@
|
||||
},
|
||||
{
|
||||
"name": "buildOrBuildJobs",
|
||||
"description": "Coalesced Build (EAS) or BuildJob (Classic) items for this app. Use \"createdBefore\" to offset a query.",
|
||||
"description": "Coalesced Build (EAS) or BuildJob (Classic) items for this app.",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
@@ -2840,7 +2923,7 @@
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"description": " Use a Date to offset queries ",
|
||||
"description": " Offset the query ",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
@@ -2866,8 +2949,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use activityTimelineProjectActivities with filterTypes instead"
|
||||
},
|
||||
{
|
||||
"name": "submissions",
|
||||
@@ -3168,7 +3251,7 @@
|
||||
},
|
||||
{
|
||||
"name": "activityTimelineProjectActivities",
|
||||
"description": "Coalesced project activity for an app. Use \"createdBefore\" to offset a query.",
|
||||
"description": "Coalesced project activity for an app",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
@@ -3186,13 +3269,31 @@
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"description": " Use a Date to offset queries ",
|
||||
"description": " Offset the query ",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "filterTypes",
|
||||
"description": " Types of objects to filter ",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "ActivityTimelineProjectActivityType",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
@@ -4068,6 +4169,18 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iosEnterpriseProvisioning",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "BuildIosEnterpriseProvisioning",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "buildProfile",
|
||||
"description": "",
|
||||
@@ -4668,6 +4781,73 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "activityTimelineProjectActivities",
|
||||
"description": "Coalesced project activity for all apps belonging to all accounts this user belongs to. Only resolves for the viewer.",
|
||||
"args": [
|
||||
{
|
||||
"name": "limit",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "createdBefore",
|
||||
"description": " Offset the query ",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "filterTypes",
|
||||
"description": " Types of objects to filter ",
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "ActivityTimelineProjectActivityType",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INTERFACE",
|
||||
"name": "ActivityTimelineProjectActivity",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "featureGates",
|
||||
"description": "Server feature gate values for this actor, optionally filtering by desired gates.\nOnly resolves for the viewer.",
|
||||
@@ -5340,6 +5520,41 @@
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "ActivityTimelineProjectActivityType",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "BUILD_JOB",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "BUILD",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "UPDATE",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SUBMISSION",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "JSONObject",
|
||||
@@ -5449,6 +5664,29 @@
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "BuildIosEnterpriseProvisioning",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "ADHOC",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "UNIVERSAL",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "BuildError",
|
||||
@@ -10857,6 +11095,79 @@
|
||||
},
|
||||
"isDeprecated": true,
|
||||
"deprecationReason": "Use App.builds instead"
|
||||
},
|
||||
{
|
||||
"name": "all",
|
||||
"description": "Get all builds.\nBy default, they are sorted from latest to oldest.\nAvailable only for admin users.",
|
||||
"args": [
|
||||
{
|
||||
"name": "statuses",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "BuildStatus",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "order",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "Order",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Build",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
@@ -10864,6 +11175,29 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "Order",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "DESC",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "ASC",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ClientBuildQuery",
|
||||
@@ -10993,6 +11327,113 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildQuery",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "killSwitches",
|
||||
"description": "Get EAS Build kill switches state",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitch",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitch",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "EasBuildKillSwitchName",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "EasBuildKillSwitchName",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "STOP_ACCEPTING_BUILDS",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "STOP_SCHEDULING_BUILDS",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "STOP_ACCEPTING_NORMAL_PRIORITY_BUILDS",
|
||||
"description": "",
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ExperimentationQuery",
|
||||
@@ -12080,6 +12521,22 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "easBuildKillSwitch",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitchMutation",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "emailSubscription",
|
||||
"description": "Mutations that modify an EmailSubscription",
|
||||
@@ -16103,6 +16560,16 @@
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "iosEnterpriseProvisioning",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "BuildIosEnterpriseProvisioning",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "appName",
|
||||
"description": "",
|
||||
@@ -18730,9 +19197,13 @@
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Update",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18968,6 +19439,16 @@
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "extra",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "JSONObject",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
@@ -20464,6 +20945,86 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitchMutation",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "set",
|
||||
"description": "Set an EAS Build kill switch to a given value",
|
||||
"args": [
|
||||
{
|
||||
"name": "name",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "EasBuildKillSwitchName",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitch",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "resetAll",
|
||||
"description": "Reset all EAS Build kill switches (set them to false)",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EasBuildKillSwitch",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EmailSubscriptionMutation",
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "0.20.0",
|
||||
"version": "0.22.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@amplitude/identify": "1.5.0",
|
||||
"@amplitude/node": "1.5.0",
|
||||
"@expo/apple-utils": "0.0.0-alpha.20",
|
||||
"@amplitude/identify": "1.7.0",
|
||||
"@amplitude/node": "1.7.0",
|
||||
"@expo/apple-utils": "0.0.0-alpha.24",
|
||||
"@expo/config": "3.3.42",
|
||||
"@expo/config-plugins": "3.0.3",
|
||||
"@expo/eas-build-job": "0.2.41",
|
||||
"@expo/eas-json": "^0.20.0",
|
||||
"@expo/eas-build-job": "0.2.44",
|
||||
"@expo/eas-json": "^0.22.0",
|
||||
"@expo/json-file": "8.2.25",
|
||||
"@expo/pkcs12": "0.0.4",
|
||||
"@expo/plist": "0.0.11",
|
||||
@@ -131,15 +131,27 @@
|
||||
],
|
||||
"helpClass": "./build/help",
|
||||
"topics": {
|
||||
"account": {
|
||||
"description": "manage your account"
|
||||
},
|
||||
"build": {
|
||||
"description": "build app binaries"
|
||||
},
|
||||
"channel": {
|
||||
"description": "manage update channels"
|
||||
},
|
||||
"device": {
|
||||
"description": "manage your Apple devices for internal distribution"
|
||||
},
|
||||
"project": {
|
||||
"description": "manage your project"
|
||||
},
|
||||
"release": {
|
||||
"description": "manage update releases"
|
||||
},
|
||||
"secret": {
|
||||
"description": "manage project and account secrets"
|
||||
},
|
||||
"update": {
|
||||
"description": "manage individual updates"
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function initAsync(): Promise<void> {
|
||||
process.env.EXPO_STAGING || process.env.EXPO_LOCAL
|
||||
? 'cdebbc678931403439486c4750781544'
|
||||
: '4ac443afd5073c0df6169291db1d3495';
|
||||
client = Amplitude.init(apiKey);
|
||||
client = Amplitude.init(apiKey, { retryClass: new Amplitude.OfflineRetryHandler(apiKey) });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export async function readChannelSafelyAsync(projectDir: string): Promise<string
|
||||
const androidManifest = await getAndroidManifestAsync(projectDir);
|
||||
const stringifiedRequestHeaders = AndroidConfig.Manifest.getMainApplicationMetaDataValue(
|
||||
androidManifest,
|
||||
'expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY' //TODO-JJ AndroidConfig.Updates.Config.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY once https://github.com/expo/expo-cli/pull/3571 is published
|
||||
AndroidConfig.Updates.Config.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY
|
||||
);
|
||||
if (!stringifiedRequestHeaders) {
|
||||
return null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArchiveSource, Job, sanitizeJob } from '@expo/eas-build-job';
|
||||
import { Android, ArchiveSource, Job, sanitizeJob } from '@expo/eas-build-job';
|
||||
import path from 'path';
|
||||
|
||||
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
||||
@@ -34,6 +34,12 @@ export async function prepareJobAsync(
|
||||
}
|
||||
: {};
|
||||
|
||||
const { gradleCommand } = ctx.buildProfile;
|
||||
let buildType: Android.BuildType | undefined = ctx.buildProfile.buildType;
|
||||
if (!buildType && !gradleCommand && ctx.buildProfile.distribution === 'internal') {
|
||||
buildType = Android.BuildType.APK;
|
||||
}
|
||||
|
||||
const job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.ANDROID,
|
||||
@@ -57,11 +63,11 @@ export async function prepareJobAsync(
|
||||
releaseChannel: ctx.buildProfile.releaseChannel,
|
||||
updates: { channel: ctx.buildProfile.channel },
|
||||
|
||||
gradleCommand: ctx.buildProfile.gradleCommand,
|
||||
gradleCommand,
|
||||
artifactPath: ctx.buildProfile.artifactPath,
|
||||
|
||||
username,
|
||||
buildType: ctx.buildProfile.buildType,
|
||||
buildType,
|
||||
};
|
||||
|
||||
return sanitizeJob(job);
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function readVersionCodeAsync(
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return AndroidConfig.Version.getVersionCode(exp) ?? undefined;
|
||||
return AndroidConfig.Version.getVersionCode(exp) ?? 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,13 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
'EAS Build is down for maintenance, please try again later. Check https://status.expo.dev/ for updates.'
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_FREE_TIER_DISABLED'
|
||||
) {
|
||||
Log.error(
|
||||
'EAS Build free tier is temporarily disabled, please try again later. Check https://status.expo.dev/ for updates.'
|
||||
);
|
||||
throw new Error('Build request failed.');
|
||||
} else if (
|
||||
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS'
|
||||
) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ArchiveSource, ArchiveSourceType, Metadata, Workflow } from '@expo/eas-
|
||||
|
||||
import {
|
||||
BuildCredentialsSource,
|
||||
BuildIosEnterpriseProvisioning,
|
||||
BuildMetadataInput,
|
||||
BuildWorkflow,
|
||||
DistributionType,
|
||||
@@ -32,6 +33,9 @@ export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
||||
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
|
||||
distribution: metadata.distribution && transformDistribution(metadata.distribution),
|
||||
workflow: metadata.workflow && transformWorkflow(metadata.workflow),
|
||||
iosEnterpriseProvisioning:
|
||||
metadata.iosEnterpriseProvisioning &&
|
||||
transformIosEnterpriseProvisioning(metadata.iosEnterpriseProvisioning),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,3 +66,13 @@ export function transformWorkflow(workflow: Workflow): BuildWorkflow {
|
||||
return BuildWorkflow.Managed;
|
||||
}
|
||||
}
|
||||
|
||||
export function transformIosEnterpriseProvisioning(
|
||||
enterpriseProvisioning: Metadata['iosEnterpriseProvisioning']
|
||||
): BuildIosEnterpriseProvisioning {
|
||||
if (enterpriseProvisioning === 'adhoc') {
|
||||
return BuildIosEnterpriseProvisioning.Adhoc;
|
||||
} else {
|
||||
return BuildIosEnterpriseProvisioning.Universal;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Metadata } from '@expo/eas-build-job';
|
||||
import { CredentialsSource } from '@expo/eas-json';
|
||||
import { CredentialsSource, IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
|
||||
import { getApplicationIdAsync } from '../project/android/applicationId';
|
||||
import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier';
|
||||
@@ -44,6 +44,7 @@ export async function collectMetadata<T extends Platform>(
|
||||
workflow: ctx.workflow,
|
||||
credentialsSource,
|
||||
sdkVersion: ctx.commandCtx.exp.sdkVersion,
|
||||
runtimeVersion: ctx.commandCtx.exp.runtimeVersion,
|
||||
...channelOrReleaseChannel,
|
||||
distribution: ctx.buildProfile.distribution ?? 'store',
|
||||
appName: ctx.commandCtx.exp.name,
|
||||
@@ -51,6 +52,11 @@ export async function collectMetadata<T extends Platform>(
|
||||
buildProfile: ctx.commandCtx.profile,
|
||||
gitCommitHash: await vcs.getCommitHashAsync(),
|
||||
username: getUsername(ctx.commandCtx.exp, await ensureLoggedInAsync()),
|
||||
...(ctx.platform === Platform.IOS && {
|
||||
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
|
||||
ctx as BuildContext<Platform.IOS>
|
||||
),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -134,3 +140,9 @@ async function getNativeChannelAsync<T extends Platform>(
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveIosEnterpriseProvisioning(
|
||||
ctx: BuildContext<Platform.IOS>
|
||||
): IosEnterpriseProvisioning | undefined {
|
||||
return ctx.buildProfile.enterpriseProvisioning;
|
||||
}
|
||||
@@ -7,9 +7,10 @@ import { requestedPlatformDisplayNames } from '../constants';
|
||||
|
||||
export function logCredentialsSource(credentialsSource: CredentialsSource, platform: Platform) {
|
||||
let message = `Using ${credentialsSource} ${requestedPlatformDisplayNames[platform]} credentials`;
|
||||
if (credentialsSource === CredentialsSource.LOCAL)
|
||||
if (credentialsSource === CredentialsSource.LOCAL) {
|
||||
message += ` ${chalk.dim('(credentials.json)')}`;
|
||||
else if (credentialsSource === CredentialsSource.REMOTE)
|
||||
} else if (credentialsSource === CredentialsSource.REMOTE) {
|
||||
message += ` ${chalk.dim('(Expo server)')}`;
|
||||
}
|
||||
Log.succeed(message);
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { BuildFragment, BuildStatus as GraphQLBuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildStatus as GraphQLBuildStatus,
|
||||
} from '../../graphql/generated';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { appPlatformDisplayNames } from '../constants';
|
||||
import { getBuildLogsUrl } from './url';
|
||||
@@ -8,7 +12,7 @@ import { getBuildLogsUrl } from './url';
|
||||
export function formatGraphQLBuild(build: BuildFragment) {
|
||||
const actor = getActorName(build);
|
||||
const account = build.project.__typename === 'App' ? build.project.ownerAccount.name : 'unknown';
|
||||
const fields: { label: string; value: string }[] = [
|
||||
const fields: { label: string; value?: string | null }[] = [
|
||||
{ label: 'ID', value: build.id },
|
||||
{
|
||||
label: 'Platform',
|
||||
@@ -37,11 +41,39 @@ export function formatGraphQLBuild(build: BuildFragment) {
|
||||
},
|
||||
{
|
||||
label: 'Distribution',
|
||||
value: build.distribution?.toLowerCase() ?? chalk.gray('unknown'),
|
||||
value: build.distribution?.toLowerCase(),
|
||||
},
|
||||
{
|
||||
label: 'Enterprise Provisioning',
|
||||
value: build.iosEnterpriseProvisioning?.toLowerCase(),
|
||||
},
|
||||
{
|
||||
label: 'Release Channel',
|
||||
value: build.releaseChannel ?? chalk.gray('unknown'),
|
||||
value: build.releaseChannel,
|
||||
},
|
||||
{
|
||||
label: 'Channel',
|
||||
value: build.channel,
|
||||
},
|
||||
{
|
||||
label: 'SDK Version',
|
||||
value: build.sdkVersion,
|
||||
},
|
||||
{
|
||||
label: 'Runtime Version',
|
||||
value: build.runtimeVersion,
|
||||
},
|
||||
{
|
||||
label: 'Version',
|
||||
value: build.appVersion,
|
||||
},
|
||||
{
|
||||
label: build.platform === AppPlatform.Android ? 'Version code' : 'Build number',
|
||||
value: build.appBuildVersion,
|
||||
},
|
||||
{
|
||||
label: 'Commit',
|
||||
value: build.gitCommitHash,
|
||||
},
|
||||
{
|
||||
label: 'Logs',
|
||||
@@ -57,13 +89,13 @@ export function formatGraphQLBuild(build: BuildFragment) {
|
||||
return '<in progress>';
|
||||
case GraphQLBuildStatus.Canceled:
|
||||
case GraphQLBuildStatus.Errored:
|
||||
return '---------';
|
||||
return null;
|
||||
case GraphQLBuildStatus.Finished: {
|
||||
const url = build.artifacts?.buildUrl;
|
||||
return url ? url : chalk.red('not found');
|
||||
}
|
||||
default:
|
||||
return 'unknown';
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -81,7 +113,11 @@ export function formatGraphQLBuild(build: BuildFragment) {
|
||||
{ label: 'Started by', value: actor ?? 'unknown' },
|
||||
];
|
||||
|
||||
return formatFields(fields);
|
||||
const filteredFields = fields.filter(({ value }) => value !== undefined && value !== null) as {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
return formatFields(filteredFields);
|
||||
}
|
||||
|
||||
const getActorName = (build: BuildFragment): string => {
|
||||
|
||||
@@ -130,7 +130,10 @@ export default class BranchPublish extends Command {
|
||||
throw new Error('Please run this command inside a project directory.');
|
||||
}
|
||||
|
||||
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
|
||||
const { exp } = getConfig(projectDir, {
|
||||
skipSDKVersionRequirement: true,
|
||||
isPublicConfig: true,
|
||||
});
|
||||
let { runtimeVersion, sdkVersion } = exp;
|
||||
|
||||
// When a SDK version is supplied instead of a runtime version and we're in the managed workflow
|
||||
@@ -260,7 +263,7 @@ export default class BranchPublish extends Command {
|
||||
const platforms = platformFlag === 'all' ? defaultPublishPlatforms : [platformFlag];
|
||||
const assets = collectAssets({ inputDir: inputDir!, platforms });
|
||||
await uploadAssetsAsync(assets);
|
||||
updateInfoGroup = await buildUpdateInfoGroupAsync(assets);
|
||||
updateInfoGroup = await buildUpdateInfoGroupAsync(assets, exp);
|
||||
assetSpinner.succeed('Uploaded assets!');
|
||||
} catch (e) {
|
||||
assetSpinner.fail('Failed to upload assets');
|
||||
|
||||
@@ -23,8 +23,6 @@ import formatFields from '../../utils/formatFields';
|
||||
|
||||
const PAGE_LIMIT = 10_000;
|
||||
|
||||
type TruncatedUpdate = Pick<Update, 'group' | 'message' | 'createdAt' | 'actor'>;
|
||||
|
||||
export async function viewUpdateBranchAsync({
|
||||
appId,
|
||||
name,
|
||||
|
||||
@@ -31,7 +31,6 @@ async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
|
||||
}
|
||||
|
||||
export default class ProjectInfo extends Command {
|
||||
static hidden = true;
|
||||
static description = 'information about the current project';
|
||||
|
||||
async run() {
|
||||
@@ -49,8 +48,8 @@ export default class ProjectInfo extends Command {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'ID', value: projectId },
|
||||
{ label: 'fullName', value: app.byId.fullName },
|
||||
{ label: 'ID', value: projectId },
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import Log from '../../log';
|
||||
import { findProjectRootAsync, setProjectIdAsync } from '../../project/projectUtils';
|
||||
|
||||
export default class ProjectInit extends Command {
|
||||
static hidden = true;
|
||||
static description = 'create or link an EAS project';
|
||||
static aliases = ['init'];
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Command, flags } from '@oclif/command';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
|
||||
import {
|
||||
EnvironmentSecretScope,
|
||||
EnvironmentSecretsQuery,
|
||||
} from '../../graphql/queries/EnvironmentSecretsQuery';
|
||||
import Log from '../../log';
|
||||
import {
|
||||
isEasEnabledForProjectAsync,
|
||||
@@ -18,10 +22,6 @@ import { findAccountByName } from '../../user/Account';
|
||||
import { getActorDisplayName } from '../../user/User';
|
||||
import { ensureLoggedInAsync } from '../../user/actions';
|
||||
|
||||
export enum EnvironmentSecretScope {
|
||||
ACCOUNT = 'account',
|
||||
PROJECT = 'project',
|
||||
}
|
||||
export default class EnvironmentSecretCreate extends Command {
|
||||
static description = 'Create an environment secret on the current project or owner account.';
|
||||
|
||||
@@ -37,12 +37,16 @@ export default class EnvironmentSecretCreate extends Command {
|
||||
value: flags.string({
|
||||
description: 'Value of the secret',
|
||||
}),
|
||||
force: flags.boolean({
|
||||
description: 'Delete and recreate existing secrets',
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
async run() {
|
||||
const actor = await ensureLoggedInAsync();
|
||||
let {
|
||||
flags: { name, value: secretValue, scope },
|
||||
flags: { name, value: secretValue, scope, force },
|
||||
} = this.parse(EnvironmentSecretCreate);
|
||||
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
@@ -93,7 +97,9 @@ export default class EnvironmentSecretCreate extends Command {
|
||||
},
|
||||
}));
|
||||
|
||||
if (!name) throw new Error('Secret name may not be empty.');
|
||||
if (!name) {
|
||||
throw new Error('Secret name may not be empty.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!secretValue) {
|
||||
@@ -106,10 +112,26 @@ export default class EnvironmentSecretCreate extends Command {
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
|
||||
if (!secretValue) throw new Error(validationMessage);
|
||||
if (!secretValue) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (scope === EnvironmentSecretScope.PROJECT) {
|
||||
if (force) {
|
||||
const existingSecrets = await EnvironmentSecretsQuery.byAppIdAsync(projectId);
|
||||
const existingSecret = existingSecrets.find(secret => secret.name === name);
|
||||
|
||||
if (existingSecret) {
|
||||
await EnvironmentSecretMutation.delete(existingSecret.id);
|
||||
Log.withTick(
|
||||
`Deleting existing secret ${chalk.bold(name)} on project ${chalk.bold(
|
||||
`@${accountName}/${slug}`
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const secret = await EnvironmentSecretMutation.createForApp(
|
||||
{ name, value: secretValue },
|
||||
projectId
|
||||
@@ -136,6 +158,20 @@ export default class EnvironmentSecretCreate extends Command {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
const existingSecrets = await EnvironmentSecretsQuery.byAcccountNameAsync(projectId);
|
||||
const existingSecret = existingSecrets.find(secret => secret.name === name);
|
||||
|
||||
if (existingSecret) {
|
||||
await EnvironmentSecretMutation.delete(existingSecret.id);
|
||||
Log.withTick(
|
||||
`Deleting existing secret ${chalk.bold(name)} on account ${chalk.bold(
|
||||
ownerAccount.name
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const secret = await EnvironmentSecretMutation.createForAccount(
|
||||
{ name, value: secretValue },
|
||||
ownerAccount.id
|
||||
|
||||
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
||||
|
||||
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
|
||||
import {
|
||||
EnvironmentSecretScope,
|
||||
EnvironmentSecretWithScope,
|
||||
EnvironmentSecretsQuery,
|
||||
} from '../../graphql/queries/EnvironmentSecretsQuery';
|
||||
@@ -15,12 +16,10 @@ import {
|
||||
import {
|
||||
findProjectRootAsync,
|
||||
getProjectAccountNameAsync,
|
||||
getProjectFullNameAsync,
|
||||
getProjectIdAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { promptAsync, toggleConfirmAsync } from '../../prompts';
|
||||
import { ensureLoggedInAsync } from '../../user/actions';
|
||||
import { EnvironmentSecretScope } from './create';
|
||||
|
||||
export default class EnvironmentSecretDelete extends Command {
|
||||
static description = `Delete an environment secret by ID.
|
||||
@@ -38,7 +37,6 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
const projectFullName = await getProjectFullNameAsync(exp);
|
||||
const projectAccountName = await getProjectAccountNameAsync(exp);
|
||||
|
||||
if (!(await isEasEnabledForProjectAsync(projectId))) {
|
||||
@@ -55,7 +53,7 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
|
||||
if (!id) {
|
||||
const validationMessage = 'You must select which secret to delete.';
|
||||
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectFullName);
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectId);
|
||||
|
||||
({ secret } = await promptAsync({
|
||||
type: 'autocomplete',
|
||||
@@ -69,7 +67,9 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
|
||||
|
||||
id = secret?.id;
|
||||
|
||||
if (!id) throw new Error(validationMessage);
|
||||
if (!id) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import {
|
||||
findProjectRootAsync,
|
||||
getProjectAccountNameAsync,
|
||||
getProjectFullNameAsync,
|
||||
getProjectIdAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import { ensureLoggedInAsync } from '../../user/actions';
|
||||
@@ -27,7 +26,6 @@ export default class EnvironmentSecretList extends Command {
|
||||
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
|
||||
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
const projectFullName = await getProjectFullNameAsync(exp);
|
||||
const projectAccountName = await getProjectAccountNameAsync(exp);
|
||||
|
||||
if (!(await isEasEnabledForProjectAsync(projectId))) {
|
||||
@@ -40,7 +38,7 @@ export default class EnvironmentSecretList extends Command {
|
||||
throw new Error("Please run this command inside your project's directory");
|
||||
}
|
||||
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectFullName);
|
||||
const secrets = await EnvironmentSecretsQuery.allAsync(projectAccountName, projectId);
|
||||
|
||||
const table = new Table({
|
||||
head: ['Name', 'Scope', 'ID', 'Updated at'],
|
||||
|
||||
+40
-1
@@ -1,6 +1,21 @@
|
||||
import { BundleIdCapability, CapabilityType } from '@expo/apple-utils';
|
||||
|
||||
import { syncCapabilitiesForEntitlementsAsync } from '../bundleIdCapabilities';
|
||||
import {
|
||||
CapabilityMapping,
|
||||
assertValidOptions,
|
||||
syncCapabilitiesForEntitlementsAsync,
|
||||
} from '../bundleIdCapabilities';
|
||||
|
||||
describe(assertValidOptions, () => {
|
||||
it(`adds a reason for asserting capability identifiers`, () => {
|
||||
const classifier = CapabilityMapping.find(
|
||||
({ capabilityIdPrefix }) => capabilityIdPrefix === 'merchant.'
|
||||
)!;
|
||||
expect(() => assertValidOptions(classifier, ['foobar'])).toThrowError(
|
||||
/Expected an array of strings, where each string is prefixed with "merchant."/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
it('enables all capabilities', async () => {
|
||||
@@ -33,6 +48,10 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
'com.apple.developer.applesignin': ['Default'],
|
||||
'com.apple.developer.siri': true,
|
||||
'com.apple.developer.networking.wifi-info': true,
|
||||
'com.apple.developer.usernotifications.communication': true,
|
||||
'com.apple.developer.usernotifications.time-sensitive': true,
|
||||
'com.apple.developer.group-session': true,
|
||||
'com.apple.developer.family-controls': true,
|
||||
'com.apple.developer.authentication-services.autofill-credential-provider': true,
|
||||
// 'com.apple.developer.game-center': true,
|
||||
'com.apple.security.application-groups': [
|
||||
@@ -61,6 +80,22 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
{ capabilityType: 'APPLE_ID_AUTH', option: 'ON' },
|
||||
{ capabilityType: 'SIRIKIT', option: 'ON' },
|
||||
{ capabilityType: 'ACCESS_WIFI_INFORMATION', option: 'ON' },
|
||||
{
|
||||
capabilityType: 'USERNOTIFICATIONS_COMMUNICATION',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'USERNOTIFICATIONS_TIMESENSITIVE',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'GROUP_ACTIVITIES',
|
||||
option: 'ON',
|
||||
},
|
||||
{
|
||||
capabilityType: 'FAMILY_CONTROLS',
|
||||
option: 'ON',
|
||||
},
|
||||
{ capabilityType: 'AUTOFILL_CREDENTIAL_PROVIDER', option: 'ON' },
|
||||
{ capabilityType: 'APP_GROUPS', option: 'ON' },
|
||||
]);
|
||||
@@ -86,6 +121,10 @@ describe(syncCapabilitiesForEntitlementsAsync, () => {
|
||||
'Sign In with Apple',
|
||||
'SiriKit',
|
||||
'Access WiFi Information',
|
||||
'Communication Notifications',
|
||||
'Time Sensitive Notifications',
|
||||
'Group Activities',
|
||||
'Family Controls',
|
||||
'AutoFill Credential Provider',
|
||||
'App Groups',
|
||||
]);
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
import { syncCapabilityIdentifiersForEntitlementsAsync } from '../capabilityIdentifiers';
|
||||
|
||||
function mockCapabilities(Apple: any) {
|
||||
Apple.MerchantId.getAsync = jest.fn(() => [
|
||||
{
|
||||
id: 'XXX-merch-1',
|
||||
attributes: {
|
||||
identifier: 'merchant.expo',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
Apple.MerchantId.createAsync = jest.fn((ctx, { identifier }) => ({
|
||||
id: 'XXX-merch-2',
|
||||
attributes: {
|
||||
identifier,
|
||||
},
|
||||
}));
|
||||
|
||||
Apple.AppGroup.getAsync = jest.fn(() => [
|
||||
{
|
||||
id: 'XXX-group-1',
|
||||
attributes: {
|
||||
identifier: 'group.expo',
|
||||
},
|
||||
},
|
||||
]);
|
||||
Apple.AppGroup.createAsync = jest.fn((ctx, { identifier }) => ({
|
||||
id: 'XXX-group-2',
|
||||
attributes: {
|
||||
identifier,
|
||||
},
|
||||
}));
|
||||
|
||||
Apple.CloudContainer.getAsync = jest.fn(() => [
|
||||
{
|
||||
id: 'XXX-icloud-1',
|
||||
attributes: {
|
||||
identifier: 'iCloud.expo',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
Apple.CloudContainer.createAsync = jest.fn((ctx, { identifier }) => ({
|
||||
id: 'XXX-icloud-2',
|
||||
attributes: {
|
||||
identifier,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
describe(syncCapabilityIdentifiersForEntitlementsAsync, () => {
|
||||
it(`creates missing capability identifiers`, async () => {
|
||||
const Apple = require('@expo/apple-utils');
|
||||
mockCapabilities(Apple);
|
||||
|
||||
const bundleId = {
|
||||
context: {},
|
||||
updateBundleIdCapabilityAsync: jest.fn(),
|
||||
id: 'XXX',
|
||||
} as any;
|
||||
|
||||
await syncCapabilityIdentifiersForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.in-app-payments': ['merchant.bacon'],
|
||||
'com.apple.security.application-groups': ['group.bacon'],
|
||||
'com.apple.developer.icloud-container-identifiers': ['iCloud.bacon'],
|
||||
});
|
||||
|
||||
// Ensure we create missing ids
|
||||
expect(Apple.MerchantId.createAsync).toHaveBeenLastCalledWith(
|
||||
// auth context
|
||||
{},
|
||||
// props
|
||||
{ identifier: 'merchant.bacon' }
|
||||
);
|
||||
// Ensure we create missing ids
|
||||
expect(Apple.AppGroup.createAsync).toHaveBeenLastCalledWith(
|
||||
// auth context
|
||||
{},
|
||||
// props
|
||||
{ identifier: 'group.bacon' }
|
||||
);
|
||||
// Ensure we create missing ids
|
||||
expect(Apple.CloudContainer.createAsync).toHaveBeenLastCalledWith(
|
||||
// auth context
|
||||
{},
|
||||
// props
|
||||
{ identifier: 'iCloud.bacon' }
|
||||
);
|
||||
|
||||
expect(bundleId.updateBundleIdCapabilityAsync).toBeCalledWith([
|
||||
{
|
||||
capabilityType: 'APP_GROUPS',
|
||||
option: 'ON',
|
||||
relationships: {
|
||||
appGroups: ['XXX-group-2'],
|
||||
},
|
||||
},
|
||||
{
|
||||
capabilityType: 'APPLE_PAY',
|
||||
option: 'ON',
|
||||
relationships: {
|
||||
merchantIds: ['XXX-merch-2'],
|
||||
},
|
||||
},
|
||||
{
|
||||
capabilityType: 'ICLOUD',
|
||||
option: 'ON',
|
||||
relationships: {
|
||||
cloudContainers: ['XXX-icloud-2'],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it(`removes local duplicates`, async () => {
|
||||
const Apple = require('@expo/apple-utils');
|
||||
mockCapabilities(Apple);
|
||||
const bundleId = {
|
||||
context: {},
|
||||
updateBundleIdCapabilityAsync: jest.fn(),
|
||||
id: 'XXX',
|
||||
} as any;
|
||||
|
||||
await syncCapabilityIdentifiersForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.in-app-payments': ['merchant.expo', 'merchant.expo'],
|
||||
});
|
||||
|
||||
// Only called once because we remove local duplicates to minimize network requests.
|
||||
expect(Apple.MerchantId.getAsync).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it(`creates missing capability identifiers`, async () => {
|
||||
const Apple = require('@expo/apple-utils');
|
||||
mockCapabilities(Apple);
|
||||
const bundleId = {
|
||||
context: {},
|
||||
updateBundleIdCapabilityAsync: jest.fn(),
|
||||
id: 'XXX',
|
||||
} as any;
|
||||
|
||||
await syncCapabilityIdentifiersForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.in-app-payments': ['merchant.expo'],
|
||||
});
|
||||
|
||||
expect(Apple.MerchantId.createAsync).not.toBeCalled();
|
||||
});
|
||||
|
||||
it(`throws when creating a missing capability that is reserved`, async () => {
|
||||
const Apple = require('@expo/apple-utils');
|
||||
mockCapabilities(Apple);
|
||||
Apple.MerchantId.createAsync = jest.fn((ctx: any, { identifier }: { identifier: string }) => {
|
||||
// e2e test with: merchant.expodemo
|
||||
throw new Error(
|
||||
`There is a problem with the request entity - A Merchant ID with Identifier '${identifier}' is not available. Please enter a different string.`
|
||||
);
|
||||
});
|
||||
const bundleId = {
|
||||
context: {},
|
||||
updateBundleIdCapabilityAsync: jest.fn(),
|
||||
id: 'XXX',
|
||||
} as any;
|
||||
|
||||
await expect(
|
||||
syncCapabilityIdentifiersForEntitlementsAsync(bundleId, {
|
||||
'com.apple.developer.in-app-payments': ['merchant.bacon'],
|
||||
})
|
||||
).rejects.toThrow(/is not available. Please enter a different string/);
|
||||
});
|
||||
});
|
||||
@@ -1,17 +1,20 @@
|
||||
import {
|
||||
AppGroup,
|
||||
BundleId,
|
||||
BundleIdCapability,
|
||||
CapabilityOptionMap,
|
||||
CapabilityType,
|
||||
CapabilityTypeDataProtectionOption,
|
||||
CapabilityTypeOption,
|
||||
CloudContainer,
|
||||
MerchantId,
|
||||
} from '@expo/apple-utils';
|
||||
import { JSONObject, JSONValue } from '@expo/json-file';
|
||||
import getenv from 'getenv';
|
||||
|
||||
import Log from '../../../log';
|
||||
|
||||
const EXPO_NO_CAPABILITY_SYNC = getenv.boolish('EXPO_NO_CAPABILITY_SYNC', false);
|
||||
export const EXPO_NO_CAPABILITY_SYNC = getenv.boolish('EXPO_NO_CAPABILITY_SYNC', false);
|
||||
|
||||
type GetOptionsMethod<T extends CapabilityType = any> = (
|
||||
entitlement: JSONValue,
|
||||
@@ -22,6 +25,13 @@ const validateBooleanOptions = (options: any): boolean => {
|
||||
return typeof options === 'boolean';
|
||||
};
|
||||
|
||||
const validatePrefixedStringArrayOptions = (prefix: string) => (options: any): boolean => {
|
||||
return (
|
||||
Array.isArray(options) &&
|
||||
options.every(option => typeof option === 'string' && option.startsWith(prefix))
|
||||
);
|
||||
};
|
||||
|
||||
const validateStringArrayOptions = (options: any): boolean => {
|
||||
return Array.isArray(options) && options.every(option => typeof option === 'string');
|
||||
};
|
||||
@@ -63,7 +73,9 @@ export async function syncCapabilitiesForEntitlementsAsync(
|
||||
bundleId: BundleId,
|
||||
entitlements: JSONObject = {}
|
||||
): Promise<{ enabled: string[]; disabled: string[] }> {
|
||||
if (EXPO_NO_CAPABILITY_SYNC) return { enabled: [], disabled: [] };
|
||||
if (EXPO_NO_CAPABILITY_SYNC) {
|
||||
return { enabled: [], disabled: [] };
|
||||
}
|
||||
const currentCapabilities = await bundleId.getBundleIdCapabilitiesAsync();
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Current remote capabilities:\n${JSON.stringify(currentCapabilities, null, 2)}`);
|
||||
@@ -107,7 +119,7 @@ function getCapabilitiesToEnable(
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingIndex = currentCapabilities.findIndex(existing =>
|
||||
const existingIndex = remainingCapabilities.findIndex(existing =>
|
||||
existing.isType(staticCapabilityInfo.capability)
|
||||
);
|
||||
const existing = existingIndex > -1 ? remainingCapabilities[existingIndex] : null;
|
||||
@@ -120,16 +132,19 @@ function getCapabilitiesToEnable(
|
||||
remainingCapabilities.splice(existingIndex, 1);
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Skipping existing capability: ${key} (${staticCapabilityInfo.name})`);
|
||||
Log.log(
|
||||
`Remaining to remove: `,
|
||||
remainingCapabilities.map(({ id }) => id)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!staticCapabilityInfo.validateOptions(value)) {
|
||||
throw new Error(`iOS entitlement "${key}" has invalid value "${value}".`);
|
||||
}
|
||||
assertValidOptions(staticCapabilityInfo, value);
|
||||
|
||||
enabledCapabilityNames.push(staticCapabilityInfo.name);
|
||||
|
||||
const option = staticCapabilityInfo.getOptions(value!, entitlements);
|
||||
const option = staticCapabilityInfo.getOptions(value, entitlements);
|
||||
|
||||
request.push({
|
||||
capabilityType: staticCapabilityInfo.capability,
|
||||
@@ -140,11 +155,30 @@ function getCapabilitiesToEnable(
|
||||
return { enabledCapabilityNames, request, remainingCapabilities };
|
||||
}
|
||||
|
||||
export function assertValidOptions(classifier: CapabilityClassifier, value: any): asserts value {
|
||||
if (!classifier.validateOptions(value)) {
|
||||
let reason = '';
|
||||
if (classifier.capabilityIdPrefix) {
|
||||
// Assert string array matching prefix. ASC will throw if the IDs are invalid, this just saves some time.
|
||||
reason = ` Expected an array of strings, where each string is prefixed with "${classifier.capabilityIdPrefix}", ex: ["${classifier.capabilityIdPrefix}myapp"]`;
|
||||
}
|
||||
throw new Error(
|
||||
`iOS entitlement "${classifier.entitlement}" has invalid value "${value}".${reason}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getCapabilitiesToDisable(
|
||||
bundleId: BundleId,
|
||||
currentCapabilities: BundleIdCapability[],
|
||||
request: { capabilityType: CapabilityType; option: any }[]
|
||||
) {
|
||||
if (Log.isDebug) {
|
||||
Log.log(
|
||||
`Existing to disable: `,
|
||||
currentCapabilities.map(({ id }) => id)
|
||||
);
|
||||
}
|
||||
const disabledCapabilityNames: string[] = [];
|
||||
|
||||
// Disable any extras that aren't present, this functionality is kinda unreliable because managed apps
|
||||
@@ -192,17 +226,21 @@ function getCapabilitiesToDisable(
|
||||
return { disabledCapabilityNames, request };
|
||||
}
|
||||
|
||||
// NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
|
||||
// Last Updated: May 5th, 2021
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements
|
||||
export const CapabilityMapping: {
|
||||
type CapabilityClassifier = {
|
||||
name: string;
|
||||
entitlement: string;
|
||||
capability: CapabilityType;
|
||||
validateOptions: (options: any) => boolean;
|
||||
getOptions: GetOptionsMethod;
|
||||
capabilityIdModel?: typeof MerchantId;
|
||||
capabilityIdPrefix?: string;
|
||||
options?: undefined;
|
||||
}[] = [
|
||||
};
|
||||
|
||||
// NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
|
||||
// Last Updated: July 22nd, 2021
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements
|
||||
export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
{
|
||||
name: 'HomeKit',
|
||||
entitlement: 'com.apple.developer.homekit',
|
||||
@@ -287,24 +325,30 @@ export const CapabilityMapping: {
|
||||
entitlement: 'com.apple.security.application-groups',
|
||||
capability: CapabilityType.APP_GROUP,
|
||||
// Ex: ['group.CY-A5149AC2-49FC-11E7-B3F3-0335A16FFB8D.com.cydia.Extender']
|
||||
validateOptions: validateStringArrayOptions,
|
||||
validateOptions: validatePrefixedStringArrayOptions('group.'),
|
||||
getOptions: getDefinedOptions,
|
||||
capabilityIdModel: AppGroup,
|
||||
capabilityIdPrefix: 'group.',
|
||||
},
|
||||
{
|
||||
name: 'Apple Pay Payment Processing',
|
||||
entitlement: 'com.apple.developer.in-app-payments',
|
||||
capability: CapabilityType.APPLE_PAY,
|
||||
// Ex: ['merchant.com.example.development']
|
||||
validateOptions: validateStringArrayOptions,
|
||||
validateOptions: validatePrefixedStringArrayOptions('merchant.'),
|
||||
getOptions: getDefinedOptions,
|
||||
capabilityIdModel: MerchantId,
|
||||
capabilityIdPrefix: 'merchant.',
|
||||
},
|
||||
{
|
||||
name: 'iCloud',
|
||||
entitlement: 'com.apple.developer.icloud-container-identifiers',
|
||||
capability: CapabilityType.ICLOUD,
|
||||
validateOptions: validateStringArrayOptions,
|
||||
validateOptions: validatePrefixedStringArrayOptions('iCloud.'),
|
||||
// Only supports Xcode +6, 5 could be added if needed.
|
||||
getOptions: getDefinedOptions,
|
||||
capabilityIdModel: CloudContainer,
|
||||
capabilityIdPrefix: 'iCloud.',
|
||||
},
|
||||
{
|
||||
name: 'ClassKit',
|
||||
@@ -313,6 +357,34 @@ export const CapabilityMapping: {
|
||||
validateOptions: validateDevProdString,
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
name: 'Communication Notifications',
|
||||
entitlement: 'com.apple.developer.usernotifications.communication',
|
||||
capability: CapabilityType.USER_NOTIFICATIONS_COMMUNICATION,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
name: 'Time Sensitive Notifications',
|
||||
entitlement: 'com.apple.developer.usernotifications.time-sensitive',
|
||||
capability: CapabilityType.USER_NOTIFICATIONS_TIME_SENSITIVE,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
name: 'Group Activities',
|
||||
entitlement: 'com.apple.developer.group-session',
|
||||
capability: CapabilityType.GROUP_ACTIVITIES,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
name: 'Family Controls',
|
||||
entitlement: 'com.apple.developer.family-controls',
|
||||
capability: CapabilityType.FAMILY_CONTROLS,
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
{
|
||||
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_default-data-protection
|
||||
name: 'Data Protection',
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { BundleId, CapabilityTypeOption } from '@expo/apple-utils';
|
||||
import { JSONObject } from '@expo/json-file';
|
||||
|
||||
import Log from '../../../log';
|
||||
import {
|
||||
CapabilityMapping,
|
||||
EXPO_NO_CAPABILITY_SYNC,
|
||||
assertValidOptions,
|
||||
} from './bundleIdCapabilities';
|
||||
|
||||
type UpdateCapabilityRequest = Parameters<BundleId['updateBundleIdCapabilityAsync']>[0];
|
||||
|
||||
/**
|
||||
* Sync the capability identifiers with the bundle identifier capabilities.
|
||||
* If a capability identifier is missing, then attempt to create it.
|
||||
* Link all of the capability identifiers at the same time after parsing the entitlements file.
|
||||
*
|
||||
* @param bundleId Bundle identifier object.
|
||||
* @param entitlements JSON representation of the iOS entitlements plist
|
||||
*
|
||||
* @returns an object specifying the capabilities that were linked, and created.
|
||||
*/
|
||||
export async function syncCapabilityIdentifiersForEntitlementsAsync(
|
||||
bundleId: BundleId,
|
||||
entitlements: JSONObject = {}
|
||||
): Promise<{ created: string[]; linked: string[] }> {
|
||||
if (EXPO_NO_CAPABILITY_SYNC) {
|
||||
return { created: [], linked: [] };
|
||||
}
|
||||
|
||||
const createdIds: string[] = [];
|
||||
const linkedIds: string[] = [];
|
||||
const CapabilityIdMapping = CapabilityMapping.filter(capability => capability.capabilityIdModel);
|
||||
|
||||
const updateRequest: UpdateCapabilityRequest = [];
|
||||
|
||||
// Iterate through the supported capabilities to build the request.
|
||||
for (const classifier of CapabilityIdMapping) {
|
||||
const CapabilityModel = classifier.capabilityIdModel;
|
||||
// Skip capabilities that don't support capability IDs.
|
||||
if (!CapabilityModel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validate = (value: any): value is string[] => {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
assertValidOptions(classifier, value);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Skip capabilities that aren't defined in the entitlements file.
|
||||
const entitlementValue = entitlements[classifier.entitlement];
|
||||
|
||||
if (!validate(entitlementValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove any duplicates to cut down on network requests
|
||||
const capabilityIds: string[] = [...new Set(entitlementValue)];
|
||||
|
||||
// Get a list of all of the capability IDs that are already created on the server.
|
||||
const existingIds = await CapabilityModel.getAsync(bundleId.context);
|
||||
|
||||
// A list of server IDs for linking.
|
||||
const capabilityIdOpaqueIds = [];
|
||||
|
||||
// Iterate through all the local IDs and see if they exist on the server.
|
||||
for (const localId of capabilityIds) {
|
||||
let remoteIdModel = existingIds.find(model => model.attributes.identifier === localId);
|
||||
|
||||
// If a remote ID exists, then create it.
|
||||
if (!remoteIdModel) {
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Creating capability ID: ${localId} (${CapabilityModel.type})`);
|
||||
}
|
||||
try {
|
||||
remoteIdModel = await CapabilityModel.createAsync(bundleId.context, {
|
||||
identifier: localId,
|
||||
});
|
||||
} catch (error) {
|
||||
// Add a more helpful error message.
|
||||
error.message += `\n\nRemove the value '${localId}' from the array '${classifier.entitlement}' in the iOS project entitlements.\nIf you know that the ID is registered to one of your apps, try again with a different Apple account.`;
|
||||
throw error;
|
||||
}
|
||||
// Create a list of newly created IDs for displaying in the CLI.
|
||||
createdIds.push(localId);
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Created capability ID: ${remoteIdModel.id}`);
|
||||
}
|
||||
}
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Linking ID to ${CapabilityModel.type}: ${localId} (${remoteIdModel.id})`);
|
||||
}
|
||||
// Create a list of linked IDs for displaying in the CLI.
|
||||
linkedIds.push(remoteIdModel.attributes.identifier);
|
||||
capabilityIdOpaqueIds.push(remoteIdModel.id);
|
||||
}
|
||||
|
||||
updateRequest.push({
|
||||
capabilityType: classifier.capability,
|
||||
option: CapabilityTypeOption.ON,
|
||||
relationships: {
|
||||
// One of: `merchantIds`, `appGroups`, `cloudContainers`.
|
||||
[CapabilityModel.type]: capabilityIdOpaqueIds,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (updateRequest.length) {
|
||||
if (Log.isDebug) {
|
||||
Log.log(`Updating bundle identifier with capability identifiers:`, updateRequest);
|
||||
}
|
||||
await bundleId.updateBundleIdCapabilityAsync(updateRequest);
|
||||
} else if (Log.isDebug) {
|
||||
Log.log(`No capability identifiers need to be updated`);
|
||||
}
|
||||
return { created: createdIds, linked: linkedIds };
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import ora from 'ora';
|
||||
import Log from '../../../log';
|
||||
import { AuthCtx, getRequestContext } from './authenticate';
|
||||
import { syncCapabilitiesForEntitlementsAsync } from './bundleIdCapabilities';
|
||||
import { syncCapabilityIdentifiersForEntitlementsAsync } from './capabilityIdentifiers';
|
||||
import { assertContractMessagesAsync } from './contractMessages';
|
||||
|
||||
export interface IosCapabilitiesOptions {
|
||||
@@ -95,8 +96,6 @@ export async function syncCapabilities(
|
||||
bundleId,
|
||||
entitlements
|
||||
);
|
||||
const buildMessage = (title: string, items: string[]) =>
|
||||
items.length ? `${title}: ${items.join(', ')}` : '';
|
||||
const results =
|
||||
[buildMessage('Enabled', enabled), buildMessage('Disabled', disabled)]
|
||||
.filter(Boolean)
|
||||
@@ -107,6 +106,43 @@ export async function syncCapabilities(
|
||||
spinner.fail(`Failed to sync capabilities ${chalk.dim(bundleId.attributes.identifier)}`);
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Always run this after syncing the capabilities...
|
||||
await syncCapabilityIdentifiersAsync(bundleId, { entitlements });
|
||||
}
|
||||
|
||||
const buildMessage = (title: string, items: string[]) =>
|
||||
items.length ? `${title}: ${items.join(', ')}` : '';
|
||||
|
||||
export async function syncCapabilityIdentifiersAsync(
|
||||
bundleId: BundleId,
|
||||
{ entitlements }: IosCapabilitiesOptions
|
||||
): Promise<void> {
|
||||
const spinner = ora(`Syncing capabilities identifiers`).start();
|
||||
|
||||
// Stop spinning in debug mode so we can print other information
|
||||
if (Log.isDebug) {
|
||||
spinner.stop();
|
||||
}
|
||||
|
||||
try {
|
||||
const { created, linked } = await syncCapabilityIdentifiersForEntitlementsAsync(
|
||||
bundleId,
|
||||
entitlements
|
||||
);
|
||||
|
||||
const results =
|
||||
[buildMessage('Created', created), buildMessage('Linked', linked)]
|
||||
.filter(Boolean)
|
||||
.join(' | ') || 'No updates';
|
||||
|
||||
spinner.succeed(`Synced capability identifiers: ` + chalk.dim(results));
|
||||
} catch (err) {
|
||||
spinner.fail(
|
||||
`Failed to sync capability identifiers ${chalk.dim(bundleId.attributes.identifier)}`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureAppExistsAsync(
|
||||
|
||||
@@ -136,10 +136,11 @@ async function manageAdHocProfilesAsync(
|
||||
if (profileId) {
|
||||
existingProfile = await findProfileByIdAsync(context, profileId, bundleId);
|
||||
// Fail if we cannot find the profile that was specifically requested
|
||||
if (!existingProfile)
|
||||
if (!existingProfile) {
|
||||
throw new Error(
|
||||
`Could not find profile with profile id "${profileId}" for bundle id "${bundleId}"`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// If no profile id is passed, try to find a suitable provisioning profile for the App ID.
|
||||
const results = await findProfileByBundleIdAsync(context, bundleId, certSerialNumber);
|
||||
|
||||
@@ -41,7 +41,9 @@ function getService(rootUrl: string) {
|
||||
},
|
||||
replacement(content: string, node: any) {
|
||||
let href = node.getAttribute('href');
|
||||
if (href.startsWith('/')) href = `${rootUrl}${href}`;
|
||||
if (href.startsWith('/')) {
|
||||
href = `${rootUrl}${href}`;
|
||||
}
|
||||
if (terminalLink.isSupported) {
|
||||
return chalk.cyan(terminalLink(content, href));
|
||||
}
|
||||
|
||||
+111
-11
@@ -58,6 +58,8 @@ export type RootQuery = {
|
||||
buildJobs: BuildJobQuery;
|
||||
builds: BuildQuery;
|
||||
clientBuilds: ClientBuildQuery;
|
||||
/** Top-level query object for querying EAS Build configuration. */
|
||||
easBuild: EasBuildQuery;
|
||||
/** Top-level query object for querying Experimentation configuration. */
|
||||
experimentation: ExperimentationQuery;
|
||||
project: ProjectQuery;
|
||||
@@ -188,8 +190,13 @@ export type Account = {
|
||||
buildJobs: Array<BuildJob>;
|
||||
/** (EAS Build) Builds associated with this account */
|
||||
builds: Array<Build>;
|
||||
/** Coalesced Build (EAS) or BuildJob (Classic) items for this account. Use "createdBefore" to offset a query. */
|
||||
/**
|
||||
* Coalesced Build (EAS) or BuildJob (Classic) for all apps belonging to this account.
|
||||
* @deprecated Use activityTimelineProjectActivities with filterTypes instead
|
||||
*/
|
||||
buildOrBuildJobs: Array<BuildOrBuildJob>;
|
||||
/** Coalesced project activity for all apps belonging to this account. */
|
||||
activityTimelineProjectActivities: Array<ActivityTimelineProjectActivity>;
|
||||
/** Owning User of this account if personal account */
|
||||
owner?: Maybe<User>;
|
||||
/** Actors associated with this account and permissions they hold */
|
||||
@@ -279,6 +286,17 @@ export type AccountBuildOrBuildJobsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An account is a container owning projects, credentials, billing and other organization
|
||||
* data and settings. Actors may own and be members of accounts.
|
||||
*/
|
||||
export type AccountActivityTimelineProjectActivitiesArgs = {
|
||||
limit: Scalars['Int'];
|
||||
createdBefore?: Maybe<Scalars['DateTime']>;
|
||||
filterTypes?: Maybe<Array<ActivityTimelineProjectActivityType>>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An account is a container owning projects, credentials, billing and other organization
|
||||
* data and settings. Actors may own and be members of accounts.
|
||||
@@ -411,7 +429,10 @@ export type App = Project & {
|
||||
/** (EAS Build) Builds associated with this app */
|
||||
builds: Array<Build>;
|
||||
buildJobs: Array<BuildJob>;
|
||||
/** Coalesced Build (EAS) or BuildJob (Classic) items for this app. Use "createdBefore" to offset a query. */
|
||||
/**
|
||||
* Coalesced Build (EAS) or BuildJob (Classic) items for this app.
|
||||
* @deprecated Use activityTimelineProjectActivities with filterTypes instead
|
||||
*/
|
||||
buildOrBuildJobs: Array<BuildOrBuildJob>;
|
||||
/** EAS Submissions associated with this app */
|
||||
submissions: Array<Submission>;
|
||||
@@ -427,7 +448,7 @@ export type App = Project & {
|
||||
updateBranches: Array<UpdateBranch>;
|
||||
/** get an EAS branch owned by the app by name */
|
||||
updateBranchByName?: Maybe<UpdateBranch>;
|
||||
/** Coalesced project activity for an app. Use "createdBefore" to offset a query. */
|
||||
/** Coalesced project activity for an app */
|
||||
activityTimelineProjectActivities: Array<ActivityTimelineProjectActivity>;
|
||||
/** Environment secrets for an app */
|
||||
environmentSecrets: Array<EnvironmentSecret>;
|
||||
@@ -538,6 +559,7 @@ export type AppUpdateBranchByNameArgs = {
|
||||
export type AppActivityTimelineProjectActivitiesArgs = {
|
||||
limit: Scalars['Int'];
|
||||
createdBefore?: Maybe<Scalars['DateTime']>;
|
||||
filterTypes?: Maybe<Array<ActivityTimelineProjectActivityType>>;
|
||||
};
|
||||
|
||||
|
||||
@@ -622,6 +644,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
metrics?: Maybe<BuildMetrics>;
|
||||
distribution?: Maybe<DistributionType>;
|
||||
iosEnterpriseProvisioning?: Maybe<BuildIosEnterpriseProvisioning>;
|
||||
buildProfile?: Maybe<Scalars['String']>;
|
||||
gitCommitHash?: Maybe<Scalars['String']>;
|
||||
error?: Maybe<BuildError>;
|
||||
@@ -667,6 +690,8 @@ export type User = Actor & {
|
||||
hasPendingUserInvitations: Scalars['Boolean'];
|
||||
/** Pending UserInvitations for this user. Only resolves for the viewer. */
|
||||
pendingUserInvitations: Array<UserInvitation>;
|
||||
/** Coalesced project activity for all apps belonging to all accounts this user belongs to. Only resolves for the viewer. */
|
||||
activityTimelineProjectActivities: Array<ActivityTimelineProjectActivity>;
|
||||
/**
|
||||
* Server feature gate values for this actor, optionally filtering by desired gates.
|
||||
* Only resolves for the viewer.
|
||||
@@ -704,6 +729,14 @@ export type UserAppsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human (not robot) actor. */
|
||||
export type UserActivityTimelineProjectActivitiesArgs = {
|
||||
limit: Scalars['Int'];
|
||||
createdBefore?: Maybe<Scalars['DateTime']>;
|
||||
filterTypes?: Maybe<Array<ActivityTimelineProjectActivityType>>;
|
||||
};
|
||||
|
||||
|
||||
/** Represents a human (not robot) actor. */
|
||||
export type UserFeatureGatesArgs = {
|
||||
filter?: Maybe<Array<Scalars['String']>>;
|
||||
@@ -781,6 +814,13 @@ export enum Role {
|
||||
NotAdmin = 'NOT_ADMIN'
|
||||
}
|
||||
|
||||
export enum ActivityTimelineProjectActivityType {
|
||||
BuildJob = 'BUILD_JOB',
|
||||
Build = 'BUILD',
|
||||
Update = 'UPDATE',
|
||||
Submission = 'SUBMISSION'
|
||||
}
|
||||
|
||||
|
||||
export type BuildArtifacts = {
|
||||
__typename?: 'BuildArtifacts';
|
||||
@@ -800,6 +840,11 @@ export enum DistributionType {
|
||||
Simulator = 'SIMULATOR'
|
||||
}
|
||||
|
||||
export enum BuildIosEnterpriseProvisioning {
|
||||
Adhoc = 'ADHOC',
|
||||
Universal = 'UNIVERSAL'
|
||||
}
|
||||
|
||||
export type BuildError = {
|
||||
__typename?: 'BuildError';
|
||||
errorCode: Scalars['String'];
|
||||
@@ -1496,6 +1541,12 @@ export type BuildQuery = {
|
||||
* @deprecated Use App.builds instead
|
||||
*/
|
||||
allForApp: Array<Maybe<Build>>;
|
||||
/**
|
||||
* Get all builds.
|
||||
* By default, they are sorted from latest to oldest.
|
||||
* Available only for admin users.
|
||||
*/
|
||||
all: Array<Build>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1512,6 +1563,19 @@ export type BuildQueryAllForAppArgs = {
|
||||
limit?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
|
||||
export type BuildQueryAllArgs = {
|
||||
statuses?: Maybe<Array<BuildStatus>>;
|
||||
offset?: Maybe<Scalars['Int']>;
|
||||
limit?: Maybe<Scalars['Int']>;
|
||||
order?: Maybe<Order>;
|
||||
};
|
||||
|
||||
export enum Order {
|
||||
Desc = 'DESC',
|
||||
Asc = 'ASC'
|
||||
}
|
||||
|
||||
export type ClientBuildQuery = {
|
||||
__typename?: 'ClientBuildQuery';
|
||||
byId: ClientBuild;
|
||||
@@ -1533,6 +1597,24 @@ export type ClientBuild = {
|
||||
userId?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type EasBuildQuery = {
|
||||
__typename?: 'EasBuildQuery';
|
||||
/** Get EAS Build kill switches state */
|
||||
killSwitches: Array<EasBuildKillSwitch>;
|
||||
};
|
||||
|
||||
export type EasBuildKillSwitch = {
|
||||
__typename?: 'EasBuildKillSwitch';
|
||||
name: EasBuildKillSwitchName;
|
||||
value: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export enum EasBuildKillSwitchName {
|
||||
StopAcceptingBuilds = 'STOP_ACCEPTING_BUILDS',
|
||||
StopSchedulingBuilds = 'STOP_SCHEDULING_BUILDS',
|
||||
StopAcceptingNormalPriorityBuilds = 'STOP_ACCEPTING_NORMAL_PRIORITY_BUILDS'
|
||||
}
|
||||
|
||||
export type ExperimentationQuery = {
|
||||
__typename?: 'ExperimentationQuery';
|
||||
/** Get user experimentation config */
|
||||
@@ -1712,6 +1794,7 @@ export type RootMutation = {
|
||||
userInvitation: UserInvitationMutation;
|
||||
/** Mutations that modify the currently authenticated User */
|
||||
me?: Maybe<MeMutation>;
|
||||
easBuildKillSwitch: EasBuildKillSwitchMutation;
|
||||
/** Mutations that modify an EmailSubscription */
|
||||
emailSubscription: EmailSubscriptionMutation;
|
||||
/** Mutations that create and delete EnvironmentSecrets */
|
||||
@@ -2466,6 +2549,7 @@ export type BuildMetadataInput = {
|
||||
releaseChannel?: Maybe<Scalars['String']>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
distribution?: Maybe<DistributionType>;
|
||||
iosEnterpriseProvisioning?: Maybe<BuildIosEnterpriseProvisioning>;
|
||||
appName?: Maybe<Scalars['String']>;
|
||||
appIdentifier?: Maybe<Scalars['String']>;
|
||||
buildProfile?: Maybe<Scalars['String']>;
|
||||
@@ -2872,7 +2956,7 @@ export type UpdateBranchMutation = {
|
||||
/** Delete an EAS branch and all of its updates as long as the branch is not being used by any channels */
|
||||
deleteUpdateBranch: DeleteUpdateBranchResult;
|
||||
/** Publish an update group to a branch */
|
||||
publishUpdateGroup: Array<Maybe<Update>>;
|
||||
publishUpdateGroup: Array<Update>;
|
||||
};
|
||||
|
||||
|
||||
@@ -2924,6 +3008,7 @@ export type UpdateInfoGroup = {
|
||||
export type PartialManifest = {
|
||||
launchAsset: PartialManifestAsset;
|
||||
assets: Array<Maybe<PartialManifestAsset>>;
|
||||
extra?: Maybe<Scalars['JSONObject']>;
|
||||
};
|
||||
|
||||
export type PartialManifestAsset = {
|
||||
@@ -3207,6 +3292,20 @@ export type SecondFactorRegenerateBackupCodesResult = {
|
||||
plaintextBackupCodes: Array<Maybe<Scalars['String']>>;
|
||||
};
|
||||
|
||||
export type EasBuildKillSwitchMutation = {
|
||||
__typename?: 'EasBuildKillSwitchMutation';
|
||||
/** Set an EAS Build kill switch to a given value */
|
||||
set: EasBuildKillSwitch;
|
||||
/** Reset all EAS Build kill switches (set them to false) */
|
||||
resetAll: Array<EasBuildKillSwitch>;
|
||||
};
|
||||
|
||||
|
||||
export type EasBuildKillSwitchMutationSetArgs = {
|
||||
name: EasBuildKillSwitchName;
|
||||
value: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type EmailSubscriptionMutation = {
|
||||
__typename?: 'EmailSubscriptionMutation';
|
||||
addUser?: Maybe<AddUserPayload>;
|
||||
@@ -4640,10 +4739,10 @@ export type UpdatePublishMutation = (
|
||||
{ __typename?: 'RootMutation' }
|
||||
& { updateBranch: (
|
||||
{ __typename?: 'UpdateBranchMutation' }
|
||||
& { publishUpdateGroup: Array<Maybe<(
|
||||
& { publishUpdateGroup: Array<(
|
||||
{ __typename?: 'Update' }
|
||||
& Pick<Update, 'id' | 'group'>
|
||||
)>> }
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
|
||||
@@ -4651,6 +4750,7 @@ export type CreateSubmissionMutationVariables = Exact<{
|
||||
appId: Scalars['ID'];
|
||||
platform: AppPlatform;
|
||||
config: Scalars['JSONObject'];
|
||||
submittedBuildId?: Maybe<Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
|
||||
@@ -4796,16 +4896,16 @@ export type EnvironmentSecretsByAccountNameQuery = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type EnvironmentSecretsByAppFullNameQueryVariables = Exact<{
|
||||
fullName: Scalars['String'];
|
||||
export type EnvironmentSecretsByAppIdQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type EnvironmentSecretsByAppFullNameQuery = (
|
||||
export type EnvironmentSecretsByAppIdQuery = (
|
||||
{ __typename?: 'RootQuery' }
|
||||
& { app?: Maybe<(
|
||||
{ __typename?: 'AppQuery' }
|
||||
& { byFullName: (
|
||||
& { byId: (
|
||||
{ __typename?: 'App' }
|
||||
& Pick<App, 'id'>
|
||||
& { environmentSecrets: Array<(
|
||||
@@ -4963,7 +5063,7 @@ export type AppFragment = (
|
||||
|
||||
export type BuildFragment = (
|
||||
{ __typename?: 'Build' }
|
||||
& Pick<Build, 'id' | 'status' | 'platform' | 'releaseChannel' | 'distribution' | 'createdAt' | 'updatedAt'>
|
||||
& Pick<Build, 'id' | 'status' | 'platform' | 'channel' | 'releaseChannel' | 'distribution' | 'iosEnterpriseProvisioning' | 'buildProfile' | 'sdkVersion' | 'appVersion' | 'appBuildVersion' | 'runtimeVersion' | 'gitCommitHash' | 'createdAt' | 'updatedAt'>
|
||||
& { error?: Maybe<(
|
||||
{ __typename?: 'BuildError' }
|
||||
& Pick<BuildError, 'errorCode' | 'message' | 'docsUrl'>
|
||||
|
||||
@@ -17,6 +17,7 @@ interface CreateSubmissionResult {
|
||||
export const SubmissionMutation = {
|
||||
async createSubmissionAsync(input: {
|
||||
appId: string;
|
||||
submittedBuildId?: string;
|
||||
platform: AppPlatform;
|
||||
config: JSONObject;
|
||||
}): Promise<CreateSubmissionResult> {
|
||||
@@ -28,9 +29,17 @@ export const SubmissionMutation = {
|
||||
$appId: ID!
|
||||
$platform: AppPlatform!
|
||||
$config: JSONObject!
|
||||
$submittedBuildId: ID
|
||||
) {
|
||||
submission {
|
||||
createSubmission(input: { appId: $appId, platform: $platform, config: $config }) {
|
||||
createSubmission(
|
||||
input: {
|
||||
appId: $appId
|
||||
platform: $platform
|
||||
config: $config
|
||||
submittedBuildId: $submittedBuildId
|
||||
}
|
||||
) {
|
||||
submission {
|
||||
id
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { EnvironmentSecretScope } from '../../commands/secret/create';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
EnvironmentSecretFragment,
|
||||
EnvironmentSecretsByAccountNameQuery,
|
||||
EnvironmentSecretsByAppFullNameQuery,
|
||||
EnvironmentSecretsByAppIdQuery,
|
||||
} from '../generated';
|
||||
import { EnvironmentSecretFragmentNode } from '../types/EnvironmentSecret';
|
||||
|
||||
export enum EnvironmentSecretScope {
|
||||
ACCOUNT = 'account',
|
||||
PROJECT = 'project',
|
||||
}
|
||||
|
||||
export type EnvironmentSecretWithScope = EnvironmentSecretFragment & {
|
||||
scope: EnvironmentSecretScope;
|
||||
};
|
||||
@@ -40,14 +44,14 @@ export const EnvironmentSecretsQuery = {
|
||||
|
||||
return data.account.byName.environmentSecrets;
|
||||
},
|
||||
async byAppFullNameAsync(fullName: string): Promise<EnvironmentSecretFragment[]> {
|
||||
async byAppIdAsync(appId: string): Promise<EnvironmentSecretFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<EnvironmentSecretsByAppFullNameQuery>(
|
||||
.query<EnvironmentSecretsByAppIdQuery>(
|
||||
gql`
|
||||
query EnvironmentSecretsByAppFullName($fullName: String!) {
|
||||
query EnvironmentSecretsByAppId($appId: String!) {
|
||||
app {
|
||||
byFullName(fullName: $fullName) {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
environmentSecrets {
|
||||
id
|
||||
@@ -58,12 +62,12 @@ export const EnvironmentSecretsQuery = {
|
||||
}
|
||||
${print(EnvironmentSecretFragmentNode)}
|
||||
`,
|
||||
{ fullName }
|
||||
{ appId }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app?.byFullName.environmentSecrets ?? [];
|
||||
return data.app?.byId.environmentSecrets ?? [];
|
||||
},
|
||||
async allAsync(
|
||||
projectAccountName: string,
|
||||
@@ -71,7 +75,7 @@ export const EnvironmentSecretsQuery = {
|
||||
): Promise<EnvironmentSecretWithScope[]> {
|
||||
const [accountSecrets, appSecrets] = await Promise.all([
|
||||
this.byAcccountNameAsync(projectAccountName),
|
||||
this.byAppFullNameAsync(projectFullName),
|
||||
this.byAppIdAsync(projectFullName),
|
||||
]);
|
||||
|
||||
return [
|
||||
|
||||
@@ -34,8 +34,16 @@ export const BuildFragmentNode = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
channel
|
||||
releaseChannel
|
||||
distribution
|
||||
iosEnterpriseProvisioning
|
||||
buildProfile
|
||||
sdkVersion
|
||||
appVersion
|
||||
appBuildVersion
|
||||
runtimeVersion
|
||||
gitCommitHash
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
|
||||
@@ -34,12 +34,16 @@ class CustomCommandHelp extends CommandHelp {
|
||||
this.aliases(cmd.aliases),
|
||||
this.examples(cmd.examples || (cmd as any).example),
|
||||
]).join('\n\n');
|
||||
if (this.opts.stripAnsi) output = stripAnsi(output);
|
||||
if (this.opts.stripAnsi) {
|
||||
output = stripAnsi(output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
protected flags(flags: Config.Command.Flag[]): string | undefined {
|
||||
if (flags.length === 0) return;
|
||||
if (flags.length === 0) {
|
||||
return;
|
||||
}
|
||||
const groupableFlags = flags.map(originalFlag => {
|
||||
const { helpLabel, ...flag } = originalFlag;
|
||||
// We re-purpose `helpLabel` to mean a "header to group the flag under".
|
||||
|
||||
@@ -155,22 +155,28 @@ describe(buildUpdateInfoGroupAsync, () => {
|
||||
|
||||
it('returns the correct value', async () => {
|
||||
await expect(
|
||||
buildUpdateInfoGroupAsync({
|
||||
android: {
|
||||
launchAsset: {
|
||||
type: 'bundle',
|
||||
contentType: 'bundle/javascript',
|
||||
path: androidBundlePath,
|
||||
},
|
||||
assets: [
|
||||
{
|
||||
type: 'jpg',
|
||||
contentType: 'image/jpeg',
|
||||
path: assetPath,
|
||||
buildUpdateInfoGroupAsync(
|
||||
{
|
||||
android: {
|
||||
launchAsset: {
|
||||
type: 'bundle',
|
||||
contentType: 'bundle/javascript',
|
||||
path: androidBundlePath,
|
||||
},
|
||||
],
|
||||
assets: [
|
||||
{
|
||||
type: 'jpg',
|
||||
contentType: 'image/jpeg',
|
||||
path: assetPath,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
{
|
||||
slug: 'hello',
|
||||
name: 'hello',
|
||||
}
|
||||
)
|
||||
).resolves.toEqual({
|
||||
android: {
|
||||
assets: [
|
||||
@@ -182,7 +188,6 @@ describe(buildUpdateInfoGroupAsync, () => {
|
||||
storageKey: 'fo8Y08LktVk6qLtGbn8GRWpOUyD13ABMUnbtRCN1L7Y',
|
||||
},
|
||||
],
|
||||
|
||||
launchAsset: {
|
||||
bundleKey: 'ec0dd14670aae108f99a810df9c1482c',
|
||||
contentType: 'bundle/javascript',
|
||||
@@ -190,6 +195,12 @@ describe(buildUpdateInfoGroupAsync, () => {
|
||||
storageBucket: 'update-assets-production',
|
||||
storageKey: 'aC9N6RZlcHoIYjIsoJd2KUcigBKy98RHvZacDyPNjCQ',
|
||||
},
|
||||
extra: {
|
||||
expoClient: {
|
||||
slug: 'hello',
|
||||
name: 'hello',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Platform } from '@expo/config';
|
||||
import { ExpoConfig, Platform } from '@expo/config';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import Joi from '@hapi/joi';
|
||||
@@ -46,9 +46,14 @@ type CollectedAssets = {
|
||||
};
|
||||
};
|
||||
|
||||
type ManifestExtra = {
|
||||
expoClient?: { [key: string]: any };
|
||||
[key: string]: any;
|
||||
};
|
||||
type ManifestFragment = {
|
||||
launchAsset: PartialManifestAsset;
|
||||
assets: PartialManifestAsset[];
|
||||
extra?: ManifestExtra;
|
||||
};
|
||||
type UpdateInfoGroup = {
|
||||
[key in PublishPlatform]: ManifestFragment;
|
||||
@@ -125,7 +130,10 @@ export async function convertAssetToUpdateInfoGroupFormatAsync(
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildUpdateInfoGroupAsync(assets: CollectedAssets): Promise<UpdateInfoGroup> {
|
||||
export async function buildUpdateInfoGroupAsync(
|
||||
assets: CollectedAssets,
|
||||
exp: ExpoConfig
|
||||
): Promise<UpdateInfoGroup> {
|
||||
let platform: PublishPlatform;
|
||||
const updateInfoGroup: Partial<UpdateInfoGroup> = {};
|
||||
for (platform in assets) {
|
||||
@@ -134,6 +142,9 @@ export async function buildUpdateInfoGroupAsync(assets: CollectedAssets): Promis
|
||||
assets: await Promise.all(
|
||||
(assets[platform]?.assets ?? []).map(convertAssetToUpdateInfoGroupFormatAsync)
|
||||
),
|
||||
extra: {
|
||||
expoClient: exp,
|
||||
},
|
||||
};
|
||||
}
|
||||
return updateInfoGroup as UpdateInfoGroup;
|
||||
|
||||
@@ -22,6 +22,7 @@ abstract class BaseSubmitter<SubmissionContext, SubmissionOptions> {
|
||||
|
||||
protected async startSubmissionAsync(
|
||||
submissionConfig: SubmissionConfig,
|
||||
buildId?: string,
|
||||
verbose: boolean = false
|
||||
): Promise<SubmissionStatus> {
|
||||
Log.addNewLineIfNone();
|
||||
@@ -31,7 +32,8 @@ abstract class BaseSubmitter<SubmissionContext, SubmissionOptions> {
|
||||
submissionId = await SubmissionService.startSubmissionAsync(
|
||||
this.platform,
|
||||
submissionConfig.projectId,
|
||||
submissionConfig
|
||||
submissionConfig,
|
||||
buildId
|
||||
);
|
||||
scheduleSpinner.succeed();
|
||||
} catch (err) {
|
||||
|
||||
@@ -20,12 +20,14 @@ export const DEFAULT_CHECK_INTERVAL_MS = 5 * 1000; // 5 secs
|
||||
async function startSubmissionAsync(
|
||||
platform: AppPlatform,
|
||||
projectId: string,
|
||||
config: SubmissionConfig
|
||||
config: SubmissionConfig,
|
||||
buildId?: string
|
||||
): Promise<StartSubmissionResult> {
|
||||
const { submission } = await SubmissionMutation.createSubmissionAsync({
|
||||
appId: projectId,
|
||||
platform,
|
||||
config: (config as unknown) as JSONObject,
|
||||
submittedBuildId: buildId,
|
||||
});
|
||||
return submission.id;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,11 @@ class AndroidSubmitter extends BaseSubmitter<AndroidSubmissionContext, AndroidSu
|
||||
SummaryHumanReadableValues
|
||||
);
|
||||
|
||||
await this.startSubmissionAsync(submissionConfig, this.ctx.commandFlags.verbose);
|
||||
await this.startSubmissionAsync(
|
||||
submissionConfig,
|
||||
resolvedSourceOptions.archive.build?.id,
|
||||
this.ctx.commandFlags.verbose
|
||||
);
|
||||
}
|
||||
|
||||
private async resolveSourceOptions(): Promise<ResolvedSourceOptions> {
|
||||
@@ -88,8 +92,8 @@ class AndroidSubmitter extends BaseSubmitter<AndroidSubmissionContext, AndroidSu
|
||||
track,
|
||||
releaseStatus,
|
||||
archiveType: archive.type as AndroidArchiveType,
|
||||
...formatArchiveSourceSummary(archive),
|
||||
serviceAccountPath,
|
||||
...formatArchiveSourceSummary(archive),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -108,11 +112,11 @@ const SummaryHumanReadableKeys: Record<keyof Summary, string> = {
|
||||
archivePath: 'Archive path',
|
||||
archiveUrl: 'Download URL',
|
||||
archiveType: 'Archive type',
|
||||
buildId: 'Build ID',
|
||||
serviceAccountPath: 'Google Service Key',
|
||||
track: 'Release track',
|
||||
releaseStatus: 'Release status',
|
||||
projectId: 'Project ID',
|
||||
formattedBuild: 'Build',
|
||||
};
|
||||
|
||||
const SummaryHumanReadableValues: Partial<Record<keyof Summary, Function>> = {
|
||||
|
||||
@@ -3,11 +3,17 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { asMock } from '../../../__tests__/utils';
|
||||
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { AppPlatform, SubmissionFragment, SubmissionStatus } from '../../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
SubmissionFragment,
|
||||
SubmissionStatus,
|
||||
} from '../../../graphql/generated';
|
||||
import { createTestProject } from '../../../project/__tests__/project-utils';
|
||||
import { getProjectIdAsync } from '../../../project/projectUtils';
|
||||
import SubmissionService from '../../SubmissionService';
|
||||
import { AndroidArchiveType, AndroidSubmitCommandFlags } from '../../types';
|
||||
import { getLatestBuildForSubmissionAsync } from '../../utils/builds';
|
||||
import { AndroidSubmissionConfig, ReleaseStatus, ReleaseTrack } from '../AndroidSubmissionConfig';
|
||||
import AndroidSubmitCommand from '../AndroidSubmitCommand';
|
||||
|
||||
@@ -15,6 +21,7 @@ jest.mock('fs');
|
||||
jest.mock('ora');
|
||||
jest.mock('../../SubmissionService');
|
||||
jest.mock('../../../project/ensureProjectExists');
|
||||
jest.mock('../../utils/builds');
|
||||
jest.mock('../../../user/User', () => ({
|
||||
getUserAsync: jest.fn(() => mockJester),
|
||||
}));
|
||||
@@ -35,6 +42,13 @@ describe(AndroidSubmitCommand, () => {
|
||||
'/google-service-account.json': JSON.stringify({ service: 'account' }),
|
||||
};
|
||||
|
||||
const fakeBuildFragment: Partial<BuildFragment> = {
|
||||
id: uuidv4(),
|
||||
artifacts: { buildUrl: 'http://expo.io/fake.apk' },
|
||||
appVersion: '1.2.3',
|
||||
platform: AppPlatform.Android,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
vol.fromJSON({
|
||||
...testProject.projectTree,
|
||||
@@ -111,7 +125,52 @@ describe(AndroidSubmitCommand, () => {
|
||||
expect(SubmissionService.startSubmissionAsync).toHaveBeenCalledWith(
|
||||
AppPlatform.Android,
|
||||
projectId,
|
||||
androidSubmissionConfig
|
||||
androidSubmissionConfig,
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it('assigns the build ID to submission', async () => {
|
||||
const projectId = uuidv4();
|
||||
asMock(SubmissionService.getSubmissionAsync).mockImplementationOnce(
|
||||
async (submissionId: string): Promise<SubmissionFragment> => {
|
||||
const actualSubmission = await originalGetSubmissionAsync(submissionId);
|
||||
return {
|
||||
...actualSubmission,
|
||||
status: SubmissionStatus.Finished,
|
||||
};
|
||||
}
|
||||
);
|
||||
asMock(getProjectIdAsync).mockImplementationOnce(() => projectId);
|
||||
asMock(getLatestBuildForSubmissionAsync).mockResolvedValueOnce(fakeBuildFragment);
|
||||
|
||||
const options: AndroidSubmitCommandFlags = {
|
||||
latest: true,
|
||||
type: 'apk',
|
||||
key: '/google-service-account.json',
|
||||
track: 'internal',
|
||||
releaseStatus: 'draft',
|
||||
verbose: false,
|
||||
};
|
||||
const ctx = AndroidSubmitCommand.createContext(testProject.projectRoot, projectId, options);
|
||||
const command = new AndroidSubmitCommand(ctx);
|
||||
await command.runAsync();
|
||||
|
||||
const androidSubmissionConfig: AndroidSubmissionConfig = {
|
||||
archiveUrl: 'http://expo.io/fake.apk',
|
||||
archiveType: AndroidArchiveType.apk,
|
||||
androidPackage: testProject.appJSON.expo.android?.package,
|
||||
serviceAccount: fakeFiles['/google-service-account.json'],
|
||||
releaseStatus: ReleaseStatus.draft,
|
||||
track: ReleaseTrack.internal,
|
||||
projectId,
|
||||
};
|
||||
|
||||
expect(SubmissionService.startSubmissionAsync).toHaveBeenCalledWith(
|
||||
AppPlatform.Android,
|
||||
projectId,
|
||||
androidSubmissionConfig,
|
||||
fakeBuildFragment.id
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,10 +2,10 @@ import chalk from 'chalk';
|
||||
import { URL, parse as parseUrl } from 'url';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppPlatform, BuildFragment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { getBuildArtifactUrlByIdAsync, getLatestBuildArtifactUrlAsync } from '../utils/builds';
|
||||
import { getBuildByIdForSubmissionAsync, getLatestBuildForSubmissionAsync } from '../utils/builds';
|
||||
import { isExistingFile, uploadAppArchiveAsync } from '../utils/files';
|
||||
|
||||
export enum ArchiveFileSourceType {
|
||||
@@ -49,6 +49,7 @@ interface ArchiveFilePromptSource extends ArchiveFileSourceBase {
|
||||
export interface ResolvedArchive {
|
||||
location: string;
|
||||
realSource: ArchiveFileSource;
|
||||
build?: BuildFragment;
|
||||
}
|
||||
|
||||
export type ArchiveFileSource =
|
||||
@@ -96,9 +97,9 @@ async function handleUrlSourceAsync(source: ArchiveFileUrlSource): Promise<Resol
|
||||
|
||||
async function handleLatestSourceAsync(source: ArchiveFileLatestSource): Promise<ResolvedArchive> {
|
||||
try {
|
||||
const artifactUrl = await getLatestBuildArtifactUrlAsync(source.platform, source.projectId);
|
||||
const latestBuild = await getLatestBuildForSubmissionAsync(source.platform, source.projectId);
|
||||
|
||||
if (!artifactUrl) {
|
||||
if (!latestBuild) {
|
||||
Log.error(
|
||||
chalk.bold(
|
||||
"Couldn't find any builds for this project on EAS servers. It looks like you haven't run 'eas build' yet."
|
||||
@@ -111,8 +112,9 @@ async function handleLatestSourceAsync(source: ArchiveFileLatestSource): Promise
|
||||
}
|
||||
|
||||
return {
|
||||
location: artifactUrl,
|
||||
location: latestBuild.artifacts.buildUrl,
|
||||
realSource: source,
|
||||
build: latestBuild,
|
||||
};
|
||||
} catch (err) {
|
||||
Log.error(err);
|
||||
@@ -141,9 +143,11 @@ async function handleBuildIdSourceAsync(
|
||||
source: ArchiveFileBuildIdSource
|
||||
): Promise<ResolvedArchive> {
|
||||
try {
|
||||
const build = await getBuildByIdForSubmissionAsync(source.platform, source.id);
|
||||
return {
|
||||
location: await getBuildArtifactUrlByIdAsync(source.platform, source.id),
|
||||
location: build.artifacts.buildUrl,
|
||||
realSource: source,
|
||||
build,
|
||||
};
|
||||
} catch (err) {
|
||||
Log.error(chalk.bold(`Couldn't find build for id ${source.id}`));
|
||||
|
||||
@@ -3,10 +3,13 @@ import { vol } from 'memfs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { asMock } from '../../../__tests__/utils';
|
||||
import { AppPlatform, UploadSessionType } from '../../../graphql/generated';
|
||||
import { AppPlatform, BuildFragment, UploadSessionType } from '../../../graphql/generated';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { uploadAsync } from '../../../uploads';
|
||||
import { getBuildArtifactUrlByIdAsync, getLatestBuildArtifactUrlAsync } from '../../utils/builds';
|
||||
import {
|
||||
getBuildByIdForSubmissionAsync,
|
||||
getLatestBuildForSubmissionAsync,
|
||||
} from '../../utils/builds';
|
||||
import {
|
||||
ArchiveFileSourceType,
|
||||
ResolvedArchive,
|
||||
@@ -21,6 +24,15 @@ jest.mock('../../../uploads');
|
||||
|
||||
const ARCHIVE_URL = 'https://url.to/archive.tar.gz';
|
||||
|
||||
const MOCK_BUILD_FRAGMENT: Partial<BuildFragment> = {
|
||||
id: uuidv4(),
|
||||
artifacts: {
|
||||
buildUrl: ARCHIVE_URL,
|
||||
},
|
||||
appVersion: '1.2.3',
|
||||
platform: AppPlatform.Android,
|
||||
};
|
||||
|
||||
const SOURCE_STUB_INPUT = {
|
||||
projectId: uuidv4(),
|
||||
platform: AppPlatform.Android,
|
||||
@@ -77,7 +89,7 @@ describe(getArchiveFileLocationAsync, () => {
|
||||
|
||||
it('handles Build ID source', async () => {
|
||||
const buildId = uuidv4();
|
||||
asMock(getBuildArtifactUrlByIdAsync).mockResolvedValueOnce(ARCHIVE_URL);
|
||||
asMock(getBuildByIdForSubmissionAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
|
||||
|
||||
const resolvedArchive = await getArchiveFileLocationAsync({
|
||||
...SOURCE_STUB_INPUT,
|
||||
@@ -85,12 +97,12 @@ describe(getArchiveFileLocationAsync, () => {
|
||||
id: buildId,
|
||||
});
|
||||
|
||||
expect(getBuildArtifactUrlByIdAsync).toBeCalledWith(SOURCE_STUB_INPUT.platform, buildId);
|
||||
expect(getBuildByIdForSubmissionAsync).toBeCalledWith(SOURCE_STUB_INPUT.platform, buildId);
|
||||
assertArchiveResult(resolvedArchive, ArchiveFileSourceType.buildId);
|
||||
});
|
||||
|
||||
it('prompts again if build with provided ID doesnt exist', async () => {
|
||||
asMock(getBuildArtifactUrlByIdAsync).mockRejectedValue(new Error('Build doesnt exist'));
|
||||
asMock(getBuildByIdForSubmissionAsync).mockRejectedValue(new Error('Build doesnt exist'));
|
||||
asMock(promptAsync)
|
||||
.mockResolvedValueOnce({ sourceType: ArchiveFileSourceType.url })
|
||||
.mockResolvedValueOnce({ url: ARCHIVE_URL });
|
||||
@@ -106,7 +118,7 @@ describe(getArchiveFileLocationAsync, () => {
|
||||
|
||||
it('handles latest build source', async () => {
|
||||
const projectId = uuidv4();
|
||||
asMock(getLatestBuildArtifactUrlAsync).mockResolvedValueOnce(ARCHIVE_URL);
|
||||
asMock(getLatestBuildForSubmissionAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
|
||||
|
||||
const resolvedArchive = await getArchiveFileLocationAsync({
|
||||
...SOURCE_STUB_INPUT,
|
||||
@@ -114,12 +126,12 @@ describe(getArchiveFileLocationAsync, () => {
|
||||
sourceType: ArchiveFileSourceType.latest,
|
||||
});
|
||||
|
||||
expect(getLatestBuildArtifactUrlAsync).toBeCalledWith(SOURCE_STUB_INPUT.platform, projectId);
|
||||
expect(getLatestBuildForSubmissionAsync).toBeCalledWith(SOURCE_STUB_INPUT.platform, projectId);
|
||||
assertArchiveResult(resolvedArchive, ArchiveFileSourceType.latest);
|
||||
});
|
||||
|
||||
it('prompts again if no builds exists when selected latest', async () => {
|
||||
asMock(getLatestBuildArtifactUrlAsync).mockResolvedValueOnce(null);
|
||||
asMock(getLatestBuildForSubmissionAsync).mockResolvedValueOnce(null);
|
||||
asMock(promptAsync)
|
||||
.mockResolvedValueOnce({ sourceType: ArchiveFileSourceType.url })
|
||||
.mockResolvedValueOnce({ url: ARCHIVE_URL });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppPlatform, BuildFragment } from '../../graphql/generated';
|
||||
import { ArchiveType } from '../types';
|
||||
import {
|
||||
ArchiveFileSource,
|
||||
@@ -16,18 +16,20 @@ export interface Archive {
|
||||
location: string;
|
||||
type: ArchiveType;
|
||||
realFileSource: ArchiveFileSource;
|
||||
build?: BuildFragment;
|
||||
}
|
||||
|
||||
export async function getArchiveAsync(
|
||||
platform: AppPlatform,
|
||||
source: ArchiveSource
|
||||
): Promise<Archive> {
|
||||
const { location, realSource } = await getArchiveFileLocationAsync(source.archiveFile);
|
||||
const { location, realSource, build } = await getArchiveFileLocationAsync(source.archiveFile);
|
||||
const type = await getArchiveTypeAsync(platform, source.archiveType, location);
|
||||
return {
|
||||
location,
|
||||
type,
|
||||
realFileSource: realSource,
|
||||
build,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,12 @@ class IosSubmitter extends BaseSubmitter<IosSubmissionContext, IosSubmissionOpti
|
||||
SummaryHumanReadableKeys,
|
||||
SummaryHumanReadableValues
|
||||
);
|
||||
const result = await this.startSubmissionAsync(submissionConfig, this.ctx.commandFlags.verbose);
|
||||
|
||||
const result = await this.startSubmissionAsync(
|
||||
submissionConfig,
|
||||
resolvedSourceOptions.archive.build?.id,
|
||||
this.ctx.commandFlags.verbose
|
||||
);
|
||||
|
||||
if (result === SubmissionStatus.Finished) {
|
||||
Log.addNewLineIfNone();
|
||||
@@ -115,7 +120,7 @@ const SummaryHumanReadableKeys: Record<keyof SummaryData, string> = {
|
||||
projectId: 'Project ID',
|
||||
archiveUrl: 'Archive URL',
|
||||
archivePath: 'Archive Path',
|
||||
buildId: 'Build ID',
|
||||
formattedBuild: 'Build',
|
||||
};
|
||||
|
||||
const SummaryHumanReadableValues: Partial<Record<keyof SummaryData, Function>> = {};
|
||||
|
||||
@@ -104,7 +104,8 @@ describe(IosSubmitCommand, () => {
|
||||
expect(SubmissionService.startSubmissionAsync).toHaveBeenCalledWith(
|
||||
AppPlatform.Ios,
|
||||
projectId,
|
||||
iosSubmissionConfig
|
||||
iosSubmissionConfig,
|
||||
undefined
|
||||
);
|
||||
|
||||
delete process.env.EXPO_APPLE_APP_SPECIFIC_PASSWORD;
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
import { AppPlatform, BuildStatus } from '../../graphql/generated';
|
||||
import { AppPlatform, BuildArtifacts, BuildFragment, BuildStatus } from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
|
||||
export async function getBuildArtifactUrlByIdAsync(
|
||||
// `BuildFragment` with non-null `artifacts.buildUrl`
|
||||
type BuildFragmentWithArtifact = BuildFragment & {
|
||||
artifacts: BuildArtifacts & { buildUrl: string };
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets build by ID and ensures that `artifacts.buildUrl` exists
|
||||
*/
|
||||
export async function getBuildByIdForSubmissionAsync(
|
||||
platform: AppPlatform,
|
||||
buildId: string
|
||||
): Promise<string> {
|
||||
const { platform: buildPlatform, artifacts } = await BuildQuery.byIdAsync(buildId);
|
||||
): Promise<BuildFragmentWithArtifact> {
|
||||
const build = await BuildQuery.byIdAsync(buildId);
|
||||
|
||||
if (buildPlatform !== platform) {
|
||||
if (build.platform !== platform) {
|
||||
throw new Error("Build platform doesn't match!");
|
||||
}
|
||||
|
||||
if (!artifacts) {
|
||||
throw new Error('Build has no artifacts.');
|
||||
if (!buildFragmentHasArtifact(build)) {
|
||||
throw new Error('Build has no artifacts or build URL is not defined.');
|
||||
}
|
||||
const buildUrl = artifacts.buildUrl;
|
||||
if (!buildUrl) {
|
||||
throw new Error('Build URL is not defined.');
|
||||
}
|
||||
return buildUrl;
|
||||
|
||||
return build;
|
||||
}
|
||||
|
||||
export async function getLatestBuildArtifactUrlAsync(
|
||||
export async function getLatestBuildForSubmissionAsync(
|
||||
platform: AppPlatform,
|
||||
appId: string
|
||||
): Promise<string | null> {
|
||||
): Promise<BuildFragmentWithArtifact | null> {
|
||||
const builds = await BuildQuery.allForAppAsync(appId, {
|
||||
platform,
|
||||
status: BuildStatus.Finished,
|
||||
@@ -35,10 +40,15 @@ export async function getLatestBuildArtifactUrlAsync(
|
||||
return null;
|
||||
}
|
||||
|
||||
const { artifacts } = builds[0];
|
||||
if (!artifacts) {
|
||||
const build = builds[0];
|
||||
if (!buildFragmentHasArtifact(build)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return artifacts.buildUrl ?? null;
|
||||
return build;
|
||||
}
|
||||
|
||||
// Utility function needed to make TypeScript happy
|
||||
function buildFragmentHasArtifact(build: BuildFragment): build is BuildFragmentWithArtifact {
|
||||
return build.artifacts?.buildUrl != null;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ enum SubmissionErrorCode {
|
||||
IOS_MISSING_APP_ICON = 'SUBMISSION_SERVICE_IOS_MISSING_APP_ICON',
|
||||
IOS_INVALID_SIGNATURE = 'SUBMISSION_SERVICE_IOS_INVALID_SIGNATURE',
|
||||
IOS_INCORRECT_CREDENTIALS = 'SUBMISSION_SERVICE_IOS_INVALID_CREDENTIALS',
|
||||
IOS_IPAD_INVALID_ORIENTATION = 'SUBMISSION_SERVICE_IOS_IPAD_INVALID_ORIENTATION',
|
||||
}
|
||||
|
||||
const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
|
||||
@@ -57,6 +58,10 @@ const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
|
||||
`${learnMore('https://docs.expo.io/distribution/app-signing')}`,
|
||||
[SubmissionErrorCode.IOS_INCORRECT_CREDENTIALS]:
|
||||
'Your Apple ID or app-specific password is incorrect. Please verify that you entered them correctly and try again.',
|
||||
[SubmissionErrorCode.IOS_IPAD_INVALID_ORIENTATION]:
|
||||
"Your app doesn't support iPad multitasking and has to require full screen.\n" +
|
||||
"If you're submitting a managed Expo project, set the `expo.ios.requireFullScreen` to true in app.json and build the project again.\n" +
|
||||
`${learnMore('https://expo.fyi/ipad-requires-fullscreen')}`,
|
||||
};
|
||||
|
||||
export function printSubmissionError(error: SubmissionError): boolean {
|
||||
|
||||
@@ -1,17 +1,51 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { AppPlatform, BuildFragment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { Archive, ArchiveFileSourceType } from '../archiveSource';
|
||||
|
||||
export interface ArchiveSourceSummaryFields {
|
||||
archiveUrl?: string;
|
||||
archivePath?: string;
|
||||
buildId?: string;
|
||||
formattedBuild?: string;
|
||||
}
|
||||
|
||||
function formatSubmissionBuildSummary(build: BuildFragment) {
|
||||
const fields = [
|
||||
{
|
||||
label: 'Build ID',
|
||||
value: build.id,
|
||||
},
|
||||
{
|
||||
label: 'Build Date',
|
||||
value: new Date(build.createdAt).toLocaleString(),
|
||||
},
|
||||
{
|
||||
label: 'App Version',
|
||||
value: build.appVersion,
|
||||
},
|
||||
{
|
||||
label: build.platform === AppPlatform.Android ? 'Version code' : 'Build number',
|
||||
value: build.appBuildVersion,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredFields = fields.filter(({ value }) => value !== undefined && value !== null) as {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
|
||||
return (
|
||||
'\n' +
|
||||
formatFields(filteredFields, {
|
||||
labelFormat: label => ` ${chalk.dim(label)}:`,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function formatArchiveSourceSummary({
|
||||
realFileSource,
|
||||
build,
|
||||
}: Archive): ArchiveSourceSummaryFields {
|
||||
const summarySlice: ArchiveSourceSummaryFields = {};
|
||||
|
||||
@@ -23,11 +57,8 @@ export function formatArchiveSourceSummary({
|
||||
summarySlice.archiveUrl = realFileSource.url;
|
||||
break;
|
||||
case ArchiveFileSourceType.buildId:
|
||||
summarySlice.buildId = realFileSource.id;
|
||||
break;
|
||||
case ArchiveFileSourceType.latest:
|
||||
// TODO: Resolve real build ID here
|
||||
summarySlice.buildId = '[latest]';
|
||||
summarySlice.formattedBuild = formatSubmissionBuildSummary(build!);
|
||||
break;
|
||||
}
|
||||
return summarySlice;
|
||||
|
||||
@@ -14,7 +14,9 @@ export function endTimer(label = LABEL, clear: boolean = true) {
|
||||
const startTime = startTimes[label];
|
||||
if (startTime) {
|
||||
const delta = endTime - startTime;
|
||||
if (clear) delete startTimes[label];
|
||||
if (clear) {
|
||||
delete startTimes[label];
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
throw new Error(`Timer '${label}' has not be started yet`);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with the eas.json",
|
||||
"version": "0.20.0",
|
||||
"version": "0.22.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/eas-build-job": "0.2.41",
|
||||
"@expo/eas-build-job": "0.2.44",
|
||||
"@hapi/joi": "17.1.1",
|
||||
"fs-extra": "9.0.1",
|
||||
"tslib": "1.14.1"
|
||||
|
||||
@@ -2,36 +2,36 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@amplitude/identify@1.5.0", "@amplitude/identify@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/identify/-/identify-1.5.0.tgz#6d31aaae615393867328abc6fcad037e9d1639e8"
|
||||
integrity sha512-GCDxvwZvfFInZQ6m5LYnb9sVmJP+n+Re6vkBdtclp9uTNVPpdsXtdw2ann5Ts5pV10fivDpgdZ3OHehSxe742A==
|
||||
"@amplitude/identify@1.7.0", "@amplitude/identify@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/identify/-/identify-1.7.0.tgz#ea8e435d55e55027ced0abfe1a5e250038e75f6c"
|
||||
integrity sha512-AhaYj76cExvABu2C2Q6oO4aRNQhQkP7foQb7Lk7qW5ZPhdiWpAOGThdMre0pu5CSYF21G+FUnM6/1WQ5Cfkw3A==
|
||||
dependencies:
|
||||
"@amplitude/types" "^1.5.0"
|
||||
"@amplitude/utils" "^1.5.0"
|
||||
"@amplitude/types" "^1.7.0"
|
||||
"@amplitude/utils" "^1.7.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@amplitude/node@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/node/-/node-1.5.0.tgz#23e1650fed6302696b669dd8ceea0164b73f220e"
|
||||
integrity sha512-/jfmYHanhln/OAuL8QG76EjThnaRDnfxLxO4oYstMFZKRktnUahE5i0sH02+tJ7I2nzq+4KTYY2hc8Rvi+Vt4Q==
|
||||
"@amplitude/node@1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/node/-/node-1.7.0.tgz#534745e79a621ef74b1153c19c5683559ff930ac"
|
||||
integrity sha512-+2z0V+3fVbSihbJipADvFPb2bDCLvpgDnzLkNUka606DTyLWPuoDKzawUgl2yanuFXowFYEmQ2EltuQHoXd3YQ==
|
||||
dependencies:
|
||||
"@amplitude/identify" "^1.5.0"
|
||||
"@amplitude/types" "^1.5.0"
|
||||
"@amplitude/utils" "^1.5.0"
|
||||
"@amplitude/identify" "^1.7.0"
|
||||
"@amplitude/types" "^1.7.0"
|
||||
"@amplitude/utils" "^1.7.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@amplitude/types@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/types/-/types-1.5.0.tgz#cf34d0f304558d8b0d847a6217d506e6226e653c"
|
||||
integrity sha512-XspuOsUzUcxwAptHeGiIn4giuLWs285xTJa7h8kAEEynxtEI3/krWCoDYZSB9PekaPXB6phxiO/tMd9t5V9LgQ==
|
||||
"@amplitude/types@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/types/-/types-1.7.0.tgz#c9f6636245f2de0ab58ee667b442cef58c42c453"
|
||||
integrity sha512-zPENZDWlh64WHj/7Jjghu2HGWywMPundf25ycxn0tYXtxTvPmK2m/MMV6GvUN5uW0kg65eeKETCfge1UJ9MXlQ==
|
||||
|
||||
"@amplitude/utils@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/utils/-/utils-1.5.0.tgz#b48c3233e12f2c04880dd98f3c2628fd0976d3cb"
|
||||
integrity sha512-1DrDJkb4dVX+FiBXhGpO2Dn2cRKdP+gtrVR8vZcE8wz/V2XxUI3DDx7uQbIS6WbQf6swv6Uo2eMHYtrwebostw==
|
||||
"@amplitude/utils@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@amplitude/utils/-/utils-1.7.0.tgz#91ef07f203223ad4f5309f0c8950ebb5de6bd8eb"
|
||||
integrity sha512-+WsKdFbA+rR/xSTlho90C+GlzDL7Sn51J4bmmGtOSTZk5oEPohvHzHO8pVBGDB8UvNAs2LjQQ7q7b2qRWdUtvA==
|
||||
dependencies:
|
||||
"@amplitude/types" "^1.5.0"
|
||||
"@amplitude/types" "^1.7.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@ardatan/[email protected]":
|
||||
@@ -1818,10 +1818,10 @@
|
||||
unique-filename "^1.1.1"
|
||||
which "^1.3.1"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
version "0.0.0-alpha.20"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.20.tgz#cbc92e4c7e8754b051b7293af60a55a550fb6583"
|
||||
integrity sha512-L/M9NPNlT1e38whA3M4QdnIDCClj6Y2GPXFOxBxuwzlmh847RHwZ/ojJpVP8sLVC+is54DS1hU9vtDkiPHGPRw==
|
||||
"@expo/[email protected]4":
|
||||
version "0.0.0-alpha.24"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-0.0.0-alpha.24.tgz#4208204d3763a4770ce9e63f36c14476adc635bd"
|
||||
integrity sha512-96MGsGcRIjzJ3Stqw9fiXAgOfMn4UlmlBp2EZkmpad7945ke9Gguceafb6rpbKJHEkQd2qcBcfcumU73QisP2w==
|
||||
|
||||
"@expo/babel-preset-cli@^0.2.17":
|
||||
version "0.2.18"
|
||||
@@ -1918,10 +1918,10 @@
|
||||
xcode "^3.0.0"
|
||||
xml-js "^1.6.11"
|
||||
|
||||
"@expo/[email protected]1":
|
||||
version "0.2.41"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.41.tgz#d44ce7f5e7a1f08caa415913fed43f875fce4d52"
|
||||
integrity sha512-tp3i7EZRMGvlJ3NyhpBpLdIxg6+CI6z6P3JgZVh57s2skN5zjzHcfjXPcw0gVH9fCouUnvhSSye8O+g2Zo1oaA==
|
||||
"@expo/[email protected]4":
|
||||
version "0.2.44"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.44.tgz#11f442cf9c1ac8402afbb06742bc99d4c1c6b6d9"
|
||||
integrity sha512-4/wtOyAFLa0aILfco0a/slL96CBUppSeXfPdLzcHkBIc+AUB/pUSGf/M+Z3Pn7QoA3BHCnSIW8DosqqBBOppRg==
|
||||
dependencies:
|
||||
"@hapi/joi" "^17.1.1"
|
||||
|
||||
|
||||
Reference in new issue
Block a user