Compare commits

..
Author SHA1 Message Date
Dominik Sokal dd7a3912c6 address PR feedback 2022-07-29 14:22:56 +02:00
Dominik Sokal 77ac0550a9 use commit message as default build message 2022-07-29 11:07:02 +02:00
25 changed files with 445 additions and 976 deletions

No files matched your search

+1 -2
View File
@@ -8,9 +8,7 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 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))
### 🐛 Bug fixes
@@ -23,6 +21,7 @@ This is the log of notable changes to EAS CLI and related packages.
- 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))
- 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))
### 🐛 Bug fixes
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.57.0"
"version": "0.56.0"
}
-18
View File
@@ -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',
{
+49 -58
View File
@@ -100,7 +100,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.56.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -117,7 +117,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.56.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -134,7 +134,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.56.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -148,7 +148,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.56.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -197,7 +197,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.56.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -217,7 +217,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.56.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -225,20 +225,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.56.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -257,7 +253,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.56.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -265,23 +261,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.56.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -290,10 +282,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 +302,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.56.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -325,7 +316,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.56.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -342,7 +333,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.56.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -377,7 +368,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.56.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -410,7 +401,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.56.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -455,7 +446,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.56.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -475,7 +466,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.56.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -496,7 +487,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.56.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -513,7 +504,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.56.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -533,7 +524,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.56.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -551,7 +542,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.56.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -565,7 +556,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.56.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -579,7 +570,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.56.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -597,7 +588,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.56.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -614,7 +605,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.56.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -628,7 +619,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.56.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -642,7 +633,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.56.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -724,7 +715,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.56.0/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -741,7 +732,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.56.0/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas project:info`
@@ -755,7 +746,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.56.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -772,7 +763,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.56.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -792,7 +783,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.56.0/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -809,7 +800,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.56.0/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -823,7 +814,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.56.0/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -853,7 +844,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.56.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -883,7 +874,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.56.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -900,7 +891,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.56.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -920,7 +911,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.56.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -939,7 +930,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.56.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -959,7 +950,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.56.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -979,7 +970,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.56.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -996,7 +987,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.56.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1013,7 +1004,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.56.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1034,7 +1025,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.56.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1051,7 +1042,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.56.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
-24
View File
@@ -11741,18 +11741,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "gitCommitMessage",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
@@ -13192,18 +13180,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "gitCommitMessage",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "iosEnterpriseProvisioning",
"description": null,
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "0.57.0",
"version": "0.56.0",
"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.84",
"@expo/eas-json": "0.57.0",
"@expo/eas-build-job": "0.2.82",
"@expo/eas-json": "0.56.0",
"@expo/json-file": "8.2.36",
"@expo/multipart-body-parser": "1.1.0",
"@expo/pkcs12": "0.0.8",
-109
View File
@@ -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 +1,40 @@
import { truncateGitCommitMessage } from '../metadata';
import { instance, mock, when } from 'ts-mockito';
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));
import { Client } from '../../vcs/vcs';
import { getFormattedCommitMessageAsync } from '../metadata';
describe(getFormattedCommitMessageAsync, () => {
it(`returns undefined if it's not a git project`, async () => {
const clientMock = mock<Client>();
when(clientMock.getLastCommitMessageAsync()).thenResolve(null);
const client = instance(clientMock);
await expect(getFormattedCommitMessageAsync(client)).resolves.toBe(undefined);
});
it('truncates long commit messages', () => {
expect(truncateGitCommitMessage('a'.repeat(5000))).toBe(`${'a'.repeat(4093)}...`);
it('returns commit message', async () => {
const clientMock = mock<Client>();
when(clientMock.getLastCommitMessageAsync()).thenResolve('lorem ipsum');
const client = instance(clientMock);
await expect(getFormattedCommitMessageAsync(client)).resolves.toBe('lorem ipsum');
});
it('trims long commit messages', async () => {
const clientMock = mock<Client>();
when(clientMock.getLastCommitMessageAsync()).thenResolve('a'.repeat(1500));
const client = instance(clientMock);
await expect(getFormattedCommitMessageAsync(client)).resolves.toBe(`${'a'.repeat(1021)}...`);
});
it('replaces new lines with spaces', async () => {
const clientMock = mock<Client>();
when(clientMock.getLastCommitMessageAsync()).thenResolve(`Lorem\nipsum\ndolor\nsit\namet`);
const client = instance(clientMock);
await expect(getFormattedCommitMessageAsync(client)).resolves.toBe(
`Lorem ipsum dolor sit amet`
);
});
});
+13 -11
View File
@@ -17,6 +17,7 @@ import {
import { ensureLoggedInAsync } from '../user/actions';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { Client } from '../vcs/vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
@@ -24,12 +25,13 @@ 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 vcsClient = getVcsClient();
const metadata: Metadata = {
trackingContext: ctx.trackingCtx,
...(await maybeResolveVersionsAsync(ctx)),
@@ -45,12 +47,9 @@ export async function collectMetadataAsync<T extends Platform>(
appIdentifier: resolveAppIdentifier(ctx),
buildProfile: ctx.buildProfileName,
gitCommitHash: await vcsClient.getCommitHashAsync(),
gitCommitMessage: truncateGitCommitMessage(
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
),
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
message: ctx.message,
message: ctx.message ?? (await getFormattedCommitMessageAsync(vcsClient)),
...(ctx.platform === Platform.IOS && {
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(
ctx as BuildContext<Platform.IOS>
@@ -151,12 +150,15 @@ function resolveIosEnterpriseProvisioning(
return ctx.buildProfile.enterpriseProvisioning;
}
export function truncateGitCommitMessage(
msg: string | undefined,
maxLength: number = 4096
): string | undefined {
if (msg === undefined) {
export async function getFormattedCommitMessageAsync(
vcsClient: Client
): Promise<string | undefined> {
const maybeCommitMessage = (await vcsClient.getLastCommitMessageAsync())?.trim();
if (!maybeCommitMessage) {
return undefined;
}
return msg.length > maxLength ? `${msg.substring(0, maxLength - 3)}...` : msg;
const lines = maybeCommitMessage.split('\n');
const withoutEmptyLines = lines.filter(i => i);
const message = withoutEmptyLines.join(' ');
return message.length > 1024 ? message.substring(0, 1021) + '...' : message;
}
@@ -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;
};
+86 -8
View File
@@ -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.`);
}
}
}
}
+73 -22
View File
@@ -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());
}
}
}
+6 -12
View File
@@ -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,7 +25,6 @@ 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, { link } from '../../log';
import { ora } from '../../ora';
@@ -60,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'];
@@ -129,8 +128,8 @@ export async function ensureBranchExistsAsync({
}: {
appId: string;
name: string;
limit: number;
offset: number;
limit?: number;
offset?: number;
}): Promise<{
id: string;
updates: Exclude<
@@ -292,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',
@@ -479,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
@@ -574,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);
@@ -649,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?`
);
+38 -23
View File
@@ -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],
+10 -14
View File
@@ -1726,7 +1726,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']>;
@@ -1895,7 +1894,6 @@ 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']>;
@@ -4043,6 +4041,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;
}>;
@@ -4661,15 +4667,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'];
}>;
@@ -4745,11 +4742,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'];
@@ -4759,7 +4755,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,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 ?? [];
},
};
@@ -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
}
}
}
}
@@ -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],
-95
View File
@@ -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());
}
}
+7 -38
View File
@@ -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
)})`;
@@ -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();
});
});
-133
View File
@@ -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
);
}
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "0.57.0",
"version": "0.56.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/eas-build-job": "0.2.84",
"@expo/eas-build-job": "0.2.82",
"@expo/json-file": "8.2.36",
"chalk": "4.1.2",
"env-string": "1.0.1",
Generated
+4 -4
View File
@@ -1269,10 +1269,10 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/[email protected]4":
version "0.2.84"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.84.tgz#d5b423ee1ee828b21c9b6bee611dfd97728da5d4"
integrity sha512-e4OHxRUz2DeZWOsQkNWyAbJUGfOE6O2dH1/hEyzWeXew9kPrDr/u/aKAYicwOiZACiikBl+x7bOAIdoZuOz7uA==
"@expo/[email protected]2":
version "0.2.82"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-0.2.82.tgz#b0f5ec0462314bbd94c3b126faf881beee84593a"
integrity sha512-Yamyf0GZ/jjyvsgL4N7szhcht7T22SV6VwLBdddPYUWM53+9w70CRIxsCrmW6BPuW4nQUu0M+WoK5/fkYUd/zg==
dependencies:
joi "^17.4.2"
semver "^7.3.5"