mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f33d859d75 |
No files matched your search
@@ -8,43 +8,14 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Managed app versions support. ([#1209](https://github.com/expo/eas-cli/pull/1209), [#1219](https://github.com/expo/eas-cli/pull/1219), [#1232](https://github.com/expo/eas-cli/pull/1232) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Add a warning when publishing an update with too many assets. ([#1243](https://github.com/expo/eas-cli/pull/1243) by [@kgc00](https://github.com/kgc00))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Do not include sources in `eas-json` npm package. ([#1248](https://github.com/expo/eas-cli/pull/1248) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Add ts-node dev dependency to fix bin/run script. ([#1252](https://github.com/expo/eas-cli/pull/1252) by [@brentvatne](https://github.com/brentvatne))
|
||||
- Swallow error when unable to track file in no commit workflow. ([#1109](https://github.com/expo/eas-cli/pull/1109) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
## [0.57.0](https://github.com/expo/eas-cli/releases/tag/v0.57.0) - 2022-08-03
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add new option to `eas device:create` - allow importing devices from Apple Developer Portal. ([#1236](https://github.com/expo/eas-cli/pull/1236) by [@dsokal](https://github.com/dsokal))
|
||||
- Add support for attaching messages to builds. ([#1237](https://github.com/expo/eas-cli/pull/1237) by [@dsokal](https://github.com/dsokal))
|
||||
- Introduce interactive pagination for `branch` commands. ([#1213](https://github.com/expo/eas-cli/pull/1213) by [@kgc00](https://github.com/kgc00))
|
||||
|
||||
## [0.56.0](https://github.com/expo/eas-cli/releases/tag/v0.56.0) - 2022-07-28
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Display build queue type when waiting. ([#1217](https://github.com/expo/eas-cli/pull/1217) by [@dsokal](https://github.com/dsokal))
|
||||
- Add better message on how to upgrade EAS CLI. ([#1222](https://github.com/expo/eas-cli/pull/1222) by [@jeremybarbet](https://github.com/jeremybarbet), [#1231](https://github.com/expo/eas-cli/pull/1231) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Invoke export with sourcemaps on update. ([#1228](https://github.com/expo/eas-cli/pull/1228) by [@kbrandwijk](https://github.com/kbrandwijk))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Show spinner instead of silently timing out during asset upload. ([#1206](https://github.com/expo/eas-cli/pull/1206) by [@wschurman](https://github.com/wschurman))
|
||||
- Fix build archive S3 URLs. ([#1207](https://github.com/expo/eas-cli/pull/1207) by [@dsokal](https://github.com/dsokal))
|
||||
- `autoIncrement` option on iOS will update versions of all targets that depend on an application target. ([#1219](https://github.com/expo/eas-cli/pull/1219) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Increase max number of chained "extends" in build profiles. ([#1208](https://github.com/expo/eas-cli/pull/1208) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
- Change wording on build warning for bundle id/package name. ([#1211](https://github.com/expo/eas-cli/pull/1211) by [@kbrandwijk](https://github.com/kbrandwijk))
|
||||
|
||||
## [0.55.1](https://github.com/expo/eas-cli/releases/tag/v0.55.1) - 2022-07-12
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ packages/eas-cli/src/commands/build @dsokal @wkozyra95
|
||||
packages/eas-cli/src/submit @barthap @dsokal
|
||||
packages/eas-cli/src/commands/submit.ts @barthap @dsokal
|
||||
|
||||
packages/eas-cli/src/update @kgc00 @wschurman
|
||||
packages/eas-cli/src/commands/update @kgc00 @wschurman
|
||||
|
||||
packages/eas-cli/schema @bycedric
|
||||
packages/eas-cli/src/metadata @bycedric
|
||||
packages/eas-cli/src/commands/metadata @bycedric
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.57.0"
|
||||
"version": "0.55.1"
|
||||
}
|
||||
+2
-3
@@ -29,11 +29,10 @@
|
||||
"oclif": "3.0.1",
|
||||
"prettier": "2.6.2",
|
||||
"ts-jest": "28.0.2",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.6.0",
|
||||
"yarn": "1.22.19"
|
||||
"node": "18.2.0",
|
||||
"yarn": "1.22.18"
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,6 @@ const rootEslintrc = require('../../.eslintrc.js');
|
||||
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-restricted-properties': [
|
||||
'warn',
|
||||
{
|
||||
object: 'it',
|
||||
property: 'only',
|
||||
message: 'it.only should not be committed to main.',
|
||||
},
|
||||
{
|
||||
object: 'test',
|
||||
property: 'only',
|
||||
message: 'test.only should not be committed to main.',
|
||||
},
|
||||
{
|
||||
object: 'describe',
|
||||
property: 'only',
|
||||
message: 'describe.only should not be committed to main.',
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
|
||||
+105
-112
@@ -22,68 +22,69 @@ yarn global add eas-cli
|
||||
# Usage
|
||||
|
||||
```sh
|
||||
eas COMMAND
|
||||
# runs the command
|
||||
eas (-v|--version|version)
|
||||
eas COMMAND
|
||||
# prints the version
|
||||
eas --help COMMAND
|
||||
eas (-v|--version|version)
|
||||
# outputs help for specific command
|
||||
eas --help COMMAND
|
||||
```
|
||||
|
||||
# Commands
|
||||
|
||||
<!-- commands -->
|
||||
* [`eas account:login`](#eas-accountlogin)
|
||||
* [`eas account:logout`](#eas-accountlogout)
|
||||
* [`eas account:view`](#eas-accountview)
|
||||
* [`eas analytics [STATUS]`](#eas-analytics-status)
|
||||
* [`eas autocomplete [SHELL]`](#eas-autocomplete-shell)
|
||||
* [`eas branch:create [NAME]`](#eas-branchcreate-name)
|
||||
* [`eas branch:delete [NAME]`](#eas-branchdelete-name)
|
||||
* [`eas branch:list`](#eas-branchlist)
|
||||
* [`eas branch:rename`](#eas-branchrename)
|
||||
* [`eas branch:view [NAME]`](#eas-branchview-name)
|
||||
* [`eas build`](#eas-build)
|
||||
* [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
||||
* [`eas build:configure`](#eas-buildconfigure)
|
||||
* [`eas build:inspect`](#eas-buildinspect)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:submit`](#eas-buildsubmit)
|
||||
* [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
||||
* [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
||||
* [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
||||
* [`eas channel:list`](#eas-channellist)
|
||||
* [`eas channel:view [NAME]`](#eas-channelview-name)
|
||||
* [`eas config`](#eas-config)
|
||||
* [`eas credentials`](#eas-credentials)
|
||||
* [`eas device:create`](#eas-devicecreate)
|
||||
* [`eas device:delete`](#eas-devicedelete)
|
||||
* [`eas device:list`](#eas-devicelist)
|
||||
* [`eas device:view [UDID]`](#eas-deviceview-udid)
|
||||
* [`eas diagnostics`](#eas-diagnostics)
|
||||
* [`eas help [COMMAND]`](#eas-help-command)
|
||||
* [`eas init`](#eas-init)
|
||||
* [`eas login`](#eas-login)
|
||||
* [`eas logout`](#eas-logout)
|
||||
* [`eas metadata:pull`](#eas-metadatapull)
|
||||
* [`eas metadata:push`](#eas-metadatapush)
|
||||
* [`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)
|
||||
* [`eas submit`](#eas-submit)
|
||||
* [`eas update`](#eas-update)
|
||||
* [`eas update:configure`](#eas-updateconfigure)
|
||||
* [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
|
||||
* [`eas update:list`](#eas-updatelist)
|
||||
* [`eas update:view GROUPID`](#eas-updateview-groupid)
|
||||
* [`eas webhook:create`](#eas-webhookcreate)
|
||||
* [`eas webhook:delete [ID]`](#eas-webhookdelete-id)
|
||||
* [`eas webhook:list`](#eas-webhooklist)
|
||||
* [`eas webhook:update`](#eas-webhookupdate)
|
||||
* [`eas webhook:view ID`](#eas-webhookview-id)
|
||||
* [`eas whoami`](#eas-whoami)
|
||||
|
||||
- [`eas account:login`](#eas-accountlogin)
|
||||
- [`eas account:logout`](#eas-accountlogout)
|
||||
- [`eas account:view`](#eas-accountview)
|
||||
- [`eas analytics [STATUS]`](#eas-analytics-status)
|
||||
- [`eas autocomplete [SHELL]`](#eas-autocomplete-shell)
|
||||
- [`eas branch:create [NAME]`](#eas-branchcreate-name)
|
||||
- [`eas branch:delete [NAME]`](#eas-branchdelete-name)
|
||||
- [`eas branch:list`](#eas-branchlist)
|
||||
- [`eas branch:rename`](#eas-branchrename)
|
||||
- [`eas branch:view [NAME]`](#eas-branchview-name)
|
||||
- [`eas build`](#eas-build)
|
||||
- [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
||||
- [`eas build:configure`](#eas-buildconfigure)
|
||||
- [`eas build:inspect`](#eas-buildinspect)
|
||||
- [`eas build:list`](#eas-buildlist)
|
||||
- [`eas build:submit`](#eas-buildsubmit)
|
||||
- [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
||||
- [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
||||
- [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
||||
- [`eas channel:list`](#eas-channellist)
|
||||
- [`eas channel:view [NAME]`](#eas-channelview-name)
|
||||
- [`eas config`](#eas-config)
|
||||
- [`eas credentials`](#eas-credentials)
|
||||
- [`eas device:create`](#eas-devicecreate)
|
||||
- [`eas device:delete`](#eas-devicedelete)
|
||||
- [`eas device:list`](#eas-devicelist)
|
||||
- [`eas device:view [UDID]`](#eas-deviceview-udid)
|
||||
- [`eas diagnostics`](#eas-diagnostics)
|
||||
- [`eas help [COMMAND]`](#eas-help-command)
|
||||
- [`eas init`](#eas-init)
|
||||
- [`eas login`](#eas-login)
|
||||
- [`eas logout`](#eas-logout)
|
||||
- [`eas metadata:pull`](#eas-metadatapull)
|
||||
- [`eas metadata:push`](#eas-metadatapush)
|
||||
- [`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)
|
||||
- [`eas submit`](#eas-submit)
|
||||
- [`eas update`](#eas-update)
|
||||
- [`eas update:configure`](#eas-updateconfigure)
|
||||
- [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
|
||||
- [`eas update:list`](#eas-updatelist)
|
||||
- [`eas update:view GROUPID`](#eas-updateview-groupid)
|
||||
- [`eas webhook:create`](#eas-webhookcreate)
|
||||
- [`eas webhook:delete [ID]`](#eas-webhookdelete-id)
|
||||
- [`eas webhook:list`](#eas-webhooklist)
|
||||
- [`eas webhook:update`](#eas-webhookupdate)
|
||||
- [`eas webhook:view ID`](#eas-webhookview-id)
|
||||
- [`eas whoami`](#eas-whoami)
|
||||
|
||||
## `eas account:login`
|
||||
|
||||
@@ -100,7 +101,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -117,7 +118,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -134,7 +135,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -148,7 +149,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -197,7 +198,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -217,7 +218,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -225,20 +226,16 @@ list all branches
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas branch:list [--offset <value>] [--limit <value>] [--json] [--non-interactive]
|
||||
$ eas branch:list [--json]
|
||||
|
||||
FLAGS
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--limit=<value> The number of query items to list at once. The default value is 50 (the maximum is 100). Using a
|
||||
lower value may help increase command speed.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
|
||||
--json return output as JSON
|
||||
|
||||
DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -257,7 +254,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -265,23 +262,19 @@ view a branch
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas branch:view [NAME] [--offset <value>] [--limit <value>] [--json] [--non-interactive]
|
||||
$ eas branch:view [NAME] [--json]
|
||||
|
||||
ARGUMENTS
|
||||
NAME Name of the branch to view
|
||||
|
||||
FLAGS
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--limit=<value> The number of query items to list at once. The default value is 50 (the maximum is 100). Using a
|
||||
lower value may help increase command speed.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
|
||||
--json return a json with the branch's ID name and recent update groups.
|
||||
|
||||
DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -290,10 +283,9 @@ start a build
|
||||
```
|
||||
USAGE
|
||||
$ eas build [-p android|ios|all] [--json] [--profile <value>] [--non-interactive] [--local] [--output
|
||||
<value>] [--wait] [--clear-cache] [--auto-submit | --auto-submit-with-profile <value>] [-m <value>]
|
||||
<value>] [--wait] [--clear-cache] [--auto-submit | --auto-submit-with-profile <value>]
|
||||
|
||||
FLAGS
|
||||
-m, --message=<value> A short message describing the build
|
||||
-p, --platform=(android|ios|all)
|
||||
--auto-submit Submit on build complete using the submit profile with the same name as the
|
||||
build profile
|
||||
@@ -311,7 +303,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -325,7 +317,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -342,7 +334,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -377,7 +369,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -410,7 +402,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -455,7 +447,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -475,7 +467,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -496,7 +488,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -513,7 +505,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -533,7 +525,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -551,7 +543,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -565,7 +557,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -579,7 +571,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -597,7 +589,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -614,7 +606,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -628,7 +620,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -642,7 +634,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -724,7 +716,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -741,7 +733,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -755,7 +747,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -772,7 +764,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -792,7 +784,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -809,7 +801,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -823,7 +815,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -853,7 +845,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -883,7 +875,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -900,7 +892,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -920,7 +912,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -939,7 +931,7 @@ DESCRIPTION
|
||||
view the recent updates for a branch
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -959,7 +951,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -979,7 +971,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -996,7 +988,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1013,7 +1005,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1034,7 +1026,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1051,7 +1043,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.57.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.55.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
@@ -1067,4 +1059,5 @@ DESCRIPTION
|
||||
ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
<!-- commandsstop -->
|
||||
Generated
+4
-1690
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "0.57.0",
|
||||
"version": "0.55.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
@@ -13,8 +13,8 @@
|
||||
"@expo/config": "6.0.23",
|
||||
"@expo/config-plugins": "4.1.4",
|
||||
"@expo/config-types": "45.0.0",
|
||||
"@expo/eas-build-job": "0.2.85",
|
||||
"@expo/eas-json": "0.57.0",
|
||||
"@expo/eas-build-job": "0.2.77",
|
||||
"@expo/eas-json": "0.55.0",
|
||||
"@expo/json-file": "8.2.36",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/pkcs12": "0.0.8",
|
||||
@@ -103,7 +103,6 @@
|
||||
"nock": "13.2.4",
|
||||
"tempy": "0.7.0",
|
||||
"ts-deepmerge": "2.0.1",
|
||||
"ts-mockito": "2.6.1",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -167,7 +166,7 @@
|
||||
},
|
||||
"warn-if-update-available": {
|
||||
"timeoutInDays": 0.5,
|
||||
"message": "<%= chalk('★') %> <%= chalk.bold(config.name + '@' + latest) %> <%= chalk.bold('is now available.') %>\n<%= chalk.dim('To upgrade, run ') %><%= chalk.dim(chalk.bold('npm install -g eas-cli')) %><%= chalk.dim('.') %>\n<%= chalk.dim('Proceeding with outdated version.') %>\n"
|
||||
"message": "<%= chalk.bold('★') %> <%= chalk.bold(config.name + '@' + latest) %> <%= chalk.bold('is now available, please upgrade.') %>\n<%= chalk.dim('Proceeding with outdated version') %>\n"
|
||||
},
|
||||
"update": {
|
||||
"node": {
|
||||
@@ -201,9 +200,5 @@
|
||||
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
|
||||
"generate-graphql-code": "graphql-codegen --config graphql-codegen.yml",
|
||||
"clean": "rm -rf dist build tmp node_modules yarn-error.log"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.6.0",
|
||||
"yarn": "1.22.19"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,21 @@
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"apple": {
|
||||
"AppleLocalizedPreviewFolder": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Path to folder containing preview files",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"AppleKidsAge": {
|
||||
"enum": [
|
||||
"FIVE_AND_UNDER",
|
||||
@@ -449,43 +464,62 @@
|
||||
},
|
||||
"AppleRelease": {
|
||||
"type": "object",
|
||||
"description": "Configure how the app is released in the app store.",
|
||||
"additionalProperties": false,
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"body": {
|
||||
"automaticRelease": true,
|
||||
"usesThirdPartyContent": false,
|
||||
"usesNonExemptEncryption": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"automaticRelease": {
|
||||
"description": "If and how the app should automatically be released when approved by App Store review.",
|
||||
"markdownDescription": "If and how the app should automatically be released when approved by App Store review.\n- `false` - Manually release the app after store approval. (default behavior)\n- `true` - Automatically release after store approval.\n- `Date` - Automatically schedule release on this date, after store approval.\n\nApple does not guarantee that your app is available at the chosen scheduled release date.",
|
||||
"oneOf": [
|
||||
"autoReleaseDate": {
|
||||
"description": "ISO Date to release the app automatically. Leave undefined to manually release the app to the public.",
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"body": "$CURRENT_YEAR-${1:$CURRENT_MONTH}-${2:$CURRENT_DATE}"
|
||||
}
|
||||
],
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
],
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"label": "Manual release",
|
||||
"description": "Manually release the app after store approval",
|
||||
"body": false
|
||||
"type": "string",
|
||||
"format": "date"
|
||||
},
|
||||
{
|
||||
"label": "Automatic release",
|
||||
"description": "Automatically release the app after store approval",
|
||||
"body": true
|
||||
},
|
||||
{
|
||||
"label": "Scheduled release",
|
||||
"description": "Automatically release the app on this date, and store approval",
|
||||
"body": "$CURRENT_YEAR-${1:$CURRENT_MONTH}-${2:$CURRENT_DATE}T${3:$CURRENT_HOUR}:00:00+00:00"
|
||||
"type": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phasedRelease": {
|
||||
"isPhasedReleaseEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates. Keep in mind that this version will still be available to all users as a manual update from the App Store. You can pause the phased release for up to 30 days or release this update to all users at any time.\n@see https://help.apple.com/app-store-connect/#/dev3d65fcee1",
|
||||
"markdownDescription": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates.\n\nKeep in mind that this version will still be available to all users as a manual update from the App Store.\n\nYou can pause the phased release for up to 30 days or release this update to all users at any time.\n\n[Learn More](https://help.apple.com/app-store-connect/#/dev3d65fcee1)"
|
||||
"description": "Release update over 7-day period instead of instantly.",
|
||||
"meta": {
|
||||
"storeInfo": "Phased release for automatic updates lets you gradually release this update over a 7-day period to users who have turned on automatic updates. Keep in mind that this version will still be available to all users as a manual update from the App Store. You can pause the phased release for up to 30 days or release this update to all users at any time. [Learn More](https://help.apple.com/app-store-connect/#/dev3d65fcee1)."
|
||||
}
|
||||
},
|
||||
"shouldResetRatings": {
|
||||
"type": "boolean",
|
||||
"description": "Should App Store ratings be reset for this version of the app.",
|
||||
"meta": {
|
||||
"storeInfo": "You can reset your app’s summary rating for all countries or regions when you release this version. Keep in mind that once this version is released, you won’t be able to restore the rating. Your app’s existing customer reviews will still appear on the App Store."
|
||||
}
|
||||
},
|
||||
"usesThirdPartyContent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"automaticRelease": {
|
||||
"type": "boolean",
|
||||
"description": "Should the app automatically be released when approved by App Store review."
|
||||
},
|
||||
"usesNonExemptEncryption": {
|
||||
"type": "boolean",
|
||||
"description": "Alternative to setting `ITSAppUsesNonExemptEncryption` in the binary's `Info.plist`."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -572,15 +606,11 @@
|
||||
"description": "Configuration that is specific to the Apple App Store.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The version string to edit, or create, if it doesn't exist. All the metadata will be stored on this app version.",
|
||||
"minLength": 1,
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"body": "${1:1}.${2:0}"
|
||||
}
|
||||
]
|
||||
"advisory": {
|
||||
"$ref": "#/definitions/apple/AppleAdvisory"
|
||||
},
|
||||
"categories": {
|
||||
"$ref": "#/definitions/apple/AppleCategory"
|
||||
},
|
||||
"copyright": {
|
||||
"type": "string",
|
||||
@@ -596,12 +626,6 @@
|
||||
"storeInfo": "The name of the person or entity that owns the exclusive rights to your app, preceded by the year the rights were obtained (for example, \"2008 Acme Inc.\"). Do not provide a URL."
|
||||
}
|
||||
},
|
||||
"advisory": {
|
||||
"$ref": "#/definitions/apple/AppleAdvisory"
|
||||
},
|
||||
"categories": {
|
||||
"$ref": "#/definitions/apple/AppleCategory"
|
||||
},
|
||||
"info": {
|
||||
"description": "Localized core app info",
|
||||
"type": "object",
|
||||
@@ -765,6 +789,177 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Path to localized folders containing preview files",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Localized previews object. Ex: `\"en-US\": [\"image.png\"]`",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ar-SA": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Arabic"
|
||||
},
|
||||
"ca": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Catalan"
|
||||
},
|
||||
"zh-Hans": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Chinese (Simplified)"
|
||||
},
|
||||
"zh-Hant": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Chinese (Traditional)"
|
||||
},
|
||||
"hr": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Croatian"
|
||||
},
|
||||
"cs": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Czech"
|
||||
},
|
||||
"da": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Danish"
|
||||
},
|
||||
"nl-NL": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Dutch"
|
||||
},
|
||||
"en-AU": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "English (Australia)"
|
||||
},
|
||||
"en-CA": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "English (Canada)"
|
||||
},
|
||||
"en-GB": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "English (U.K.)"
|
||||
},
|
||||
"en-US": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "English (U.S.)"
|
||||
},
|
||||
"fi": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Finnish"
|
||||
},
|
||||
"fr-FR": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "French"
|
||||
},
|
||||
"fr-CA": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "French (Canada)"
|
||||
},
|
||||
"de-DE": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "German"
|
||||
},
|
||||
"el": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Greek"
|
||||
},
|
||||
"he": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Hebrew"
|
||||
},
|
||||
"hi": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Hindi"
|
||||
},
|
||||
"hu": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Hungarian"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Indonesian"
|
||||
},
|
||||
"it": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Italian"
|
||||
},
|
||||
"ja": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Japanese"
|
||||
},
|
||||
"ko": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Korean"
|
||||
},
|
||||
"ms": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Malay"
|
||||
},
|
||||
"no": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Norwegian"
|
||||
},
|
||||
"pl": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Polish"
|
||||
},
|
||||
"pt-BR": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Portuguese (Brazil)"
|
||||
},
|
||||
"pt-PT": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Portuguese (Portugal)"
|
||||
},
|
||||
"ro": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Romanian"
|
||||
},
|
||||
"ru": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Russian"
|
||||
},
|
||||
"sk": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Slovak"
|
||||
},
|
||||
"es-MX": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Spanish (Mexico)"
|
||||
},
|
||||
"es-ES": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Spanish (Spain)"
|
||||
},
|
||||
"sv": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Swedish"
|
||||
},
|
||||
"th": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Thai"
|
||||
},
|
||||
"tr": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Turkish"
|
||||
},
|
||||
"uk": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Ukrainian"
|
||||
},
|
||||
"vi": {
|
||||
"$ref": "#/definitions/apple/AppleLocalizedPreviewFolder",
|
||||
"description": "Vietnamese"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"$ref": "#/definitions/apple/AppleRelease",
|
||||
"description": "Release strategy"
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import CliTable from 'cli-table3';
|
||||
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { UpdateBranchFragment } from '../graphql/generated';
|
||||
import { BranchQuery } from '../graphql/queries/BranchQuery';
|
||||
import Log from '../log';
|
||||
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../update/utils';
|
||||
import { printJsonOnlyOutput } from '../utils/json';
|
||||
import {
|
||||
paginatedQueryWithConfirmPromptAsync,
|
||||
paginatedQueryWithSelectPromptAsync,
|
||||
} from '../utils/queries';
|
||||
|
||||
export const BRANCHES_LIMIT = 50;
|
||||
|
||||
export async function selectBranchFromPaginatedQueryAsync(
|
||||
projectId: string,
|
||||
promptTitle: string,
|
||||
options: PaginatedQueryOptions
|
||||
): Promise<UpdateBranchFragment> {
|
||||
if (options.nonInteractive) {
|
||||
throw new Error('Unable to select a branch in non-interactive mode.');
|
||||
}
|
||||
|
||||
const selectedBranch = await paginatedQueryWithSelectPromptAsync({
|
||||
limit: options.limit ?? BRANCHES_LIMIT,
|
||||
offset: options.offset,
|
||||
queryToPerform: (limit, offset) => queryBranchesForProjectAsync(limit, offset, projectId),
|
||||
promptOptions: {
|
||||
title: promptTitle,
|
||||
getIdentifierForQueryItem: updateBranchFragment => updateBranchFragment.id,
|
||||
createDisplayTextForSelectionPromptListItem: updateBranchFragment =>
|
||||
updateBranchFragment.name,
|
||||
},
|
||||
});
|
||||
if (!selectedBranch) {
|
||||
throw new Error(`Could not find any branches for project "${projectId}"`);
|
||||
}
|
||||
return selectedBranch;
|
||||
}
|
||||
|
||||
export async function listAndRenderPaginatedBranchesAsync(
|
||||
projectId: string,
|
||||
options: PaginatedQueryOptions
|
||||
): Promise<void> {
|
||||
if (options.nonInteractive) {
|
||||
const branches = await queryBranchesForProjectAsync(
|
||||
options.limit ?? BRANCHES_LIMIT,
|
||||
options.offset,
|
||||
projectId
|
||||
);
|
||||
renderPageOfBranches(branches, options);
|
||||
} else {
|
||||
await paginatedQueryWithConfirmPromptAsync({
|
||||
limit: options.limit ?? BRANCHES_LIMIT,
|
||||
offset: options.offset,
|
||||
queryToPerform: (limit, offset) => queryBranchesForProjectAsync(limit, offset, projectId),
|
||||
promptOptions: {
|
||||
title: 'Load more branches?',
|
||||
renderListItems: branches => renderPageOfBranches(branches, options),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function queryBranchesForProjectAsync(
|
||||
limit: number,
|
||||
offset: number,
|
||||
projectId: string
|
||||
): Promise<UpdateBranchFragment[]> {
|
||||
return await BranchQuery.listBranchesAsync({
|
||||
appId: projectId,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
}
|
||||
|
||||
function renderPageOfBranches(
|
||||
currentPage: UpdateBranchFragment[],
|
||||
{ json }: PaginatedQueryOptions
|
||||
): void {
|
||||
if (json) {
|
||||
printJsonOnlyOutput(currentPage);
|
||||
} else {
|
||||
const table = new CliTable({
|
||||
head: ['Branch', ...UPDATE_COLUMNS],
|
||||
wordWrap: true,
|
||||
});
|
||||
|
||||
table.push(
|
||||
...currentPage.map(branch => [
|
||||
branch.name,
|
||||
formatUpdate(branch.updates[0]),
|
||||
branch.updates[0]?.runtimeVersion ?? 'N/A',
|
||||
branch.updates[0]?.group ?? 'N/A',
|
||||
getPlatformsForGroup({
|
||||
updates: branch.updates,
|
||||
group: branch.updates[0]?.group,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Branches:'));
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(table.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { truncateGitCommitMessage } from '../metadata';
|
||||
|
||||
describe(truncateGitCommitMessage, () => {
|
||||
it('returns undefined if no message was passed', () => {
|
||||
expect(truncateGitCommitMessage(undefined)).toBeUndefined();
|
||||
});
|
||||
it('returns commit message', () => {
|
||||
expect(truncateGitCommitMessage('a'.repeat(10))).toBe('a'.repeat(10));
|
||||
});
|
||||
|
||||
it('truncates long commit messages', () => {
|
||||
expect(truncateGitCommitMessage('a'.repeat(5000))).toBe(`${'a'.repeat(4093)}...`);
|
||||
});
|
||||
});
|
||||
@@ -89,7 +89,7 @@ describe(bumpVersionAsync, () => {
|
||||
expect(resolveConfigValue(buildGradle, 'versionName')).toBe('3.0.0');
|
||||
});
|
||||
|
||||
it('bumps expo.version and gradle versionCode when strategy = BumpStrategy.APP_VERSION', async () => {
|
||||
it('bumps expo.version and gradle versionCode when strategy = BumpStrategy.SHORT_VERSION', async () => {
|
||||
const nativeVersionName = '1.0.0'; // this should be overwritten by bumpVersionAsync
|
||||
const fakeExp = initBareWorkflowProject({ versionName: nativeVersionName });
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AppVersionSource } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -29,7 +28,6 @@ import { checkGoogleServicesFileAsync, checkNodeEnvVariable } from '../validate'
|
||||
import { transformJob } from './graphql';
|
||||
import { prepareJobAsync } from './prepareJob';
|
||||
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
||||
import { resolveRemoteVersionCodeAsync } from './version';
|
||||
|
||||
export async function createAndroidContextAsync(
|
||||
ctx: CommonContext<Platform.ANDROID>
|
||||
@@ -62,18 +60,8 @@ This means that it will most likely produce an AAB and you will not be able to i
|
||||
}
|
||||
|
||||
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
|
||||
const versionCodeOverride =
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? await resolveRemoteVersionCodeAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
projectId: ctx.projectId,
|
||||
exp: ctx.exp,
|
||||
applicationId,
|
||||
buildProfile,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
return { applicationId, gradleContext, versionCodeOverride };
|
||||
return { applicationId, gradleContext };
|
||||
}
|
||||
|
||||
export async function prepareAndroidBuildAsync(
|
||||
@@ -88,10 +76,7 @@ export async function prepareAndroidBuildAsync(
|
||||
await syncProjectConfigurationAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
localAutoIncrement:
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
buildProfile: ctx.buildProfile,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
|
||||
@@ -13,7 +13,6 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
secrets: job.secrets,
|
||||
builderEnvironment: job.builderEnvironment,
|
||||
cache: job.cache,
|
||||
version: job.version?.versionCode ? { versionCode: job.version.versionCode } : undefined,
|
||||
gradleCommand: job.gradleCommand,
|
||||
artifactPath: job.artifactPath,
|
||||
username: job.username,
|
||||
|
||||
@@ -75,11 +75,6 @@ export async function prepareJobAsync(
|
||||
artifactPath: buildProfile.artifactPath,
|
||||
buildType,
|
||||
username,
|
||||
...(ctx.android.versionCodeOverride && {
|
||||
version: {
|
||||
versionCode: ctx.android.versionCodeOverride,
|
||||
},
|
||||
}),
|
||||
experimental: {
|
||||
prebuildCommand: ctx.buildProfile.prebuildCommand,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { AndroidConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
|
||||
import { AndroidVersionAutoIncrement, BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
@@ -15,14 +15,15 @@ import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './ver
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
localAutoIncrement,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||
}): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
const { autoIncrement } = buildProfile;
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { AndroidConfig, Updates } from '@expo/config-plugins';
|
||||
import { AndroidConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import Log from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import {
|
||||
getAppBuildGradleAsync,
|
||||
parseGradleCommand,
|
||||
resolveConfigValue,
|
||||
} from '../../project/android/gradleUtils';
|
||||
import { getNextVersionCode } from '../../project/android/versions';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||
@@ -51,10 +46,9 @@ export async function bumpVersionAsync({
|
||||
|
||||
await bumpVersionInAppJsonAsync({ bumpStrategy, projectDir, exp });
|
||||
Log.log('Updated versions in app.json');
|
||||
await updateNativeVersionsAsync({
|
||||
await writeVersionsToBuildGradleAsync({
|
||||
projectDir,
|
||||
version: exp.version,
|
||||
versionCode: exp.android?.versionCode,
|
||||
exp,
|
||||
});
|
||||
Log.log('Synchronized versions with build gradle');
|
||||
}
|
||||
@@ -80,7 +74,7 @@ export async function bumpVersionInAppJsonAsync({
|
||||
await bumpAppVersionAsync({ appVersion, projectDir, exp });
|
||||
} else {
|
||||
const versionCode = AndroidConfig.Version.getVersionCode(exp);
|
||||
const bumpedVersionCode = getNextVersionCode(versionCode);
|
||||
const bumpedVersionCode = versionCode + 1;
|
||||
Log.log(
|
||||
`Bumping ${chalk.bold('expo.android.versionCode')} from ${chalk.bold(
|
||||
versionCode
|
||||
@@ -122,33 +116,21 @@ export async function maybeResolveVersionsAsync(
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateNativeVersionsAsync({
|
||||
async function writeVersionsToBuildGradleAsync({
|
||||
projectDir,
|
||||
version,
|
||||
versionCode,
|
||||
exp,
|
||||
}: {
|
||||
projectDir: string;
|
||||
version?: string;
|
||||
versionCode?: number;
|
||||
}): Promise<void> {
|
||||
exp: ExpoConfig;
|
||||
}): Promise<string> {
|
||||
const buildGradle = await readBuildGradleAsync(projectDir);
|
||||
if (!buildGradle) {
|
||||
throw new Error('This project is missing a build.gradle file.');
|
||||
}
|
||||
let updatedBuildGradle = buildGradle;
|
||||
if (version !== undefined) {
|
||||
updatedBuildGradle = updatedBuildGradle.replace(
|
||||
new RegExp(`versionName ".*"`),
|
||||
`versionName "${version}"`
|
||||
);
|
||||
}
|
||||
if (versionCode !== undefined) {
|
||||
updatedBuildGradle = updatedBuildGradle.replace(
|
||||
new RegExp(`versionCode.*`),
|
||||
`versionCode ${versionCode}`
|
||||
);
|
||||
}
|
||||
let updatedBuildGradle = AndroidConfig.Version.setVersionName(exp, buildGradle);
|
||||
updatedBuildGradle = AndroidConfig.Version.setVersionCode(exp, updatedBuildGradle);
|
||||
await writeBuildGradleAsync({ projectDir, buildGradle: updatedBuildGradle });
|
||||
return updatedBuildGradle;
|
||||
}
|
||||
|
||||
async function readBuildGradleAsync(projectDir: string): Promise<string | undefined> {
|
||||
@@ -169,87 +151,3 @@ async function writeBuildGradleAsync({
|
||||
const buildGradlePath = AndroidConfig.Paths.getAppBuildGradleFilePath(projectDir);
|
||||
await fs.writeFile(buildGradlePath, buildGradle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns buildNumber that will be used for the next build. If current build profile
|
||||
* has an 'autoIncrement' option set, it increments the version on server.
|
||||
*/
|
||||
export async function resolveRemoteVersionCodeAsync({
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationId,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationId: string;
|
||||
buildProfile: BuildProfile<Platform.ANDROID>;
|
||||
}): Promise<string> {
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
projectId,
|
||||
AppPlatform.Android,
|
||||
applicationId
|
||||
);
|
||||
|
||||
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile);
|
||||
let currentBuildVersion: string;
|
||||
if (remoteVersions?.buildVersion) {
|
||||
currentBuildVersion = remoteVersions.buildVersion;
|
||||
} else {
|
||||
if (localVersions.appBuildVersion) {
|
||||
Log.warn(
|
||||
'No remote versions are configured for this project, versionCode will be initialized based on the value from the local project.'
|
||||
);
|
||||
currentBuildVersion = localVersions.appBuildVersion;
|
||||
} else {
|
||||
Log.error(
|
||||
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
|
||||
);
|
||||
throw new Error('Remote versions are not configured.');
|
||||
}
|
||||
}
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
const spinner = ora(`Initializing the versionCode with ${currentBuildVersion}.`).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Android,
|
||||
applicationIdentifier: applicationId,
|
||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized the versionCode with ${currentBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(`Failed to initialize the versionCode with ${currentBuildVersion}.`);
|
||||
throw err;
|
||||
}
|
||||
return currentBuildVersion;
|
||||
} else {
|
||||
const nextBuildVersion = getNextVersionCode(currentBuildVersion);
|
||||
const spinner = ora(
|
||||
`Incrementing the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Android,
|
||||
applicationIdentifier: applicationId,
|
||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||
buildVersion: String(nextBuildVersion),
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Incremented the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(
|
||||
`Failed to increment the versionCode ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
return String(nextBuildVersion);
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
appPlatformEmojis,
|
||||
requestedPlatformDisplayNames,
|
||||
} from '../platform';
|
||||
import { uploadFileAtPathToS3Async } from '../uploads';
|
||||
import { uploadAsync } from '../uploads';
|
||||
import { formatBytes } from '../utils/files';
|
||||
import { createProgressTracker } from '../utils/progress';
|
||||
import { sleepAsync } from '../utils/promise';
|
||||
@@ -179,7 +179,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
const projectTarball = await makeProjectTarballAsync();
|
||||
projectTarballPath = projectTarball.path;
|
||||
|
||||
const { bucketKey } = await uploadFileAtPathToS3Async(
|
||||
const { bucketKey } = await uploadAsync(
|
||||
UploadSessionType.EasBuildProjectSources,
|
||||
projectTarball.path,
|
||||
createProgressTracker({
|
||||
@@ -352,7 +352,7 @@ async function handleSingleBuildProgressAsync(
|
||||
);
|
||||
}
|
||||
Log.newLine();
|
||||
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
|
||||
Log.log('Waiting in queue');
|
||||
queueProgressBar.start(
|
||||
build.initialQueuePosition + 1,
|
||||
build.initialQueuePosition - build.queuePosition + 1,
|
||||
@@ -385,12 +385,6 @@ async function handleSingleBuildProgressAsync(
|
||||
return { refetch: true };
|
||||
}
|
||||
|
||||
const priorityToQueueDisplayName: Record<BuildPriority, string> = {
|
||||
[BuildPriority.Normal]: 'queue',
|
||||
[BuildPriority.NormalPlus]: 'queue',
|
||||
[BuildPriority.High]: 'priority queue',
|
||||
};
|
||||
|
||||
const statusToDisplayName: Record<BuildStatus, string> = {
|
||||
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
|
||||
[BuildStatus.InQueue]: 'in queue',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { TrackingContext } from '../analytics/common';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
@@ -16,20 +17,18 @@ export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android'
|
||||
export interface AndroidBuildContext {
|
||||
applicationId: string;
|
||||
gradleContext?: GradleBuildContext;
|
||||
versionCodeOverride?: string;
|
||||
}
|
||||
|
||||
export interface IosBuildContext {
|
||||
bundleIdentifier: string;
|
||||
applicationTargetBuildSettings: XCBuildConfiguration['buildSettings'];
|
||||
applicationTarget: Target;
|
||||
targets: Target[];
|
||||
xcodeBuildContext: XcodeBuildContext;
|
||||
buildNumberOverride?: string;
|
||||
}
|
||||
|
||||
export interface BuildContext<T extends Platform> {
|
||||
accountName: string;
|
||||
easJsonCliConfig: EasJson['cli'];
|
||||
buildProfile: BuildProfile<T>;
|
||||
buildProfileName: string;
|
||||
resourceClass: BuildResourceClass;
|
||||
@@ -38,13 +37,10 @@ export interface BuildContext<T extends Platform> {
|
||||
exp: ExpoConfig;
|
||||
localBuildOptions: LocalBuildOptions;
|
||||
nonInteractive: boolean;
|
||||
noWait: boolean;
|
||||
runFromCI: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
message?: string;
|
||||
trackingCtx: TrackingContext;
|
||||
user: Actor;
|
||||
workflow: Workflow;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import getenv from 'getenv';
|
||||
import resolveFrom from 'resolve-from';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -22,27 +21,21 @@ import { LocalBuildOptions } from './local';
|
||||
export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfileName,
|
||||
buildProfile,
|
||||
easJsonCliConfig,
|
||||
clearCache = false,
|
||||
localBuildOptions,
|
||||
nonInteractive,
|
||||
noWait,
|
||||
nonInteractive = false,
|
||||
platform,
|
||||
projectDir,
|
||||
resourceClass,
|
||||
message,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
easJsonCliConfig: EasJson['cli'];
|
||||
clearCache: boolean;
|
||||
localBuildOptions: LocalBuildOptions;
|
||||
nonInteractive: boolean;
|
||||
noWait: boolean;
|
||||
platform: T;
|
||||
projectDir: string;
|
||||
resourceClass: BuildResourceClass;
|
||||
message?: string;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const exp = getExpoConfig(projectDir, { env: buildProfile.env });
|
||||
|
||||
@@ -52,7 +45,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
const projectId = await getProjectIdAsync(exp, { env: buildProfile.env });
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||
const accountId = findAccountByName(user.accounts, accountName)?.id;
|
||||
const runFromCI = getenv.boolish('CI', false);
|
||||
|
||||
const credentialsCtx = new CredentialsContext({
|
||||
exp,
|
||||
@@ -74,8 +66,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
project_id: projectId,
|
||||
project_type: workflow,
|
||||
...devClientProperties,
|
||||
no_wait: noWait,
|
||||
run_from_ci: runFromCI,
|
||||
};
|
||||
Analytics.logEvent(BuildEvent.BUILD_COMMAND, trackingCtx);
|
||||
|
||||
@@ -84,13 +74,11 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
resourceClass,
|
||||
easJsonCliConfig,
|
||||
clearCache,
|
||||
credentialsCtx,
|
||||
exp,
|
||||
localBuildOptions,
|
||||
nonInteractive,
|
||||
noWait,
|
||||
platform,
|
||||
projectDir,
|
||||
projectId,
|
||||
@@ -98,8 +86,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
trackingCtx,
|
||||
user,
|
||||
workflow,
|
||||
message,
|
||||
runFromCI,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { IOSConfig } from '@expo/config-plugins';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { readPlistAsync } from '../../../utils/plist';
|
||||
import {
|
||||
@@ -18,12 +17,6 @@ import {
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
|
||||
IOSConfig.Target,
|
||||
'getXCBuildConfigurationFromPbxproj'
|
||||
);
|
||||
const getPbxproj = jest.spyOn(IOSConfig.XcodeUtils, 'getPbxproj');
|
||||
|
||||
afterAll(async () => {
|
||||
// do not remove the following line
|
||||
// this fixes a weird error with tempy in @expo/image-utils
|
||||
@@ -58,34 +51,14 @@ describe(evaluateTemplateString, () => {
|
||||
|
||||
// bare workflow
|
||||
describe(bumpVersionAsync, () => {
|
||||
beforeEach(() => {
|
||||
getPbxproj.mockImplementation(() => ({} as any));
|
||||
});
|
||||
afterEach(() => {
|
||||
getXCBuildConfigurationFromPbxproj.mockReset();
|
||||
getPbxproj.mockReset();
|
||||
});
|
||||
it('bumps expo.ios.buildNumber and CFBundleVersion when strategy = BumpStrategy.BUILD_NUMBER', async () => {
|
||||
const fakeExp = initBareWorkflowProject();
|
||||
getXCBuildConfigurationFromPbxproj.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
buildSettings: {
|
||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||
},
|
||||
} as XCBuildConfiguration)
|
||||
);
|
||||
|
||||
await bumpVersionAsync({
|
||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||
projectDir: '/repo',
|
||||
exp: fakeExp,
|
||||
targets: [
|
||||
{
|
||||
targetName: 'example',
|
||||
bundleIdentifier: 'example.com',
|
||||
entitlements: {},
|
||||
},
|
||||
],
|
||||
buildSettings: {},
|
||||
});
|
||||
|
||||
const appJSON = await fs.readJSON('/repo/app.json');
|
||||
@@ -103,25 +76,13 @@ describe(bumpVersionAsync, () => {
|
||||
it('bumps expo.ios.buildNumber and CFBundleVersion for non default Info.plist location', async () => {
|
||||
const fakeExp = initBareWorkflowProject({ infoPlistName: 'Info2.plist' });
|
||||
|
||||
getXCBuildConfigurationFromPbxproj.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
buildSettings: {
|
||||
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
|
||||
},
|
||||
} as XCBuildConfiguration)
|
||||
);
|
||||
await bumpVersionAsync({
|
||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||
projectDir: '/repo',
|
||||
exp: fakeExp,
|
||||
targets: [
|
||||
{
|
||||
targetName: 'example',
|
||||
bundleIdentifier: 'example.com',
|
||||
entitlements: {},
|
||||
},
|
||||
],
|
||||
buildSettings: {
|
||||
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
|
||||
},
|
||||
});
|
||||
|
||||
const appJSON = await fs.readJSON('/repo/app.json');
|
||||
@@ -136,27 +97,14 @@ describe(bumpVersionAsync, () => {
|
||||
expect(infoPlist['CFBundleVersion']).toBe('2');
|
||||
});
|
||||
|
||||
it('bumps expo.version and CFBundleShortVersionString when strategy = BumpStrategy.APP_VERSION', async () => {
|
||||
it('bumps expo.version and CFBundleShortVersionString when strategy = BumpStrategy.SHORT_VERSION', async () => {
|
||||
const fakeExp = initBareWorkflowProject();
|
||||
getXCBuildConfigurationFromPbxproj.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
buildSettings: {
|
||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||
},
|
||||
} as XCBuildConfiguration)
|
||||
);
|
||||
|
||||
await bumpVersionAsync({
|
||||
bumpStrategy: BumpStrategy.APP_VERSION,
|
||||
projectDir: '/repo',
|
||||
exp: fakeExp,
|
||||
targets: [
|
||||
{
|
||||
targetName: 'example',
|
||||
bundleIdentifier: 'example.com',
|
||||
entitlements: {},
|
||||
},
|
||||
],
|
||||
buildSettings: {},
|
||||
});
|
||||
|
||||
const appJSON = await fs.readJSON('/repo/app.json');
|
||||
@@ -173,25 +121,12 @@ describe(bumpVersionAsync, () => {
|
||||
|
||||
it('does not bump any version when strategy = BumpStrategy.NOOP', async () => {
|
||||
const fakeExp = initBareWorkflowProject();
|
||||
getXCBuildConfigurationFromPbxproj.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
buildSettings: {
|
||||
INFOPLIST_FILE: 'myproject/Info.plist',
|
||||
},
|
||||
} as XCBuildConfiguration)
|
||||
);
|
||||
|
||||
await bumpVersionAsync({
|
||||
bumpStrategy: BumpStrategy.NOOP,
|
||||
projectDir: '/repo',
|
||||
exp: fakeExp,
|
||||
targets: [
|
||||
{
|
||||
targetName: 'example',
|
||||
bundleIdentifier: 'example.com',
|
||||
entitlements: {},
|
||||
},
|
||||
],
|
||||
buildSettings: {},
|
||||
});
|
||||
|
||||
const appJSON = await fs.readJSON('/repo/app.json');
|
||||
@@ -353,9 +288,9 @@ describe(getInfoPlistPath, () => {
|
||||
|
||||
function initBareWorkflowProject({
|
||||
appVersion = '1.0.0',
|
||||
buildNumber = '1',
|
||||
version = '1',
|
||||
infoPlistName = 'Info.plist',
|
||||
}: { appVersion?: string; buildNumber?: string; infoPlistName?: string } = {}): ExpoConfig {
|
||||
}: { appVersion?: string; version?: string; infoPlistName?: string } = {}): ExpoConfig {
|
||||
const fakeExp: ExpoConfig = {
|
||||
name: 'myproject',
|
||||
slug: 'myproject',
|
||||
@@ -376,7 +311,7 @@ function initBareWorkflowProject({
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${appVersion}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${buildNumber}</string>
|
||||
<string>${version}</string>
|
||||
</dict>
|
||||
</plist>`,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AppVersionSource } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { IosCredentials } from '../../credentials/ios/types';
|
||||
import { IosCredentials, Target } from '../../credentials/ios/types';
|
||||
import { BuildParamsInput } from '../../graphql/generated';
|
||||
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
|
||||
import { ensureBundleIdentifierIsDefinedForManagedProjectAsync } from '../../project/ios/bundleIdentifier';
|
||||
@@ -15,7 +16,6 @@ import { ensureIosCredentialsAsync } from './credentials';
|
||||
import { transformJob } from './graphql';
|
||||
import { prepareJobAsync } from './prepareJob';
|
||||
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
|
||||
import { resolveRemoteBuildNumberAsync } from './version';
|
||||
|
||||
export async function createIosContextAsync(
|
||||
ctx: CommonContext<Platform.IOS>
|
||||
@@ -44,22 +44,14 @@ export async function createIosContextAsync(
|
||||
env: buildProfile.env,
|
||||
});
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
const buildNumberOverride =
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? await resolveRemoteBuildNumberAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
projectId: ctx.projectId,
|
||||
exp: ctx.exp,
|
||||
applicationTarget,
|
||||
buildProfile,
|
||||
})
|
||||
: undefined;
|
||||
const applicationTargetBuildSettings = resolveBuildSettings(ctx, applicationTarget);
|
||||
|
||||
return {
|
||||
bundleIdentifier: applicationTarget.bundleIdentifier,
|
||||
applicationTarget,
|
||||
applicationTargetBuildSettings,
|
||||
targets,
|
||||
xcodeBuildContext,
|
||||
buildNumberOverride,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,11 +67,8 @@ export async function prepareIosBuildAsync(
|
||||
await syncProjectConfigurationAsync({
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
targets: ctx.ios.targets,
|
||||
localAutoIncrement:
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
buildProfile: ctx.buildProfile,
|
||||
buildSettings: ctx.ios.applicationTargetBuildSettings,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
@@ -108,3 +97,18 @@ export async function prepareIosBuildAsync(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function resolveBuildSettings(
|
||||
ctx: CommonContext<Platform.IOS>,
|
||||
applicationTarget: Target
|
||||
): XCBuildConfiguration['buildSettings'] {
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
return {};
|
||||
}
|
||||
const project = IOSConfig.XcodeUtils.getPbxproj(ctx.projectDir);
|
||||
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
|
||||
targetName: applicationTarget.targetName,
|
||||
buildConfiguration: applicationTarget.buildConfiguration,
|
||||
});
|
||||
return xcBuildConfiguration?.buildSettings ?? {};
|
||||
}
|
||||
@@ -14,7 +14,6 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
secrets: transformIosSecrets(job.secrets),
|
||||
builderEnvironment: job.builderEnvironment,
|
||||
cache: job.cache,
|
||||
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
|
||||
scheme: job.scheme,
|
||||
buildConfiguration: job.buildConfiguration,
|
||||
artifactPath: job.artifactPath,
|
||||
|
||||
@@ -66,11 +66,6 @@ export async function prepareJobAsync(
|
||||
buildConfiguration: ctx.buildProfile.buildConfiguration,
|
||||
artifactPath: ctx.buildProfile.artifactPath,
|
||||
username,
|
||||
...(ctx.ios.buildNumberOverride && {
|
||||
version: {
|
||||
buildNumber: ctx.ios.buildNumberOverride,
|
||||
},
|
||||
}),
|
||||
experimental: {
|
||||
prebuildCommand: ctx.buildProfile.prebuildCommand,
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
import { BuildProfile, IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||
@@ -11,22 +11,23 @@ import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './ver
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
localAutoIncrement,
|
||||
buildProfile,
|
||||
buildSettings,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
targets: Target[];
|
||||
localAutoIncrement?: IosVersionAutoIncrement;
|
||||
buildProfile: BuildProfile<Platform.IOS>;
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
const { autoIncrement } = buildProfile;
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(autoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
await syncUpdatesConfigurationAsync(projectDir, exp);
|
||||
}
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, buildSettings });
|
||||
} else {
|
||||
await bumpVersionInAppJsonAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
|
||||
}
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { IOSConfig, Updates } from '@expo/config-plugins';
|
||||
import { ExpoConfig, getConfigFilePaths } from '@expo/config';
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import path from 'path';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import Log from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { findApplicationTarget } from '../../project/ios/target';
|
||||
import { getNextBuildNumber, isValidBuildNumber } from '../../project/ios/versions';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
|
||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||
import { bumpAppVersionAsync } from '../utils/version';
|
||||
|
||||
export enum BumpStrategy {
|
||||
APP_VERSION,
|
||||
@@ -31,25 +22,21 @@ export async function bumpVersionAsync({
|
||||
bumpStrategy,
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
buildSettings,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
bumpStrategy: BumpStrategy;
|
||||
targets: Target[];
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}): Promise<void> {
|
||||
if (bumpStrategy === BumpStrategy.NOOP) {
|
||||
return;
|
||||
}
|
||||
ensureStaticConfigExists(projectDir);
|
||||
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
||||
await bumpVersionInAppJsonAsync({ bumpStrategy, projectDir, exp });
|
||||
Log.log('Updated versions in app.json');
|
||||
await updateNativeVersionsAsync({
|
||||
projectDir,
|
||||
version: exp.version,
|
||||
buildNumber: exp.ios?.buildNumber,
|
||||
targets,
|
||||
});
|
||||
await writeVersionsToInfoPlistAsync({ projectDir, exp, infoPlist, buildSettings });
|
||||
Log.log('Synchronized versions with Info.plist');
|
||||
}
|
||||
|
||||
@@ -72,8 +59,10 @@ export async function bumpVersionInAppJsonAsync({
|
||||
await bumpAppVersionAsync({ appVersion, projectDir, exp });
|
||||
} else {
|
||||
const buildNumber = IOSConfig.Version.getBuildNumber(exp);
|
||||
if (isValidBuildNumber(buildNumber)) {
|
||||
const bumpedBuildNumber = getNextBuildNumber(buildNumber);
|
||||
if (buildNumber.match(/^\d+(\.\d+)*$/)) {
|
||||
const comps = buildNumber.split('.');
|
||||
comps[comps.length - 1] = String(Number(comps[comps.length - 1]) + 1);
|
||||
const bumpedBuildNumber = comps.join('.');
|
||||
Log.log(
|
||||
`Bumping ${chalk.bold('expo.ios.buildNumber')} from ${chalk.bold(
|
||||
buildNumber
|
||||
@@ -132,21 +121,12 @@ export async function readBuildNumberAsync(
|
||||
export async function maybeResolveVersionsAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
targets: Target[]
|
||||
buildSettings: XCBuildConfiguration['buildSettings']
|
||||
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
try {
|
||||
return {
|
||||
appBuildVersion: await readBuildNumberAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
applicationTarget.buildSettings ?? {}
|
||||
),
|
||||
appVersion: await readShortVersionAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
applicationTarget.buildSettings ?? {}
|
||||
),
|
||||
appBuildVersion: await readBuildNumberAsync(projectDir, exp, buildSettings),
|
||||
appVersion: await readShortVersionAsync(projectDir, exp, buildSettings),
|
||||
};
|
||||
} catch (err: any) {
|
||||
Log.warn('Failed to read app versions.');
|
||||
@@ -157,6 +137,23 @@ export async function maybeResolveVersionsAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function writeVersionsToInfoPlistAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
infoPlist,
|
||||
buildSettings,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
infoPlist: IOSConfig.InfoPlist;
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}): Promise<IOSConfig.InfoPlist> {
|
||||
let updatedInfoPlist = IOSConfig.Version.setVersion(exp, infoPlist);
|
||||
updatedInfoPlist = IOSConfig.Version.setBuildNumber(exp, updatedInfoPlist);
|
||||
await writeInfoPlistAsync({ projectDir, infoPlist: updatedInfoPlist, buildSettings });
|
||||
return updatedInfoPlist;
|
||||
}
|
||||
|
||||
export function getInfoPlistPath(
|
||||
projectDir: string,
|
||||
buildSettings: XCBuildConfiguration['buildSettings']
|
||||
@@ -184,55 +181,24 @@ async function readInfoPlistAsync(
|
||||
return ((await readPlistAsync(infoPlistPath)) ?? {}) as IOSConfig.InfoPlist;
|
||||
}
|
||||
|
||||
export async function updateNativeVersionsAsync({
|
||||
async function writeInfoPlistAsync({
|
||||
projectDir,
|
||||
version,
|
||||
buildNumber,
|
||||
targets,
|
||||
infoPlist,
|
||||
buildSettings,
|
||||
}: {
|
||||
projectDir: string;
|
||||
version?: string;
|
||||
buildNumber?: string;
|
||||
targets: Target[];
|
||||
infoPlist: IOSConfig.InfoPlist;
|
||||
buildSettings: XCBuildConfiguration['buildSettings'];
|
||||
}): Promise<void> {
|
||||
const project = IOSConfig.XcodeUtils.getPbxproj(projectDir);
|
||||
const iosDir = path.join(projectDir, 'ios');
|
||||
|
||||
const infoPlistFiles: string[] = [];
|
||||
for (const target of targets) {
|
||||
const { targetName, buildConfiguration } = target;
|
||||
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
|
||||
targetName,
|
||||
buildConfiguration,
|
||||
});
|
||||
const infoPlist = xcBuildConfiguration?.buildSettings?.INFOPLIST_FILE;
|
||||
if (infoPlist) {
|
||||
const evaluatedInfoPlistPath = trimQuotes(
|
||||
evaluateTemplateString(infoPlist, {
|
||||
SRCROOT: iosDir,
|
||||
})
|
||||
);
|
||||
const absolutePath = path.isAbsolute(evaluatedInfoPlistPath)
|
||||
? evaluatedInfoPlistPath
|
||||
: path.join(iosDir, evaluatedInfoPlistPath);
|
||||
infoPlistFiles.push(path.normalize(absolutePath));
|
||||
}
|
||||
}
|
||||
const uniqueInfoPlistPaths = uniqBy(infoPlistFiles, i => i);
|
||||
for (const infoPlistPath of uniqueInfoPlistPaths) {
|
||||
const infoPlist = (await readPlistAsync(infoPlistPath)) as IOSConfig.InfoPlist;
|
||||
if (buildNumber) {
|
||||
infoPlist.CFBundleVersion = buildNumber;
|
||||
}
|
||||
if (version) {
|
||||
infoPlist.CFBundleShortVersionString = version;
|
||||
}
|
||||
await writePlistAsync(infoPlistPath, infoPlist);
|
||||
}
|
||||
const infoPlistPath = getInfoPlistPath(projectDir, buildSettings);
|
||||
await writePlistAsync(infoPlistPath, infoPlist);
|
||||
}
|
||||
|
||||
function trimQuotes(s: string): string {
|
||||
return s?.startsWith('"') && s.endsWith('"') ? s.slice(1, -1) : s;
|
||||
function ensureStaticConfigExists(projectDir: string): void {
|
||||
const paths = getConfigFilePaths(projectDir);
|
||||
if (!paths.staticConfigPath) {
|
||||
throw new Error('autoIncrement option is not supported when using app.config.js');
|
||||
}
|
||||
}
|
||||
|
||||
export function evaluateTemplateString(
|
||||
@@ -250,96 +216,3 @@ export function evaluateTemplateString(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns buildNumber that will be used for the next build. If current build profile
|
||||
* has an 'autoIncrement' option set, it increments the version on server.
|
||||
*/
|
||||
export async function resolveRemoteBuildNumberAsync({
|
||||
projectDir,
|
||||
projectId,
|
||||
exp,
|
||||
applicationTarget,
|
||||
buildProfile,
|
||||
}: {
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
exp: ExpoConfig;
|
||||
applicationTarget: Target;
|
||||
buildProfile: BuildProfile<Platform.IOS>;
|
||||
}): Promise<string> {
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
projectId,
|
||||
AppPlatform.Ios,
|
||||
applicationTarget.bundleIdentifier
|
||||
);
|
||||
|
||||
const localBuildNumber = await readBuildNumberAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
applicationTarget.buildSettings ?? {}
|
||||
);
|
||||
const localShortVersion = await readShortVersionAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
applicationTarget.buildSettings ?? {}
|
||||
);
|
||||
let currentBuildVersion: string;
|
||||
if (remoteVersions?.buildVersion) {
|
||||
currentBuildVersion = remoteVersions.buildVersion;
|
||||
} else {
|
||||
if (localBuildNumber) {
|
||||
Log.warn(
|
||||
'No remote versions are configured for this project, buildNumber will be initialized based on the value from the local project.'
|
||||
);
|
||||
currentBuildVersion = localBuildNumber;
|
||||
} else {
|
||||
Log.error(
|
||||
`Remote versions are not configured and EAS CLI was not able to read the current version from your project. Use "eas build:version:set" to initialize remote versions.`
|
||||
);
|
||||
throw new Error('Remote versions are not configured.');
|
||||
}
|
||||
}
|
||||
if (!buildProfile.autoIncrement && remoteVersions?.buildVersion) {
|
||||
return currentBuildVersion;
|
||||
} else if (!buildProfile.autoIncrement && !remoteVersions?.buildVersion) {
|
||||
const spinner = ora(`Initializing the buildNumber with ${currentBuildVersion}.`).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Ios,
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
storeVersion: localShortVersion ?? '1.0.0',
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized the buildNumber with ${currentBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(`Failed to initialize the buildNumber with ${currentBuildVersion}.`);
|
||||
throw err;
|
||||
}
|
||||
return currentBuildVersion;
|
||||
} else {
|
||||
const nextBuildVersion = getNextBuildNumber(currentBuildVersion);
|
||||
const spinner = ora(
|
||||
`Incrementing buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
).start();
|
||||
try {
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
platform: AppPlatform.Ios,
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
storeVersion: localShortVersion ?? '1.0.0',
|
||||
buildVersion: nextBuildVersion,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Incremented buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`);
|
||||
} catch (err) {
|
||||
spinner.fail(
|
||||
`Failed to increment buildNumber ${currentBuildVersion} -> ${nextBuildVersion}.`
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
return nextBuildVersion;
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,12 @@ import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios
|
||||
export async function collectMetadataAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
): Promise<Metadata> {
|
||||
const vcsClient = getVcsClient();
|
||||
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
|
||||
const distribution =
|
||||
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
|
||||
? 'simulator'
|
||||
: ctx.buildProfile.distribution) ?? 'store';
|
||||
const metadata: Metadata = {
|
||||
const metadata = {
|
||||
trackingContext: ctx.trackingCtx,
|
||||
...(await maybeResolveVersionsAsync(ctx)),
|
||||
cliVersion: easCliVersion,
|
||||
@@ -44,20 +43,14 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
appName: ctx.exp.name,
|
||||
appIdentifier: resolveAppIdentifier(ctx),
|
||||
buildProfile: ctx.buildProfileName,
|
||||
gitCommitHash: await vcsClient.getCommitHashAsync(),
|
||||
gitCommitMessage: truncateGitCommitMessage(
|
||||
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
|
||||
),
|
||||
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
|
||||
gitCommitHash: await getVcsClient().getCommitHashAsync(),
|
||||
isGitWorkingTreeDirty: await getVcsClient().hasUncommittedChangesAsync(),
|
||||
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
|
||||
message: ctx.message,
|
||||
...(ctx.platform === Platform.IOS && {
|
||||
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
|
||||
ctx as BuildContext<Platform.IOS>
|
||||
),
|
||||
}),
|
||||
runWithNoWaitFlag: ctx.noWait,
|
||||
runFromCI: ctx.runFromCI,
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
}
|
||||
@@ -67,32 +60,14 @@ async function maybeResolveVersionsAsync<T extends Platform>(
|
||||
): Promise<{ appBuildVersion?: string; appVersion?: string }> {
|
||||
if (ctx.platform === Platform.IOS) {
|
||||
const iosContext = ctx as BuildContext<Platform.IOS>;
|
||||
const resolvedVersion = await maybeResolveIosVersionsAsync(
|
||||
return await maybeResolveIosVersionsAsync(
|
||||
ctx.projectDir,
|
||||
ctx.exp,
|
||||
iosContext.ios.targets
|
||||
iosContext.ios.applicationTargetBuildSettings
|
||||
);
|
||||
if (iosContext.ios.buildNumberOverride) {
|
||||
return {
|
||||
...resolvedVersion,
|
||||
appBuildVersion: iosContext.ios.buildNumberOverride,
|
||||
};
|
||||
}
|
||||
return resolvedVersion;
|
||||
} else if (ctx.platform === Platform.ANDROID) {
|
||||
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
|
||||
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
|
||||
ctx.projectDir,
|
||||
ctx.exp,
|
||||
androidCtx.buildProfile
|
||||
);
|
||||
if (androidCtx.android.versionCodeOverride) {
|
||||
return {
|
||||
...resolvedVersion,
|
||||
appBuildVersion: androidCtx.android.versionCodeOverride,
|
||||
};
|
||||
}
|
||||
return resolvedVersion;
|
||||
return await maybeResolveAndroidVersionsAsync(ctx.projectDir, ctx.exp, androidCtx.buildProfile);
|
||||
} else {
|
||||
throw new Error(`Unsupported platform ${ctx.platform}`);
|
||||
}
|
||||
@@ -174,13 +149,3 @@ function resolveIosEnterpriseProvisioning(
|
||||
): IosEnterpriseProvisioning | undefined {
|
||||
return ctx.buildProfile.enterpriseProvisioning;
|
||||
}
|
||||
|
||||
export function truncateGitCommitMessage(
|
||||
msg: string | undefined,
|
||||
maxLength: number = 4096
|
||||
): string | undefined {
|
||||
if (msg === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return msg.length > maxLength ? `${msg.substring(0, maxLength - 3)}...` : msg;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson, EasJsonReader, SubmitProfile } from '@expo/eas-json';
|
||||
import { BuildProfile, EasJsonReader, SubmitProfile } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -20,10 +20,6 @@ import {
|
||||
} from '../platform';
|
||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||
import {
|
||||
validateAppConfigForRemoteVersionSource,
|
||||
validateBuildProfileVersionSettings,
|
||||
} from '../project/remoteVersionSource';
|
||||
import { createSubmissionContextAsync } from '../submit/context';
|
||||
import {
|
||||
submitAsync,
|
||||
@@ -59,7 +55,6 @@ export interface BuildFlags {
|
||||
submitProfile?: string;
|
||||
localBuildOptions: LocalBuildOptions;
|
||||
userInputResourceClass?: UserInputResourceClass;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const platformToGraphQLResourceClassMapping: Record<
|
||||
@@ -85,7 +80,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
const easJsonCliConfig: EasJson['cli'] = (await easJsonReader.getCliConfigAsync()) ?? {};
|
||||
|
||||
const platforms = toPlatforms(flags.requestedPlatform);
|
||||
const buildProfiles = await getProfilesAsync({
|
||||
@@ -101,10 +95,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
buildProfiles,
|
||||
});
|
||||
|
||||
for (const buildProfile of buildProfiles) {
|
||||
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
||||
}
|
||||
|
||||
const startedBuilds: {
|
||||
build: BuildFragment;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
@@ -121,7 +111,6 @@ export async function runBuildAndSubmitAsync(projectDir: string, flags: BuildFla
|
||||
platformToGraphQLResourceClassMapping[buildProfile.platform][
|
||||
flags.userInputResourceClass ?? UserInputResourceClass.DEFAULT
|
||||
],
|
||||
easJsonCliConfig,
|
||||
});
|
||||
if (maybeBuild) {
|
||||
startedBuilds.push({ build: maybeBuild, buildProfile });
|
||||
@@ -199,14 +188,12 @@ async function prepareAndStartBuildAsync({
|
||||
moreBuilds,
|
||||
buildProfile,
|
||||
resourceClass,
|
||||
easJsonCliConfig,
|
||||
}: {
|
||||
projectDir: string;
|
||||
flags: BuildFlags;
|
||||
moreBuilds: boolean;
|
||||
buildProfile: ProfileData<'build'>;
|
||||
resourceClass: BuildResourceClass;
|
||||
easJsonCliConfig: EasJson['cli'];
|
||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||
const buildCtx = await createBuildContextAsync({
|
||||
buildProfileName: buildProfile.profileName,
|
||||
@@ -214,12 +201,9 @@ async function prepareAndStartBuildAsync({
|
||||
clearCache: flags.clearCache,
|
||||
buildProfile: buildProfile.profile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
noWait: !flags.wait,
|
||||
platform: buildProfile.platform,
|
||||
projectDir,
|
||||
localBuildOptions: flags.localBuildOptions,
|
||||
easJsonCliConfig,
|
||||
message: flags.message,
|
||||
});
|
||||
|
||||
if (moreBuilds) {
|
||||
@@ -232,7 +216,6 @@ async function prepareAndStartBuildAsync({
|
||||
);
|
||||
}
|
||||
|
||||
validateAppConfigForRemoteVersionSource(buildCtx.exp, buildProfile.platform);
|
||||
if (buildCtx.workflow === Workflow.MANAGED) {
|
||||
if (!sdkVersionChecked) {
|
||||
await checkExpoSdkIsSupportedAsync(buildCtx);
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
export const getPaginatedQueryOptions = (
|
||||
flags: Record<keyof typeof EasPaginatedQueryFlags, any>
|
||||
): PaginatedQueryOptions => {
|
||||
return {
|
||||
json: flags.json ?? false,
|
||||
offset: flags.offset ?? 0,
|
||||
nonInteractive: flags['non-interactive'] ?? false,
|
||||
...('limit' in flags && { limit: flags.limit }),
|
||||
};
|
||||
};
|
||||
|
||||
const parseFlagInputStringAsInteger = (
|
||||
input: string,
|
||||
flagName: string,
|
||||
lowerLimit: number,
|
||||
upperLimit: number
|
||||
): number => {
|
||||
const inputAsNumber = Number(input);
|
||||
if (isNaN(inputAsNumber)) {
|
||||
throw new Error(`Unable to parse ${input} as a number`);
|
||||
}
|
||||
if (inputAsNumber < lowerLimit || inputAsNumber > upperLimit) {
|
||||
throw new Error(`--${flagName} must be between ${lowerLimit} and ${upperLimit}`);
|
||||
}
|
||||
return inputAsNumber;
|
||||
};
|
||||
|
||||
export const EasPaginatedQueryFlags = {
|
||||
offset: Flags.integer({
|
||||
description: 'Start queries from specified index. Use for paginating results. Defaults to 0.',
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
parse: async input =>
|
||||
parseFlagInputStringAsInteger(input, 'offset', 0, Number.MAX_SAFE_INTEGER),
|
||||
}),
|
||||
limit: Flags.integer({
|
||||
description:
|
||||
'The number of query items to list at once. The default value is 50 (the maximum is 100). Using a lower value may help increase command speed.',
|
||||
// eslint-disable-next-line async-protect/async-suffix
|
||||
parse: async input => parseFlagInputStringAsInteger(input, 'limit', 1, 100),
|
||||
}),
|
||||
json: Flags.boolean({
|
||||
description: 'Enable JSON output, non-JSON messages will be printed to stderr.',
|
||||
}),
|
||||
'non-interactive': Flags.boolean({
|
||||
description: 'Run the command in non-interactive mode.',
|
||||
}),
|
||||
};
|
||||
|
||||
// options required to control a paginated query
|
||||
// from user input
|
||||
export type PaginatedQueryOptions = {
|
||||
nonInteractive: boolean;
|
||||
json: boolean;
|
||||
limit?: number;
|
||||
offset: number;
|
||||
};
|
||||
@@ -1,28 +1,106 @@
|
||||
import { listAndRenderPaginatedBranchesAsync } from '../../branch/queries';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import CliTable from 'cli-table3';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
BranchesByAppQuery,
|
||||
BranchesByAppQueryVariables,
|
||||
UpdateBranchFragment,
|
||||
} from '../../graphql/generated';
|
||||
import { UpdateBranchFragmentNode } from '../../graphql/types/UpdateBranch';
|
||||
import Log from '../../log';
|
||||
import { getExpoConfig } from '../../project/expoConfig';
|
||||
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
const BRANCHES_LIMIT = 10_000;
|
||||
|
||||
export async function listBranchesAsync({
|
||||
projectId,
|
||||
}: {
|
||||
projectId: string;
|
||||
}): Promise<UpdateBranchFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<BranchesByAppQuery, BranchesByAppQueryVariables>(
|
||||
gql`
|
||||
query BranchesByAppQuery($appId: String!, $limit: Int!) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
updateBranches(offset: 0, limit: $limit) {
|
||||
id
|
||||
...UpdateBranchFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateBranchFragmentNode)}
|
||||
`,
|
||||
{
|
||||
appId: projectId,
|
||||
limit: BRANCHES_LIMIT,
|
||||
},
|
||||
{ additionalTypenames: ['UpdateBranch'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data?.app?.byId.updateBranches ?? [];
|
||||
}
|
||||
|
||||
export default class BranchList extends EasCommand {
|
||||
static description = 'list all branches';
|
||||
|
||||
static flags = {
|
||||
...EasPaginatedQueryFlags,
|
||||
json: Flags.boolean({
|
||||
description: 'return output as JSON',
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BranchList);
|
||||
const options = getPaginatedQueryOptions(flags);
|
||||
|
||||
if (options.json) {
|
||||
if (flags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const projectDir = await findProjectRootAsync();
|
||||
const exp = getExpoConfig(projectDir);
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
await listAndRenderPaginatedBranchesAsync(projectId, options);
|
||||
const branches = await listBranchesAsync({ projectId });
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(branches);
|
||||
} else {
|
||||
const table = new CliTable({
|
||||
head: ['Branch', ...UPDATE_COLUMNS],
|
||||
wordWrap: true,
|
||||
});
|
||||
|
||||
table.push(
|
||||
...branches.map(branch => [
|
||||
branch.name,
|
||||
formatUpdate(branch.updates[0]),
|
||||
branch.updates[0]?.runtimeVersion ?? 'N/A',
|
||||
branch.updates[0]?.group ?? 'N/A',
|
||||
getPlatformsForGroup({
|
||||
updates: branch.updates,
|
||||
group: branch.updates[0]?.group,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Branches with their most recent update group:'));
|
||||
Log.log(table.toString());
|
||||
if (branches.length >= BRANCHES_LIMIT) {
|
||||
Log.warn(`Showing first ${BRANCHES_LIMIT} branches, some results might be omitted.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
import { selectBranchFromPaginatedQueryAsync } from '../../branch/queries';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import Table from 'cli-table3';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasPaginatedQueryFlags, getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
|
||||
import Log from '../../log';
|
||||
import { getExpoConfig } from '../../project/expoConfig';
|
||||
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
|
||||
import { listAndRenderUpdatesOnBranchByNameAsync } from '../../update/queries';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
|
||||
import groupBy from '../../utils/expodash/groupBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
export default class BranchView extends EasCommand {
|
||||
static description = 'view a branch';
|
||||
@@ -18,17 +25,18 @@ export default class BranchView extends EasCommand {
|
||||
];
|
||||
|
||||
static flags = {
|
||||
...EasPaginatedQueryFlags,
|
||||
json: Flags.boolean({
|
||||
description: `return a json with the branch's ID name and recent update groups.`,
|
||||
default: false,
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
let {
|
||||
args: { name: branchName },
|
||||
flags,
|
||||
args: { name },
|
||||
flags: { json: jsonFlag },
|
||||
} = await this.parse(BranchView);
|
||||
const options = getPaginatedQueryOptions(flags);
|
||||
|
||||
if (options.json) {
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
@@ -36,20 +44,63 @@ export default class BranchView extends EasCommand {
|
||||
const exp = getExpoConfig(projectDir);
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
|
||||
// provide help to a user if they ran the command with missing args
|
||||
if (!branchName) {
|
||||
if (options.nonInteractive) {
|
||||
throw new Error('Branch name may not be empty.');
|
||||
if (!name) {
|
||||
const validationMessage = 'Branch name may not be empty.';
|
||||
if (jsonFlag) {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
|
||||
({ name: branchName } = await selectBranchFromPaginatedQueryAsync(
|
||||
projectId,
|
||||
'Which branch would you like to view?',
|
||||
// discard limit and offset because this query is not those flag's intended target
|
||||
{ json: options.json, nonInteractive: options.nonInteractive, offset: 0 }
|
||||
));
|
||||
({ name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Please enter the name of the branch to view:',
|
||||
validate: value => (value ? true : validationMessage),
|
||||
}));
|
||||
}
|
||||
|
||||
await listAndRenderUpdatesOnBranchByNameAsync(projectId, branchName, options);
|
||||
const { app } = await UpdateQuery.viewBranchAsync({
|
||||
appId: projectId,
|
||||
name,
|
||||
});
|
||||
const UpdateBranch = app?.byId.updateBranchByName;
|
||||
if (!UpdateBranch) {
|
||||
throw new Error(`Could not find branch "${name}"`);
|
||||
}
|
||||
|
||||
const updates = Object.values(groupBy(UpdateBranch.updates, u => u.group)).map(
|
||||
group => group[0]
|
||||
);
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput({ ...UpdateBranch, updates });
|
||||
} else {
|
||||
const groupTable = new Table({
|
||||
head: UPDATE_COLUMNS,
|
||||
wordWrap: true,
|
||||
});
|
||||
|
||||
for (const update of updates) {
|
||||
groupTable.push([
|
||||
formatUpdate(update),
|
||||
update.runtimeVersion,
|
||||
update.group,
|
||||
getPlatformsForGroup({
|
||||
updates: UpdateBranch.updates,
|
||||
group: update.group,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Branch:'));
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: UpdateBranch.name },
|
||||
{ label: 'ID', value: UpdateBranch.id },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Recently published update groups:'));
|
||||
Log.log(groupTable.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ interface RawBuildFlags {
|
||||
'auto-submit': boolean;
|
||||
'auto-submit-with-profile'?: string;
|
||||
'resource-class'?: UserInputResourceClass;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export default class Build extends EasCommand {
|
||||
@@ -87,10 +86,6 @@ export default class Build extends EasCommand {
|
||||
hidden: true,
|
||||
description: 'The instance type that will be used to run this build [experimental]',
|
||||
}),
|
||||
message: Flags.string({
|
||||
char: 'm',
|
||||
description: 'A short message describing the build',
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -148,11 +143,6 @@ export default class Build extends EasCommand {
|
||||
Log.newLine();
|
||||
}
|
||||
|
||||
const message = flags['message'];
|
||||
if (message && message.length > 1024) {
|
||||
Errors.error('Message cannot be longer than 1024 characters.', { exit: 1 });
|
||||
}
|
||||
|
||||
const profile = flags['profile'];
|
||||
return {
|
||||
requestedPlatform,
|
||||
@@ -173,7 +163,6 @@ export default class Build extends EasCommand {
|
||||
autoSubmit: flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined,
|
||||
submitProfile: flags['auto-submit-with-profile'] ?? profile,
|
||||
userInputResourceClass: flags['resource-class'] ?? UserInputResourceClass.DEFAULT,
|
||||
message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import { getRuntimeVersionNullable } from '@expo/config-plugins/build/utils/Updates';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJsonReader } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||
import Log from '../../../log';
|
||||
import { selectPlatformAsync } from '../../../platform';
|
||||
import { VERSION_CODE_REQUIREMENTS, isValidVersionCode } from '../../../project/android/versions';
|
||||
import { getApplicationIdentifierAsync } from '../../../project/applicationIdentifier';
|
||||
import { getExpoConfig } from '../../../project/expoConfig';
|
||||
import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/ios/versions';
|
||||
import {
|
||||
findProjectRootAsync,
|
||||
getProjectFullNameAsync,
|
||||
getProjectIdAsync,
|
||||
} from '../../../project/projectUtils';
|
||||
import {
|
||||
ensureVersionSourceIsRemoteAsync,
|
||||
getBuildVersionName,
|
||||
validateAppConfigForRemoteVersionSource,
|
||||
} from '../../../project/remoteVersionSource';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
|
||||
export default class BuildVersionSetView extends EasCommand {
|
||||
static description = 'Update version of an app.';
|
||||
|
||||
static flags = {
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: ['android', 'ios'],
|
||||
}),
|
||||
profile: Flags.string({
|
||||
description:
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
};
|
||||
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSetView);
|
||||
|
||||
const projectDir = await findProjectRootAsync();
|
||||
|
||||
const platform = await selectPlatformAsync(flags.platform);
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
|
||||
const profile = await easJsonReader.getBuildProfileAsync(platform, flags.profile ?? undefined);
|
||||
|
||||
const exp = getExpoConfig(projectDir, { env: profile.env });
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
const projectFullName = await getProjectFullNameAsync(exp);
|
||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
profile,
|
||||
platform
|
||||
);
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
projectId,
|
||||
toAppPlatform(platform),
|
||||
applicationIdentifier
|
||||
);
|
||||
const currentStateMessage = remoteVersions?.buildVersion
|
||||
? `Project ${chalk.bold(projectFullName)} with ${getApplicationIdentifierName(
|
||||
platform
|
||||
)} "${applicationIdentifier}" is configured with ${getBuildVersionName(platform)} ${
|
||||
remoteVersions.buildVersion
|
||||
}.`
|
||||
: `Project ${chalk.bold(projectFullName)} with ${getApplicationIdentifierName(
|
||||
platform
|
||||
)} "${applicationIdentifier}" does not have any ${getBuildVersionName(
|
||||
platform
|
||||
)} configured.`;
|
||||
|
||||
const versionPromptMessage = remoteVersions?.buildVersion
|
||||
? `What version would you like to set?`
|
||||
: `What version would you like to initialize it with?`;
|
||||
Log.log(currentStateMessage);
|
||||
|
||||
const { version } = await promptAsync({
|
||||
type: platform === Platform.ANDROID ? 'number' : 'text',
|
||||
name: 'version',
|
||||
message: versionPromptMessage,
|
||||
validate:
|
||||
platform === Platform.ANDROID
|
||||
? value => isValidVersionCode(value) || `Invalid value: ${VERSION_CODE_REQUIREMENTS}.`
|
||||
: value => isValidBuildNumber(value) || `Invalid value: ${BUILD_NUMBER_REQUIREMENTS}.`,
|
||||
});
|
||||
await AppVersionMutation.createAppVersionAsync({
|
||||
appId: projectId,
|
||||
platform: toAppPlatform(platform),
|
||||
applicationIdentifier,
|
||||
storeVersion: exp.version ?? '1.0.0',
|
||||
buildVersion: String(version),
|
||||
runtimeVersion: getRuntimeVersionNullable(exp, platform) ?? undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getApplicationIdentifierName(platform: Platform): string {
|
||||
if (platform === Platform.ANDROID) {
|
||||
return 'application ID';
|
||||
} else {
|
||||
return 'bundle identifier';
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJsonReader } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../../build/android/version';
|
||||
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../../build/ios/version';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||
import Log from '../../../log';
|
||||
import {
|
||||
appPlatformDisplayNames,
|
||||
selectRequestedPlatformAsync,
|
||||
toPlatforms,
|
||||
} from '../../../platform';
|
||||
import { getAppBuildGradleAsync } from '../../../project/android/gradleUtils';
|
||||
import { VERSION_CODE_REQUIREMENTS, isValidVersionCode } from '../../../project/android/versions';
|
||||
import { getApplicationIdentifierAsync } from '../../../project/applicationIdentifier';
|
||||
import { getExpoConfig } from '../../../project/expoConfig';
|
||||
import { resolveXcodeBuildContextAsync } from '../../../project/ios/scheme';
|
||||
import { resolveTargetsAsync } from '../../../project/ios/target';
|
||||
import { BUILD_NUMBER_REQUIREMENTS, isValidBuildNumber } from '../../../project/ios/versions';
|
||||
import { findProjectRootAsync, getProjectIdAsync } from '../../../project/projectUtils';
|
||||
import {
|
||||
ensureVersionSourceIsRemoteAsync,
|
||||
getBuildVersionName,
|
||||
validateAppConfigForRemoteVersionSource,
|
||||
} from '../../../project/remoteVersionSource';
|
||||
import { resolveWorkflowAsync } from '../../../project/workflow';
|
||||
import { getProfilesAsync } from '../../../utils/profiles';
|
||||
|
||||
interface SyncContext<T extends Platform> {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
workflow: Workflow;
|
||||
profile: BuildProfile<T>;
|
||||
buildVersion: string;
|
||||
}
|
||||
|
||||
export default class BuildVersionSyncView extends EasCommand {
|
||||
public static description = 'Update a version in native code with a value stored on EAS servers';
|
||||
|
||||
public static flags = {
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: ['android', 'ios', 'all'],
|
||||
}),
|
||||
profile: Flags.string({
|
||||
description:
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
};
|
||||
|
||||
public async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(BuildVersionSyncView);
|
||||
const projectDir = await findProjectRootAsync();
|
||||
|
||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
await ensureVersionSourceIsRemoteAsync(projectDir, easJsonReader);
|
||||
|
||||
const platforms = toPlatforms(requestedPlatform);
|
||||
const buildProfiles = await getProfilesAsync({
|
||||
type: 'build',
|
||||
easJsonReader,
|
||||
platforms,
|
||||
profileName: flags.profile ?? undefined,
|
||||
});
|
||||
for (const profileInfo of buildProfiles) {
|
||||
const exp = getExpoConfig(projectDir, { env: profileInfo.profile.env });
|
||||
const projectId = await getProjectIdAsync(exp);
|
||||
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
|
||||
const platformDisplayName = appPlatformDisplayNames[toAppPlatform(profileInfo.platform)];
|
||||
|
||||
const applicationIdentifier = await getApplicationIdentifierAsync(
|
||||
projectDir,
|
||||
exp,
|
||||
profileInfo.profile,
|
||||
profileInfo.platform
|
||||
);
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
projectId,
|
||||
toAppPlatform(profileInfo.platform),
|
||||
applicationIdentifier
|
||||
);
|
||||
const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform);
|
||||
if (!remoteVersions?.buildVersion) {
|
||||
Log.warn(
|
||||
`Skipping versions sync for ${platformDisplayName}. There are no versions configured on Expo servers, use "eas build:version:set" or run a build to initialize it.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (workflow === Workflow.MANAGED) {
|
||||
Log.warn(
|
||||
`The remote value for the ${platformDisplayName} ${getBuildVersionName(
|
||||
profileInfo.platform
|
||||
)} is ${chalk.bold(
|
||||
remoteVersions?.buildVersion
|
||||
)}, but it was not synced to the local project. This command has no effect on projects using managed workflow.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (profileInfo.platform === Platform.ANDROID) {
|
||||
this.syncAndroidAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
profile: profileInfo.profile as BuildProfile<Platform.ANDROID>,
|
||||
workflow,
|
||||
buildVersion: remoteVersions.buildVersion,
|
||||
});
|
||||
} else {
|
||||
this.syncIosAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
profile: profileInfo.profile as BuildProfile<Platform.IOS>,
|
||||
workflow,
|
||||
buildVersion: remoteVersions.buildVersion,
|
||||
});
|
||||
}
|
||||
Log.withTick(
|
||||
`Successfully updated the ${platformDisplayName} ${getBuildVersionName(
|
||||
profileInfo.platform
|
||||
)} in native code to ${chalk.bold(remoteVersions?.buildVersion)}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async syncIosAsync({
|
||||
workflow,
|
||||
projectDir,
|
||||
exp,
|
||||
profile,
|
||||
buildVersion,
|
||||
}: SyncContext<Platform.IOS>): Promise<void> {
|
||||
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
|
||||
{ exp, projectDir, nonInteractive: false },
|
||||
profile
|
||||
);
|
||||
const targets = await resolveTargetsAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
xcodeBuildContext,
|
||||
env: profile.env,
|
||||
});
|
||||
|
||||
if (!isValidBuildNumber(buildVersion)) {
|
||||
throw new Error(`Invalid buildNumber (${buildVersion}), ${BUILD_NUMBER_REQUIREMENTS}.`);
|
||||
}
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await updateIosNativeVersionsAsync({
|
||||
projectDir,
|
||||
buildNumber: buildVersion,
|
||||
targets,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async syncAndroidAsync({
|
||||
projectDir,
|
||||
workflow,
|
||||
buildVersion,
|
||||
}: SyncContext<Platform.ANDROID>): Promise<void> {
|
||||
if (!isValidVersionCode(buildVersion)) {
|
||||
throw new Error(`Invalid versionCode (${buildVersion}), ${VERSION_CODE_REQUIREMENTS}.`);
|
||||
}
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
const buildGradle = await getAppBuildGradleAsync(projectDir);
|
||||
const isMultiFlavor =
|
||||
buildGradle.android?.productFlavors || buildGradle.android?.flavorDimensions;
|
||||
if (isMultiFlavor) {
|
||||
throw new Error(
|
||||
"Versions in native code can't be automatically synced in multi-flavor Android projects. If you are using EAS Build with app version source set to remote, the correct values will be injected at the build time."
|
||||
);
|
||||
}
|
||||
await updateAndroidNativeVersionsAsync({ projectDir, versionCode: Number(buildVersion) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,8 +242,8 @@ async function configureAppJSONForEASUpdateAsync({
|
||||
}
|
||||
|
||||
function isRuntimeEqual(
|
||||
runtimeVersionA: string | { policy: 'sdkVersion' | 'nativeVersion' | 'appVersion' },
|
||||
runtimeVersionB: string | { policy: 'sdkVersion' | 'nativeVersion' | 'appVersion' }
|
||||
runtimeVersionA: string | { policy: 'sdkVersion' | 'nativeVersion' },
|
||||
runtimeVersionB: string | { policy: 'sdkVersion' | 'nativeVersion' }
|
||||
): boolean {
|
||||
if (typeof runtimeVersionA === 'string' && typeof runtimeVersionB === 'string') {
|
||||
return runtimeVersionA === runtimeVersionB;
|
||||
|
||||
@@ -9,7 +9,6 @@ import gql from 'graphql-tag';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { getEASUpdateURL } from '../../api';
|
||||
import { BRANCHES_LIMIT } from '../../branch/queries';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import fetch from '../../fetch';
|
||||
@@ -26,9 +25,8 @@ import {
|
||||
ViewBranchUpdatesQuery,
|
||||
} from '../../graphql/generated';
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import { BranchQuery } from '../../graphql/queries/BranchQuery';
|
||||
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import Log, { link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { getExpoConfig } from '../../project/expoConfig';
|
||||
import {
|
||||
@@ -43,7 +41,6 @@ import {
|
||||
buildBundlesAsync,
|
||||
buildUnsortedUpdateInfoGroupAsync,
|
||||
collectAssetsAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
uploadAssetsAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
@@ -61,6 +58,7 @@ import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
import { createUpdateBranchOnAppAsync } from '../branch/create';
|
||||
import { listBranchesAsync } from '../branch/list';
|
||||
import { createUpdateChannelOnAppAsync } from '../channel/create';
|
||||
|
||||
export const defaultPublishPlatforms: PublishPlatform[] = ['android', 'ios'];
|
||||
@@ -130,8 +128,8 @@ export async function ensureBranchExistsAsync({
|
||||
}: {
|
||||
appId: string;
|
||||
name: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<{
|
||||
id: string;
|
||||
updates: Exclude<
|
||||
@@ -293,11 +291,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
throw new Error(validationMessage);
|
||||
}
|
||||
|
||||
const branches = await BranchQuery.listBranchesAsync({
|
||||
appId: projectId,
|
||||
limit: BRANCHES_LIMIT,
|
||||
offset: 0,
|
||||
});
|
||||
const branches = await listBranchesAsync({ projectId });
|
||||
if (branches.length === 0) {
|
||||
({ name: branchName } = await promptAsync({
|
||||
type: 'text',
|
||||
@@ -327,7 +321,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
let unsortedUpdateInfoGroups: UpdateInfoGroup = {};
|
||||
let oldMessage: string, oldRuntimeVersion: string;
|
||||
let uploadedAssetCount = 0;
|
||||
let assetLimitPerUpdateGroup = 0;
|
||||
|
||||
if (republish) {
|
||||
// If we are republishing, we don't need to worry about building the bundle or uploading the assets.
|
||||
@@ -448,17 +441,15 @@ export default class UpdatePublish extends EasCommand {
|
||||
try {
|
||||
const platforms = platformFlag === 'all' ? defaultPublishPlatforms : [platformFlag];
|
||||
const assets = await collectAssetsAsync({ inputDir: inputDir!, platforms });
|
||||
const uploadResults = await uploadAssetsAsync(
|
||||
const { uniqueUploadedAssetCount } = await uploadAssetsAsync(
|
||||
assets,
|
||||
projectId,
|
||||
(totalAssets, missingAssets) => {
|
||||
assetSpinner.text = `Uploading assets. Finished (${
|
||||
totalAssets - missingAssets
|
||||
}/${totalAssets})`;
|
||||
}
|
||||
);
|
||||
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
|
||||
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
|
||||
uploadedAssetCount = uniqueUploadedAssetCount;
|
||||
unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
|
||||
const uploadAssetSuccessMessage = uploadedAssetCount
|
||||
? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'asset' : 'assets'}!`
|
||||
@@ -483,7 +474,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
appId: projectId,
|
||||
name: branchName,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
// Sort the updates into different groups based on their platform specific runtime versions
|
||||
@@ -578,7 +568,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime
|
||||
);
|
||||
if (newUpdatesForRuntimeVersion.length === 0) {
|
||||
if (!newUpdatesForRuntimeVersion.length) {
|
||||
throw new Error(`Publish response is missing updates with runtime ${runtime}.`);
|
||||
}
|
||||
const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
|
||||
@@ -608,18 +598,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
if (
|
||||
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
|
||||
) {
|
||||
Log.warn(
|
||||
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
|
||||
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
|
||||
learnMoreMessage: 'Consider optimizing your usage of assets',
|
||||
dim: false,
|
||||
})}.`
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -665,7 +643,7 @@ export async function getUpdatesToRepublishInteractiveAsync(
|
||||
title: formatUpdateTitle(update),
|
||||
value: update.group,
|
||||
}));
|
||||
if (updateGroups.length === 0) {
|
||||
if (!updateGroups.length) {
|
||||
throw new Error(
|
||||
`There are no updates on branch "${branchName}" published for the platform(s) ${platformFlag}. Did you mean to publish a new update instead?`
|
||||
);
|
||||
|
||||
@@ -4,21 +4,24 @@ import chalk from 'chalk';
|
||||
import CliTable from 'cli-table3';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ViewAllUpdatesQuery } from '../../graphql/generated';
|
||||
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
|
||||
import Log from '../../log';
|
||||
import { getExpoConfig } from '../../project/expoConfig';
|
||||
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { UPDATES_LIMIT } from '../../update/queries';
|
||||
import {
|
||||
UPDATE_COLUMNS,
|
||||
UpdateGroupDescription,
|
||||
formatUpdate,
|
||||
getUpdateGroupsWithPlatforms,
|
||||
} from '../../update/utils';
|
||||
import { FormatUpdateParameter, UPDATE_COLUMNS, formatUpdate } from '../../update/utils';
|
||||
import groupBy from '../../utils/expodash/groupBy';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
|
||||
type UpdateGroupDescription = FormatUpdateParameter & {
|
||||
branch: string;
|
||||
group: string;
|
||||
platforms: string;
|
||||
runtimeVersion: string;
|
||||
};
|
||||
|
||||
export default class BranchView extends EasCommand {
|
||||
static description = 'view the recent updates for a branch';
|
||||
|
||||
@@ -52,17 +55,10 @@ export default class BranchView extends EasCommand {
|
||||
|
||||
let updateGroupDescriptions: UpdateGroupDescription[];
|
||||
if (all) {
|
||||
const branchesAndUpdates = await UpdateQuery.viewAllAsync({
|
||||
appId: projectId,
|
||||
limit: UPDATES_LIMIT,
|
||||
offset: 0,
|
||||
});
|
||||
updateGroupDescriptions = getUpdateGroupsWithPlatforms(
|
||||
branchesAndUpdates.app.byId.updates.map(update => ({
|
||||
...update,
|
||||
branch: update.branch.name,
|
||||
}))
|
||||
).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
const branchesAndUpdates = await UpdateQuery.viewAllAsync({ appId: projectId });
|
||||
updateGroupDescriptions = getUpdateGroupDescriptions(
|
||||
branchesAndUpdates.app.byId.updateBranches
|
||||
);
|
||||
} else {
|
||||
let branchInteractive: string | undefined;
|
||||
if (!branchFlag) {
|
||||
@@ -84,16 +80,12 @@ export default class BranchView extends EasCommand {
|
||||
const branchesAndUpdates = await UpdateQuery.viewBranchAsync({
|
||||
appId: projectId,
|
||||
name: branch,
|
||||
limit: UPDATES_LIMIT,
|
||||
offset: 0,
|
||||
});
|
||||
const UpdateBranch = branchesAndUpdates.app?.byId.updateBranchByName;
|
||||
if (!UpdateBranch) {
|
||||
throw new Error(`Could not find branch "${branch}"`);
|
||||
}
|
||||
updateGroupDescriptions = getUpdateGroupsWithPlatforms(
|
||||
UpdateBranch.updates.map(update => ({ ...update, branch }))
|
||||
).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
||||
updateGroupDescriptions = getUpdateGroupDescriptions([UpdateBranch]);
|
||||
}
|
||||
|
||||
if (jsonFlag) {
|
||||
@@ -104,6 +96,29 @@ export default class BranchView extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
function getUpdateGroupDescriptions(
|
||||
branchesAndUpdates: ViewAllUpdatesQuery['app']['byId']['updateBranches']
|
||||
): UpdateGroupDescription[] {
|
||||
const flattenedBranchesAndUpdates = branchesAndUpdates.flatMap(branch =>
|
||||
branch.updates.map(update => {
|
||||
return { branch: branch.name, ...update };
|
||||
})
|
||||
);
|
||||
const updateGroupDescriptions = Object.values(
|
||||
groupBy(flattenedBranchesAndUpdates, update => update.group)
|
||||
).map(updateGroup => {
|
||||
const platforms = updateGroup
|
||||
.map(update => update.platform)
|
||||
.sort()
|
||||
.join(', ');
|
||||
return { ...updateGroup[0], platforms };
|
||||
});
|
||||
updateGroupDescriptions.sort(
|
||||
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
);
|
||||
return updateGroupDescriptions;
|
||||
}
|
||||
|
||||
function logAsTable(updateGroupDescriptions: UpdateGroupDescription[]): void {
|
||||
const table = new CliTable({
|
||||
head: ['Branch', ...UPDATE_COLUMNS],
|
||||
|
||||
@@ -254,11 +254,11 @@ export class SetUpAdhocProvisioningProfile {
|
||||
ctx: CredentialsContext,
|
||||
appleTeam: AppleTeamFragment
|
||||
): Promise<AppleDeviceFragment[]> {
|
||||
const action = new DeviceCreateAction(ctx.appStore, this.app.account, appleTeam);
|
||||
const action = new DeviceCreateAction(this.app.account, appleTeam);
|
||||
const method = await action.runAsync();
|
||||
|
||||
while (true) {
|
||||
if (method === RegistrationMethod.WEBSITE) {
|
||||
if (method !== RegistrationMethod.INPUT) {
|
||||
Log.newLine();
|
||||
Log.log(chalk.bold("Press any key if you've already finished device registration."));
|
||||
await pressAnyKeyToContinueAsync();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { JSONObject } from '@expo/json-file';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import {
|
||||
CommonIosAppCredentialsFragment,
|
||||
@@ -18,7 +17,6 @@ export interface Target {
|
||||
bundleIdentifier: string;
|
||||
parentBundleIdentifier?: string;
|
||||
entitlements: JSONObject;
|
||||
buildSettings?: XCBuildConfiguration['buildSettings'];
|
||||
}
|
||||
|
||||
export interface TargetCredentials {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const runDeveloperPortalMethodAsync = jest.fn();
|
||||
@@ -1,17 +1,13 @@
|
||||
import prompts from 'prompts';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import AppStoreApi from '../../../../credentials/ios/appstore/AppStoreApi';
|
||||
import { Account } from '../../../../user/Account';
|
||||
import DeviceCreateAction, { RegistrationMethod } from '../action';
|
||||
import { runDeveloperPortalMethodAsync } from '../developerPortalMethod';
|
||||
import { runInputMethodAsync } from '../inputMethod';
|
||||
import { runRegistrationUrlMethodAsync } from '../registrationUrlMethod';
|
||||
|
||||
jest.mock('prompts');
|
||||
jest.mock('../developerPortalMethod');
|
||||
jest.mock('../inputMethod');
|
||||
jest.mock('../registrationUrlMethod');
|
||||
jest.mock('../inputMethod');
|
||||
|
||||
beforeEach(() => {
|
||||
const promptsMock = jest.mocked(prompts);
|
||||
@@ -29,8 +25,6 @@ describe(DeviceCreateAction, () => {
|
||||
jest.mocked(prompts).mockImplementationOnce(async () => ({
|
||||
method: RegistrationMethod.WEBSITE,
|
||||
}));
|
||||
const appStoreApiMock = mock<AppStoreApi>();
|
||||
const appStoreApi = instance(appStoreApiMock);
|
||||
|
||||
const account: Account = {
|
||||
id: 'account_id',
|
||||
@@ -41,7 +35,7 @@ describe(DeviceCreateAction, () => {
|
||||
appleTeamIdentifier: 'ABC123Y',
|
||||
appleTeamName: 'John Doe (Individual)',
|
||||
};
|
||||
const action = new DeviceCreateAction(appStoreApi, account, appleTeam);
|
||||
const action = new DeviceCreateAction(account, appleTeam);
|
||||
await action.runAsync();
|
||||
|
||||
expect(runRegistrationUrlMethodAsync).toBeCalled();
|
||||
@@ -51,8 +45,6 @@ describe(DeviceCreateAction, () => {
|
||||
jest.mocked(prompts).mockImplementationOnce(async () => ({
|
||||
method: RegistrationMethod.INPUT,
|
||||
}));
|
||||
const appStoreApiMock = mock<AppStoreApi>();
|
||||
const appStoreApi = instance(appStoreApiMock);
|
||||
|
||||
const account: Account = {
|
||||
id: 'account_id',
|
||||
@@ -63,32 +55,10 @@ describe(DeviceCreateAction, () => {
|
||||
appleTeamIdentifier: 'ABC123Y',
|
||||
appleTeamName: 'John Doe (Individual)',
|
||||
};
|
||||
const action = new DeviceCreateAction(appStoreApi, account, appleTeam);
|
||||
const action = new DeviceCreateAction(account, appleTeam);
|
||||
await action.runAsync();
|
||||
|
||||
expect(runInputMethodAsync).toBeCalled();
|
||||
});
|
||||
|
||||
it('calls runDeveloperPortalMethodAsync if user chooses the developer portal method', async () => {
|
||||
jest.mocked(prompts).mockImplementationOnce(async () => ({
|
||||
method: RegistrationMethod.DEVELOPER_PORTAL,
|
||||
}));
|
||||
const appStoreApiMock = mock<AppStoreApi>();
|
||||
const appStoreApi = instance(appStoreApiMock);
|
||||
|
||||
const account: Account = {
|
||||
id: 'account_id',
|
||||
name: 'foobar',
|
||||
};
|
||||
const appleTeam = {
|
||||
id: 'apple-team-id',
|
||||
appleTeamIdentifier: 'ABC123Y',
|
||||
appleTeamName: 'John Doe (Individual)',
|
||||
};
|
||||
const action = new DeviceCreateAction(appStoreApi, account, appleTeam);
|
||||
await action.runAsync();
|
||||
|
||||
expect(runDeveloperPortalMethodAsync).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,24 +1,20 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import AppStoreApi from '../../../credentials/ios/appstore/AppStoreApi';
|
||||
import { AppleTeam } from '../../../graphql/generated';
|
||||
import Log from '../../../log';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { Account } from '../../../user/Account';
|
||||
import { runDeveloperPortalMethodAsync } from './developerPortalMethod';
|
||||
import { runInputMethodAsync } from './inputMethod';
|
||||
import { runRegistrationUrlMethodAsync } from './registrationUrlMethod';
|
||||
|
||||
export enum RegistrationMethod {
|
||||
WEBSITE,
|
||||
INPUT,
|
||||
DEVELOPER_PORTAL,
|
||||
EXIT,
|
||||
}
|
||||
|
||||
export default class DeviceCreateAction {
|
||||
constructor(
|
||||
private appStoreApi: AppStoreApi,
|
||||
private account: Account,
|
||||
private appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>
|
||||
) {}
|
||||
@@ -27,8 +23,6 @@ export default class DeviceCreateAction {
|
||||
const method = await this.askForRegistrationMethodAsync();
|
||||
if (method === RegistrationMethod.WEBSITE) {
|
||||
await runRegistrationUrlMethodAsync(this.account.id, this.appleTeam);
|
||||
} else if (method === RegistrationMethod.DEVELOPER_PORTAL) {
|
||||
await runDeveloperPortalMethodAsync(this.appStoreApi, this.account.id, this.appleTeam);
|
||||
} else if (method === RegistrationMethod.INPUT) {
|
||||
await runInputMethodAsync(this.account.id, this.appleTeam);
|
||||
} else if (method === RegistrationMethod.EXIT) {
|
||||
@@ -50,12 +44,6 @@ export default class DeviceCreateAction {
|
||||
)} - generates a registration URL to be opened on your devices`,
|
||||
value: RegistrationMethod.WEBSITE,
|
||||
},
|
||||
{
|
||||
title: `${chalk.bold(
|
||||
'Developer Portal'
|
||||
)} - import devices already registered on Apple Developer Portal`,
|
||||
value: RegistrationMethod.DEVELOPER_PORTAL,
|
||||
},
|
||||
{
|
||||
title: `${chalk.bold('Input')} - allows you to type in UDIDs (advanced option)`,
|
||||
value: RegistrationMethod.INPUT,
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import { Device, DeviceClass } from '@expo/apple-utils';
|
||||
|
||||
import { AppleDeviceMutation } from '../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
|
||||
import {
|
||||
AppleDeviceFragmentWithAppleTeam,
|
||||
AppleDeviceQuery,
|
||||
} from '../../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
|
||||
import AppStoreApi from '../../../credentials/ios/appstore/AppStoreApi';
|
||||
import { getRequestContext } from '../../../credentials/ios/appstore/authenticate';
|
||||
import { AuthCtx } from '../../../credentials/ios/appstore/authenticateTypes';
|
||||
import { AppleDeviceClass, AppleTeam } from '../../../graphql/generated';
|
||||
import Log from '../../../log';
|
||||
import { ora } from '../../../ora';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import chunk from '../../../utils/expodash/chunk';
|
||||
|
||||
const DEVICE_IMPORT_CHUNK_SIZE = 10;
|
||||
const DEVICE_CLASS_TO_GRAPHQL_TYPE: Partial<Record<DeviceClass, AppleDeviceClass>> = {
|
||||
[DeviceClass.IPAD]: AppleDeviceClass.Ipad,
|
||||
[DeviceClass.IPHONE]: AppleDeviceClass.Iphone,
|
||||
};
|
||||
|
||||
export async function runDeveloperPortalMethodAsync(
|
||||
appStoreApi: AppStoreApi,
|
||||
accountId: string,
|
||||
appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>
|
||||
): Promise<void> {
|
||||
const appleAuthCtx = await appStoreApi.ensureAuthenticatedAsync();
|
||||
const unregisteredPortalDevices = await findUnregisteredPortalDevicesAsync(
|
||||
appleAuthCtx,
|
||||
accountId,
|
||||
appleTeam
|
||||
);
|
||||
if (unregisteredPortalDevices.length === 0) {
|
||||
Log.log('All your devices registered on Apple Developer Portal are already imported to EAS.');
|
||||
return;
|
||||
}
|
||||
const devicesToImport = await chooseDevicesToImportAsync(unregisteredPortalDevices);
|
||||
await importDevicesAsync(accountId, appleTeam, devicesToImport);
|
||||
}
|
||||
|
||||
async function importDevicesAsync(
|
||||
accountId: string,
|
||||
appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>,
|
||||
devices: Device[]
|
||||
): Promise<void> {
|
||||
const spinner = ora(
|
||||
`Importing ${devices.length} Apple device${devices.length === 1 ? '' : 's'} to EAS`
|
||||
).start();
|
||||
const deviceChunks = chunk(devices, DEVICE_IMPORT_CHUNK_SIZE);
|
||||
try {
|
||||
for (const deviceChunk of deviceChunks) {
|
||||
await importDeviceChunkAsync(accountId, appleTeam, deviceChunk);
|
||||
}
|
||||
} catch (err) {
|
||||
spinner.fail();
|
||||
throw err;
|
||||
}
|
||||
spinner.succeed();
|
||||
}
|
||||
|
||||
async function importDeviceChunkAsync(
|
||||
accountId: string,
|
||||
appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>,
|
||||
devices: Device[]
|
||||
): Promise<void> {
|
||||
const promises = devices.map(device => {
|
||||
return AppleDeviceMutation.createAppleDeviceAsync(
|
||||
{
|
||||
appleTeamId: appleTeam.id,
|
||||
identifier: device.attributes.udid,
|
||||
name: device.attributes.name,
|
||||
deviceClass: DEVICE_CLASS_TO_GRAPHQL_TYPE[device.attributes.deviceClass] ?? undefined,
|
||||
},
|
||||
accountId
|
||||
);
|
||||
});
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async function findUnregisteredPortalDevicesAsync(
|
||||
appleAuthCtx: AuthCtx,
|
||||
accountId: string,
|
||||
appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>
|
||||
): Promise<Device[]> {
|
||||
const expoRegisteredDevices = await AppleDeviceQuery.getAllByAppleTeamIdentifierAsync(
|
||||
accountId,
|
||||
appleTeam.appleTeamIdentifier,
|
||||
{ useCache: false }
|
||||
);
|
||||
const expoRegisteredDevicesByUdid = expoRegisteredDevices.reduce((acc, device) => {
|
||||
acc[device.identifier] = device;
|
||||
return acc;
|
||||
}, {} as Record<string, AppleDeviceFragmentWithAppleTeam>);
|
||||
|
||||
const portalDevices = await Device.getAllIOSProfileDevicesAsync(getRequestContext(appleAuthCtx));
|
||||
return portalDevices.filter(
|
||||
portalDevice =>
|
||||
!(portalDevice.attributes.udid in expoRegisteredDevicesByUdid) &&
|
||||
[DeviceClass.IPAD, DeviceClass.IPHONE].includes(portalDevice.attributes.deviceClass)
|
||||
);
|
||||
}
|
||||
|
||||
async function chooseDevicesToImportAsync(devices: Device[]): Promise<Device[]> {
|
||||
const { chosenDevices } = await promptAsync({
|
||||
type: 'multiselect',
|
||||
name: 'chosenDevices',
|
||||
message: 'Which devices do you want to import to EAS?',
|
||||
choices: devices.map(device => ({
|
||||
value: device,
|
||||
title: formatDeviceLabel(device),
|
||||
selected: true,
|
||||
})),
|
||||
});
|
||||
return chosenDevices;
|
||||
}
|
||||
|
||||
export function formatDeviceLabel(device: Device): string {
|
||||
const deviceDetails = formatDeviceDetails(device);
|
||||
return `${device.attributes.name} - ${device.attributes.udid})${
|
||||
deviceDetails !== '' ? ` - ${deviceDetails}` : ''
|
||||
}`;
|
||||
}
|
||||
|
||||
function formatDeviceDetails(device: Device): string {
|
||||
let details: string = device.attributes.deviceClass;
|
||||
if (device.attributes.model) {
|
||||
details = device.attributes.model;
|
||||
}
|
||||
return details === '' ? details : `(${details})`;
|
||||
}
|
||||
@@ -29,12 +29,12 @@ export default class DeviceManager {
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
const account = await this.resolveAccountAsync();
|
||||
const appleAuthCtx = await this.ctx.appStore.ensureAuthenticatedAsync();
|
||||
const { team } = await this.ctx.appStore.ensureAuthenticatedAsync();
|
||||
const appleTeam = await ensureAppleTeamExistsAsync(account.id, {
|
||||
appleTeamIdentifier: appleAuthCtx.team.id,
|
||||
appleTeamName: appleAuthCtx.team.name,
|
||||
appleTeamIdentifier: team.id,
|
||||
appleTeamName: team.name,
|
||||
});
|
||||
const action = new DeviceCreateAction(this.ctx.appStore, account, appleTeam);
|
||||
const action = new DeviceCreateAction(account, appleTeam);
|
||||
await action.runAsync();
|
||||
}
|
||||
|
||||
|
||||
+12
-262
@@ -123,8 +123,6 @@ export type Account = {
|
||||
requiresAccessTokenForPushSecurity: Scalars['Boolean'];
|
||||
/** Snacks associated with this account */
|
||||
snacks: Array<Snack>;
|
||||
/** SSO configuration for this account */
|
||||
ssoConfiguration?: Maybe<AccountSsoConfiguration>;
|
||||
/** Subscription info visible to members that have VIEWER role */
|
||||
subscription?: Maybe<SubscriptionDetails>;
|
||||
/** @deprecated No longer needed */
|
||||
@@ -395,90 +393,6 @@ export type AccountQueryByNameArgs = {
|
||||
accountName: Scalars['String'];
|
||||
};
|
||||
|
||||
/** Auth configuration data for an SSO account. */
|
||||
export type AccountSsoConfiguration = {
|
||||
__typename?: 'AccountSSOConfiguration';
|
||||
authEndpoint?: Maybe<Scalars['String']>;
|
||||
authProtocol: AuthProtocolType;
|
||||
authProviderIdentifier: Scalars['String'];
|
||||
clientIdentifier: Scalars['String'];
|
||||
clientSecret: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
issuer?: Maybe<Scalars['String']>;
|
||||
jwksEndpoint?: Maybe<Scalars['String']>;
|
||||
redirectUri: Scalars['String'];
|
||||
tokenEndpoint?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
userInfoEndpoint?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AccountSsoConfigurationData = {
|
||||
authEndpoint?: InputMaybe<Scalars['String']>;
|
||||
authProtocol: AuthProtocolType;
|
||||
authProviderIdentifier: Scalars['String'];
|
||||
clientIdentifier: Scalars['String'];
|
||||
clientSecret: Scalars['String'];
|
||||
issuer?: InputMaybe<Scalars['String']>;
|
||||
jwksEndpoint?: InputMaybe<Scalars['String']>;
|
||||
redirectUri: Scalars['String'];
|
||||
tokenEndpoint?: InputMaybe<Scalars['String']>;
|
||||
userInfoEndpoint?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AccountSsoConfigurationMutation = {
|
||||
__typename?: 'AccountSSOConfigurationMutation';
|
||||
/** Create an AccountSSOConfiguration for an Account */
|
||||
createAccountSSOConfiguration: AccountSsoConfiguration;
|
||||
/** Delete an AccountSSOConfiguration */
|
||||
deleteAccountSSOConfiguration: DeleteAccountSsoConfigurationResult;
|
||||
/** Update an AccountSSOConfiguration */
|
||||
updateAccountSSOConfiguration: AccountSsoConfiguration;
|
||||
};
|
||||
|
||||
|
||||
export type AccountSsoConfigurationMutationCreateAccountSsoConfigurationArgs = {
|
||||
accountId: Scalars['ID'];
|
||||
accountSSOConfigurationData: AccountSsoConfigurationData;
|
||||
};
|
||||
|
||||
|
||||
export type AccountSsoConfigurationMutationDeleteAccountSsoConfigurationArgs = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
|
||||
export type AccountSsoConfigurationMutationUpdateAccountSsoConfigurationArgs = {
|
||||
accountSSOConfigurationData: AccountSsoConfigurationData;
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
/** Public auth configuration data for an SSO account. */
|
||||
export type AccountSsoConfigurationPublicData = {
|
||||
__typename?: 'AccountSSOConfigurationPublicData';
|
||||
authEndpoint?: Maybe<Scalars['String']>;
|
||||
authProtocol: AuthProtocolType;
|
||||
authProviderIdentifier: Scalars['String'];
|
||||
clientIdentifier: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
issuer?: Maybe<Scalars['String']>;
|
||||
jwksEndpoint?: Maybe<Scalars['String']>;
|
||||
redirectUri: Scalars['String'];
|
||||
tokenEndpoint?: Maybe<Scalars['String']>;
|
||||
userInfoEndpoint?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AccountSsoConfigurationPublicDataQuery = {
|
||||
__typename?: 'AccountSSOConfigurationPublicDataQuery';
|
||||
/** Get AccountSSOConfiguration public data by account name */
|
||||
publicDataByAccountName: AccountSsoConfigurationPublicData;
|
||||
};
|
||||
|
||||
|
||||
export type AccountSsoConfigurationPublicDataQueryPublicDataByAccountNameArgs = {
|
||||
accountName: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AccountUsageMetric = {
|
||||
__typename?: 'AccountUsageMetric';
|
||||
id: Scalars['ID'];
|
||||
@@ -513,7 +427,6 @@ export type AccountUsageMetricsByBillingPeriodArgs = {
|
||||
|
||||
|
||||
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
|
||||
filterParams?: InputMaybe<Scalars['JSONObject']>;
|
||||
granularity: UsageMetricsGranularity;
|
||||
serviceMetric: EasServiceMetric;
|
||||
timespan: UsageMetricsTimespan;
|
||||
@@ -795,7 +708,6 @@ export type AndroidJobInput = {
|
||||
type: BuildWorkflow;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
version?: InputMaybe<AndroidJobVersionInput>;
|
||||
};
|
||||
|
||||
export type AndroidJobKeystoreInput = {
|
||||
@@ -810,10 +722,6 @@ export type AndroidJobSecretsInput = {
|
||||
environmentSecrets?: InputMaybe<Scalars['JSONObject']>;
|
||||
};
|
||||
|
||||
export type AndroidJobVersionInput = {
|
||||
versionCode: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AndroidKeystore = {
|
||||
__typename?: 'AndroidKeystore';
|
||||
account: Account;
|
||||
@@ -894,7 +802,6 @@ export type App = Project & {
|
||||
androidAppCredentials: Array<AndroidAppCredentials>;
|
||||
/** ios.appStoreUrl field from most recent classic update manifest */
|
||||
appStoreUrl?: Maybe<Scalars['String']>;
|
||||
assetLimitPerUpdateGroup: Scalars['Int'];
|
||||
buildJobs: Array<BuildJob>;
|
||||
/**
|
||||
* Coalesced Build (EAS) or BuildJob (Classic) items for this app.
|
||||
@@ -927,7 +834,6 @@ export type App = Project & {
|
||||
isLikedByMe: Scalars['Boolean'];
|
||||
/** @deprecated No longer supported */
|
||||
lastPublishedTime: Scalars['DateTime'];
|
||||
latestAppVersionByPlatformAndApplicationIdentifier?: Maybe<AppVersion>;
|
||||
latestReleaseForReleaseChannel?: Maybe<AppRelease>;
|
||||
/** ID of latest classic update release */
|
||||
latestReleaseId: Scalars['ID'];
|
||||
@@ -1050,13 +956,6 @@ export type AppIosAppCredentialsArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppLatestAppVersionByPlatformAndApplicationIdentifierArgs = {
|
||||
applicationIdentifier: Scalars['String'];
|
||||
platform: AppPlatform;
|
||||
};
|
||||
|
||||
|
||||
/** Represents an Exponent App (or Experience in legacy terms) */
|
||||
export type AppLatestReleaseForReleaseChannelArgs = {
|
||||
platform: AppPlatform;
|
||||
@@ -1299,52 +1198,6 @@ export enum AppStoreConnectUserRole {
|
||||
Unknown = 'UNKNOWN'
|
||||
}
|
||||
|
||||
/** Represents Play Store/App Store version of an application */
|
||||
export type AppVersion = {
|
||||
__typename?: 'AppVersion';
|
||||
/**
|
||||
* Store identifier for an application
|
||||
* - Android - applicationId
|
||||
* - iOS - bundle identifier
|
||||
*/
|
||||
applicationIdentifier: Scalars['String'];
|
||||
/**
|
||||
* Value that identifies build in a store (it's visible to developers, but not to end users)
|
||||
* - Android - versionCode in build.gradle ("android.versionCode" field in app.json)
|
||||
* - iOS - CFBundleVersion in Info.plist ("ios.buildNumber" field in app.json)
|
||||
*/
|
||||
buildVersion: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
platform: AppPlatform;
|
||||
runtimeVersion?: Maybe<Scalars['String']>;
|
||||
/**
|
||||
* User-facing version in a store
|
||||
* - Android - versionName in build.gradle ("version" field in app.json)
|
||||
* - iOS - CFBundleShortVersionString in Info.plist ("version" field in app.json)
|
||||
*/
|
||||
storeVersion: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AppVersionInput = {
|
||||
appId: Scalars['ID'];
|
||||
applicationIdentifier: Scalars['String'];
|
||||
buildVersion: Scalars['String'];
|
||||
platform: AppPlatform;
|
||||
runtimeVersion?: InputMaybe<Scalars['String']>;
|
||||
storeVersion: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AppVersionMutation = {
|
||||
__typename?: 'AppVersionMutation';
|
||||
/** Create an app version */
|
||||
createAppVersion: AppVersion;
|
||||
};
|
||||
|
||||
|
||||
export type AppVersionMutationCreateAppVersionArgs = {
|
||||
appVersionInput: AppVersionInput;
|
||||
};
|
||||
|
||||
export type AppleAppIdentifier = {
|
||||
__typename?: 'AppleAppIdentifier';
|
||||
account: Account;
|
||||
@@ -1561,7 +1414,7 @@ export type ApplePushKey = {
|
||||
};
|
||||
|
||||
export type ApplePushKeyInput = {
|
||||
appleTeamId: Scalars['ID'];
|
||||
appleTeamId?: InputMaybe<Scalars['ID']>;
|
||||
keyIdentifier: Scalars['String'];
|
||||
keyP8: Scalars['String'];
|
||||
};
|
||||
@@ -1686,10 +1539,6 @@ export type AssetQueryMetadataArgs = {
|
||||
storageKeys: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export enum AuthProtocolType {
|
||||
Oidc = 'OIDC'
|
||||
}
|
||||
|
||||
export type Billing = {
|
||||
__typename?: 'Billing';
|
||||
/** History of invoices */
|
||||
@@ -1718,7 +1567,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
canRetry: Scalars['Boolean'];
|
||||
cancelingActor?: Maybe<Actor>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
childBuild?: Maybe<Build>;
|
||||
completedAt?: Maybe<Scalars['DateTime']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
distribution?: Maybe<DistributionType>;
|
||||
@@ -1727,7 +1575,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
estimatedWaitTimeLeftSeconds?: Maybe<Scalars['Int']>;
|
||||
expirationDate?: Maybe<Scalars['DateTime']>;
|
||||
gitCommitHash?: Maybe<Scalars['String']>;
|
||||
gitCommitMessage?: Maybe<Scalars['String']>;
|
||||
id: Scalars['ID'];
|
||||
/** Queue position is 1-indexed */
|
||||
initialQueuePosition?: Maybe<Scalars['Int']>;
|
||||
@@ -1737,10 +1584,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
iosEnterpriseProvisioning?: Maybe<BuildIosEnterpriseProvisioning>;
|
||||
isGitWorkingTreeDirty?: Maybe<Scalars['Boolean']>;
|
||||
logFiles: Array<Scalars['String']>;
|
||||
maxBuildTimeSeconds: Scalars['Int'];
|
||||
/** Retry time starts after completedAt */
|
||||
maxRetryTimeMinutes: Scalars['Int'];
|
||||
message?: Maybe<Scalars['String']>;
|
||||
metrics?: Maybe<BuildMetrics>;
|
||||
parentBuild?: Maybe<Build>;
|
||||
platform: AppPlatform;
|
||||
@@ -1752,7 +1595,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
reactNativeVersion?: Maybe<Scalars['String']>;
|
||||
releaseChannel?: Maybe<Scalars['String']>;
|
||||
resourceClass?: Maybe<BuildResourceClass>;
|
||||
runFromCI?: Maybe<Scalars['Boolean']>;
|
||||
runtimeVersion?: Maybe<Scalars['String']>;
|
||||
sdkVersion?: Maybe<Scalars['String']>;
|
||||
status: BuildStatus;
|
||||
@@ -1897,14 +1739,10 @@ export type BuildMetadataInput = {
|
||||
credentialsSource?: InputMaybe<BuildCredentialsSource>;
|
||||
distribution?: InputMaybe<DistributionType>;
|
||||
gitCommitHash?: InputMaybe<Scalars['String']>;
|
||||
gitCommitMessage?: InputMaybe<Scalars['String']>;
|
||||
iosEnterpriseProvisioning?: InputMaybe<BuildIosEnterpriseProvisioning>;
|
||||
isGitWorkingTreeDirty?: InputMaybe<Scalars['Boolean']>;
|
||||
message?: InputMaybe<Scalars['String']>;
|
||||
reactNativeVersion?: InputMaybe<Scalars['String']>;
|
||||
releaseChannel?: InputMaybe<Scalars['String']>;
|
||||
runFromCI?: InputMaybe<Scalars['Boolean']>;
|
||||
runWithNoWaitFlag?: InputMaybe<Scalars['Boolean']>;
|
||||
runtimeVersion?: InputMaybe<Scalars['String']>;
|
||||
sdkVersion?: InputMaybe<Scalars['String']>;
|
||||
trackingContext?: InputMaybe<Scalars['JSONObject']>;
|
||||
@@ -1979,8 +1817,7 @@ export type BuildParamsInput = {
|
||||
|
||||
export enum BuildPriority {
|
||||
High = 'HIGH',
|
||||
Normal = 'NORMAL',
|
||||
NormalPlus = 'NORMAL_PLUS'
|
||||
Normal = 'NORMAL'
|
||||
}
|
||||
|
||||
/** Publicly visible data for a Build. */
|
||||
@@ -2175,12 +2012,6 @@ export type CreateIosSubmissionInput = {
|
||||
submittedBuildId?: InputMaybe<Scalars['ID']>;
|
||||
};
|
||||
|
||||
export type CreateServerlessFunctionUploadUrlResult = {
|
||||
__typename?: 'CreateServerlessFunctionUploadUrlResult';
|
||||
formDataFields: Scalars['JSONObject'];
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type CreateSubmissionResult = {
|
||||
__typename?: 'CreateSubmissionResult';
|
||||
/** Created submission */
|
||||
@@ -2197,11 +2028,6 @@ export type DeleteAccountResult = {
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteAccountSsoConfigurationResult = {
|
||||
__typename?: 'DeleteAccountSSOConfigurationResult';
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
export type DeleteAndroidKeystoreResult = {
|
||||
__typename?: 'DeleteAndroidKeystoreResult';
|
||||
id: Scalars['ID'];
|
||||
@@ -2297,9 +2123,7 @@ export enum EasBuildDeprecationInfoType {
|
||||
export enum EasServiceMetric {
|
||||
AssetsRequests = 'ASSETS_REQUESTS',
|
||||
BandwidthUsage = 'BANDWIDTH_USAGE',
|
||||
Builds = 'BUILDS',
|
||||
ManifestRequests = 'MANIFEST_REQUESTS',
|
||||
UniqueUpdaters = 'UNIQUE_UPDATERS',
|
||||
UniqueUsers = 'UNIQUE_USERS'
|
||||
}
|
||||
|
||||
@@ -2392,15 +2216,6 @@ export enum Feature {
|
||||
Teams = 'TEAMS'
|
||||
}
|
||||
|
||||
export type FutureSubscription = {
|
||||
__typename?: 'FutureSubscription';
|
||||
createdAt: Scalars['DateTime'];
|
||||
id: Scalars['ID'];
|
||||
meteredBillingStatus: MeteredBillingStatus;
|
||||
planId: Scalars['String'];
|
||||
startDate: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type GetSignedAssetUploadSpecificationsResult = {
|
||||
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
||||
specifications: Array<Scalars['String']>;
|
||||
@@ -2673,7 +2488,6 @@ export type IosJobInput = {
|
||||
type: BuildWorkflow;
|
||||
updates?: InputMaybe<BuildUpdatesInput>;
|
||||
username?: InputMaybe<Scalars['String']>;
|
||||
version?: InputMaybe<IosJobVersionInput>;
|
||||
};
|
||||
|
||||
export type IosJobSecretsInput = {
|
||||
@@ -2687,10 +2501,6 @@ export type IosJobTargetCredentialsInput = {
|
||||
targetName: Scalars['String'];
|
||||
};
|
||||
|
||||
export type IosJobVersionInput = {
|
||||
buildNumber: Scalars['String'];
|
||||
};
|
||||
|
||||
/** @deprecated Use developmentClient option instead. */
|
||||
export enum IosManagedBuildType {
|
||||
DevelopmentClient = 'DEVELOPMENT_CLIENT',
|
||||
@@ -2872,11 +2682,6 @@ export type MeMutationUpdateProfileArgs = {
|
||||
userData: UserDataInput;
|
||||
};
|
||||
|
||||
export type MeteredBillingStatus = {
|
||||
__typename?: 'MeteredBillingStatus';
|
||||
EAS_UPDATE: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type Offer = {
|
||||
__typename?: 'Offer';
|
||||
features?: Maybe<Array<Maybe<Feature>>>;
|
||||
@@ -3093,8 +2898,6 @@ export type RootMutation = {
|
||||
accessToken: AccessTokenMutation;
|
||||
/** Mutations that modify an Account */
|
||||
account: AccountMutation;
|
||||
/** Mutations that create, update, and delete an AccountSSOConfiguration */
|
||||
accountSSOConfiguration: AccountSsoConfigurationMutation;
|
||||
/** Mutations that modify the build credentials for an Android app */
|
||||
androidAppBuildCredentials: AndroidAppBuildCredentialsMutation;
|
||||
/** Mutations that modify the credentials for an Android app */
|
||||
@@ -3107,8 +2910,6 @@ export type RootMutation = {
|
||||
app?: Maybe<AppMutation>;
|
||||
/** Mutations that modify an App Store Connect Api Key */
|
||||
appStoreConnectApiKey: AppStoreConnectApiKeyMutation;
|
||||
/** Mutations that modify an AppVersion */
|
||||
appVersion: AppVersionMutation;
|
||||
/** Mutations that modify an Identifier for an iOS App */
|
||||
appleAppIdentifier: AppleAppIdentifierMutation;
|
||||
/** Mutations that modify an Apple Device */
|
||||
@@ -3143,7 +2944,6 @@ export type RootMutation = {
|
||||
me: MeMutation;
|
||||
/** Mutations that create, update, and delete Robots */
|
||||
robot: RobotMutation;
|
||||
serverlessFunction: ServerlessFunctionMutation;
|
||||
/** Mutations that modify an EAS Submit submission */
|
||||
submission: SubmissionMutation;
|
||||
update: UpdateMutation;
|
||||
@@ -3185,8 +2985,6 @@ export type RootQuery = {
|
||||
_doNotUse?: Maybe<Scalars['String']>;
|
||||
/** Top-level query object for querying Accounts. */
|
||||
account: AccountQuery;
|
||||
/** Top-level query object for querying AccountSSOConfigurationPublicData */
|
||||
accountSSOConfigurationPublicData: AccountSsoConfigurationPublicDataQuery;
|
||||
/** Top-level query object for querying Actors. */
|
||||
actor: ActorQuery;
|
||||
/**
|
||||
@@ -3311,21 +3109,6 @@ export type SecondFactorRegenerateBackupCodesResult = {
|
||||
plaintextBackupCodes: Array<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type ServerlessFunctionIdentifierInput = {
|
||||
gitCommitSHA1: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ServerlessFunctionMutation = {
|
||||
__typename?: 'ServerlessFunctionMutation';
|
||||
createUploadPresignedUrl: CreateServerlessFunctionUploadUrlResult;
|
||||
};
|
||||
|
||||
|
||||
export type ServerlessFunctionMutationCreateUploadPresignedUrlArgs = {
|
||||
appId: Scalars['ID'];
|
||||
serverlessFunctionIdentifierInput: ServerlessFunctionIdentifierInput;
|
||||
};
|
||||
|
||||
export type Snack = Project & {
|
||||
__typename?: 'Snack';
|
||||
/** Description of the Snack */
|
||||
@@ -3395,16 +3178,12 @@ export type Submission = ActivityTimelineProjectActivity & {
|
||||
archiveUrl?: Maybe<Scalars['String']>;
|
||||
canRetry: Scalars['Boolean'];
|
||||
cancelingActor?: Maybe<Actor>;
|
||||
childSubmission?: Maybe<Submission>;
|
||||
completedAt?: Maybe<Scalars['DateTime']>;
|
||||
createdAt: Scalars['DateTime'];
|
||||
error?: Maybe<SubmissionError>;
|
||||
id: Scalars['ID'];
|
||||
initiatingActor?: Maybe<Actor>;
|
||||
iosConfig?: Maybe<IosSubmissionConfig>;
|
||||
logsUrl?: Maybe<Scalars['String']>;
|
||||
/** Retry time starts after completedAt */
|
||||
maxRetryTimeMinutes: Scalars['Int'];
|
||||
parentSubmission?: Maybe<Submission>;
|
||||
platform: AppPlatform;
|
||||
status: SubmissionStatus;
|
||||
@@ -3500,10 +3279,8 @@ export type SubscriptionDetails = {
|
||||
cancelledAt?: Maybe<Scalars['DateTime']>;
|
||||
concurrencies?: Maybe<Concurrencies>;
|
||||
endedAt?: Maybe<Scalars['DateTime']>;
|
||||
futureSubscription?: Maybe<FutureSubscription>;
|
||||
id: Scalars['ID'];
|
||||
isDowngrading?: Maybe<Scalars['Boolean']>;
|
||||
meteredBillingStatus: MeteredBillingStatus;
|
||||
name?: Maybe<Scalars['String']>;
|
||||
nextInvoice?: Maybe<Scalars['DateTime']>;
|
||||
planId?: Maybe<Scalars['String']>;
|
||||
@@ -3698,9 +3475,7 @@ export type UsageMetricTotal = {
|
||||
|
||||
export enum UsageMetricType {
|
||||
Bandwidth = 'BANDWIDTH',
|
||||
Build = 'BUILD',
|
||||
Request = 'REQUEST',
|
||||
Update = 'UPDATE',
|
||||
User = 'USER'
|
||||
}
|
||||
|
||||
@@ -4071,6 +3846,14 @@ export type DeleteUpdateBranchMutationVariables = Exact<{
|
||||
|
||||
export type DeleteUpdateBranchMutation = { __typename?: 'RootMutation', updateBranch: { __typename?: 'UpdateBranchMutation', deleteUpdateBranch: { __typename?: 'DeleteUpdateBranchResult', id: string } } };
|
||||
|
||||
export type BranchesByAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
limit: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, createdAt: any, message?: string | null, runtimeVersion: string, group: string, platform: string, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'User', username: string, id: string } | null }> }> } } };
|
||||
|
||||
export type EditUpdateBranchMutationVariables = Exact<{
|
||||
input: EditUpdateBranchInput;
|
||||
}>;
|
||||
@@ -4547,13 +4330,6 @@ export type CreateAppMutationVariables = Exact<{
|
||||
|
||||
export type CreateAppMutation = { __typename?: 'RootMutation', app?: { __typename?: 'AppMutation', createApp: { __typename?: 'App', id: string } } | null };
|
||||
|
||||
export type CreateAppVersionMutationVariables = Exact<{
|
||||
appVersionInput: AppVersionInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateAppVersionMutation = { __typename?: 'RootMutation', appVersion: { __typename?: 'AppVersionMutation', createAppVersion: { __typename?: 'AppVersion', id: string } } };
|
||||
|
||||
export type CreateAndroidBuildMutationVariables = Exact<{
|
||||
appId: Scalars['ID'];
|
||||
job: AndroidJobInput;
|
||||
@@ -4672,15 +4448,6 @@ export type DeleteWebhookMutationVariables = Exact<{
|
||||
|
||||
export type DeleteWebhookMutation = { __typename?: 'RootMutation', webhook: { __typename?: 'WebhookMutation', deleteWebhook: { __typename?: 'DeleteWebhookResult', id: string } } };
|
||||
|
||||
export type LatestAppVersionQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
platform: AppPlatform;
|
||||
applicationIdentifier: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type LatestAppVersionQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, latestAppVersionByPlatformAndApplicationIdentifier?: { __typename?: 'AppVersion', id: string, storeVersion: string, buildVersion: string } | null } } };
|
||||
|
||||
export type ViewBranchQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
@@ -4689,15 +4456,6 @@ export type ViewBranchQueryVariables = Exact<{
|
||||
|
||||
export type ViewBranchQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, name: string } | null } } };
|
||||
|
||||
export type BranchesByAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
limit: Scalars['Int'];
|
||||
offset: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BranchesByAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, createdAt: any, message?: string | null, runtimeVersion: string, group: string, platform: string, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'User', username: string, id: string } | null }> }> } } };
|
||||
|
||||
export type BuildsByIdQueryVariables = Exact<{
|
||||
buildId: Scalars['ID'];
|
||||
}>;
|
||||
@@ -4752,13 +4510,6 @@ export type GetAssetMetadataQueryVariables = Exact<{
|
||||
|
||||
export type GetAssetMetadataQuery = { __typename?: 'RootQuery', asset: { __typename?: 'AssetQuery', metadata: Array<{ __typename?: 'AssetMetadataResult', storageKey: string, status: AssetMetadataStatus }> } };
|
||||
|
||||
export type GetAssetLimitPerUpdateGroupForAppQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetAssetLimitPerUpdateGroupForAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, assetLimitPerUpdateGroup: number } } };
|
||||
|
||||
export type SubmissionsByIdQueryVariables = Exact<{
|
||||
submissionId: Scalars['ID'];
|
||||
}>;
|
||||
@@ -4780,11 +4531,10 @@ export type GetAllSubmissionsForAppQuery = { __typename?: 'RootQuery', app: { __
|
||||
export type ViewAllUpdatesQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
limit: Scalars['Int'];
|
||||
offset: Scalars['Int'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewAllUpdatesQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, actor?: { __typename?: 'Robot', firstName?: string | null, id: string } | { __typename?: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string } }> } } };
|
||||
export type ViewAllUpdatesQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranches: Array<{ __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, actor?: { __typename?: 'Robot', firstName?: string | null, id: string } | { __typename?: 'User', username: string, id: string } | null }> }> } } };
|
||||
|
||||
export type ViewBranchUpdatesQueryVariables = Exact<{
|
||||
appId: Scalars['String'];
|
||||
@@ -4794,7 +4544,7 @@ export type ViewBranchUpdatesQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ViewBranchUpdatesQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, actor?: { __typename?: 'Robot', firstName?: string | null, id: string } | { __typename?: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string } }> } | null } } };
|
||||
export type ViewBranchUpdatesQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, updateBranchByName?: { __typename?: 'UpdateBranch', id: string, name: string, updates: Array<{ __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, actor?: { __typename?: 'Robot', firstName?: string | null, id: string } | { __typename?: 'User', username: string, id: string } | null }> } | null } } };
|
||||
|
||||
export type CurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import assert from 'assert';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
AppPlatform,
|
||||
CreateAppVersionMutation,
|
||||
CreateAppVersionMutationVariables,
|
||||
} from '../generated';
|
||||
|
||||
export const AppVersionMutation = {
|
||||
async createAppVersionAsync(appVersionInput: {
|
||||
appId: string;
|
||||
platform: AppPlatform;
|
||||
applicationIdentifier: string;
|
||||
storeVersion: string;
|
||||
buildVersion: string;
|
||||
runtimeVersion?: string;
|
||||
}): Promise<string> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateAppVersionMutation, CreateAppVersionMutationVariables>(
|
||||
gql`
|
||||
mutation CreateAppVersionMutation($appVersionInput: AppVersionInput!) {
|
||||
appVersion {
|
||||
createAppVersion(appVersionInput: $appVersionInput) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
appVersionInput,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
const appVersionId = data.appVersion?.createAppVersion.id;
|
||||
assert(appVersionId, 'AppVersion ID must be defined');
|
||||
return appVersionId;
|
||||
},
|
||||
};
|
||||
@@ -3,20 +3,19 @@ import gql from 'graphql-tag';
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
CodeSigningInfoInput,
|
||||
GetSignedUploadMutation,
|
||||
GetSignedUploadMutationVariables,
|
||||
PublishUpdateGroupInput,
|
||||
SetCodeSigningInfoMutation,
|
||||
UpdatePublishMutation,
|
||||
} from '../generated';
|
||||
|
||||
export const PublishMutation = {
|
||||
async getUploadURLsAsync(
|
||||
contentTypes: string[]
|
||||
): Promise<GetSignedUploadMutation['asset']['getSignedAssetUploadSpecifications']> {
|
||||
async getUploadURLsAsync(contentTypes: string[]): Promise<{ specifications: string[] }> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<GetSignedUploadMutation, GetSignedUploadMutationVariables>(
|
||||
.mutation<
|
||||
{ asset: { getSignedAssetUploadSpecifications: { specifications: string[] } } },
|
||||
{ contentTypes: string[] }
|
||||
>(
|
||||
gql`
|
||||
mutation GetSignedUploadMutation($contentTypes: [String!]!) {
|
||||
asset {
|
||||
@@ -34,6 +33,7 @@ export const PublishMutation = {
|
||||
);
|
||||
return data.asset.getSignedAssetUploadSpecifications;
|
||||
},
|
||||
|
||||
async publishUpdateGroupAsync(
|
||||
publishUpdateGroupsInput: PublishUpdateGroupInput[]
|
||||
): Promise<UpdatePublishMutation['updateBranch']['publishUpdateGroups']> {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
AppPlatform,
|
||||
AppVersion,
|
||||
LatestAppVersionQuery,
|
||||
LatestAppVersionQueryVariables,
|
||||
} from '../generated';
|
||||
|
||||
export const AppVersionQuery = {
|
||||
async latestVersionAsync(
|
||||
appId: string,
|
||||
platform: AppPlatform,
|
||||
applicationIdentifier: string
|
||||
): Promise<Pick<AppVersion, 'storeVersion' | 'buildVersion'> | null> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<LatestAppVersionQuery, LatestAppVersionQueryVariables>(
|
||||
gql`
|
||||
query LatestAppVersion(
|
||||
$appId: String!
|
||||
$platform: AppPlatform!
|
||||
$applicationIdentifier: String!
|
||||
) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
latestAppVersionByPlatformAndApplicationIdentifier(
|
||||
platform: $platform
|
||||
applicationIdentifier: $applicationIdentifier
|
||||
) {
|
||||
id
|
||||
storeVersion
|
||||
buildVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ appId, applicationIdentifier, platform },
|
||||
{
|
||||
additionalTypenames: ['App', 'AppVersion'],
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app.byId.latestAppVersionByPlatformAndApplicationIdentifier ?? null;
|
||||
},
|
||||
};
|
||||
@@ -1,15 +1,7 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
BranchesByAppQuery,
|
||||
BranchesByAppQueryVariables,
|
||||
UpdateBranchFragment,
|
||||
ViewBranchQuery,
|
||||
ViewBranchQueryVariables,
|
||||
} from '../generated';
|
||||
import { UpdateBranchFragmentNode } from '../types/UpdateBranch';
|
||||
import { ViewBranchQuery, ViewBranchQueryVariables } from '../generated';
|
||||
|
||||
export const BranchQuery = {
|
||||
async getBranchByNameAsync({
|
||||
@@ -49,38 +41,4 @@ export const BranchQuery = {
|
||||
);
|
||||
return branch;
|
||||
},
|
||||
async listBranchesAsync({
|
||||
appId,
|
||||
limit,
|
||||
offset,
|
||||
}: BranchesByAppQueryVariables): Promise<UpdateBranchFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<BranchesByAppQuery, BranchesByAppQueryVariables>(
|
||||
gql`
|
||||
query BranchesByAppQuery($appId: String!, $limit: Int!, $offset: Int!) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
updateBranches(limit: $limit, offset: $offset) {
|
||||
id
|
||||
...UpdateBranchFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateBranchFragmentNode)}
|
||||
`,
|
||||
{
|
||||
appId,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
{ additionalTypenames: ['UpdateBranch'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data?.app?.byId.updateBranches ?? [];
|
||||
},
|
||||
};
|
||||
@@ -1,12 +1,7 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
AssetMetadataResult,
|
||||
GetAssetLimitPerUpdateGroupForAppQuery,
|
||||
GetAssetLimitPerUpdateGroupForAppQueryVariables,
|
||||
GetAssetMetadataQuery,
|
||||
} from '../generated';
|
||||
import { AssetMetadataResult, GetAssetMetadataQuery } from '../generated';
|
||||
|
||||
export const PublishQuery = {
|
||||
async getAssetMetadataAsync(storageKeys: string[]): Promise<AssetMetadataResult[]> {
|
||||
@@ -35,30 +30,4 @@ export const PublishQuery = {
|
||||
);
|
||||
return data.asset.metadata;
|
||||
},
|
||||
async getAssetLimitPerUpdateGroupAsync(
|
||||
appId: string
|
||||
): Promise<GetAssetLimitPerUpdateGroupForAppQuery['app']['byId']['assetLimitPerUpdateGroup']> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<
|
||||
GetAssetLimitPerUpdateGroupForAppQuery,
|
||||
GetAssetLimitPerUpdateGroupForAppQueryVariables
|
||||
>(
|
||||
gql`
|
||||
query GetAssetLimitPerUpdateGroupForApp($appId: String!) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
assetLimitPerUpdateGroup
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ appId },
|
||||
{ additionalTypenames: [] } // required arg
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return data.app.byId.assetLimitPerUpdateGroup;
|
||||
},
|
||||
};
|
||||
@@ -8,44 +8,41 @@ import {
|
||||
ViewBranchUpdatesQueryVariables,
|
||||
} from '../generated';
|
||||
|
||||
export type BranchUpdateObject = Exclude<
|
||||
ViewBranchUpdatesQuery['app']['byId']['updateBranchByName'],
|
||||
null | undefined
|
||||
>['updates'][number];
|
||||
export const viewBranchUpdatesQueryUpdateLimit = 300;
|
||||
|
||||
export type AppUpdateObject = ViewAllUpdatesQuery['app']['byId']['updates'][number];
|
||||
|
||||
export type UpdateObject = BranchUpdateObject | AppUpdateObject;
|
||||
type ViewBranchUpdatesQueryVariablesWithOptionalLimitAndOffset =
|
||||
Partial<ViewBranchUpdatesQueryVariables> &
|
||||
Pick<ViewBranchUpdatesQueryVariables, 'appId' | 'name'>;
|
||||
|
||||
export const UpdateQuery = {
|
||||
async viewAllAsync({ appId, limit, offset }: ViewAllUpdatesQueryVariables) {
|
||||
async viewAllAsync({ appId }: { appId: string }): Promise<ViewAllUpdatesQuery> {
|
||||
return withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<ViewAllUpdatesQuery, ViewAllUpdatesQueryVariables>(
|
||||
gql`
|
||||
query ViewAllUpdates($appId: String!, $limit: Int!, $offset: Int!) {
|
||||
query ViewAllUpdates($appId: String!, $limit: Int!) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
updates(limit: $limit, offset: $offset) {
|
||||
updateBranches(offset: 0, limit: $limit) {
|
||||
id
|
||||
group
|
||||
message
|
||||
createdAt
|
||||
runtimeVersion
|
||||
platform
|
||||
actor {
|
||||
name
|
||||
updates(offset: 0, limit: $limit) {
|
||||
id
|
||||
... on User {
|
||||
username
|
||||
group
|
||||
message
|
||||
createdAt
|
||||
runtimeVersion
|
||||
platform
|
||||
actor {
|
||||
id
|
||||
... on User {
|
||||
username
|
||||
}
|
||||
... on Robot {
|
||||
firstName
|
||||
}
|
||||
}
|
||||
... on Robot {
|
||||
firstName
|
||||
}
|
||||
}
|
||||
branch {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,16 +51,20 @@ export const UpdateQuery = {
|
||||
`,
|
||||
{
|
||||
appId,
|
||||
limit,
|
||||
offset,
|
||||
limit: viewBranchUpdatesQueryUpdateLimit,
|
||||
},
|
||||
{ additionalTypenames: ['UpdateBranch', 'Update'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
},
|
||||
async viewBranchAsync({ appId, name, limit, offset }: ViewBranchUpdatesQueryVariables) {
|
||||
return withErrorHandlingAsync(
|
||||
async viewBranchAsync({
|
||||
appId,
|
||||
name,
|
||||
limit = viewBranchUpdatesQueryUpdateLimit,
|
||||
offset = 0,
|
||||
}: ViewBranchUpdatesQueryVariablesWithOptionalLimitAndOffset) {
|
||||
return withErrorHandlingAsync<ViewBranchUpdatesQuery>(
|
||||
graphqlClient
|
||||
.query<ViewBranchUpdatesQuery, ViewBranchUpdatesQueryVariables>(
|
||||
gql`
|
||||
@@ -74,7 +75,7 @@ export const UpdateQuery = {
|
||||
updateBranchByName(name: $name) {
|
||||
id
|
||||
name
|
||||
updates(limit: $limit, offset: $offset) {
|
||||
updates(offset: $offset, limit: $limit) {
|
||||
id
|
||||
group
|
||||
message
|
||||
@@ -91,10 +92,6 @@ export const UpdateQuery = {
|
||||
firstName
|
||||
}
|
||||
}
|
||||
branch {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { AppStoreVersionPhasedRelease, PhasedReleaseState } from '@expo/apple-utils';
|
||||
|
||||
import { AttributesOf } from '../../../../utils/asc';
|
||||
|
||||
export const phasedRelease: AttributesOf<AppStoreVersionPhasedRelease> = {
|
||||
phasedReleaseState: PhasedReleaseState.COMPLETE,
|
||||
currentDayNumber: 7,
|
||||
startDate: null,
|
||||
totalPauseDuration: null,
|
||||
};
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
AppCategoryId,
|
||||
AppSubcategoryId,
|
||||
PhasedReleaseState,
|
||||
ReleaseType,
|
||||
} from '@expo/apple-utils';
|
||||
import { AppCategoryId, AppSubcategoryId } from '@expo/apple-utils';
|
||||
|
||||
import { AppleConfigReader } from '../reader';
|
||||
import { leastRestrictiveAdvisory, mostRestrictiveAdvisory } from './fixtures/ageRatingDeclaration';
|
||||
@@ -88,91 +83,3 @@ describe('getAgeRating', () => {
|
||||
expect(reader.getAgeRating()).toMatchObject(mostRestrictiveAdvisory);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersionReleaseType', () => {
|
||||
it('ignores automatic release when not set', () => {
|
||||
const reader = new AppleConfigReader({ release: undefined });
|
||||
expect(reader.getVersionReleaseType()).toBeNull();
|
||||
});
|
||||
|
||||
it('ignores automatic release when automaticRelease not set', () => {
|
||||
const reader = new AppleConfigReader({ release: {} });
|
||||
expect(reader.getVersionReleaseType()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns scheduled release date with iso string', () => {
|
||||
const reader = new AppleConfigReader({
|
||||
release: { automaticRelease: '2020-06-17T12:00:00-00:00' },
|
||||
});
|
||||
expect(reader.getVersionReleaseType()).toMatchObject({
|
||||
releaseType: ReleaseType.SCHEDULED,
|
||||
earliestReleaseDate: '2020-06-17T12:00:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns scheduled release with unparsable date string', () => {
|
||||
const reader = new AppleConfigReader({ release: { automaticRelease: '2020-06-17-12:00:00' } });
|
||||
expect(reader.getVersionReleaseType()).toMatchObject({
|
||||
releaseType: ReleaseType.SCHEDULED,
|
||||
earliestReleaseDate: '2020-06-17-12:00:00',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns automatic release', () => {
|
||||
const reader = new AppleConfigReader({ release: { automaticRelease: true } });
|
||||
expect(reader.getVersionReleaseType()).toMatchObject({
|
||||
releaseType: ReleaseType.AFTER_APPROVAL,
|
||||
earliestReleaseDate: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns manual release', () => {
|
||||
const reader = new AppleConfigReader({ release: { automaticRelease: false } });
|
||||
expect(reader.getVersionReleaseType()).toMatchObject({
|
||||
releaseType: ReleaseType.MANUAL,
|
||||
earliestReleaseDate: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersionReleasePhased', () => {
|
||||
it('ignores phased release when not set', () => {
|
||||
const reader = new AppleConfigReader({ release: undefined });
|
||||
expect(reader.getVersionReleasePhased()).toBeNull();
|
||||
});
|
||||
|
||||
it('ignores phased release when phasedRelease not set', () => {
|
||||
const reader = new AppleConfigReader({ release: {} });
|
||||
expect(reader.getVersionReleasePhased()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns phased release when enabled', () => {
|
||||
const reader = new AppleConfigReader({ release: { phasedRelease: true } });
|
||||
expect(reader.getVersionReleasePhased()).toMatchObject({
|
||||
phasedReleaseState: PhasedReleaseState.ACTIVE,
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores phased release when disabled', () => {
|
||||
const reader = new AppleConfigReader({ release: { phasedRelease: false } });
|
||||
expect(reader.getVersionReleasePhased()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getVersion', () => {
|
||||
it('ignores version when not set', () => {
|
||||
const reader = new AppleConfigReader({});
|
||||
expect(reader.getVersion()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns version and copyright when set', () => {
|
||||
const reader = new AppleConfigReader({
|
||||
version: '2.0',
|
||||
copyright: '2022 - ACME',
|
||||
});
|
||||
expect(reader.getVersion()).toMatchObject({
|
||||
versionString: '2.0',
|
||||
copyright: '2022 - ACME',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,7 +12,6 @@ import { dutchInfo, englishInfo } from './fixtures/appInfoLocalization';
|
||||
import { nameAndDemoReviewDetails, nameOnlyReviewDetails } from './fixtures/appStoreReviewDetail';
|
||||
import { automaticRelease, manualRelease, scheduledRelease } from './fixtures/appStoreVersion';
|
||||
import { dutchVersion, englishVersion } from './fixtures/appStoreVersionLocalization';
|
||||
import { phasedRelease } from './fixtures/appStoreVersionPhasedRelease';
|
||||
|
||||
describe('toSchema', () => {
|
||||
it('returns object with apple schema', () => {
|
||||
@@ -183,72 +182,41 @@ describe('setCategories', () => {
|
||||
});
|
||||
|
||||
describe('setVersion', () => {
|
||||
it('modifies the copyright and version string', () => {
|
||||
it('modifies the copyright', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersion(manualRelease);
|
||||
expect(writer.schema.version).toBe(manualRelease.versionString);
|
||||
expect(writer.schema.copyright).toBe(manualRelease.copyright);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setVersionReleaseType', () => {
|
||||
describe('setVersionRelease', () => {
|
||||
it('modifies scheduled release', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleaseType(scheduledRelease);
|
||||
writer.setVersionRelease(scheduledRelease);
|
||||
expect(writer.schema.release).toMatchObject({
|
||||
automaticRelease: scheduledRelease.earliestReleaseDate,
|
||||
autoReleaseDate: scheduledRelease.earliestReleaseDate,
|
||||
});
|
||||
});
|
||||
|
||||
it('modifies automatic release', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleaseType(automaticRelease);
|
||||
writer.setVersionRelease(automaticRelease);
|
||||
expect(writer.schema.release).toMatchObject({
|
||||
automaticRelease: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('modifies manual release', () => {
|
||||
it('does not modify manual release', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleaseType(manualRelease);
|
||||
expect(writer.schema.release).toMatchObject({
|
||||
automaticRelease: false,
|
||||
});
|
||||
writer.setVersionRelease(manualRelease);
|
||||
expect(writer.schema.release).toBeUndefined();
|
||||
});
|
||||
|
||||
it('does not overwrite phasedRelease', () => {
|
||||
it('overwrites all release fields', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleasePhased(phasedRelease);
|
||||
writer.setVersionReleaseType(manualRelease);
|
||||
expect(writer.schema.release).toMatchObject({
|
||||
automaticRelease: false,
|
||||
phasedRelease: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setVersionReleasePhased', () => {
|
||||
it('modifies enabled phased release', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleasePhased(phasedRelease);
|
||||
expect(writer.schema.release).toHaveProperty('phasedRelease', true);
|
||||
});
|
||||
|
||||
it('deletes phased release when undefined', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleasePhased(phasedRelease);
|
||||
writer.setVersionReleasePhased(undefined);
|
||||
expect(writer.schema.release).not.toHaveProperty('phasedRelease');
|
||||
});
|
||||
|
||||
it('does not overwrite automaticRelease', () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
writer.setVersionReleaseType(automaticRelease);
|
||||
writer.setVersionReleasePhased(phasedRelease);
|
||||
expect(writer.schema.release).toMatchObject({
|
||||
automaticRelease: true,
|
||||
phasedRelease: true,
|
||||
});
|
||||
writer.setVersionRelease(scheduledRelease);
|
||||
writer.setVersionRelease(automaticRelease);
|
||||
expect(writer.schema.release).not.toHaveProperty('autoReleaseDate');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ import {
|
||||
AppStoreReviewDetail,
|
||||
AppStoreVersion,
|
||||
AppStoreVersionLocalization,
|
||||
AppStoreVersionPhasedRelease,
|
||||
CategoryIds,
|
||||
PhasedReleaseState,
|
||||
Rating,
|
||||
ReleaseType,
|
||||
} from '@expo/apple-utils';
|
||||
@@ -113,26 +111,19 @@ export class AppleConfigReader {
|
||||
public getVersion(): Partial<
|
||||
Omit<AttributesOf<AppStoreVersion>, 'releaseType' | 'earliestReleaseDate'>
|
||||
> | null {
|
||||
const attributes: Pick<AttributesOf<AppStoreVersion>, 'versionString' | 'copyright'> = {
|
||||
versionString: this.schema.version ?? '',
|
||||
copyright: this.schema.copyright ?? null,
|
||||
};
|
||||
|
||||
const hasValues = Object.values(attributes).some(Boolean);
|
||||
return hasValues ? attributes : null;
|
||||
return this.schema.copyright ? { copyright: this.schema.copyright } : null;
|
||||
}
|
||||
|
||||
public getVersionReleaseType(): Partial<
|
||||
public getVersionRelease(): Partial<
|
||||
Pick<AttributesOf<AppStoreVersion>, 'releaseType' | 'earliestReleaseDate'>
|
||||
> | null {
|
||||
const { release } = this.schema;
|
||||
|
||||
if (typeof release?.automaticRelease === 'string') {
|
||||
if (release?.autoReleaseDate) {
|
||||
return {
|
||||
releaseType: ReleaseType.SCHEDULED,
|
||||
// Convert time format to 2020-06-17T12:00:00-07:00, if that fails, try the date anyways.
|
||||
earliestReleaseDate:
|
||||
removeDatePrecision(release.automaticRelease)?.toISOString() ?? release.automaticRelease,
|
||||
// Convert time format to 2020-06-17T12:00:00-07:00
|
||||
earliestReleaseDate: removeDatePrecision(release.autoReleaseDate)?.toISOString() ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,21 +144,6 @@ export class AppleConfigReader {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getVersionReleasePhased(): Pick<
|
||||
AttributesOf<AppStoreVersionPhasedRelease>,
|
||||
'phasedReleaseState'
|
||||
> | null {
|
||||
if (this.schema.release?.phasedRelease === true) {
|
||||
return {
|
||||
phasedReleaseState: PhasedReleaseState.ACTIVE,
|
||||
};
|
||||
}
|
||||
|
||||
// When phased release is turned off, we need to delete the phased release request.
|
||||
// There is no concept (yet) of pausing the phased release through EAS metadata.
|
||||
return null;
|
||||
}
|
||||
|
||||
public getVersionLocale(
|
||||
locale: string,
|
||||
context: { versionIsFirst: boolean }
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
AppStoreReviewDetail,
|
||||
AppStoreVersion,
|
||||
AppStoreVersionLocalization,
|
||||
AppStoreVersionPhasedRelease,
|
||||
Rating,
|
||||
ReleaseType,
|
||||
} from '@expo/apple-utils';
|
||||
@@ -109,43 +108,28 @@ export class AppleConfigWriter {
|
||||
public setVersion(
|
||||
attributes: Omit<AttributesOf<AppStoreVersion>, 'releaseType' | 'earliestReleaseDate'>
|
||||
): void {
|
||||
this.schema.version = optional(attributes.versionString);
|
||||
this.schema.copyright = optional(attributes.copyright);
|
||||
}
|
||||
|
||||
public setVersionReleaseType(
|
||||
public setVersionRelease(
|
||||
attributes: Pick<AttributesOf<AppStoreVersion>, 'releaseType' | 'earliestReleaseDate'>
|
||||
): void {
|
||||
if (attributes.releaseType === ReleaseType.SCHEDULED && attributes.earliestReleaseDate) {
|
||||
if (attributes.releaseType === ReleaseType.SCHEDULED) {
|
||||
this.schema.release = {
|
||||
...this.schema.release,
|
||||
automaticRelease: attributes.earliestReleaseDate,
|
||||
autoReleaseDate: optional(attributes.earliestReleaseDate),
|
||||
};
|
||||
}
|
||||
|
||||
if (attributes.releaseType === ReleaseType.AFTER_APPROVAL) {
|
||||
this.schema.release = {
|
||||
...this.schema.release,
|
||||
automaticRelease: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (attributes.releaseType === ReleaseType.MANUAL) {
|
||||
this.schema.release = {
|
||||
...this.schema.release,
|
||||
automaticRelease: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public setVersionReleasePhased(attributes?: AttributesOf<AppStoreVersionPhasedRelease>): void {
|
||||
if (!attributes) {
|
||||
delete this.schema.release?.phasedRelease;
|
||||
} else {
|
||||
this.schema.release = {
|
||||
...this.schema.release,
|
||||
phasedRelease: true,
|
||||
};
|
||||
// ReleaseType.MANUAL is the default behavior, so we don't need to configure it.
|
||||
// Setting `"automaticRelease": false` is a bit confusing for people who don't know what automaticRelease does.
|
||||
this.schema.release = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ import {
|
||||
AppStoreState,
|
||||
AppStoreVersion,
|
||||
AppStoreVersionLocalization,
|
||||
AppStoreVersionPhasedRelease,
|
||||
PhasedReleaseState,
|
||||
} from '@expo/apple-utils';
|
||||
import nock from 'nock';
|
||||
|
||||
@@ -29,12 +27,6 @@ describe(AppVersionTask, () => {
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query((params: any) => params['filter[appStoreState]'] !== AppStoreState.READY_FOR_SALE)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-200.json'))
|
||||
// Respond to version.getPhasedReleaseAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/appStoreVersionPhasedRelease`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionPhasedRelease-200-empty.json')
|
||||
)
|
||||
// Respond to version.getLocalizationsAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
@@ -68,12 +60,6 @@ describe(AppVersionTask, () => {
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query(true)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-200.json'))
|
||||
// Respond to version.getPhasedReleaseAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/appStoreVersionPhasedRelease`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionPhasedRelease-200-empty.json')
|
||||
)
|
||||
// Respond to version.getLocalizationsAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
@@ -103,12 +89,6 @@ describe(AppVersionTask, () => {
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query((params: any) => params['filter[appStoreState]'] !== AppStoreState.READY_FOR_SALE)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-200.json'))
|
||||
// Respond to version.getPhasedReleaseAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/appStoreVersionPhasedRelease`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionPhasedRelease-200-empty.json')
|
||||
)
|
||||
// Respond to version.getLocalizationsAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
@@ -138,12 +118,6 @@ describe(AppVersionTask, () => {
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query((params: any) => params['filter[appStoreState]'] !== AppStoreState.READY_FOR_SALE)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-multiple-200.json'))
|
||||
// Respond to version.getPhasedReleaseAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/appStoreVersionPhasedRelease`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionPhasedRelease-200-empty.json')
|
||||
)
|
||||
// Respond to version.getLocalizationsAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
@@ -160,97 +134,6 @@ describe(AppVersionTask, () => {
|
||||
expect(context.versionIsFirst).toBeFalsy();
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('fetches the existing version from options', async () => {
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
// Respond to app.getAppStoreVersionsAsync in findEditAppStoreVersionAsync
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query(true)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-multiple-200'))
|
||||
// Respond to app.getAppStoreVersionsAsync
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query((params: any) => params['filter[appStoreState]'] !== AppStoreState.READY_FOR_SALE)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-multiple-200.json'))
|
||||
// Respond to version.getLocalizationsAsync (for version 2.0)
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_2/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionLocalizations-200.json')
|
||||
)
|
||||
// Respond to version.getPhasedReleaseAsync
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_2/appStoreVersionPhasedRelease`)
|
||||
.reply(200, {
|
||||
data: {
|
||||
id: 'APP_STORE_VERSION_PHASED_RELEASE_1',
|
||||
type: AppStoreVersionPhasedRelease.type,
|
||||
attributes: {
|
||||
phasedReleaseState: PhasedReleaseState.INACTIVE,
|
||||
currentDayNumber: null,
|
||||
startDate: null,
|
||||
totalPauseDuration: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const context: PartialAppleData = {
|
||||
app: new App(requestContext, 'stub-id', {} as any),
|
||||
};
|
||||
|
||||
await new AppVersionTask({ version: '2.0' }).prepareAsync({ context });
|
||||
|
||||
expect(context.version).toBeInstanceOf(AppStoreVersion);
|
||||
expect(context.version?.attributes).toHaveProperty('versionString', '2.0');
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('creates the non-existing version from options', async () => {
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
// Respond to app.getAppStoreVersionsAsync in findEditAppStoreVersionAsync
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query(true)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-multiple-200.json'))
|
||||
// Respond to app.getAppStoreVersionsAsync in createOrUpdateEditAppStoreVersionAsync
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query(true)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-200-empty.json'))
|
||||
// Respond to app.createVersionAsync in createOrUpdateEditAppStoreVersionAsync
|
||||
.post(`/v1/${AppStoreVersion.type}`)
|
||||
.reply(201, require('./fixtures/appStoreVersions/post-201.json'))
|
||||
// Respond to app.getAppStoreVersionsAsync
|
||||
.get(`/v1/${App.type}/stub-id/${AppStoreVersion.type}`)
|
||||
.query((params: any) => params['filter[appStoreState]'] !== AppStoreState.READY_FOR_SALE)
|
||||
.reply(200, require('./fixtures/apps/get-appStoreVersions-count-multiple-200.json'))
|
||||
// Respond to version.getLocalizationsAsync (for version 3.0)
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_3/${AppStoreVersionLocalization.type}`)
|
||||
.reply(
|
||||
200,
|
||||
require('./fixtures/appStoreVersions/get-appStoreVersionLocalizations-200.json')
|
||||
)
|
||||
// Respond to version.getPhasedReleaseAsync (for version 3.0)
|
||||
.get(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_3/appStoreVersionPhasedRelease`)
|
||||
.reply(200, {
|
||||
data: {
|
||||
id: 'APP_STORE_VERSION_PHASED_RELEASE_1',
|
||||
type: AppStoreVersionPhasedRelease.type,
|
||||
attributes: {
|
||||
phasedReleaseState: PhasedReleaseState.INACTIVE,
|
||||
currentDayNumber: null,
|
||||
startDate: null,
|
||||
totalPauseDuration: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const context: PartialAppleData = {
|
||||
app: new App(requestContext, 'stub-id', {} as any),
|
||||
};
|
||||
|
||||
await new AppVersionTask({ version: '3.0' }).prepareAsync({ context });
|
||||
|
||||
expect(context.version).toBeInstanceOf(AppStoreVersion);
|
||||
expect(context.version?.attributes).toHaveProperty('versionString', '3.0');
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadAsync', () => {
|
||||
@@ -275,7 +158,7 @@ describe(AppVersionTask, () => {
|
||||
expect(writer.setVersion).toBeCalledWith(version.attributes);
|
||||
});
|
||||
|
||||
it('sets version release type when loaded', async () => {
|
||||
it('sets version release when loaded', async () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
const version = new AppStoreVersion(requestContext, 'stub-id', {} as any);
|
||||
|
||||
@@ -284,24 +167,7 @@ describe(AppVersionTask, () => {
|
||||
context: { version, versionLocales: [] } as any,
|
||||
});
|
||||
|
||||
expect(writer.setVersionReleaseType).toBeCalledWith(version.attributes);
|
||||
});
|
||||
|
||||
it('sets version phased release when loaded', async () => {
|
||||
const writer = new AppleConfigWriter();
|
||||
const version = new AppStoreVersion(requestContext, 'stub-id', {} as any);
|
||||
const versionPhasedRelease = new AppStoreVersionPhasedRelease(
|
||||
requestContext,
|
||||
'stub-id',
|
||||
{} as any
|
||||
);
|
||||
|
||||
await new AppVersionTask().downloadAsync({
|
||||
config: writer,
|
||||
context: { version, versionPhasedRelease, versionLocales: [] } as any,
|
||||
});
|
||||
|
||||
expect(writer.setVersionReleasePhased).toBeCalledWith(versionPhasedRelease.attributes);
|
||||
expect(writer.setVersionRelease).toBeCalledWith(version.attributes);
|
||||
});
|
||||
|
||||
it('skips when no locales are loaded', async () => {
|
||||
@@ -354,7 +220,7 @@ describe(AppVersionTask, () => {
|
||||
release: { automaticRelease: true },
|
||||
});
|
||||
|
||||
const attributes = { ...config.getVersion(), ...config.getVersionReleaseType() };
|
||||
const attributes = { ...config.getVersion(), ...config.getVersionRelease() };
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
// Respond to version.updateAsync
|
||||
.patch(`/v1/${AppStoreVersion.type}/APP_STORE_VERSION_1`)
|
||||
@@ -376,81 +242,5 @@ describe(AppVersionTask, () => {
|
||||
expect(context.version?.attributes).toMatchObject(attributes);
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('creates phased release when enabled in config', async () => {
|
||||
const config = new AppleConfigReader({ release: { phasedRelease: true } });
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
.post(`/v1/${AppStoreVersionPhasedRelease.type}`)
|
||||
.reply(201, {
|
||||
data: {
|
||||
id: 'APP_STORE_VERSION_PHASED_RELEASE_1',
|
||||
type: AppStoreVersionPhasedRelease.type,
|
||||
attributes: {
|
||||
phasedReleaseState: PhasedReleaseState.ACTIVE,
|
||||
currentDayNumber: null,
|
||||
startDate: null,
|
||||
totalPauseDuration: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const context: PartialAppleData = {
|
||||
app: new App(requestContext, 'stub-id', {} as any),
|
||||
version: new AppStoreVersion(requestContext, 'APP_STORE_VERSION_1', {} as any),
|
||||
versionPhasedRelease: null, // Not enabled yet
|
||||
};
|
||||
|
||||
await new AppVersionTask().uploadAsync({ context: context as AppleData, config });
|
||||
|
||||
expect(context.versionPhasedRelease).toBeInstanceOf(AppStoreVersionPhasedRelease);
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('deletes active phased release when disabled in config', async () => {
|
||||
const config = new AppleConfigReader({ release: { phasedRelease: false } });
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
.delete(`/v1/${AppStoreVersionPhasedRelease.type}/APP_STORE_VERSION_PHASED_RELEASE_1`)
|
||||
.reply(204);
|
||||
|
||||
const context: PartialAppleData = {
|
||||
app: new App(requestContext, 'stub-id', {} as any),
|
||||
version: new AppStoreVersion(requestContext, 'APP_STORE_VERSION_1', {} as any),
|
||||
// Enabled, and not completed yet
|
||||
versionPhasedRelease: new AppStoreVersionPhasedRelease(
|
||||
requestContext,
|
||||
'APP_STORE_VERSION_PHASED_RELEASE_1',
|
||||
{ phasedReleaseState: PhasedReleaseState.ACTIVE } as any
|
||||
),
|
||||
};
|
||||
|
||||
await new AppVersionTask().uploadAsync({ context: context as AppleData, config });
|
||||
|
||||
expect(context.versionPhasedRelease).toBeNull();
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not delete completed phased release when disabled in config', async () => {
|
||||
const config = new AppleConfigReader({ release: { phasedRelease: false } });
|
||||
const scope = nock('https://api.appstoreconnect.apple.com')
|
||||
.delete(`/v1/${AppStoreVersionPhasedRelease.type}/APP_STORE_VERSION_PHASED_RELEASE_1`)
|
||||
.reply(204);
|
||||
|
||||
const context: PartialAppleData = {
|
||||
app: new App(requestContext, 'stub-id', {} as any),
|
||||
version: new AppStoreVersion(requestContext, 'APP_STORE_VERSION_1', {} as any),
|
||||
// Enabled, and not completed yet
|
||||
versionPhasedRelease: new AppStoreVersionPhasedRelease(
|
||||
requestContext,
|
||||
'APP_STORE_VERSION_PHASED_RELEASE_1',
|
||||
{ phasedReleaseState: PhasedReleaseState.COMPLETE } as any
|
||||
),
|
||||
};
|
||||
|
||||
await new AppVersionTask().uploadAsync({ context: context as AppleData, config });
|
||||
|
||||
expect(context.versionPhasedRelease).toBe(context.versionPhasedRelease);
|
||||
expect(scope.isDone()).toBeFalsy();
|
||||
nock.cleanAll();
|
||||
});
|
||||
});
|
||||
});
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"data": null,
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_PHASED_RELEASE_1/appStoreVersionPhasedRelease"
|
||||
}
|
||||
}
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"type": "appStoreVersions",
|
||||
"id": "APP_STORE_VERSION_3",
|
||||
"attributes": {
|
||||
"platform": "IOS",
|
||||
"versionString": "3.0",
|
||||
"appStoreState": "PREPARE_FOR_SUBMISSION",
|
||||
"storeIcon": null,
|
||||
"watchStoreIcon": null,
|
||||
"copyright": "2022 name",
|
||||
"releaseType": "SCHEDULED",
|
||||
"earliestReleaseDate": "2022-03-14T17:00:00-07:00",
|
||||
"usesIdfa": null,
|
||||
"isWatchOnly": false,
|
||||
"downloadable": true,
|
||||
"createdDate": "2022-02-21T09:26:24-08:00",
|
||||
"promotedDate": null
|
||||
},
|
||||
"relationships": {
|
||||
"ageRatingDeclaration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/ageRatingDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/ageRatingDeclaration"
|
||||
}
|
||||
},
|
||||
"appStoreVersionLocalizations": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreVersionLocalizations",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreVersionLocalizations"
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/build",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/build"
|
||||
}
|
||||
},
|
||||
"appStoreVersionPhasedRelease": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreVersionPhasedRelease",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreVersionPhasedRelease"
|
||||
}
|
||||
},
|
||||
"gameCenterConfiguration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/gameCenterConfiguration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/gameCenterConfiguration"
|
||||
}
|
||||
},
|
||||
"routingAppCoverage": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/routingAppCoverage",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/routingAppCoverage"
|
||||
}
|
||||
},
|
||||
"appStoreReviewDetail": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreReviewDetail",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreReviewDetail"
|
||||
}
|
||||
},
|
||||
"appStoreVersionSubmission": {
|
||||
"data": null,
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreVersionSubmission",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreVersionSubmission"
|
||||
}
|
||||
},
|
||||
"resetRatingsRequest": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/resetRatingsRequest",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/resetRatingsRequest"
|
||||
}
|
||||
},
|
||||
"idfaDeclaration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/idfaDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/idfaDeclaration"
|
||||
}
|
||||
},
|
||||
"appClipDefaultExperience": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appClipDefaultExperience",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appClipDefaultExperience"
|
||||
}
|
||||
},
|
||||
"appStoreVersionStateChanges": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreVersionStateChanges",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreVersionStateChanges"
|
||||
}
|
||||
},
|
||||
"appStoreVersionExperiments": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/relationships/appStoreVersionExperiments",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3/appStoreVersionExperiments"
|
||||
}
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/apps/1611050397/appStoreVersions?include=appStoreVersionSubmission&filter%5Bplatform%5D=IOS&filter%5BappStoreState%5D=DEVELOPER_REJECTED%2CINVALID_BINARY%2CMETADATA_REJECTED%2CPREPARE_FOR_SUBMISSION%2CREJECTED%2CWAITING_FOR_REVIEW"
|
||||
},
|
||||
"meta": {
|
||||
"paging": {
|
||||
"total": 1,
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"data": null,
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/apps/1611050397/appStoreVersions?include=appStoreVersionSubmission&filter%5Bplatform%5D=IOS&filter%5BappStoreState%5D=DEVELOPER_REJECTED%2CINVALID_BINARY%2CMETADATA_REJECTED%2CPREPARE_FOR_SUBMISSION%2CREJECTED%2CWAITING_FOR_REVIEW"
|
||||
},
|
||||
"meta": {
|
||||
"paging": {
|
||||
"total": 0,
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-29
@@ -105,10 +105,10 @@
|
||||
},
|
||||
{
|
||||
"type": "appStoreVersions",
|
||||
"id": "APP_STORE_VERSION_2",
|
||||
"id": "APP-STORE-VERSION-2",
|
||||
"attributes": {
|
||||
"platform": "IOS",
|
||||
"versionString": "2.0",
|
||||
"versionString": "1.1",
|
||||
"appStoreState": "PREPARE_FOR_SUBMISSION",
|
||||
"storeIcon": null,
|
||||
"watchStoreIcon": null,
|
||||
@@ -124,86 +124,86 @@
|
||||
"relationships": {
|
||||
"ageRatingDeclaration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/ageRatingDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/ageRatingDeclaration"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/ageRatingDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/ageRatingDeclaration"
|
||||
}
|
||||
},
|
||||
"appStoreVersionLocalizations": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreVersionLocalizations",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreVersionLocalizations"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreVersionLocalizations",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreVersionLocalizations"
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/build",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/build"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/build",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/build"
|
||||
}
|
||||
},
|
||||
"appStoreVersionPhasedRelease": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreVersionPhasedRelease",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreVersionPhasedRelease"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreVersionPhasedRelease",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreVersionPhasedRelease"
|
||||
}
|
||||
},
|
||||
"gameCenterConfiguration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/gameCenterConfiguration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/gameCenterConfiguration"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/gameCenterConfiguration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/gameCenterConfiguration"
|
||||
}
|
||||
},
|
||||
"routingAppCoverage": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/routingAppCoverage",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/routingAppCoverage"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/routingAppCoverage",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/routingAppCoverage"
|
||||
}
|
||||
},
|
||||
"appStoreReviewDetail": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreReviewDetail",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreReviewDetail"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreReviewDetail",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreReviewDetail"
|
||||
}
|
||||
},
|
||||
"appStoreVersionSubmission": {
|
||||
"data": null,
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreVersionSubmission",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreVersionSubmission"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreVersionSubmission",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreVersionSubmission"
|
||||
}
|
||||
},
|
||||
"resetRatingsRequest": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/resetRatingsRequest",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/resetRatingsRequest"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/resetRatingsRequest",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/resetRatingsRequest"
|
||||
}
|
||||
},
|
||||
"idfaDeclaration": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/idfaDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/idfaDeclaration"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/idfaDeclaration",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/idfaDeclaration"
|
||||
}
|
||||
},
|
||||
"appClipDefaultExperience": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appClipDefaultExperience",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appClipDefaultExperience"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appClipDefaultExperience",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appClipDefaultExperience"
|
||||
}
|
||||
},
|
||||
"appStoreVersionStateChanges": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreVersionStateChanges",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreVersionStateChanges"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreVersionStateChanges",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreVersionStateChanges"
|
||||
}
|
||||
},
|
||||
"appStoreVersionExperiments": {
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/relationships/appStoreVersionExperiments",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2/appStoreVersionExperiments"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/relationships/appStoreVersionExperiments",
|
||||
"related": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2/appStoreVersionExperiments"
|
||||
}
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP_STORE_VERSION_2"
|
||||
"self": "https://appstoreconnect.apple.com/iris/v1/appStoreVersions/APP-STORE-VERSION-2"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
App,
|
||||
AppStoreState,
|
||||
AppStoreVersion,
|
||||
AppStoreVersionLocalization,
|
||||
AppStoreVersionPhasedRelease,
|
||||
PhasedReleaseState,
|
||||
Platform,
|
||||
} from '@expo/apple-utils';
|
||||
import { App, AppStoreVersion, AppStoreVersionLocalization, Platform } from '@expo/apple-utils';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
|
||||
@@ -20,8 +12,6 @@ export type AppVersionOptions = {
|
||||
editLive: boolean;
|
||||
/** The platform to use (defaults to IOS) */
|
||||
platform: Platform;
|
||||
/** A version to create or select, if defined in the store configuration */
|
||||
version: string | null;
|
||||
};
|
||||
|
||||
export type AppVersionData = {
|
||||
@@ -33,8 +23,6 @@ export type AppVersionData = {
|
||||
versionIsFirst: boolean;
|
||||
/** All version locales that should be, or are enabled */
|
||||
versionLocales: AppStoreVersionLocalization[];
|
||||
/** The (existing) phased release configuration, when set */
|
||||
versionPhasedRelease: AppStoreVersionPhasedRelease | null;
|
||||
};
|
||||
|
||||
export class AppVersionTask extends AppleTask {
|
||||
@@ -45,7 +33,6 @@ export class AppVersionTask extends AppleTask {
|
||||
this.options = {
|
||||
platform: options.platform ?? Platform.IOS,
|
||||
editLive: options.editLive ?? false,
|
||||
version: options.version ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,15 +50,13 @@ export class AppVersionTask extends AppleTask {
|
||||
context.versionIsFirst = versionIsFirst;
|
||||
context.versionIsLive = versionIsLive;
|
||||
context.versionLocales = await version.getLocalizationsAsync();
|
||||
context.versionPhasedRelease = await version.getPhasedReleaseAsync();
|
||||
}
|
||||
|
||||
public async downloadAsync({ config, context }: TaskDownloadOptions): Promise<void> {
|
||||
assert(context.version, `App version not initialized, can't download version`);
|
||||
|
||||
config.setVersion(context.version.attributes);
|
||||
config.setVersionReleaseType(context.version.attributes);
|
||||
config.setVersionReleasePhased(context.versionPhasedRelease?.attributes);
|
||||
config.setVersionRelease(context.version.attributes);
|
||||
|
||||
for (const locale of context.versionLocales) {
|
||||
config.setVersionLocale(locale.attributes);
|
||||
@@ -79,31 +64,14 @@ export class AppVersionTask extends AppleTask {
|
||||
}
|
||||
|
||||
public async uploadAsync({ config, context }: TaskUploadOptions): Promise<void> {
|
||||
const version = config.getVersion();
|
||||
|
||||
if (!context.version && version?.versionString) {
|
||||
context.version = await logAsync(
|
||||
() => {
|
||||
return context.app.createVersionAsync({
|
||||
versionString: version.versionString!,
|
||||
platform: this.options.platform,
|
||||
});
|
||||
},
|
||||
{
|
||||
pending: `Creating new version ${chalk.bold(version.versionString)}...`,
|
||||
success: `Created new version ${chalk.bold(version.versionString)}`,
|
||||
failure: `Failed creating new version ${chalk.bold(version.versionString)}`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
assert(context.version, `App version not initialized, can't update version`);
|
||||
const { versionString } = context.version.attributes;
|
||||
|
||||
const release = config.getVersionReleaseType();
|
||||
const version = config.getVersion();
|
||||
const release = config.getVersionRelease();
|
||||
if (!version && !release) {
|
||||
Log.log(chalk`{dim - Skipped version and release update, not configured}`);
|
||||
} else {
|
||||
const { versionString } = context.version.attributes;
|
||||
const description = [version && 'version', release && 'release']
|
||||
.filter(Boolean)
|
||||
.join(' and ');
|
||||
@@ -118,27 +86,6 @@ export class AppVersionTask extends AppleTask {
|
||||
);
|
||||
}
|
||||
|
||||
const phasedRelease = config.getVersionReleasePhased();
|
||||
if (!phasedRelease && shouldDeletePhasedRelease(context.versionPhasedRelease)) {
|
||||
// if phased release was enabled, but now disabled, we need to remove it
|
||||
await logAsync(() => context.versionPhasedRelease!.deleteAsync(), {
|
||||
pending: `Disabling phased release for ${chalk.bold(versionString)}...`,
|
||||
success: `Disabled phased release for ${chalk.bold(versionString)}`,
|
||||
failure: `Failed disabling phased release for ${chalk.bold(versionString)}`,
|
||||
});
|
||||
context.versionPhasedRelease = null;
|
||||
} else if (phasedRelease && !context.versionPhasedRelease) {
|
||||
// if phased release was not yet set, but now enabled, we need to create it
|
||||
context.versionPhasedRelease = await logAsync(
|
||||
() => context.version.createPhasedReleaseAsync({ state: phasedRelease.phasedReleaseState }),
|
||||
{
|
||||
pending: `Enabling phased release for ${chalk.bold(versionString)}...`,
|
||||
success: `Enabled phased release for ${chalk.bold(versionString)}`,
|
||||
failure: `Failed enabling phased release for ${chalk.bold(versionString)}`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const locales = config.getLocales();
|
||||
if (locales.length <= 0) {
|
||||
Log.log(chalk`{dim - Skipped localized version update, no locales configured}`);
|
||||
@@ -176,12 +123,12 @@ export class AppVersionTask extends AppleTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the AppStoreVersion instance, either from the store config, live, or editable version.
|
||||
* Resolve the AppStoreVersion instance, either from live or editable version.
|
||||
* This also checks if this is the first version, which disallow release notes.
|
||||
*/
|
||||
async function resolveVersionAsync(
|
||||
app: App,
|
||||
{ editLive, platform, version: versionString }: AppVersionOptions
|
||||
{ editLive, platform }: AppVersionOptions
|
||||
): Promise<{
|
||||
version: AppStoreVersion | null;
|
||||
versionIsLive: boolean;
|
||||
@@ -190,19 +137,7 @@ async function resolveVersionAsync(
|
||||
let version: AppStoreVersion | null = null;
|
||||
let versionIsLive = false;
|
||||
|
||||
if (versionString) {
|
||||
version = await findEditAppStoreVersionAsync(app, { platform, version: versionString });
|
||||
if (!version) {
|
||||
version = await createOrUpdateEditAppStoreVersionAsync(app, {
|
||||
platform,
|
||||
version: versionString,
|
||||
});
|
||||
}
|
||||
|
||||
versionIsLive = version?.attributes.appStoreState === AppStoreState.READY_FOR_SALE;
|
||||
}
|
||||
|
||||
if (!version && editLive) {
|
||||
if (editLive) {
|
||||
version = await app.getLiveAppStoreVersionAsync({ platform });
|
||||
versionIsLive = !!version;
|
||||
}
|
||||
@@ -221,76 +156,3 @@ async function resolveVersionAsync(
|
||||
versionIsFirst: versions.length === 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we can, and should, delete the phased release instance.
|
||||
* This returns true if the instance exist, and has one of the states below:
|
||||
* - PhasedReleaseState.INACTIVE
|
||||
* - PhasedReleaseState.ACTIVE
|
||||
* - PhasedReleaseState.PAUSED
|
||||
*/
|
||||
function shouldDeletePhasedRelease(phasedRelease: AppStoreVersionPhasedRelease | null): boolean {
|
||||
if (
|
||||
!phasedRelease ||
|
||||
phasedRelease.attributes.phasedReleaseState === PhasedReleaseState.COMPLETE
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for editable app store versions that matches the `versionString` option.
|
||||
* When nothing is found, it will return `null`, and a new version should be created.
|
||||
*/
|
||||
async function findEditAppStoreVersionAsync(
|
||||
app: App,
|
||||
options: { version: string; platform: Platform }
|
||||
): Promise<AppStoreVersion | null> {
|
||||
if (options.version) {
|
||||
const versions = await app.getAppStoreVersionsAsync({
|
||||
query: {
|
||||
limit: 200,
|
||||
filter: {
|
||||
platform: options.platform,
|
||||
appStoreState: [
|
||||
AppStoreState.PREPARE_FOR_SUBMISSION,
|
||||
AppStoreState.DEVELOPER_REJECTED,
|
||||
AppStoreState.REJECTED,
|
||||
AppStoreState.METADATA_REJECTED,
|
||||
AppStoreState.WAITING_FOR_REVIEW,
|
||||
AppStoreState.INVALID_BINARY,
|
||||
].join(','),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const version = versions.find(model => model.attributes.versionString === options.version);
|
||||
if (version) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can reuse an existing editable app version that has not been published yet.
|
||||
* If not, it creates a new version based on the version string.
|
||||
*/
|
||||
async function createOrUpdateEditAppStoreVersionAsync(
|
||||
app: App,
|
||||
options: { version: string; platform: Platform }
|
||||
): Promise<AppStoreVersion> {
|
||||
const version = await app.getEditAppStoreVersionAsync({ platform: options.platform });
|
||||
|
||||
if (version) {
|
||||
return await version.updateAsync({ versionString: options.version });
|
||||
}
|
||||
|
||||
return await app.createVersionAsync({
|
||||
versionString: options.version,
|
||||
platform: options.platform,
|
||||
});
|
||||
}
|
||||
@@ -3,23 +3,11 @@ import { AppleTask } from '../task';
|
||||
import { AgeRatingTask } from './age-rating';
|
||||
import { AppInfoTask } from './app-info';
|
||||
import { AppReviewDetailTask } from './app-review-detail';
|
||||
import { AppVersionOptions, AppVersionTask } from './app-version';
|
||||
|
||||
type AppleTaskOptions = {
|
||||
version?: AppVersionOptions['version'];
|
||||
};
|
||||
import { AppVersionTask } from './app-version';
|
||||
|
||||
/**
|
||||
* List of all eligible tasks to sync local store configuration to the App store.
|
||||
*/
|
||||
export function createAppleTasks(
|
||||
_ctx: MetadataContext,
|
||||
{ version }: AppleTaskOptions = {}
|
||||
): AppleTask[] {
|
||||
return [
|
||||
new AppVersionTask({ version }),
|
||||
new AppInfoTask(),
|
||||
new AgeRatingTask(),
|
||||
new AppReviewDetailTask(),
|
||||
];
|
||||
export function createAppleTasks(_ctx: MetadataContext): AppleTask[] {
|
||||
return [new AppVersionTask(), new AppInfoTask(), new AgeRatingTask(), new AppReviewDetailTask()];
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import type { AgeRatingDeclarationProps } from '@expo/apple-utils';
|
||||
export type AppleLocale = string;
|
||||
|
||||
export interface AppleMetadata {
|
||||
version?: string;
|
||||
copyright?: string;
|
||||
info?: Record<AppleLocale, AppleInfo>;
|
||||
categories?: AppleCategory;
|
||||
@@ -19,8 +18,13 @@ export type AppleAdvisory = Partial<AgeRatingDeclarationProps>;
|
||||
export type AppleCategory = (string | string[])[];
|
||||
|
||||
export interface AppleRelease {
|
||||
automaticRelease?: boolean | string;
|
||||
phasedRelease?: boolean;
|
||||
isPhasedReleaseEnabled?: boolean;
|
||||
shouldResetRatings?: boolean;
|
||||
autoReleaseDate?: number | string;
|
||||
automaticRelease?: boolean;
|
||||
usesThirdPartyContent?: boolean;
|
||||
/** Alternative to setting `ITSAppUsesNonExemptEncryption` in the binary's `Info.plist`. */
|
||||
usesNonExemptEncryption?: boolean;
|
||||
}
|
||||
|
||||
export interface AppleInfo {
|
||||
|
||||
@@ -51,12 +51,7 @@ export async function uploadMetadataAsync(
|
||||
|
||||
const errors: Error[] = [];
|
||||
const config = createAppleReader(fileData);
|
||||
const tasks = createAppleTasks(metadataCtx, {
|
||||
// We need to resolve a different version as soon as possible.
|
||||
// This version is the parent model of all changes we are going to push.
|
||||
version: config.getVersion()?.versionString,
|
||||
});
|
||||
|
||||
const tasks = createAppleTasks(metadataCtx);
|
||||
const taskCtx = { app };
|
||||
|
||||
for (const task of tasks) {
|
||||
|
||||
@@ -47,23 +47,6 @@ export async function selectRequestedPlatformAsync(platform?: string): Promise<R
|
||||
return requestedPlatform;
|
||||
}
|
||||
|
||||
export async function selectPlatformAsync(platform?: string): Promise<Platform> {
|
||||
if (platform && Object.values(Platform).includes(platform.toLowerCase() as Platform)) {
|
||||
return platform.toLowerCase() as Platform;
|
||||
}
|
||||
|
||||
const { resolvedPlatform } = await promptAsync({
|
||||
type: 'select',
|
||||
message: 'Select platform',
|
||||
name: 'resolvedPlatform',
|
||||
choices: [
|
||||
{ title: 'Android', value: Platform.ANDROID },
|
||||
{ title: 'iOS', value: Platform.IOS },
|
||||
],
|
||||
});
|
||||
return resolvedPlatform;
|
||||
}
|
||||
|
||||
export function toPlatforms(requestedPlatform: RequestedPlatform): Platform[] {
|
||||
if (requestedPlatform === RequestedPlatform.All) {
|
||||
return [Platform.ANDROID, Platform.IOS];
|
||||
|
||||
@@ -88,7 +88,6 @@ describe('MetadataJoi', () => {
|
||||
expect(error).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe(guessContentTypeFromExtension, () => {
|
||||
it('returns the correct content type for jpg', () => {
|
||||
expect(guessContentTypeFromExtension('jpg')).toBe('image/jpeg');
|
||||
@@ -369,8 +368,20 @@ describe(uploadAssetsAsync, () => {
|
||||
const iosBundlePath = uuidv4();
|
||||
const dummyFilePath = uuidv4();
|
||||
const userDefinedPath = uuidv4();
|
||||
const testProjectId = uuidv4();
|
||||
const expectedAssetLimit = 1400;
|
||||
|
||||
beforeAll(async () => {
|
||||
await fs.writeFile(androidBundlePath, publishBundles.android.code);
|
||||
await fs.writeFile(iosBundlePath, publishBundles.ios.code);
|
||||
await fs.writeFile(dummyFilePath, dummyFileBuffer);
|
||||
await fs.writeFile(userDefinedPath, 'I am an octet stream');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.remove(androidBundlePath);
|
||||
await fs.remove(iosBundlePath);
|
||||
await fs.remove(dummyFilePath);
|
||||
await fs.remove(userDefinedPath);
|
||||
});
|
||||
|
||||
const userDefinedAsset = {
|
||||
type: 'bundle',
|
||||
@@ -396,29 +407,10 @@ describe(uploadAssetsAsync, () => {
|
||||
assets: [userDefinedAsset, { type: 'jpg', contentType: 'image/jpeg', path: dummyFilePath }],
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
await fs.writeFile(androidBundlePath, publishBundles.android.code);
|
||||
await fs.writeFile(iosBundlePath, publishBundles.ios.code);
|
||||
await fs.writeFile(dummyFilePath, dummyFileBuffer);
|
||||
await fs.writeFile(userDefinedPath, 'I am an octet stream');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.remove(androidBundlePath);
|
||||
await fs.remove(iosBundlePath);
|
||||
await fs.remove(dummyFilePath);
|
||||
await fs.remove(userDefinedPath);
|
||||
});
|
||||
|
||||
jest.spyOn(PublishMutation, 'getUploadURLsAsync').mockImplementation(async () => {
|
||||
return { specifications: ['{}', '{}', '{}'] };
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(PublishQuery, 'getAssetLimitPerUpdateGroupAsync')
|
||||
.mockImplementation(async () => expectedAssetLimit);
|
||||
|
||||
it('resolves if the assets are already uploaded', async () => {
|
||||
jest.spyOn(PublishQuery, 'getAssetMetadataAsync').mockImplementation(async () => {
|
||||
const status = AssetMetadataStatus.Exists;
|
||||
@@ -443,11 +435,10 @@ describe(uploadAssetsAsync, () => {
|
||||
});
|
||||
|
||||
mockdate.set(0);
|
||||
await expect(uploadAssetsAsync(assetsForUpdateInfoGroup, testProjectId)).resolves.toEqual({
|
||||
await expect(uploadAssetsAsync(assetsForUpdateInfoGroup)).resolves.toEqual({
|
||||
assetCount: 6,
|
||||
uniqueAssetCount: 3,
|
||||
uniqueUploadedAssetCount: 0,
|
||||
assetLimitPerUpdateGroup: expectedAssetLimit,
|
||||
});
|
||||
});
|
||||
it('resolves if the assets are eventually uploaded', async () => {
|
||||
@@ -476,14 +467,12 @@ describe(uploadAssetsAsync, () => {
|
||||
});
|
||||
|
||||
mockdate.set(0);
|
||||
await expect(uploadAssetsAsync(assetsForUpdateInfoGroup, testProjectId)).resolves.toEqual({
|
||||
await expect(uploadAssetsAsync(assetsForUpdateInfoGroup)).resolves.toEqual({
|
||||
assetCount: 6,
|
||||
uniqueAssetCount: 3,
|
||||
uniqueUploadedAssetCount: 2,
|
||||
assetLimitPerUpdateGroup: expectedAssetLimit,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates spinner text throughout execution', async () => {
|
||||
jest.spyOn(PublishQuery, 'getAssetMetadataAsync').mockImplementation(async () => {
|
||||
const status =
|
||||
@@ -511,7 +500,7 @@ describe(uploadAssetsAsync, () => {
|
||||
const updateSpinnerFn = jest.fn((_totalAssets, _missingAssets) => {});
|
||||
|
||||
mockdate.set(0);
|
||||
await uploadAssetsAsync(assetsForUpdateInfoGroup, testProjectId, updateSpinnerFn);
|
||||
await uploadAssetsAsync(assetsForUpdateInfoGroup, updateSpinnerFn);
|
||||
const calls = updateSpinnerFn.mock.calls;
|
||||
expect(calls).toEqual([
|
||||
[3, 3],
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { getNextVersionCode, isValidVersionCode } from '../versions';
|
||||
|
||||
describe(isValidVersionCode, () => {
|
||||
test('isValidVersionCode()', () => {
|
||||
expect(isValidVersionCode(123)).toBe(true);
|
||||
expect(isValidVersionCode(12.3)).toBe(false);
|
||||
expect(isValidVersionCode(-123)).toBe(false);
|
||||
expect(isValidVersionCode(2000000000)).toBe(true);
|
||||
expect(isValidVersionCode(2000000001)).toBe(false);
|
||||
expect(isValidVersionCode('123')).toBe(true);
|
||||
expect(isValidVersionCode('12.3')).toBe(false);
|
||||
expect(isValidVersionCode('-123')).toBe(false);
|
||||
expect(isValidVersionCode('2000000000')).toBe(true);
|
||||
expect(isValidVersionCode('2000000001')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(getNextVersionCode, () => {
|
||||
test('getNextVersionCode()', () => {
|
||||
expect(getNextVersionCode(123)).toBe(124);
|
||||
expect(() => getNextVersionCode(12.3)).toThrowError();
|
||||
expect(getNextVersionCode(1999999999)).toBe(2000000000);
|
||||
expect(() => getNextVersionCode(2000000000)).toThrowError();
|
||||
expect(getNextVersionCode('123')).toBe(124);
|
||||
expect(() => getNextVersionCode('12.3')).toThrowError();
|
||||
});
|
||||
});
|
||||
@@ -159,10 +159,10 @@ export function warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(
|
||||
): void {
|
||||
if (AndroidConfig.Package.getPackage(exp) && !warnPrinted) {
|
||||
Log.warn(
|
||||
`Specified value for "android.package" in ${getProjectConfigDescription(
|
||||
`Specifying "android.package" in ${getProjectConfigDescription(
|
||||
projectDir
|
||||
)} is ignored because an ${chalk.bold('android')} directory was detected in the project.\n` +
|
||||
'EAS Build will use the value found in the native code.'
|
||||
)} is deprecated for bare workflow projects.\n` +
|
||||
'EAS Build depends only on the value in the native code. Please remove the deprecated configuration.'
|
||||
);
|
||||
warnPrinted = true;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import assert from 'assert';
|
||||
|
||||
const MAX_VERSION_CODE = 2000000000;
|
||||
export const VERSION_CODE_REQUIREMENTS = `versionCode needs to be a positive integer smaller or equal to ${MAX_VERSION_CODE}`;
|
||||
|
||||
export function isValidVersionCode(versionCode: string | number): boolean {
|
||||
const numericVersionCode = typeof versionCode === 'string' ? Number(versionCode) : versionCode;
|
||||
return (
|
||||
Number.isInteger(numericVersionCode) &&
|
||||
numericVersionCode <= MAX_VERSION_CODE &&
|
||||
numericVersionCode > 0
|
||||
);
|
||||
}
|
||||
|
||||
export function getNextVersionCode(versionCode: string | number): number {
|
||||
assert(isValidVersionCode(versionCode), `Invalid versionCode ${versionCode}`);
|
||||
const numericVersionCode = typeof versionCode === 'string' ? Number(versionCode) : versionCode;
|
||||
if (numericVersionCode >= MAX_VERSION_CODE) {
|
||||
throw new Error(`Invalid value: ${VERSION_CODE_REQUIREMENTS}.`);
|
||||
}
|
||||
return numericVersionCode + 1;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import {
|
||||
ensureApplicationIdIsDefinedForManagedProjectAsync,
|
||||
getApplicationIdAsync,
|
||||
} from './android/applicationId';
|
||||
import { resolveGradleBuildContextAsync } from './android/gradle';
|
||||
import { getBundleIdentifierAsync } from './ios/bundleIdentifier';
|
||||
import { resolveXcodeBuildContextAsync } from './ios/scheme';
|
||||
import { findApplicationTarget, resolveTargetsAsync } from './ios/target';
|
||||
import { resolveWorkflowAsync } from './workflow';
|
||||
|
||||
export async function getApplicationIdentifierAsync(
|
||||
projectDir: string,
|
||||
exp: ExpoConfig,
|
||||
buildProfile: BuildProfile,
|
||||
platform: Platform
|
||||
): Promise<string> {
|
||||
if (platform === Platform.ANDROID) {
|
||||
const profile = buildProfile as BuildProfile<Platform.ANDROID>;
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
|
||||
const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile);
|
||||
|
||||
if (workflow === Workflow.MANAGED) {
|
||||
await ensureApplicationIdIsDefinedForManagedProjectAsync(projectDir, exp);
|
||||
}
|
||||
|
||||
return await getApplicationIdAsync(projectDir, exp, gradleContext);
|
||||
} else {
|
||||
const profile = buildProfile as BuildProfile<Platform.IOS>;
|
||||
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
|
||||
{ exp, projectDir, nonInteractive: false },
|
||||
profile
|
||||
);
|
||||
const targets = await resolveTargetsAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
xcodeBuildContext,
|
||||
env: profile.env,
|
||||
});
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
return await getBundleIdentifierAsync(projectDir, exp, {
|
||||
targetName: applicationTarget.targetName,
|
||||
buildConfiguration: applicationTarget.buildConfiguration,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,8 @@ export async function ensureProjectExistsAsync(projectInfo: ProjectInfo): Promis
|
||||
});
|
||||
|
||||
const spinner = ora(`Linking to project ${chalk.bold(projectFullName)}`).start();
|
||||
const maybeId = await findProjectIdByAccountNameAndSlugNullableAsync(accountName, projectName);
|
||||
|
||||
const maybeId = await findProjectIdByAccountNameAndSlugNullableAsync(accountName, projectName);
|
||||
if (maybeId) {
|
||||
spinner.succeed(`Linked to project ${chalk.bold(projectLink)}`);
|
||||
projectCache[projectFullName] = maybeId;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { getNextBuildNumber, isValidBuildNumber } from '../versions';
|
||||
|
||||
describe(isValidBuildNumber, () => {
|
||||
test('isValidBuildNumber()', () => {
|
||||
expect(isValidBuildNumber('1.2.3')).toBe(true);
|
||||
expect(isValidBuildNumber('1.2')).toBe(true);
|
||||
expect(isValidBuildNumber('1')).toBe(true);
|
||||
expect(isValidBuildNumber('1.2.3.4')).toBe(false);
|
||||
expect(isValidBuildNumber('1.2.-3')).toBe(false);
|
||||
expect(isValidBuildNumber('1.a.3')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe(getNextBuildNumber, () => {
|
||||
test('getNextBuildNumber', () => {
|
||||
expect(getNextBuildNumber('1.2.3')).toBe('1.2.4');
|
||||
expect(getNextBuildNumber('1.2')).toBe('1.3');
|
||||
expect(getNextBuildNumber('1')).toBe('2');
|
||||
expect(() => getNextBuildNumber('1.2.3.4')).toThrowError();
|
||||
});
|
||||
});
|
||||
@@ -152,10 +152,10 @@ export function warnIfBundleIdentifierDefinedInAppConfigForBareWorkflowProject(
|
||||
): void {
|
||||
if (IOSConfig.BundleIdentifier.getBundleIdentifier(exp) && !warnPrinted) {
|
||||
Log.warn(
|
||||
`Specified value for "ios.bundleIdentifier" in ${getProjectConfigDescription(
|
||||
`Specifying "ios.bundleIdentifier" in ${getProjectConfigDescription(
|
||||
projectDir
|
||||
)} is ignored because an ${chalk.bold('ios')} directory was detected in the project.\n` +
|
||||
'EAS Build will use the value found in the native code.'
|
||||
)} is deprecated for bare workflow projects.\n` +
|
||||
'EAS Build depends only on the value in the native code. Please remove the deprecated configuration.'
|
||||
);
|
||||
warnPrinted = true;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { IOSConfig, XcodeProject } from '@expo/config-plugins';
|
||||
import { IOSConfig } from '@expo/config-plugins';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { JSONObject } from '@expo/json-file';
|
||||
import Joi from 'joi';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { resolveWorkflowAsync } from '../workflow';
|
||||
@@ -92,7 +91,6 @@ export async function resolveBareProjectTargetsAsync({
|
||||
const { buildScheme, buildConfiguration } = xcodeBuildContext;
|
||||
const result: Target[] = [];
|
||||
|
||||
const pbxProject = IOSConfig.XcodeUtils.getPbxproj(projectDir);
|
||||
const applicationTarget = await IOSConfig.Target.findApplicationTargetWithDependenciesAsync(
|
||||
projectDir,
|
||||
buildScheme
|
||||
@@ -110,11 +108,6 @@ export async function resolveBareProjectTargetsAsync({
|
||||
bundleIdentifier,
|
||||
buildConfiguration,
|
||||
entitlements: entitlements ?? {},
|
||||
buildSettings: resolveBareProjectBuildSettings(
|
||||
pbxProject,
|
||||
applicationTarget.name,
|
||||
buildConfiguration
|
||||
),
|
||||
});
|
||||
|
||||
const dependencies = await resolveBareProjectDependenciesAsync({
|
||||
@@ -123,7 +116,6 @@ export async function resolveBareProjectTargetsAsync({
|
||||
buildConfiguration,
|
||||
target: applicationTarget,
|
||||
bundleIdentifier,
|
||||
pbxProject,
|
||||
});
|
||||
if (dependencies.length > 0) {
|
||||
result.push(...dependencies);
|
||||
@@ -149,14 +141,12 @@ async function resolveBareProjectDependenciesAsync({
|
||||
buildConfiguration,
|
||||
target,
|
||||
bundleIdentifier,
|
||||
pbxProject,
|
||||
}: {
|
||||
exp: ExpoConfig;
|
||||
projectDir: string;
|
||||
buildConfiguration?: string;
|
||||
target: IOSConfig.Target.Target;
|
||||
bundleIdentifier: string;
|
||||
pbxProject: XcodeProject;
|
||||
}): Promise<Target[]> {
|
||||
const result: Target[] = [];
|
||||
|
||||
@@ -176,11 +166,6 @@ async function resolveBareProjectDependenciesAsync({
|
||||
bundleIdentifier: dependencyBundleIdentifier,
|
||||
parentBundleIdentifier: bundleIdentifier,
|
||||
entitlements: entitlements ?? {},
|
||||
buildSettings: resolveBareProjectBuildSettings(
|
||||
pbxProject,
|
||||
dependency.name,
|
||||
buildConfiguration
|
||||
),
|
||||
});
|
||||
const dependencyDependencies = await resolveBareProjectDependenciesAsync({
|
||||
exp,
|
||||
@@ -188,7 +173,6 @@ async function resolveBareProjectDependenciesAsync({
|
||||
buildConfiguration,
|
||||
target: dependency,
|
||||
bundleIdentifier: dependencyBundleIdentifier,
|
||||
pbxProject,
|
||||
});
|
||||
if (dependencyDependencies.length > 0) {
|
||||
result.push(...dependencyDependencies);
|
||||
@@ -214,15 +198,3 @@ export function findTargetByName(targets: Target[], name: string): Target {
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function resolveBareProjectBuildSettings(
|
||||
project: XcodeProject,
|
||||
targetName: string,
|
||||
buildConfiguration?: string
|
||||
): XCBuildConfiguration['buildSettings'] {
|
||||
const xcBuildConfiguration = IOSConfig.Target.getXCBuildConfigurationFromPbxproj(project, {
|
||||
targetName,
|
||||
buildConfiguration,
|
||||
});
|
||||
return xcBuildConfiguration?.buildSettings ?? {};
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import assert from 'assert';
|
||||
|
||||
export const BUILD_NUMBER_REQUIREMENTS = `buildNumber needs to consist only of up to 3 dot-separated positive integers`;
|
||||
|
||||
export function isValidBuildNumber(buildNumber: string): boolean {
|
||||
return !!buildNumber.match(/^\d+(\.\d+)?(\.\d+)?$/);
|
||||
}
|
||||
|
||||
export function getNextBuildNumber(buildNumber: string): string {
|
||||
assert(isValidBuildNumber(buildNumber), `Invalid buildNumber ${buildNumber}`);
|
||||
const comps = buildNumber.split('.');
|
||||
comps[comps.length - 1] = String(Number(comps[comps.length - 1]) + 1);
|
||||
return comps.join('.');
|
||||
}
|
||||
@@ -161,7 +161,6 @@ export async function buildBundlesAsync({
|
||||
inputDir,
|
||||
'--experimental-bundle',
|
||||
'--non-interactive',
|
||||
'--dump-sourcemap',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -250,12 +249,10 @@ type AssetUploadResult = {
|
||||
assetCount: number;
|
||||
uniqueAssetCount: number;
|
||||
uniqueUploadedAssetCount: number;
|
||||
assetLimitPerUpdateGroup: number;
|
||||
};
|
||||
|
||||
export async function uploadAssetsAsync(
|
||||
assetsForUpdateInfoGroup: CollectedAssets,
|
||||
projectId: string,
|
||||
updateSpinnerText?: (totalAssets: number, missingAssets: number) => void
|
||||
): Promise<AssetUploadResult> {
|
||||
let assets: RawAsset[] = [];
|
||||
@@ -291,19 +288,19 @@ export async function uploadAssetsAsync(
|
||||
const { specifications } = await PublishMutation.getUploadURLsAsync(
|
||||
missingAssets.map(ma => ma.contentType)
|
||||
);
|
||||
|
||||
updateSpinnerText?.(totalAssets, missingAssets.length);
|
||||
|
||||
const assetUploadPromiseLimit = promiseLimit(15);
|
||||
|
||||
const [assetLimitPerUpdateGroup] = await Promise.all([
|
||||
PublishQuery.getAssetLimitPerUpdateGroupAsync(projectId),
|
||||
await Promise.all(
|
||||
missingAssets.map((missingAsset, i) => {
|
||||
assetUploadPromiseLimit(async () => {
|
||||
const presignedPost: PresignedPost = JSON.parse(specifications[i]);
|
||||
await uploadWithPresignedPostAsync(missingAsset.path, presignedPost);
|
||||
});
|
||||
}),
|
||||
]);
|
||||
})
|
||||
);
|
||||
|
||||
let timeout = 1;
|
||||
while (missingAssets.length > 0) {
|
||||
@@ -319,14 +316,5 @@ export async function uploadAssetsAsync(
|
||||
assetCount: assets.length,
|
||||
uniqueAssetCount: uniqueAssets.length,
|
||||
uniqueUploadedAssetCount,
|
||||
assetLimitPerUpdateGroup,
|
||||
};
|
||||
}
|
||||
|
||||
export function isUploadedAssetCountAboveWarningThreshold(
|
||||
uploadedAssetCount: number,
|
||||
assetLimitPerUpdateGroup: number
|
||||
): boolean {
|
||||
const warningThreshold = Math.floor(assetLimitPerUpdateGroup * 0.75);
|
||||
return uploadedAssetCount > warningThreshold;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { AppVersionSource, EasJson, EasJsonReader } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import Log from '../log';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { ProfileData } from '../utils/profiles';
|
||||
|
||||
export async function ensureVersionSourceIsRemoteAsync(
|
||||
projectDir: string,
|
||||
easJsonReader: EasJsonReader
|
||||
): Promise<void> {
|
||||
const easJsonCliConfig = await easJsonReader.getCliConfigAsync();
|
||||
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.log(
|
||||
`The app version source defines whether the app version is stored locally in your project source (e.g. in app.json, Info.plist, or build.gradle) or remotely on EAS servers and only applied to the project at build time. To use this command, you will need to enable the remote version policy by adding ${chalk.bold(
|
||||
'{"cli": { "appVersionSource": "remote" }}'
|
||||
)} in eas.json.`
|
||||
);
|
||||
// TODO: add link to docs
|
||||
const confirm = await confirmAsync({
|
||||
message: 'Do you want to set app version source to remote now?',
|
||||
});
|
||||
if (!confirm) {
|
||||
throw new Error('Aborting...');
|
||||
}
|
||||
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(projectDir);
|
||||
const easJson = await fs.readJSON(easJsonPath);
|
||||
easJson.cli = { ...easJson?.cli, appVersionSource: AppVersionSource.REMOTE };
|
||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
||||
Log.withTick('Updated eas.json');
|
||||
}
|
||||
|
||||
export function validateBuildProfileVersionSettings(
|
||||
profileInfo: ProfileData<'build'>,
|
||||
cliConfig: EasJson['cli']
|
||||
): void {
|
||||
if (cliConfig?.appVersionSource !== AppVersionSource.REMOTE) {
|
||||
return;
|
||||
}
|
||||
if (profileInfo.profile.autoIncrement === 'version') {
|
||||
throw new Error(
|
||||
`${chalk.bold(
|
||||
'{"autoIncrement": "version"}'
|
||||
)} is not supported when app version source is set to remote.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateAppConfigForRemoteVersionSource(exp: ExpoConfig, platform: Platform): void {
|
||||
if (typeof exp.runtimeVersion === 'object' && exp.runtimeVersion?.policy === 'nativeVersion') {
|
||||
throw new Error(
|
||||
`${chalk.bold('nativeVersion')} policy for ${chalk.bold(
|
||||
'runtimeVersion'
|
||||
)} is currently not supported when version source is set to remote. Switch policy e.g. to ${chalk.bold(
|
||||
'appVersion'
|
||||
)} or define version explicitly.`
|
||||
);
|
||||
}
|
||||
if (platform === Platform.IOS && exp.ios?.buildNumber !== undefined) {
|
||||
Log.warn(
|
||||
`${chalk.bold(
|
||||
'ios.buildNumber'
|
||||
)} field in app config is ignored when version source is set to remote, but this value will still be in the manifest available via ${chalk.bold(
|
||||
'expo-constants'
|
||||
)}. It's recommended to remove this value from app config.`
|
||||
);
|
||||
}
|
||||
if (platform === Platform.ANDROID && exp.android?.versionCode !== undefined) {
|
||||
Log.warn(
|
||||
`${chalk.bold(
|
||||
'android.versionCode'
|
||||
)} field in app config is ignored when version source is set to remote, but this value will still be in the manifest available via ${chalk.bold(
|
||||
'expo-constants'
|
||||
)}. It's recommended to remove this value from app config.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getBuildVersionName(platform: Platform): string {
|
||||
if (platform === Platform.ANDROID) {
|
||||
return 'versionCode';
|
||||
} else {
|
||||
return 'buildNumber';
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { AppPlatform, BuildFragment, UploadSessionType } from '../../graphql/gen
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { toAppPlatform } from '../../graphql/types/AppPlatform';
|
||||
import { confirmAsync, promptAsync } from '../../prompts';
|
||||
import { uploadFileAtPathToS3Async } from '../../uploads';
|
||||
import { uploadAsync } from '../../uploads';
|
||||
import {
|
||||
Archive,
|
||||
ArchiveSourceType,
|
||||
@@ -243,9 +243,7 @@ describe(getArchiveAsync, () => {
|
||||
const path = '/archive.apk';
|
||||
await fs.writeFile(path, 'some content');
|
||||
|
||||
jest
|
||||
.mocked(uploadFileAtPathToS3Async)
|
||||
.mockResolvedValueOnce({ url: ARCHIVE_URL, bucketKey: 'wat' });
|
||||
jest.mocked(uploadAsync).mockResolvedValueOnce({ url: ARCHIVE_URL, bucketKey: 'wat' });
|
||||
|
||||
const archive = await getArchiveAsync({
|
||||
...SOURCE_STUB_INPUT,
|
||||
@@ -253,7 +251,7 @@ describe(getArchiveAsync, () => {
|
||||
path,
|
||||
});
|
||||
|
||||
expect(uploadFileAtPathToS3Async).toBeCalledWith(
|
||||
expect(uploadAsync).toBeCalledWith(
|
||||
UploadSessionType.EasSubmitAppArchive,
|
||||
path,
|
||||
expect.anything()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { UploadSessionType } from '../../graphql/generated';
|
||||
import { uploadFileAtPathToS3Async } from '../../uploads';
|
||||
import { uploadAsync } from '../../uploads';
|
||||
import { createProgressTracker } from '../../utils/progress';
|
||||
|
||||
export async function isExistingFileAsync(filePath: string): Promise<boolean> {
|
||||
@@ -15,7 +15,7 @@ export async function isExistingFileAsync(filePath: string): Promise<boolean> {
|
||||
|
||||
export async function uploadAppArchiveAsync(path: string): Promise<string> {
|
||||
const fileSize = (await fs.stat(path)).size;
|
||||
const { url } = await uploadFileAtPathToS3Async(
|
||||
const { url } = await uploadAsync(
|
||||
UploadSessionType.EasSubmitAppArchive,
|
||||
path,
|
||||
createProgressTracker({
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { PublishPlatformFlag } from '../../../commands/update';
|
||||
import { UpdateObject } from '../../../graphql/queries/UpdateQuery';
|
||||
|
||||
export function createMockUpdates(
|
||||
{
|
||||
updateCount = 1,
|
||||
platformFlag = 'all',
|
||||
groupId,
|
||||
}: Partial<{
|
||||
updateCount: number;
|
||||
platformFlag: PublishPlatformFlag;
|
||||
groupId: string;
|
||||
}> = {
|
||||
updateCount: 1,
|
||||
platformFlag: 'all',
|
||||
}
|
||||
): UpdateObject[] {
|
||||
let updates: UpdateObject[] = [];
|
||||
if (platformFlag === 'all') {
|
||||
updateCount = Math.ceil(updateCount / 2);
|
||||
}
|
||||
for (let i = 0; i < updateCount; i++) {
|
||||
const androidUpdate = {
|
||||
id: uuidv4(),
|
||||
group: uuidv4(),
|
||||
message: 'default test message',
|
||||
createdAt: new Date(),
|
||||
runtimeVersion: 'exposdk:44.0.0',
|
||||
platform: 'android',
|
||||
manifestFragment: '',
|
||||
};
|
||||
const iosUpdate = {
|
||||
...androidUpdate,
|
||||
id: uuidv4(),
|
||||
platform: 'ios',
|
||||
};
|
||||
if (!i && groupId) {
|
||||
androidUpdate.group = groupId;
|
||||
iosUpdate.group = groupId;
|
||||
}
|
||||
const isOddNumberedUpdateCount = !(updateCount % 2);
|
||||
const isFinalLoop = i === updateCount - 1;
|
||||
switch (platformFlag) {
|
||||
case 'all':
|
||||
updates = [
|
||||
...updates,
|
||||
androidUpdate as UpdateObject,
|
||||
...(isFinalLoop && isOddNumberedUpdateCount ? [] : [iosUpdate as UpdateObject]),
|
||||
];
|
||||
break;
|
||||
case 'android':
|
||||
updates = [...updates, androidUpdate as UpdateObject];
|
||||
break;
|
||||
case 'ios':
|
||||
updates = [...updates, iosUpdate as UpdateObject];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import {
|
||||
PublishPlatformFlag,
|
||||
ensureBranchExistsAsync,
|
||||
getUpdatesToRepublishInteractiveAsync,
|
||||
truncatePublishUpdateMessage,
|
||||
} from '../../commands/update';
|
||||
import { graphqlClient } from '../../graphql/client';
|
||||
import { UpdateObject } from '../../graphql/queries/UpdateQuery';
|
||||
import { ViewBranchUpdatesQuery } from '../../graphql/generated';
|
||||
import { viewBranchUpdatesQueryUpdateLimit } from '../../graphql/queries/UpdateQuery';
|
||||
import Log from '../../log';
|
||||
import { selectAsync } from '../../prompts';
|
||||
import { createMockUpdates } from './fixtures/updates';
|
||||
|
||||
const appId = '6c94sxe6-37d2-4700-52fa-1b813204dad2';
|
||||
const branchId = '5e84e3cb-563e-4022-a65e-6e7a42fe4ed3';
|
||||
@@ -35,7 +36,12 @@ jest.mock('../../commands/update', () => {
|
||||
getUpdatesToRepublishInteractiveAsync: jest.fn(actual.getUpdatesToRepublishInteractiveAsync),
|
||||
};
|
||||
});
|
||||
const updatesToResolve = jest.fn((): UpdateObject[] => []);
|
||||
const updatesToResolve = jest.fn(
|
||||
(): Exclude<
|
||||
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
|
||||
null | undefined
|
||||
>['updates'] => []
|
||||
);
|
||||
jest.mock('../../graphql/client', () => ({
|
||||
...jest.requireActual('../../graphql/client'),
|
||||
graphqlClient: {
|
||||
@@ -78,6 +84,70 @@ jest.mock('../../graphql/client', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function createMockUpdates(
|
||||
{
|
||||
updateCount = 1,
|
||||
platformFlag = 'all',
|
||||
groupId,
|
||||
}: Partial<{
|
||||
updateCount: number;
|
||||
platformFlag: PublishPlatformFlag;
|
||||
groupId: string;
|
||||
}> = {
|
||||
updateCount: 1,
|
||||
platformFlag: 'all',
|
||||
}
|
||||
): Exclude<
|
||||
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
|
||||
null | undefined
|
||||
>['updates'] {
|
||||
let updates: Exclude<
|
||||
Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'],
|
||||
null | undefined
|
||||
>['updates'] = [];
|
||||
if (platformFlag === 'all') {
|
||||
updateCount = Math.ceil(updateCount / 2);
|
||||
}
|
||||
for (let i = 0; i < updateCount; i++) {
|
||||
const androidUpdate = {
|
||||
id: uuidv4(),
|
||||
group: uuidv4(),
|
||||
message: 'default test message',
|
||||
createdAt: new Date(),
|
||||
runtimeVersion: 'exposdk:44.0.0',
|
||||
platform: 'android',
|
||||
manifestFragment: '',
|
||||
};
|
||||
const iosUpdate = {
|
||||
...androidUpdate,
|
||||
id: uuidv4(),
|
||||
platform: 'ios',
|
||||
};
|
||||
if (!i && groupId) {
|
||||
androidUpdate.group = groupId;
|
||||
iosUpdate.group = groupId;
|
||||
}
|
||||
const isOddNumberedUpdateCount = !(updateCount % 2);
|
||||
const isFinalLoop = i === updateCount - 1;
|
||||
switch (platformFlag) {
|
||||
case 'all':
|
||||
updates = [
|
||||
...updates,
|
||||
androidUpdate,
|
||||
...(isFinalLoop && isOddNumberedUpdateCount ? [] : [iosUpdate]),
|
||||
];
|
||||
break;
|
||||
case 'android':
|
||||
updates = [...updates, androidUpdate];
|
||||
break;
|
||||
case 'ios':
|
||||
updates = [...updates, iosUpdate];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
|
||||
describe('UpdatePublish', () => {
|
||||
describe('ensureBranchExistsAsync', () => {
|
||||
beforeEach(() => {
|
||||
@@ -85,6 +155,18 @@ describe('UpdatePublish', () => {
|
||||
jest.mocked(graphqlClient.mutation).mockClear();
|
||||
});
|
||||
|
||||
it('defaults limit to viewBranchUpdatesQueryUpdateLimit and offset to 0 when none are provided', async () => {
|
||||
await ensureBranchExistsAsync({ appId, name: appName });
|
||||
const [[, bindings]] = jest.mocked(graphqlClient.query).mock.calls;
|
||||
|
||||
expect(bindings).toEqual({
|
||||
appId,
|
||||
name: appName,
|
||||
limit: viewBranchUpdatesQueryUpdateLimit,
|
||||
offset: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[0, 0],
|
||||
[10, 50],
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import CliTable from 'cli-table3';
|
||||
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { BranchUpdateObject, UpdateQuery } from '../graphql/queries/UpdateQuery';
|
||||
import Log from '../log';
|
||||
import formatFields from '../utils/formatFields';
|
||||
import { printJsonOnlyOutput } from '../utils/json';
|
||||
import { paginatedQueryWithConfirmPromptAsync } from '../utils/queries';
|
||||
import { UPDATE_COLUMNS, formatUpdate, getUpdateGroupsWithPlatforms } from './utils';
|
||||
|
||||
export const UPDATES_LIMIT = 50;
|
||||
|
||||
export async function listAndRenderUpdatesOnBranchByNameAsync(
|
||||
projectId: string,
|
||||
branchName: string,
|
||||
options: PaginatedQueryOptions
|
||||
): Promise<void> {
|
||||
if (options.nonInteractive) {
|
||||
const updates = await queryUpdateGroupsForBranchAsync(
|
||||
options.limit ?? UPDATES_LIMIT,
|
||||
options.offset,
|
||||
projectId,
|
||||
branchName
|
||||
);
|
||||
renderUpdateGroups(updates, options, branchName);
|
||||
} else {
|
||||
await paginatedQueryWithConfirmPromptAsync({
|
||||
limit: options.limit ?? UPDATES_LIMIT,
|
||||
offset: options.offset,
|
||||
queryToPerform: (limit, offset) =>
|
||||
queryUpdateGroupsForBranchAsync(limit, offset, projectId, branchName),
|
||||
promptOptions: {
|
||||
title: 'Load more branches?',
|
||||
renderListItems: updates => renderUpdateGroups(updates, options, branchName),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function queryUpdateGroupsForBranchAsync(
|
||||
limit: number,
|
||||
offset: number,
|
||||
projectId: string,
|
||||
branchName: string
|
||||
): Promise<BranchUpdateObject[]> {
|
||||
const { app } = await UpdateQuery.viewBranchAsync({
|
||||
appId: projectId,
|
||||
name: branchName,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
|
||||
const updateBranch = app?.byId.updateBranchByName;
|
||||
if (!updateBranch) {
|
||||
throw new Error(`Could not find branch "${branchName}"`);
|
||||
}
|
||||
|
||||
return updateBranch.updates;
|
||||
}
|
||||
|
||||
function renderUpdateGroups(
|
||||
updates: BranchUpdateObject[],
|
||||
{ json }: PaginatedQueryOptions,
|
||||
branchName: string
|
||||
): void {
|
||||
const branch = { name: branchName, id: updates[0]?.branch.id ?? 'N/A' };
|
||||
if (json) {
|
||||
printJsonOnlyOutput({ ...branch, currentPage: updates });
|
||||
} else {
|
||||
const updateGroupsTable = new CliTable({
|
||||
head: UPDATE_COLUMNS,
|
||||
wordWrap: true,
|
||||
});
|
||||
updateGroupsTable.push(
|
||||
...getUpdateGroupsWithPlatforms(updates).map(update => [
|
||||
formatUpdate(update),
|
||||
update.runtimeVersion,
|
||||
update.group,
|
||||
update.platforms,
|
||||
])
|
||||
);
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(chalk.bold('Branch:'));
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: branch.name },
|
||||
{ label: 'ID', value: branch.id },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(updateGroupsTable.toString());
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,6 @@ export type FormatUpdateParameter = Pick<Update, 'id' | 'createdAt' | 'message'>
|
||||
actor?: Maybe<Pick<User, 'username' | 'id'> | Pick<Robot, 'firstName' | 'id'>>;
|
||||
};
|
||||
|
||||
export type UpdateGroupDescription = FormatUpdateParameter & {
|
||||
branch: string;
|
||||
group: string;
|
||||
platforms: string;
|
||||
runtimeVersion: string;
|
||||
};
|
||||
|
||||
export const UPDATE_COLUMNS = [
|
||||
'Update message',
|
||||
'Update runtime version',
|
||||
@@ -26,15 +19,6 @@ export const UPDATE_COLUMNS = [
|
||||
'Update platforms',
|
||||
];
|
||||
|
||||
export function getUpdateGroupsWithPlatforms<
|
||||
UpdateFragment extends Pick<Update, 'platform' | 'group'>
|
||||
>(updates: UpdateFragment[]): (UpdateFragment & { platforms: string })[] {
|
||||
return Object.values(groupBy(updates, updates => updates.group)).map(updateGroup => ({
|
||||
...updateGroup[0],
|
||||
platforms: formatPlatformForUpdateGroup(updateGroup),
|
||||
}));
|
||||
}
|
||||
|
||||
export function getPlatformsForGroup({
|
||||
group,
|
||||
updates,
|
||||
@@ -43,35 +27,20 @@ export function getPlatformsForGroup({
|
||||
updates: { group: string; platform: string }[];
|
||||
}): string {
|
||||
const groupedUpdates = groupBy(updates, update => update.group);
|
||||
return formatPlatformForUpdateGroup(groupedUpdates[group]);
|
||||
}
|
||||
|
||||
export function formatPlatformForUpdateGroup(
|
||||
updateGroup: {
|
||||
group: string;
|
||||
platform: string;
|
||||
}[]
|
||||
): string {
|
||||
return updateGroup.length === 0
|
||||
? 'N/A'
|
||||
: updateGroup
|
||||
.map(update => update.platform)
|
||||
.sort()
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
export function truncateString(originalMessage: string, length: number = 512): string {
|
||||
if (originalMessage.length > length) {
|
||||
return originalMessage.substring(0, length - 3) + '...';
|
||||
if (Object.keys(groupedUpdates).length === 0) {
|
||||
return 'N/A';
|
||||
}
|
||||
return originalMessage;
|
||||
return groupedUpdates[group]
|
||||
.map(update => update.platform)
|
||||
.sort()
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
export function formatUpdate(update: FormatUpdateParameter): string {
|
||||
if (!update) {
|
||||
return 'N/A';
|
||||
}
|
||||
const message = update.message ? `"${truncateString(update.message)}" ` : '';
|
||||
const message = update.message ? `"${update.message}" ` : '';
|
||||
return `${message}(${format(update.createdAt, 'en_US')} by ${getActorDisplayName(
|
||||
update.actor as any
|
||||
)})`;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { UploadSessionType } from './graphql/generated';
|
||||
import { PresignedPost, UploadSessionMutation } from './graphql/mutations/UploadSessionMutation';
|
||||
import { ProgressHandler } from './utils/progress';
|
||||
|
||||
export async function uploadFileAtPathToS3Async(
|
||||
export async function uploadAsync(
|
||||
type: UploadSessionType,
|
||||
path: string,
|
||||
handleProgressEvent: ProgressHandler
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
import { confirmAsync, selectAsync } from '../../prompts';
|
||||
import {
|
||||
paginatedQueryWithConfirmPromptAsync,
|
||||
paginatedQueryWithSelectPromptAsync,
|
||||
} from '../queries';
|
||||
|
||||
jest.mock('../../prompts', () => ({
|
||||
selectAsync: jest.fn(),
|
||||
confirmAsync: jest.fn(),
|
||||
}));
|
||||
|
||||
type MockDataType = {
|
||||
id: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const mockQuery = jest.fn<Promise<MockDataType[]>, any>();
|
||||
const mockRenderMethod = jest.fn();
|
||||
|
||||
const selectMoreObject = {
|
||||
title: 'Next page...',
|
||||
value: '_fetchMore',
|
||||
};
|
||||
|
||||
function createPaginatedQueryResponse(
|
||||
responseLength: number,
|
||||
uniqueIdentifier?: string,
|
||||
firstItem?: MockDataType
|
||||
): MockDataType[] {
|
||||
return Array.from({ length: responseLength }).map((_, index) => {
|
||||
return !index && firstItem
|
||||
? firstItem
|
||||
: {
|
||||
id: `${uniqueIdentifier ?? 'x'}-${index}`,
|
||||
value: `${uniqueIdentifier ?? 'x'}-${index}`,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
describe(paginatedQueryWithConfirmPromptAsync.name, () => {
|
||||
beforeEach(() => {
|
||||
mockQuery.mockClear();
|
||||
mockRenderMethod.mockClear();
|
||||
jest.mocked(confirmAsync).mockClear();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[10, 50],
|
||||
[77, 30],
|
||||
])(
|
||||
'sets the limit to %s + 1 and offset to %s and queries until no results are left',
|
||||
async (limit, offset) => {
|
||||
mockQuery
|
||||
.mockResolvedValueOnce(createPaginatedQueryResponse(limit + 1))
|
||||
.mockResolvedValueOnce(createPaginatedQueryResponse(limit + 1))
|
||||
.mockResolvedValueOnce(createPaginatedQueryResponse(Math.floor(limit / 2)));
|
||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||
|
||||
await paginatedQueryWithConfirmPromptAsync({
|
||||
limit,
|
||||
offset,
|
||||
queryToPerform: mockQuery,
|
||||
promptOptions: {
|
||||
title: '',
|
||||
renderListItems: mockRenderMethod,
|
||||
},
|
||||
});
|
||||
|
||||
const { calls } = mockQuery.mock;
|
||||
const finalResults = await mockQuery.mock.results.pop()?.value;
|
||||
expect(mockQuery.mock.calls.length).toEqual(3);
|
||||
calls.forEach(([limitArg, offsetArg], i) => {
|
||||
expect(limitArg).toEqual(limit + 1);
|
||||
expect(offsetArg).toEqual(offset + limit * i);
|
||||
});
|
||||
expect(finalResults.length).toEqual(Math.floor(limit / 2));
|
||||
expect(mockRenderMethod).toBeCalledTimes(3);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe(paginatedQueryWithSelectPromptAsync.name, () => {
|
||||
const mockHandleEmptySelectList = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
mockQuery.mockClear();
|
||||
mockRenderMethod.mockClear();
|
||||
mockHandleEmptySelectList.mockClear();
|
||||
jest.mocked(selectAsync).mockClear();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[10, 50],
|
||||
[77, 30],
|
||||
])(
|
||||
'queries multiple pages of %s + 1 items with an offset of %s and returns the selected list item',
|
||||
async (limit, offset) => {
|
||||
const selectedItem = {
|
||||
title: 'second-3',
|
||||
value: 'second-3',
|
||||
};
|
||||
mockQuery
|
||||
.mockResolvedValueOnce(createPaginatedQueryResponse(limit + 1, 'first'))
|
||||
// we need to insert a the last item from the previous query
|
||||
// as the first item in the following query.
|
||||
// the id is used for grouping items, so it must match an id in the previous query
|
||||
.mockResolvedValueOnce(
|
||||
createPaginatedQueryResponse(limit + 1, 'second', {
|
||||
id: `first-${limit}`,
|
||||
value: `first-${limit}`,
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
createPaginatedQueryResponse(Math.floor(limit / 2), 'third', {
|
||||
id: `second-${limit}`,
|
||||
value: `second-${limit}`,
|
||||
})
|
||||
);
|
||||
jest
|
||||
.mocked(selectAsync)
|
||||
.mockResolvedValueOnce(selectMoreObject.value)
|
||||
.mockResolvedValueOnce(selectMoreObject.value)
|
||||
.mockResolvedValueOnce(selectedItem.value);
|
||||
|
||||
const selectedQueryItem = await paginatedQueryWithSelectPromptAsync({
|
||||
limit,
|
||||
offset,
|
||||
queryToPerform: mockQuery,
|
||||
promptOptions: {
|
||||
title: '',
|
||||
getIdentifierForQueryItem: item => item.id,
|
||||
createDisplayTextForSelectionPromptListItem: item => 'item: ' + item.value,
|
||||
},
|
||||
});
|
||||
|
||||
// return value
|
||||
expect(selectedQueryItem).toEqual({ id: selectedItem.value, value: selectedItem.value });
|
||||
|
||||
// query
|
||||
const { calls: queryCalls } = mockQuery.mock;
|
||||
const finalQueryResults = await mockQuery.mock.results.pop()?.value;
|
||||
expect(mockQuery.mock.calls.length).toEqual(3);
|
||||
queryCalls.forEach(([limitArg, offsetArg], i) => {
|
||||
expect(limitArg).toEqual(limit + 1);
|
||||
expect(offsetArg).toEqual(offset + limit * i);
|
||||
});
|
||||
expect(finalQueryResults.length).toEqual(Math.floor(limit / 2));
|
||||
|
||||
// select prompt
|
||||
const selectCalls = jest.mocked(selectAsync).mock.calls;
|
||||
selectCalls.forEach(([, listItems], i) => {
|
||||
const oneBasedIndex = i + 1;
|
||||
const isLastCall = i === 2;
|
||||
if (isLastCall) {
|
||||
expect(listItems[listItems.length - 1]).not.toEqual(selectMoreObject);
|
||||
expect(listItems.length).toEqual(
|
||||
limit * oneBasedIndex -
|
||||
// ceiling gives us the extra query item
|
||||
// used for pagination in odd limit queries
|
||||
Math.ceil(limit / 2)
|
||||
);
|
||||
} else {
|
||||
expect(listItems[listItems.length - 1]).toEqual(selectMoreObject);
|
||||
expect(listItems.length).toEqual(
|
||||
limit * oneBasedIndex +
|
||||
// pagination item
|
||||
1
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('returns nothing when there are no items to select', async () => {
|
||||
mockQuery.mockResolvedValueOnce([]);
|
||||
|
||||
const result = await paginatedQueryWithSelectPromptAsync({
|
||||
limit: 50,
|
||||
offset: 0,
|
||||
queryToPerform: mockQuery,
|
||||
promptOptions: {
|
||||
title: '',
|
||||
getIdentifierForQueryItem: item => item.id,
|
||||
createDisplayTextForSelectionPromptListItem: item => item.value,
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
import chunk from '../chunk';
|
||||
|
||||
describe(chunk, () => {
|
||||
it('works with an empty list', () => {
|
||||
expect(chunk([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('works with list length undividable by chunk size', () => {
|
||||
expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
|
||||
});
|
||||
|
||||
it('works with list length dividable by chunk size', () => {
|
||||
expect(chunk([1, 2, 3, 4, 5, 6], 2)).toEqual([
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
[5, 6],
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
export default function chunk<T>(list: T[], size = 1): T[][] {
|
||||
const result: T[][] = [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const isFirstElementInChunk = i % size === 0;
|
||||
if (isFirstElementInChunk) {
|
||||
result.push([list[i]]);
|
||||
} else {
|
||||
result[result.length - 1].push(list[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import { confirmAsync, selectAsync } from '../prompts';
|
||||
import uniqBy from './expodash/uniqBy';
|
||||
|
||||
const fetchMoreValue = '_fetchMore';
|
||||
|
||||
type BasePaginatedQueryArgs<QueryReturnType extends Record<string, any>> = {
|
||||
limit: number;
|
||||
offset: number;
|
||||
queryToPerform: (limit: number, offset: number) => Promise<QueryReturnType[]>;
|
||||
};
|
||||
|
||||
type PaginatedQueryWithConfirmPromptArgs<QueryReturnType extends Record<string, any>> =
|
||||
BasePaginatedQueryArgs<QueryReturnType> & {
|
||||
promptOptions: {
|
||||
readonly title: string;
|
||||
renderListItems: (currentPage: QueryReturnType[]) => void;
|
||||
};
|
||||
};
|
||||
|
||||
type PaginatedQueryWithSelectPromptArgs<QueryReturnType extends Record<string, any>> =
|
||||
BasePaginatedQueryArgs<QueryReturnType> & {
|
||||
promptOptions: {
|
||||
readonly title: string;
|
||||
createDisplayTextForSelectionPromptListItem: (queryItem: QueryReturnType) => string;
|
||||
getIdentifierForQueryItem: (queryItem: QueryReturnType) => string;
|
||||
};
|
||||
};
|
||||
|
||||
export async function paginatedQueryWithConfirmPromptAsync<
|
||||
QueryReturnType extends Record<string, any>
|
||||
>(queryArgs: PaginatedQueryWithConfirmPromptArgs<QueryReturnType>): Promise<void> {
|
||||
return await paginatedQueryWithConfirmPromptInternalAsync(queryArgs, []);
|
||||
}
|
||||
|
||||
async function paginatedQueryWithConfirmPromptInternalAsync<
|
||||
QueryReturnType extends Record<string, any>
|
||||
>(
|
||||
{
|
||||
limit,
|
||||
offset,
|
||||
queryToPerform,
|
||||
promptOptions,
|
||||
}: PaginatedQueryWithConfirmPromptArgs<QueryReturnType>,
|
||||
accumulator: QueryReturnType[]
|
||||
): Promise<void> {
|
||||
// query an extra item to determine if there are more pages left
|
||||
const paginatedItems = await queryToPerform(limit + 1, offset);
|
||||
const areMorePagesAvailable = paginatedItems.length > limit;
|
||||
// drop that extra item used for pagination from our render logic
|
||||
const currentPage = paginatedItems.slice(0, limit);
|
||||
const newAccumulator = [...accumulator, ...currentPage];
|
||||
|
||||
promptOptions.renderListItems(currentPage);
|
||||
|
||||
if (!areMorePagesAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await confirmAsync({ message: promptOptions.title })) {
|
||||
return await paginatedQueryWithConfirmPromptInternalAsync(
|
||||
{
|
||||
limit,
|
||||
offset: offset + limit,
|
||||
queryToPerform,
|
||||
promptOptions,
|
||||
},
|
||||
newAccumulator
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of item(s) where the id is equal to the id of the user's selected item
|
||||
* If no items are available for a user to select, this will return an empty array.
|
||||
*/
|
||||
export async function paginatedQueryWithSelectPromptAsync<
|
||||
QueryReturnType extends Record<string, any>
|
||||
>(queryArgs: PaginatedQueryWithSelectPromptArgs<QueryReturnType>): Promise<QueryReturnType | void> {
|
||||
return await paginatedQueryWithSelectPromptInternalAsync(queryArgs, []);
|
||||
}
|
||||
|
||||
async function paginatedQueryWithSelectPromptInternalAsync<
|
||||
QueryReturnType extends Record<string, any>
|
||||
>(
|
||||
{
|
||||
limit,
|
||||
offset,
|
||||
queryToPerform,
|
||||
promptOptions,
|
||||
}: PaginatedQueryWithSelectPromptArgs<QueryReturnType>,
|
||||
accumulator: QueryReturnType[]
|
||||
): Promise<QueryReturnType | void> {
|
||||
// query an extra item to determine if there are more pages left
|
||||
const paginatedItems = await queryToPerform(limit + 1, offset);
|
||||
const areMorePagesAvailable = paginatedItems.length > limit;
|
||||
// drop that extra item used for pagination from our render logic
|
||||
const currentPage = paginatedItems.slice(0, limit);
|
||||
const newAccumulator = [...accumulator, ...currentPage];
|
||||
|
||||
const selectionPromptListItems = uniqBy(newAccumulator, queryItem =>
|
||||
promptOptions.getIdentifierForQueryItem(queryItem)
|
||||
).map(queryItem => ({
|
||||
title: promptOptions.createDisplayTextForSelectionPromptListItem(queryItem),
|
||||
value: promptOptions.getIdentifierForQueryItem(queryItem),
|
||||
}));
|
||||
if (areMorePagesAvailable) {
|
||||
selectionPromptListItems.push({ title: 'Next page...', value: fetchMoreValue });
|
||||
}
|
||||
if (selectionPromptListItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const valueOfUserSelectedListItem = await selectAsync<string>(
|
||||
promptOptions.title,
|
||||
selectionPromptListItems
|
||||
);
|
||||
|
||||
if (valueOfUserSelectedListItem === fetchMoreValue) {
|
||||
return await paginatedQueryWithSelectPromptInternalAsync(
|
||||
{
|
||||
limit,
|
||||
offset: offset + limit,
|
||||
queryToPerform,
|
||||
promptOptions,
|
||||
},
|
||||
newAccumulator
|
||||
);
|
||||
}
|
||||
|
||||
return newAccumulator.find(
|
||||
items => promptOptions.getIdentifierForQueryItem(items) === valueOfUserSelectedListItem
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import chalk from 'chalk';
|
||||
import path from 'path';
|
||||
|
||||
import Log from '../../log';
|
||||
import { Ignore, makeShallowCopyAsync } from '../local';
|
||||
import GitClient from './git';
|
||||
|
||||
@@ -27,18 +25,4 @@ export default class GitNoCommitClient extends GitClient {
|
||||
await ignore.initIgnoreAsync();
|
||||
return ignore.ignores(filePath);
|
||||
}
|
||||
|
||||
public async trackFileAsync(file: string): Promise<void> {
|
||||
try {
|
||||
await super.trackFileAsync(file);
|
||||
} catch {
|
||||
// In the no commit workflow it doesn't matter if we fail to track changes,
|
||||
// so we can ignore if this throws an exception
|
||||
Log.warn(
|
||||
`Unable to track ${chalk.bold(path.basename(file))} in Git. Proceeding without tracking.`
|
||||
);
|
||||
Log.warn(` Reason: the command ${chalk.bold(`"git add ${file}"`)} exited with an error.`);
|
||||
Log.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "0.57.0",
|
||||
"version": "0.55.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/eas-build-job": "0.2.85",
|
||||
"@expo/eas-build-job": "0.2.77",
|
||||
"@expo/json-file": "8.2.36",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
@@ -36,14 +36,7 @@
|
||||
"test": "jest",
|
||||
"clean": "rm -rf build node_modules yarn-error.log"
|
||||
},
|
||||
"files": [
|
||||
"/build"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.6.0",
|
||||
"yarn": "1.22.19"
|
||||
}
|
||||
}
|
||||
@@ -1,478 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-06/schema",
|
||||
"type": "object",
|
||||
"description": "The EAS config (eas.json) validation and documentation.",
|
||||
"meta": {
|
||||
"updatedAt": "2022-07-18T15:00:00.000Z"
|
||||
},
|
||||
"properties": {
|
||||
"cli": {
|
||||
"$ref": "#/definitions/Cli"
|
||||
},
|
||||
"build": {
|
||||
"$ref": "#/definitions/Build"
|
||||
},
|
||||
"submit": {
|
||||
"$ref": "#/definitions/Submit"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"Cli": {
|
||||
"type": "object",
|
||||
"description": "Configure the basic behavior of your project with the EAS CLI",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "The compatible versions of EAS CLI with this config",
|
||||
"examples": [
|
||||
">=0.54.1"
|
||||
]
|
||||
},
|
||||
"requireCommit": {
|
||||
"type": "boolean",
|
||||
"description": "If all changes required to be committed before building or submitting"
|
||||
},
|
||||
"appVersionSource": {
|
||||
"enum": [
|
||||
"local",
|
||||
"remote"
|
||||
],
|
||||
"markdownDescription": "Version policy defines whether version of your app should be based on your local project or values stored on EAS servers (remote).\n\nThis is the configuration required for `eas build:version:set` and works with the `autoIncrement` options per platform.",
|
||||
"markdownEnumDescriptions": [
|
||||
"When using local, the `autoIncrement` is based on your local project values.",
|
||||
"When using remote, the `autoIncrement` is based on the values stored on EAS servers."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Build": {
|
||||
"type": "object",
|
||||
"description": "EAS Build configuration. Learn more: https://docs.expo.dev/build-reference/eas-json/",
|
||||
"markdownDescription": "EAS Build configuration. [Learn more](https://docs.expo.dev/build-reference/eas-json/)",
|
||||
"patternProperties": {
|
||||
".*": {
|
||||
"$ref": "#/definitions/BuildProfile"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BuildCache": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"disabled": {
|
||||
"type": "boolean",
|
||||
"description": "Disables caching. Defaults to false."
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"description": "Cache key. You can invalidate the cache by changing this value."
|
||||
},
|
||||
"customPaths": {
|
||||
"type": "array",
|
||||
"description": "List of the paths that will be saved after a successful build and restored at the beginning of the next one.",
|
||||
"markdownDescription": "List of the paths that will be saved after a successful build and restored at the beginning of the next one. Both absolute and relative paths are supported, where relative paths are resolved from the directory with `eas.json`.",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cacheDefaultPaths": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to cache the recommended set of files, currently only Podfile.lock is cached by default for iOS build and nothing is cached for Android.",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"BuildProfile": {
|
||||
"type": "object",
|
||||
"description": "The build profile configuration.",
|
||||
"properties": {
|
||||
"extends": {
|
||||
"type": "string",
|
||||
"description": "The name of the build profile that the current one should inherit values from. This value can't be specified per platform."
|
||||
},
|
||||
"credentialsSource": {
|
||||
"enum": [
|
||||
"remote",
|
||||
"local"
|
||||
],
|
||||
"description": "The source of credentials used to sign build artifacts. Learn more: https://docs.expo.dev/app-signing/local-credentials/",
|
||||
"markdownDescription": "The source of credentials used to sign build artifacts.\n\n- `remote` - if you want to use the credentials managed by EAS.\n- `local` - if you want to provide your own `credentials.json` file. [learn more](https://docs.expo.dev/app-signing/local-credentials/)",
|
||||
"default": "remote",
|
||||
"markdownEnumDescriptions": [
|
||||
"Use the credentials managed by EAS.",
|
||||
"Provide your own `credentials.json` file. [learn more](https://docs.expo.dev/app-signing/local-credentials/)"
|
||||
]
|
||||
},
|
||||
"releaseChannel": {
|
||||
"type": "string",
|
||||
"description": "Name of the release channel for the `expo-updates` package. Learn more: https://docs.expo.dev/distribution/release-channels/",
|
||||
"markdownDescription": "Name of the release channel for the `expo-updates` package. [Learn more](https://docs.expo.dev/distribution/release-channels/)\n\nIf you do not specify a channel, your binary will pull releases from the `default` channel. If you do not use `expo-updates` in your project then this property will have no effect.\n\n**This field only applies to the Classic Update service**; if you use EAS Update, use the [channel](https://docs.expo.dev/build-reference/eas-json/#channel) field instead.",
|
||||
"pattern": "^[a-z0-9._-]+$",
|
||||
"patternErrorMessage": "Release channel names can only contain lowercase letters, numbers and special characters `.`, `_` and `-`."
|
||||
},
|
||||
"channel": {
|
||||
"type": "string",
|
||||
"description": "The channel is a name we can give to multiple builds to identify them easily. Learn more: https://docs.expo.dev/eas-update/how-eas-update-works/",
|
||||
"markdownDescription": "The channel is a name we can give to multiple builds to identify them easily. [Learn more](https://docs.expo.dev/eas-update/how-eas-update-works/)\n\n**This field only applies to the EAS Update service**, if your project still uses Classic Updates then use the [releaseChannel](https://docs.expo.dev/build-reference/eas-json/#releasechannel) field instead."
|
||||
},
|
||||
"distribution": {
|
||||
"enum": [
|
||||
"store",
|
||||
"internal"
|
||||
],
|
||||
"description": "The method of distributing your app. Learn more: https://docs.expo.dev/build/internal-distribution/",
|
||||
"markdownDescription": "The method of distributing your app.\n\n- `internal` - with this option you'll be able to share your build URLs with anyone, and they will be able to install the builds to their devices straight from the Expo website. When using `internal`, make sure the build produces an APK or IPA file. Otherwise, the shareable URL will be useless. [Learn more](https://docs.expo.dev/build/internal-distribution/)\n- `store` - produces builds for store uploads, your build URLs won't be shareable.",
|
||||
"markdownEnumDescriptions": [
|
||||
"With this option you'll be able to share your build URLs with anyone, and they will be able to install the builds to their devices straight from the Expo website. When using `internal`, make sure the build produces an APK or IPA file. Otherwise, the shareable URL will be useless. [Learn more](https://docs.expo.dev/build/internal-distribution/)",
|
||||
"Produces builds for store uploads, your build URLs won't be shareable."
|
||||
]
|
||||
},
|
||||
"developmentClient": {
|
||||
"type": "boolean",
|
||||
"description": "If set to true, this field expresses the intent to produce a development client build. Learn more: https://docs.expo.dev/development/introduction/",
|
||||
"markdownDescription": "If set to true, this field expresses the intent to produce a development client build.\n\nFor the build to be successful, the project must have `expo-dev-client` installed and configured.\n> This field is sugar for setting the iOS `buildConfiguration` to `Debug` and Android `gradleCommand` to `:app:assembleDebug`. Those fields, if provided for the same build profile, will take precedence.\n\n[Learn more](https://docs.expo.dev/development/introduction/)",
|
||||
"default": false
|
||||
},
|
||||
"prebuildCommand": {
|
||||
"type": "string",
|
||||
"description": "Optional override of the prebuild command used by EAS. Learn more: https://docs.expo.dev/workflow/expo-cli/#expo-prebuild",
|
||||
"markdownDescription": "Optional override of the prebuild command used by EAS.\n\nFor example, you can specify `prebuild --template example-template` to use a custom template.\n>`--platform` and `--non-interactive` will be added automatically by the build engine, so you do not need to specify them manually.\n\n[Learn more](https://docs.expo.dev/workflow/expo-cli/#expo-prebuild)"
|
||||
},
|
||||
"node": {
|
||||
"type": "string",
|
||||
"description": "The version of Node.js to use for the build. See: https://nodejs.org/en/about/releases/",
|
||||
"markdownDescription": "The exact version of [Node.js](https://nodejs.org/en/about/releases/) to use for the build."
|
||||
},
|
||||
"yarn": {
|
||||
"type": "string",
|
||||
"description": "The version of Yarn to use for the build. See: https://www.npmjs.com/package/yarn",
|
||||
"markdownDescription": "The exact version of [Yarn](https://www.npmjs.com/package/yarn) to use for the build."
|
||||
},
|
||||
"expoCli": {
|
||||
"type": "string",
|
||||
"description": "The version of `expo-cli` used to `prebuild` your app.",
|
||||
"markdownDescription": "Version of [expo-cli](https://www.npmjs.com/package/expo-cli) used to [prebuild](https://docs.expo.dev/workflow/expo-cli/#expo-prebuild) your app. It does not have any effect on bare projects."
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Environment variables that should be set during the build process.\n\nShould only be used for values that you would commit to your git repository, i.e. not passwords or secrets.",
|
||||
"patternProperties": {
|
||||
".*": {
|
||||
"type": "string",
|
||||
"description": "This environment variable will be set during the build process."
|
||||
}
|
||||
}
|
||||
},
|
||||
"cache": {
|
||||
"$ref": "#/definitions/BuildCache",
|
||||
"description": "The cache configuration.",
|
||||
"markdownDescription": "Cache configuration. This feature is intended for caching values that require a lot of computation, e.g. compilation results (both final binaries and any intermediate files), but it wouldn't work well for `node_modules` because the cache is not local to the machine, so the download speed is similar to downloading from the npm registry."
|
||||
},
|
||||
"android": {
|
||||
"$ref": "#/definitions/BuildProfileAndroid"
|
||||
},
|
||||
"ios": {
|
||||
"$ref": "#/definitions/BuildProfileIos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BuildProfileAndroid": {
|
||||
"type": "object",
|
||||
"description": "The Android-specific build profile configuration.",
|
||||
"properties": {
|
||||
"withoutCredentials": {
|
||||
"type": "boolean",
|
||||
"description": "When set to `true`, EAS CLI won't require you to configure credentials when building the app. This comes in handy when you want to build debug binaries and the debug keystore is checked in to the repository.",
|
||||
"markdownDescription": "When set to `true`, EAS CLI won't require you to configure credentials when building the app. This comes in handy when you want to build debug binaries and the debug keystore is checked in to the repository.",
|
||||
"default": false
|
||||
},
|
||||
"image": {
|
||||
"description": "Image with build environment. Learn more: https://docs.expo.dev/build-reference/infrastructure/",
|
||||
"markdownDescription": "Image with build environment. [Learn more](https://docs.expo.dev/build-reference/infrastructure/)",
|
||||
"default": "default",
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"default",
|
||||
"latest",
|
||||
"stable",
|
||||
"ubuntu-22.04-jdk-11-ndk-r21e",
|
||||
"ubuntu-22.04-jdk-8-ndk-r21e",
|
||||
"ubuntu-20.04-jdk-11-ndk-r21e",
|
||||
"ubuntu-20.04-jdk-8-ndk-r21e",
|
||||
"ubuntu-18.04-jdk-11-ndk-r19c",
|
||||
"ubuntu-18.04-jdk-8-ndk-r19c"
|
||||
],
|
||||
"markdownEnumDescriptions": [
|
||||
"- React Native `>=0.68.0` - `ubuntu-20.04-jdk-11-ndk-r21e`\n- React Native `<0.68.0` - `ubuntu-18.04-jdk-8-ndk-r19c`",
|
||||
"`ubuntu-22.04-jdk-11-ndk-r21e`",
|
||||
"`ubuntu-20.04-jdk-11-ndk-r21e`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ndk": {
|
||||
"type": "string",
|
||||
"description": "The version of Android NDK."
|
||||
},
|
||||
"autoIncrement": {
|
||||
"enum": [
|
||||
false,
|
||||
true,
|
||||
"versionCode",
|
||||
"version"
|
||||
],
|
||||
"description": "Controls how EAS CLI bumps your application build version.",
|
||||
"markdownDescription": "Controls how EAS CLI bumps your application build version.\n\nAllowed values:\n- `\"version\"` - the patch of `expo.version` is bumped (e.g. `1.2.3` -> `1.2.4`).\n- `\"versionCode\"` (or `true`) - `expo.android.versionCode` is bumped (e.g. `3` -> `4`).\n- `false` - versions won't be bumped automatically (default)\n\nIn the case of a bare project, it also updates versions in native code. `expo.version` corresponds to `versionName` and `expo.android.versionCode` to `versionCode` in the `build.gradle`. Google Play uses these values to identify the app build, `versionName` is the version visible to users, whereas `versionCode` defines the version number. The combination of those needs to be unique, so you can bump either of them.\n\nThis feature is not intended for use with dynamic configuration (app.config.js). EAS CLI will throw an error if you don't use app.json.",
|
||||
"default": false,
|
||||
"markdownEnumDescriptions": [
|
||||
"Versions won't be bumped automatically (default).",
|
||||
"`expo.android.versionCode` is bumped (e.g. `3` -> `4`).",
|
||||
"`expo.android.versionCode` is bumped (e.g. `3` -> `4`).",
|
||||
"The patch of `expo.version` is bumped (e.g. `1.2.3` -> `1.2.4`)."
|
||||
]
|
||||
},
|
||||
"buildType": {
|
||||
"enum": ["app-bundle", "apk"],
|
||||
"description": "Type of the artifact you want to build.",
|
||||
"markdownDescription": "Type of the artifact you want to build. It controls what Gradle task will be used, can be overridden by `gradleCommand` or `developmentClient: true` option.\n- `app-bundle` - `:app:bundleRelease`\n- `apk` - `:app:assembleRelease`",
|
||||
"markdownEnumDescriptions": [
|
||||
"Gradle task `:app:bundleRelease` is used",
|
||||
"Gradle task `:app:assembleRelease` is used"
|
||||
]
|
||||
},
|
||||
"gradleCommand": {
|
||||
"type": "string",
|
||||
"description": "Gradle task that will be used to build your project, e.g. `:app:assembleDebug` to build a debug binary.",
|
||||
"markdownDescription": "Gradle task that will be used to build your project, e.g. `:app:assembleDebug` to build a debug binary.\n\nIt's not recommended unless you need to run a task that `buildType` does not support, it takes priority over `buildType` and `developmentClient`."
|
||||
},
|
||||
"artifactPath": {
|
||||
"type": "string",
|
||||
"description": "Path (or pattern) where EAS Build is going to look for the build artifacts. See: https://github.com/mrmlnc/fast-glob#pattern-syntax",
|
||||
"markdownDescription": "Path (or pattern) where EAS Build is going to look for the build artifacts. EAS Build uses the `fast-glob` npm package for pattern matching ([see their README to learn more about the syntax you can use](https://github.com/mrmlnc/fast-glob#pattern-syntax)).",
|
||||
"default": "android/app/build/outputs/**/*.{apk,aab}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BuildProfileIos": {
|
||||
"type": "object",
|
||||
"description": "The iOS-specific build profile configuration.",
|
||||
"properties": {
|
||||
"simulator": {
|
||||
"type": "boolean",
|
||||
"description": "If set to true, creates build for simulator.",
|
||||
"default": false
|
||||
},
|
||||
"enterpriseProvisioning": {
|
||||
"enum": ["universal", "adhoc"],
|
||||
"markdownDescription": "Provisioning method used for `\"distribution\": \"internal\"` when you have an Apple account with Apple Developer Enterprise Program membership.\n\nYou can choose if you want to use `adhoc` or `universal` provisioning. The latter is recommended as it does not require you to register each individual device. If you don't provide this option and you still authenticate with an enterprise team, you'll be prompted which provisioning method to use.",
|
||||
"markdownEnumDescriptions": [
|
||||
"Recommended as it does not require you to register each individual device"
|
||||
]
|
||||
},
|
||||
"autoIncrement": {
|
||||
"enum": [
|
||||
false,
|
||||
true,
|
||||
"buildNumber",
|
||||
"version"
|
||||
],
|
||||
"description": "Controls how EAS CLI bumps your application build version.",
|
||||
"markdownDescription": "Controls how EAS CLI bumps your application build version.\n\nAllowed values:\n\n- `\"version\"` - the patch of `expo.version` is bumped (e.g. `1.2.3` -> `1.2.4`).\n- `\"buildNumber\"` (or `true`) - the last component of `expo.ios.buildNumber` is bumped (e.g. `1.2.3.39` -> `1.2.3.40`).\n- `false` - versions won't be bumped automatically (default)\n\nIn the case of a bare project, it also updates versions in native code. `expo.version` corresponds to `CFBundleShortVersionString` and `expo.ios.buildNumber` to `CFBundleVersion` in the `Info.plist`. The App Store is using those values to identify the app build, `CFBundleShortVersionString` is the version visible to users, whereas `CFBundleVersion` defines the build number. The combination of those needs to be unique, so you can bump either of them.\n\nThis feature is not intended for use with dynamic configuration (app.config.js). EAS CLI will throw an error if you don't use app.json.",
|
||||
"default": false,
|
||||
"markdownEnumDescriptions": [
|
||||
"Versions won't be bumped automatically (default)",
|
||||
"The last component of `expo.ios.buildNumber` is bumped (e.g. `1.2.3.39` -> `1.2.3.40`)",
|
||||
"The last component of `expo.ios.buildNumber` is bumped (e.g. `1.2.3.39` -> `1.2.3.40`)",
|
||||
"The patch of `expo.version` is bumped (e.g. `1.2.3` -> `1.2.4`)"
|
||||
]
|
||||
},
|
||||
"image": {
|
||||
"description": "Image with build environment. Learn more: https://docs.expo.dev/build-reference/infrastructure/",
|
||||
"markdownDescription": "Image with build environment. [Learn more](https://docs.expo.dev/build-reference/infrastructure/)",
|
||||
"default": "default",
|
||||
"anyOf": [
|
||||
{
|
||||
"enum": [
|
||||
"default",
|
||||
"latest",
|
||||
"stable",
|
||||
"macos-monterey-12.4-xcode-13.4",
|
||||
"macos-monterey-12.3-xcode-13.3",
|
||||
"macos-monterey-12.1-xcode-13.2",
|
||||
"macos-big-sur-11.4-xcode-13.0",
|
||||
"macos-big-sur-11.4-xcode-12.5"
|
||||
],
|
||||
"markdownEnumDescriptions": [
|
||||
"- Expo SDK `>=45` - `macos-monterey-12.3-xcode-13.3`\n- Expo SDK `<45` - `macos-big-sur-11.4-xcode-13.0`",
|
||||
"`macos-monterey-12.4-xcode-13.4`",
|
||||
"`macos-monterey-12.3-xcode-13.3`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The version of bundler to use for the build. See: https://bundler.io/.",
|
||||
"markdownDescription": "The version of [bundler](https://bundler.io/) to use for the build."
|
||||
},
|
||||
"fastlane": {
|
||||
"type": "string",
|
||||
"description": "The version of fastlane to use for the build. See: https://fastlane.tools/",
|
||||
"markdownDescription": "The version of [fastlane](https://fastlane.tools/) to use for the build."
|
||||
},
|
||||
"cocoapods": {
|
||||
"type": "string",
|
||||
"description": "The version of CocoaPods to use for the build. See: https://cocoapods.org/",
|
||||
"markdownDescription": "The version of [CocoaPods](https://cocoapods.org/) to use for the build."
|
||||
},
|
||||
"scheme": {
|
||||
"type": "string",
|
||||
"description": "Xcode project's scheme.",
|
||||
"markdownDescription": "Xcode project's scheme.\n- managed project: does not have any effect\n- bare project\n - If your project has multiple schemes, you should set this value.\n - If the project has only one scheme, it will be detected automatically.\n - If multiple schemes exist and this value is **not** set, EAS CLI will prompt you to select one of them."
|
||||
},
|
||||
"buildConfiguration": {
|
||||
"type": "string",
|
||||
"description": "Xcode project's Build Configuration.",
|
||||
"markdownDescription": "Xcode project's Build Configuration.\n- managed project: \"Release\" or \"Debug\", defaults to \"Release\"\n- bare project: defaults to the value specified in the scheme\n\nIt takes priority over `developmentClient` field."
|
||||
},
|
||||
"artifactPath": {
|
||||
"type": "string",
|
||||
"description": "Path (or pattern) where EAS Build is going to look for the build artifacts. See: https://github.com/mrmlnc/fast-glob#pattern-syntax",
|
||||
"markdownDescription": "Path (or pattern) where EAS Build is going to look for the build artifacts.\n\nEAS Build uses the `fast-glob` npm package for pattern matching, ([See their README to learn more about the syntax you can use](https://github.com/mrmlnc/fast-glob#pattern-syntax)).\n\nYou should modify that path only if you are using a custom `Gymfile`.\n\nThe default is `ios/build/Build/Products/*-iphonesimulator/*.app` when building for simulator and `ios/build/*.ipa` in other cases."
|
||||
}
|
||||
}
|
||||
},
|
||||
"Submit": {
|
||||
"type": "object",
|
||||
"description": "EAS Submit configuration. Learn more: https://docs.expo.dev/submit/eas-json/",
|
||||
"markdownDescription": "EAS Submit configuration. [Learn more](https://docs.expo.dev/submit/eas-json/)",
|
||||
"patternProperties": {
|
||||
".*": {
|
||||
"$ref": "#/definitions/SubmitProfile",
|
||||
"description": "The submit profile name with the configuration."
|
||||
}
|
||||
}
|
||||
},
|
||||
"SubmitProfile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"extends": {
|
||||
"type": "string",
|
||||
"description": "The name of the submit profile that the current one should inherit values from."
|
||||
},
|
||||
"android": {
|
||||
"$ref": "#/definitions/SubmitProfileAndroid"
|
||||
},
|
||||
"ios": {
|
||||
"$ref": "#/definitions/SubmitProfileIos"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SubmitProfileAndroid": {
|
||||
"type": "object",
|
||||
"description": "The Android-specific submit profile configuration.",
|
||||
"properties": {
|
||||
"serviceAccountKeyPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the JSON file with service account key used to authenticate with Google Play. Learn more: https://expo.fyi/creating-google-service-account",
|
||||
"markdownDescription": "Path to the JSON file with service account key used to authenticate with Google Play. [Learn more](https://expo.fyi/creating-google-service-account)"
|
||||
},
|
||||
"track": {
|
||||
"enum": ["beta", "alpha", "internal", "production"],
|
||||
"description": "The track of the application to use. Learn more: https://support.google.com/googleplay/android-developer/answer/9859348?hl=en",
|
||||
"markdownDescription": "The [track of the application](https://support.google.com/googleplay/android-developer/answer/9859348?hl=en) to use.",
|
||||
"markdownEnumDescriptions": [
|
||||
"**Open testing** releases are available to testers on Google Play. Users can join tests from your store listing.",
|
||||
"**Closed testing** releases are available to a limited number of testers that you choose, who can test a pre-release version of your app and submit feedback.",
|
||||
"**Internal testing** releases are available to up to 100 testers that you choose.",
|
||||
"**Production releases** are available to all Google Play users in your chosen countries."
|
||||
]
|
||||
},
|
||||
"releaseStatus": {
|
||||
"enum": ["draft", "inProgress", "halted", "completed"],
|
||||
"description": "The status of a release. Learn more: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.tracks",
|
||||
"markdownDescription": "The status of a release. [Learn more](https://developers.google.com/android-publisher/api-ref/rest/v3/edits.tracks)",
|
||||
"enumDescriptions": [
|
||||
"The release's APKs are not being served to users.",
|
||||
"The release's APKs are being served to a fraction of users, if set.",
|
||||
"The release's APKs will no longer be served to users. Users who already have these APKs are unaffected.",
|
||||
"The release will have no further changes. Its APKs are being served to all users, unless they are eligible to APKs of a more recent release."
|
||||
]
|
||||
},
|
||||
"changesNotSentForReview": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates that the changes sent with this submission will not be reviewed until they are explicitly sent for review from the Google Play Console UI. Defaults to false.",
|
||||
"default": false
|
||||
},
|
||||
"applicationId": {
|
||||
"type": "string",
|
||||
"description": "The application id that will be used when accessing Service Account Keys managed by Expo, it does not have any effect if you are using local credentials. In most cases this value will be autodetected, but if you have multiple product flavors, this value might be necessary."
|
||||
}
|
||||
}
|
||||
},
|
||||
"SubmitProfileIos": {
|
||||
"type": "object",
|
||||
"description": "The iOS-specific submit profile configuration.",
|
||||
"properties": {
|
||||
"appleId": {
|
||||
"type": "string",
|
||||
"description": "Your Apple ID username (you can also set the `EXPO_APPLE_ID` env variable).",
|
||||
"markdownDescription": "Your Apple ID username (you can also set the `EXPO_APPLE_ID` env variable)."
|
||||
},
|
||||
"ascAppId": {
|
||||
"type": "string",
|
||||
"description": "App Store Connect unique application Apple ID number. When set, results in skipping the app creation step. Learn more: https://expo.fyi/asc-app-id",
|
||||
"markdownDescription": "App Store Connect unique application Apple ID number. When set, results in skipping the app creation step. [Learn more](https://expo.fyi/asc-app-id)"
|
||||
},
|
||||
"appleTeamId": {
|
||||
"type": "string",
|
||||
"description": "Your Apple Developer Team ID."
|
||||
},
|
||||
"sku": {
|
||||
"type": "string",
|
||||
"description": "An unique ID for your app that is not visible on the App Store, will be generated unless provided."
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"description": "Primary language. Defaults to `en-US`.",
|
||||
"markdownDescription": "Primary language. Defaults to `en-US`."
|
||||
},
|
||||
"companyName": {
|
||||
"type": "string",
|
||||
"description": "The name of your company, needed only for the first submission of any app to the App Store."
|
||||
},
|
||||
"appName": {
|
||||
"type": "string",
|
||||
"description": "The name of your app as it will appear on the App Store. Defaults to `expo.name` from the app config.",
|
||||
"markdownDescription": "The name of your app as it will appear on the App Store. Defaults to `expo.name` from the app config."
|
||||
},
|
||||
"ascApiKeyPath": {
|
||||
"type": "string",
|
||||
"description": "The path to your App Store Connect Api Key .p8 file. Learn more: https://expo.fyi/creating-asc-api-key",
|
||||
"markdownDescription": "The path to your App Store Connect Api Key .p8 file. [Learn more](https://expo.fyi/creating-asc-api-key)"
|
||||
},
|
||||
"ascApiKeyIssuerId": {
|
||||
"type": "string",
|
||||
"description": "The Issuer ID of your App Store Connect Api Key. Learn more: https://expo.fyi/creating-asc-api-key",
|
||||
"markdownDescription": "The Issuer ID of your App Store Connect Api Key. [Learn more](https://expo.fyi/creating-asc-api-key)"
|
||||
},
|
||||
"ascApiKeyId": {
|
||||
"type": "string",
|
||||
"description": "The Key ID of your App Store Connect Api Key. Learn more: https://expo.fyi/creating-asc-api-key",
|
||||
"markdownDescription": "The Key ID of your App Store Connect Api Key. [Learn more](https://expo.fyi/creating-asc-api-key)"
|
||||
},
|
||||
"bundleIdentifier": {
|
||||
"type": "string",
|
||||
"description": "The Bundle identifier that will be used when accessing submit credentials managed by Expo, it does not have any effect if you are using local credentials. In most cases this value will be autodetected, but if you have multiple Xcode schemes and targets, this value might be necessary."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
export { AndroidReleaseStatus, AndroidReleaseTrack, SubmitProfile } from './submit/types';
|
||||
export { getDefaultProfile as getDefaultSubmitProfile } from './submit/resolver';
|
||||
export { EasJson, ProfileType, AppVersionSource } from './types';
|
||||
export { EasJson, ProfileType } from './types';
|
||||
export {
|
||||
AndroidVersionAutoIncrement,
|
||||
BuildProfile,
|
||||
|
||||
@@ -2,13 +2,11 @@ import Joi from 'joi';
|
||||
|
||||
import { BuildProfileSchema } from './build/schema';
|
||||
import { SubmitProfileSchema } from './submit/schema';
|
||||
import { AppVersionSource } from './types';
|
||||
|
||||
export const EasJsonSchema = Joi.object({
|
||||
cli: Joi.object({
|
||||
version: Joi.string(),
|
||||
requireCommit: Joi.boolean(),
|
||||
appVersionSource: Joi.string().valid(...Object.values(AppVersionSource)),
|
||||
}),
|
||||
build: Joi.object().pattern(Joi.string(), BuildProfileSchema),
|
||||
submit: Joi.object().pattern(Joi.string(), SubmitProfileSchema),
|
||||
|
||||
@@ -11,16 +11,10 @@ export enum CredentialsSource {
|
||||
REMOTE = 'remote',
|
||||
}
|
||||
|
||||
export enum AppVersionSource {
|
||||
LOCAL = 'local',
|
||||
REMOTE = 'remote',
|
||||
}
|
||||
|
||||
export interface EasJson {
|
||||
cli?: {
|
||||
version?: string;
|
||||
requireCommit?: boolean;
|
||||
appVersionSource?: AppVersionSource;
|
||||
};
|
||||
build?: { [profileName: string]: EasJsonBuildProfile };
|
||||
submit?: { [profileName: string]: EasJsonSubmitProfile };
|
||||
|
||||
@@ -26,13 +26,9 @@
|
||||
"@types/dateformat": "3.0.1",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/lodash-es": "4.17.6",
|
||||
"@types/node": "18.0.4",
|
||||
"@types/semver": "7.3.10",
|
||||
"ts-node": "10.9.1",
|
||||
"@types/node": "17.0.32",
|
||||
"@types/semver": "7.3.9",
|
||||
"ts-node": "10.7.0",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.6.0",
|
||||
"yarn": "1.22.19"
|
||||
}
|
||||
}
|
||||
Generated
+25
-38
@@ -2,30 +2,17 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@cspotcode/source-map-support@^0.8.0":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
|
||||
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
|
||||
"@cspotcode/source-map-consumer@0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
|
||||
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
|
||||
|
||||
"@cspotcode/[email protected]":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
|
||||
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.0.3":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||
version "1.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/[email protected]":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
|
||||
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@cspotcode/source-map-consumer" "0.8.0"
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.8"
|
||||
@@ -76,15 +63,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5"
|
||||
integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==
|
||||
|
||||
"@types/node@18.0.4":
|
||||
version "18.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.4.tgz#48aedbf35efb3af1248e4cd4d792c730290cd5d6"
|
||||
integrity sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==
|
||||
"@types/node@17.0.32":
|
||||
version "17.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.32.tgz#51d59d7a90ef2d0ae961791e0900cad2393a0149"
|
||||
integrity sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw==
|
||||
|
||||
"@types/[email protected].10":
|
||||
version "7.3.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.10.tgz#5f19ee40cbeff87d916eedc8c2bfe2305d957f73"
|
||||
integrity sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw==
|
||||
"@types/[email protected].9":
|
||||
version "7.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc"
|
||||
integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ==
|
||||
|
||||
acorn-walk@^8.1.1:
|
||||
version "8.2.0"
|
||||
@@ -168,12 +155,12 @@ [email protected]:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
ts-node@10.9.1:
|
||||
version "10.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
|
||||
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
|
||||
ts-node@10.7.0:
|
||||
version "10.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5"
|
||||
integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
|
||||
dependencies:
|
||||
"@cspotcode/source-map-support" "^0.8.0"
|
||||
"@cspotcode/source-map-support" "0.7.0"
|
||||
"@tsconfig/node10" "^1.0.7"
|
||||
"@tsconfig/node12" "^1.0.7"
|
||||
"@tsconfig/node14" "^1.0.0"
|
||||
@@ -184,7 +171,7 @@ [email protected]:
|
||||
create-require "^1.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
v8-compile-cache-lib "^3.0.1"
|
||||
v8-compile-cache-lib "^3.0.0"
|
||||
yn "3.1.1"
|
||||
|
||||
[email protected]:
|
||||
@@ -202,7 +189,7 @@ universalify@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
|
||||
|
||||
v8-compile-cache-lib@^3.0.1:
|
||||
v8-compile-cache-lib@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||
|
||||
@@ -1134,13 +1134,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||
|
||||
"@cspotcode/source-map-support@^0.8.0":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
|
||||
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@endemolshinegroup/[email protected]":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d"
|
||||
@@ -1276,10 +1269,10 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@expo/[email protected].85":
|
||||
version "0.2.85"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.85.tgz#b5dd12a23943b67139dc47322428b9e1958f6804"
|
||||
integrity sha512-iPvj8ntlevBwaHw00Qj+36L1MDZk6GygjPcV8QldaWeDVjdypu9ZcX+UqYoFmQFiLZYEfwm2s89MDEwQJvNn+A==
|
||||
"@expo/[email protected].77":
|
||||
version "0.2.77"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.77.tgz#2ebdb3e6706c4020812e2e8e3d2e7da26e3c7eb1"
|
||||
integrity sha512-CFS/Uixa3XqV39vdoDnQP93kgd2EGf3gaOqLYl+9gnMCP9h2+Q9fIbfPw5DnFu2LEIyU+Xw5DtpkyTQEBjTLyA==
|
||||
dependencies:
|
||||
joi "^17.4.2"
|
||||
semver "^7.3.5"
|
||||
@@ -2240,14 +2233,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
|
||||
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
|
||||
|
||||
"@jridgewell/[email protected]":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
|
||||
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.10"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.10.tgz#db436f0917d655393851bc258918c00226c9b183"
|
||||
@@ -3556,26 +3541,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
|
||||
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
|
||||
|
||||
"@tsconfig/node12@^1.0.7":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
|
||||
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
|
||||
|
||||
"@tsconfig/node14@^1.0.0":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
|
||||
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
|
||||
|
||||
"@tsconfig/node16@^1.0.2":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
||||
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
||||
|
||||
"@types/babel__core@^7.1.14":
|
||||
version "7.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024"
|
||||
@@ -4005,16 +3970,6 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn-walk@^8.1.1:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||
|
||||
acorn@^8.4.1:
|
||||
version "8.8.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
|
||||
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
||||
|
||||
acorn@^8.7.1:
|
||||
version "8.7.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
|
||||
@@ -5411,11 +5366,6 @@ cosmiconfig@^7.0.0:
|
||||
path-type "^4.0.0"
|
||||
yaml "^1.10.0"
|
||||
|
||||
create-require@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||
|
||||
[email protected], cross-fetch@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||
@@ -8980,7 +8930,7 @@ lodash.without@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
|
||||
integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=
|
||||
|
||||
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0, lodash@~4.17.0:
|
||||
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.21, lodash@^4.7.0, lodash@~4.17.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -12182,32 +12132,6 @@ ts-log@^2.2.3:
|
||||
resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb"
|
||||
integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w==
|
||||
|
||||
[email protected]:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-mockito/-/ts-mockito-2.6.1.tgz#bc9ee2619033934e6fad1c4455aca5b5ace34e73"
|
||||
integrity sha512-qU9m/oEBQrKq5hwfbJ7MgmVN5Gu6lFnIGWvpxSjrqq6YYEVv+RwVFWySbZMBgazsWqv6ctAyVBpo9TmAxnOEKw==
|
||||
dependencies:
|
||||
lodash "^4.17.5"
|
||||
|
||||
[email protected]:
|
||||
version "10.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
|
||||
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
|
||||
dependencies:
|
||||
"@cspotcode/source-map-support" "^0.8.0"
|
||||
"@tsconfig/node10" "^1.0.7"
|
||||
"@tsconfig/node12" "^1.0.7"
|
||||
"@tsconfig/node14" "^1.0.0"
|
||||
"@tsconfig/node16" "^1.0.2"
|
||||
acorn "^8.4.1"
|
||||
acorn-walk "^8.1.1"
|
||||
arg "^4.1.0"
|
||||
create-require "^1.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
v8-compile-cache-lib "^3.0.1"
|
||||
yn "3.1.1"
|
||||
|
||||
ts-node@^9:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3"
|
||||
@@ -12549,11 +12473,6 @@ uuid@^7.0.3:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
||||
|
||||
v8-compile-cache-lib@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||
|
||||
v8-compile-cache@^2.0.3:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
|
||||
|
||||
Reference in new issue
Block a user