Compare commits

..
Author SHA1 Message Date
kbrandwijk dd02e8be4a chore: initial setup for ESM 2022-04-24 20:43:37 +02:00
105 changed files with 734 additions and 2695 deletions

No files matched your search

-6
View File
@@ -12,12 +12,6 @@ This is the log of notable changes to EAS CLI and related packages.
### 🧹 Chores
## [0.52.0](https://github.com/expo/eas-cli/releases/tag/v0.52.0) - 2022-04-26
### 🎉 New features
- Support target specific entitlements. ([#1078](https://github.com/expo/eas-cli/pull/1078) by [@wkozyra95](https://github.com/wkozyra95))
## [0.51.0](https://github.com/expo/eas-cli/releases/tag/v0.51.0) - 2022-04-19
### 🎉 New features
+5 -5
View File
@@ -1,7 +1,7 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
import easCliConfig from './packages/eas-cli/jest.config.js';
import easJsonConfig from './packages/eas-json/jest.config.js';
export default {
projects: [easCliConfig, easJsonConfig],
testPathIgnorePatterns: ['.*'],
};
+2 -1
View File
@@ -1,7 +1,8 @@
module.exports = {
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
extensionsToTreatAsEsm: ['.ts'],
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.52.0"
"version": "0.51.0"
}
+2 -1
View File
@@ -1,5 +1,6 @@
{
"private": true,
"type":"module",
"workspaces": [
"packages/*"
],
@@ -26,7 +27,7 @@
"jest": "27.5.1",
"jest-watch-typeahead": "1.0.0",
"lerna": "4.0.0",
"oclif": "2.6.3",
"oclif": "3.0.1",
"prettier": "2.6.1",
"ts-jest": "27.1.4",
"typescript": "4.6.3"
+42 -42
View File
@@ -98,7 +98,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -115,7 +115,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -132,7 +132,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -146,7 +146,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -195,7 +195,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -215,7 +215,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -232,7 +232,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -251,7 +251,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -271,7 +271,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -300,7 +300,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -314,7 +314,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -331,7 +331,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -366,7 +366,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.52.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -399,7 +399,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -444,7 +444,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -464,7 +464,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -485,7 +485,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -502,7 +502,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -522,7 +522,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -540,7 +540,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -554,7 +554,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -568,7 +568,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.52.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -586,7 +586,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -603,7 +603,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -617,7 +617,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -631,7 +631,7 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -710,7 +710,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -727,7 +727,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -747,7 +747,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.52.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -764,7 +764,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -778,7 +778,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.52.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -808,7 +808,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -838,7 +838,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -855,7 +855,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -875,7 +875,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.52.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -894,7 +894,7 @@ DESCRIPTION
view the recent updates for a branch
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -914,7 +914,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -934,7 +934,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -951,7 +951,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -968,7 +968,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -989,7 +989,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1006,7 +1006,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
File renamed without changes.
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env node
require('@oclif/core')
.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'));
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env node
import { Errors, flush, run } from '@oclif/core';
run(undefined, import.meta.url)
.then(flush)
.catch(Errors.handle);
@@ -1,7 +0,0 @@
{
"extends": "@tsconfig/node16/tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx",
"strict": true
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
overwrite: true
schema: 'http://localhost:3000/graphql'
schema: 'https://staging-api.expo.dev/graphql'
documents:
- 'src/graphql/**/!(*.d).{ts,tsx}'
- 'src/credentials/ios/api/graphql/**/!(*.d).{ts,tsx}'
-347
View File
@@ -2162,39 +2162,6 @@
"name": "AccountUsageMetrics",
"description": null,
"fields": [
{
"name": "byBillingPeriod",
"description": null,
"args": [
{
"name": "date",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "UsageMetricTotal",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metricsForServiceMetric",
"description": null,
@@ -13788,33 +13755,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeployFunctionResult",
"description": null,
"fields": [
{
"name": "success",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Deployment",
@@ -14624,210 +14564,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "FunctionMutation",
"description": null,
"fields": [
{
"name": "createFunction",
"description": null,
"args": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "bucketKey",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeployFunctionResult",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "getFunctionStatus",
"description": null,
"args": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "FunctionStatusResult",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "getSignedFunctionUploadSpecification",
"description": null,
"args": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "GetFunctionUploadUrlResult",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "FunctionStatusResult",
"description": null,
"fields": [
{
"name": "logUrls",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "status",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "GetFunctionUploadUrlResult",
"description": null,
"fields": [
{
"name": "specification",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "GetSignedAssetUploadSpecificationsResult",
@@ -19921,22 +19657,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "function",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "FunctionMutation",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "googleServiceAccountKey",
"description": "Mutations that modify a Google Service Account Key",
@@ -23259,73 +22979,6 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "UsageMetricTotal",
"description": null,
"fields": [
{
"name": "billingPeriod",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "BillingPeriod",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metrics",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AccountUsageMetric",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "total",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "UsageMetricType",
+11 -2
View File
@@ -1,5 +1,14 @@
module.exports = {
...require('../../jest/jest.config.js'),
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import baseConfig from '../../jest/jest.config.js';
const filename = fileURLToPath(import.meta.url);
// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(filename);
export default {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
+9 -19
View File
@@ -1,27 +1,27 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "0.52.0",
"version": "0.51.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
"eas": "./bin/run.js"
},
"type": "module",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.31",
"@expo/code-signing-certificates": "0.0.2",
"@expo/config": "6.0.21",
"@expo/config-plugins": "4.1.2",
"@expo/config": "6.0.19",
"@expo/config-plugins": "4.1.0",
"@expo/config-types": "44.0.0",
"@expo/eas-build-job": "0.2.71",
"@expo/eas-json": "0.52.0",
"@expo/eas-build-job": "0.2.68",
"@expo/eas-json": "0.51.0",
"@expo/json-file": "8.2.34",
"@expo/multipart-body-parser": "1.1.0",
"@expo/package-manager": "0.0.50",
"@expo/pkcs12": "0.0.7",
"@expo/plist": "0.0.17",
"@expo/plugin-warn-if-update-available": "2.1.0",
"@expo/prebuild-config": "3.1.2",
"@expo/prebuild-config": "3.1.0",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.6.0",
@@ -31,7 +31,6 @@
"@oclif/plugin-help": "5.1.12",
"@urql/core": "2.4.3",
"@urql/exchange-retry": "0.3.2",
"adm-zip": "0.5.9",
"chalk": "4.1.2",
"cli-progress": "3.10.0",
"cli-table3": "0.6.1",
@@ -44,7 +43,6 @@
"form-data": "4.0.0",
"fs-extra": "10.0.1",
"getenv": "1.0.0",
"got": "^11.1.4",
"gradle-to-js": "2.0.0",
"graphql": "16.3.0",
"graphql-tag": "2.12.6",
@@ -52,7 +50,6 @@
"ignore": "5.2.0",
"indent-string": "4.0.0",
"jks-js": "1.0.2",
"js-yaml": "^3.13.1",
"joi": "17.6.0",
"keychain": "1.3.0",
"log-symbols": "4.1.0",
@@ -66,20 +63,16 @@
"pkg-dir": "4.2.0",
"prompts": "2.4.2",
"qrcode-terminal": "0.12.0",
"pnp-webpack-plugin": "1.7.0",
"react-dev-utils": "12.0.0",
"resolve-from": "5.0.0",
"semver": "7.3.5",
"slash": "3.0.0",
"strip-ansi": "6.0.0",
"tar": "6.1.11",
"terminal-link": "2.1.1",
"ts-loader": "^9.2.8",
"tslib": "2.3.1",
"turndown": "7.1.1",
"untildify": "4.0.0",
"uuid": "8.3.2",
"webpack": "5.72.0",
"wrap-ansi": "7.0.0"
},
"devDependencies": {
@@ -88,12 +81,10 @@
"@graphql-codegen/typescript": "2.4.8",
"@graphql-codegen/typescript-operations": "2.3.5",
"@types/cli-progress": "3.9.2",
"@types/adm-zip": "0.4.34",
"@types/dateformat": "3.0.1",
"@types/envinfo": "7.8.1",
"@types/fs-extra": "9.0.13",
"@types/getenv": "^1.0.0",
"@types/js-yaml": "^3.12.2",
"@types/mime": "2.0.3",
"@types/node-fetch": "^2.5.7",
"@types/node-forge": "1.0.1",
@@ -102,7 +93,6 @@
"@types/tar": "6.1.1",
"@types/tough-cookie": "4.0.1",
"@types/uuid": "8.3.4",
"@types/webpack": "5.28.0",
"@types/wrap-ansi": "3.0.0",
"axios": "0.26.1",
"eslint-plugin-graphql": "4.0.0",
@@ -115,7 +105,7 @@
"typescript": "4.6.3"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.16"
},
"files": [
"/bin",
+3
View File
@@ -0,0 +1,3 @@
export function asMock(fn: any): jest.Mock {
return fn as jest.Mock;
}
+7 -6
View File
@@ -36,12 +36,13 @@ export async function createIosContextAsync(
},
buildProfile
);
const targets = await resolveTargetsAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
xcodeBuildContext,
env: buildProfile.env,
});
const targets = await resolveTargetsAsync(
{
projectDir: ctx.projectDir,
exp: ctx.exp,
},
xcodeBuildContext
);
const applicationTarget = findApplicationTarget(targets);
const applicationTargetBuildSettings = resolveBuildSettings(ctx, applicationTarget);
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { resolveEntitlementsJsonAsync } from '../../credentials/ios/appstore/entitlements';
import { IosCredentials, Target } from '../../credentials/ios/types';
import { CredentialsResult } from '../build';
import { BuildContext } from '../context';
@@ -18,6 +19,13 @@ export async function ensureIosCredentialsAsync(
const provider = new IosCredentialsProvider(buildCtx.credentialsCtx, {
app: getAppFromContext(buildCtx.credentialsCtx),
targets,
iosCapabilitiesOptions: {
entitlements: await resolveEntitlementsJsonAsync(
buildCtx.projectDir,
buildCtx.workflow,
buildCtx.buildProfile.env ?? {}
),
},
distribution: buildCtx.buildProfile.distribution ?? 'store',
enterpriseProvisioning: buildCtx.buildProfile.enterpriseProvisioning,
});
+1 -1
View File
@@ -6,7 +6,7 @@ import semver from 'semver';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '0.0.78';
const PLUGIN_PACKAGE_VERSION = '0.0.74';
export interface LocalBuildOptions {
enable: boolean;
@@ -1,4 +1,3 @@
import admzip from 'adm-zip';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
@@ -123,63 +122,6 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n
return { size, path: tarPath };
}
export async function makeZipAsync(pathToZip: string): Promise<{ path: string; size: number }> {
const spinner = ora('Compressing project files');
await fs.mkdirp(getTmpDirectory());
const shallowClonePath = path.join(getTmpDirectory(), `${uuidv4()}-shallow-clone`);
try {
process.chdir(pathToZip);
await getVcsClient().makeShallowCopyAsync(shallowClonePath);
process.chdir('..');
const zipPath = path.join(getTmpDirectory(), `${uuidv4()}.zip`);
// If the compression takes longer then a second, show the spinner.
// This can happen when the user has a lot of resources or doesn't ignore their CocoaPods.
// A basic project on a Mac can compress in roughly ~40ms.
// A fairly complex project without CocoaPods ignored can take up to 30s.
const timer = setTimeout(
() => {
spinner.start();
},
Log.isDebug ? 1 : 1000
);
// TODO: Possibly warn after more time about unoptimized assets.
const compressTimerLabel = 'makeProjectZipAsync';
startTimer(compressTimerLabel);
const zip = new admzip();
zip.addLocalFolder(shallowClonePath);
await zipAsync(zip, zipPath);
clearTimeout(timer);
const { size } = await fs.stat(zipPath);
const duration = endTimer(compressTimerLabel);
if (spinner.isSpinning) {
const prettyTime = formatMilliseconds(duration);
spinner.succeed(
`Compressed project files ${chalk.dim(`${prettyTime} (${formatBytes(size)})`)}`
);
}
return { size, path: zipPath };
} catch (err) {
if (spinner.isSpinning) {
spinner.fail();
}
throw err;
} finally {
await fs.remove(shallowClonePath);
}
}
async function zipAsync(zip: admzip, targetFileName?: string): Promise<void> {
return new Promise((resolve, reject) => {
zip.writeZip(targetFileName, (error: Error | null) => (error ? reject(error) : resolve()));
});
}
enum ShouldCommitChanges {
Yes,
ShowDiffFirst,
@@ -1,8 +1,12 @@
import { asMock } from '../../__tests__/utils';
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
import { getUserAsync } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand from '../EasCommand';
import TestEasCommand from './TestEasCommand';
TestEasCommand.prototype.authValue = jest.fn().mockImplementation(() => true);
jest.mock('../../user/User');
jest.mock('../../user/actions', () => ({ ensureLoggedInAsync: jest.fn() }));
@@ -28,30 +32,17 @@ afterAll(() => {
});
beforeEach(() => {
jest
.mocked(getUserAsync)
asMock(getUserAsync)
.mockReset()
.mockImplementation(async () => mockJester);
jest
.mocked(ensureLoggedInAsync)
.mockImplementation(() => mockJester);
asMock(ensureLoggedInAsync)
.mockReset()
.mockImplementation(async () => mockJester);
jest.mocked(initAsync).mockReset();
jest.mocked(flushAsync).mockReset();
jest.mocked(logEvent).mockReset();
.mockImplementation(() => mockJester);
asMock(initAsync).mockReset();
asMock(flushAsync).mockReset();
asMock(logEvent).mockReset();
});
const createTestEasCommand = (authValue: boolean): typeof EasCommand => {
class TestEasCommand extends EasCommand {
requiresAuthentication = authValue;
async runAsync(): Promise<void> {}
}
TestEasCommand.id = 'TestEasCommand'; // normally oclif will assign ids, but b/c this is located outside the commands folder it will not
return TestEasCommand;
};
describe(EasCommand.name, () => {
describe('without exceptions', () => {
// The first test in this suite should have an increased timeout
@@ -62,36 +53,32 @@ describe(EasCommand.name, () => {
// See https://github.com/oclif/command/blob/master/src/command.ts#L80
// and look for "Config.load"
it('ensures the user is logged in', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(ensureLoggedInAsync).toHaveBeenCalled();
}, 15_000);
it('ensures the user data is read from cache', async () => {
const TestEasCommand = createTestEasCommand(false);
asMock(TestEasCommand.prototype.authValue).mockImplementationOnce(() => false);
await TestEasCommand.run();
expect(jest.mocked(getUserAsync)).toBeCalledTimes(1);
expect(jest.mocked(ensureLoggedInAsync)).not.toBeCalled();
expect(getUserAsync).toHaveReturnedWith(mockJester);
});
it('initializes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(initAsync).toHaveBeenCalled();
});
it('flushes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(flushAsync).toHaveBeenCalled();
});
it('logs events', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(logEvent).toHaveBeenCalledWith('action', {
@@ -102,7 +89,6 @@ describe(EasCommand.name, () => {
describe('after exceptions', () => {
it('flushes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
try {
await TestEasCommand.run().then(() => {
throw new Error('foo');
@@ -0,0 +1,13 @@
import EasCommand from '../EasCommand';
export default class TestEasCommand extends EasCommand {
requiresAuthentication = this.authValue();
authValue(): boolean {
return false;
}
async runAsync(): Promise<void> {}
}
TestEasCommand.id = 'TestEasCommand'; // normally oclif will assign ids, but b/c this is located outside the commands folder it will not
@@ -1,151 +0,0 @@
import { getConfig } from '@expo/config';
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import admzip from 'adm-zip';
import fs from 'fs';
import fsPromise from 'fs/promises';
import got from 'got';
import fetch from 'node-fetch';
import path from 'path';
import { pipeline } from 'stream/promises';
import { makeZipAsync } from '../../build/utils/repository';
import EasCommand from '../../commandUtils/EasCommand';
import { bundleFunctionsAsync, prepareFunctionEntriesAsync } from '../../functions/webpack';
import { FunctionMutation } from '../../graphql/mutations/FunctionMutation';
import { PresignedPost } from '../../graphql/mutations/UploadSessionMutation';
import Log from '../../log';
import { ora } from '../../ora';
import {
findProjectRootAsync,
getProjectAccountName,
getProjectIdAsync,
} from '../../project/projectUtils';
import { uploadWithPresignedPostAsync } from '../../uploads';
import { ensureLoggedInAsync } from '../../user/actions';
import { expoCommandAsync } from '../../utils/expoCli';
import { sleepAsync } from '../../utils/promise';
export default class FunctionsCreate extends EasCommand {
static description = 'create a function';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
const projectId = await getProjectIdAsync(exp);
const downloadSpinner = ora('Downloading functions template...').start();
const functionsTemplateUrl =
'https://github.com/quinlanj/functions-entry-point/archive/refs/heads/main.zip';
const functionsBuild = 'functions-build';
const functionsBuildZip = `${functionsBuild}.zip`;
const originalUnzippedDirectory = 'functions-entry-point-main';
// ensure empty build directory
await fsPromise.rm(path.join(projectDir, functionsBuild), { force: true, recursive: true });
// download and unzip the functions template
const downloadStream = got.stream(functionsTemplateUrl);
await pipeline(downloadStream, fs.createWriteStream(path.join(projectDir, functionsBuildZip)));
const zip = new admzip(functionsBuildZip);
zip.extractAllTo(/*target path*/ path.join(projectDir), /*overwrite*/ true);
// rename to desired dir, remove zip
await Promise.all([
fsPromise.rename(
path.join(projectDir, originalUnzippedDirectory),
path.join(projectDir, functionsBuild)
),
fsPromise.rm(path.join(projectDir, functionsBuildZip)),
]);
downloadSpinner.succeed('Downloaded functions template');
const permissionSpinner = ora('Modifying permissions...').start();
const promises = [];
for (const file of await fsPromise.readdir(path.join(projectDir, functionsBuild, 'scripts'))) {
promises.push(fsPromise.chmod(path.join(projectDir, functionsBuild, 'scripts', file), 0o777));
}
await Promise.all(promises);
permissionSpinner.succeed('Modified permissions');
const gitSpinner = ora('Initializing inner git...').start();
process.chdir(path.join(projectDir, functionsBuild));
const { status, stderr } = await spawnAsync('git', ['init']);
if (status !== 0) {
throw new Error('Failed to initialize git: ' + stderr);
}
process.chdir('..');
gitSpinner.succeed('Initialized inner git');
Log.log('Bundling functions...');
const functionDirectory = path.join(projectDir, 'functions');
const functionEntries = await prepareFunctionEntriesAsync(functionDirectory);
const outputDirectory = path.join(projectDir, functionsBuild, 'functions');
await bundleFunctionsAsync(projectDir, functionEntries, outputDirectory);
Log.log('Successfully bundled functions...');
Log.log('Doing web build...');
const accountName = getProjectAccountName(exp, await ensureLoggedInAsync());
const functionName = `${accountName}-${exp.slug}`; // TODO: set up a Cloudflare enterprise domain so we don't have to do this
const region = 'us-east1';
const cloudFunctionProject = 'eas-functions';
const publicUrl = `https://${region}-${cloudFunctionProject}.cloudfunctions.net/${functionName}`;
const webOutputDirectory = path.join(projectDir, functionsBuild, 'web-build');
await expoCommandAsync(projectDir, ['build:web'], {
env: { WEBPACK_BUILD_OUTPUT_PATH: webOutputDirectory, WEB_PUBLIC_URL: publicUrl },
});
Log.log('Finished web build...');
const { path: compressedProjectPath } = await makeZipAsync(
path.join(projectDir, functionsBuild)
);
const uploadSpinner = ora('Uploading project...').start();
let bucketKey;
try {
const specification = await FunctionMutation.uploadAsync(projectId);
const presignedPost: PresignedPost = JSON.parse(specification);
await uploadWithPresignedPostAsync(compressedProjectPath, presignedPost);
bucketKey = presignedPost.fields.key;
uploadSpinner.succeed(`Successfully uploaded project to bucket: ${bucketKey}`);
} catch (err) {
uploadSpinner.fail('Failed to upload project');
throw err;
}
const spinner = ora('Creating a function...').start();
try {
const success = await FunctionMutation.createAsync(bucketKey, projectId);
spinner.succeed(`Successfully created function: ${success}`);
} catch (err) {
spinner.fail('Failed to create a function');
throw err;
}
Log.log('Streaming build logs....');
const logsPrinted = new Set<string>();
let sawLastLog = false;
while (true) {
const { status, logUrls } = await FunctionMutation.getStatusAsync(projectId);
Log.log(`Function status: ${status}`);
const logsUrlsToPrint = logUrls.filter(url => !logsPrinted.has(url)).sort();
const logResponses = await Promise.all(logsUrlsToPrint.map(url => fetch(url)));
const logBodies = await Promise.all(logResponses.map(body => body.text()));
const logPayloads = logBodies.map(body => JSON.parse(body)).map(body => body.textPayload);
logPayloads.forEach(response => Log.log(response));
logsUrlsToPrint.forEach(logUrl => logsPrinted.add(logUrl));
if (!sawLastLog) {
sawLastLog = logPayloads.some(log => log === 'DONE');
}
if (sawLastLog && status === 'ACTIVE') {
Log.log(`🎉🎉🎉 Hooray! Your function is complete!`);
Log.log(`🎉🎉🎉 Your web-app is available at: ${publicUrl}`);
for (const submodule of Object.keys(functionEntries)) {
Log.log(`🎉🎉🎉 ${submodule} is available at: ${publicUrl}/functions/${submodule}`);
}
break;
} else if (status !== 'ACTIVE' && status !== 'DEPLOY_IN_PROGRESS') {
Log.log(`🤔 Uh oh...your function is ${status}`);
break;
}
await sleepAsync(6000);
}
}
}
@@ -1,25 +0,0 @@
import path from 'path';
import EasCommand from '../../commandUtils/EasCommand';
import { installNodeDependenciesAsync } from '../../functions/CreateApp';
import { YarnPackageManager } from '../../functions/PackageManager';
import Log from '../../log';
import { ora } from '../../ora';
import { findProjectRootAsync } from '../../project/projectUtils';
export default class FunctionsCreate extends EasCommand {
static description = 'start a function';
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync();
const functionsBuild = 'functions-build';
const installSpinner = ora('Installing dependencies...').start();
await installNodeDependenciesAsync(path.join(projectDir, functionsBuild), 'yarn');
installSpinner.succeed('Modified permissions');
Log.log('serving up the function...');
const yarn = new YarnPackageManager({ cwd: path.join(projectDir, functionsBuild) });
await yarn.runAsync('dev');
}
}
@@ -76,9 +76,7 @@ export const testPushKey: ApplePushKeyFragment = {
iosAppCredentialsList: [],
};
export const testTargets: Target[] = [
{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com', entitlements: {} },
];
export const testTargets: Target[] = [{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com' }];
export const testProvisioningProfileFragment: AppleProvisioningProfileFragment = {
id: 'test-prov-prof-id-1',
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync, promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -16,7 +17,7 @@ import {
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('BuildCredentialsUtils', () => {
describe(canCopyLegacyCredentialsAsync, () => {
@@ -83,9 +84,7 @@ describe('BuildCredentialsUtils', () => {
});
describe(promptUserAndCopyLegacyCredentialsAsync, () => {
it('copies all legacy credentials to EAS if the user is eligible', async () => {
jest
.mocked(promptAsync)
.mockImplementation(async () => ({ providedName: 'test-provided-name' }));
asMock(promptAsync).mockImplementation(() => ({ providedName: 'test-provided-name' }));
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { CreateFcm } from '../CreateFcm';
jest.mock('../../../../prompts');
jest.mocked(promptAsync).mockImplementation(async () => ({ fcmApiKey: 'blah' }));
asMock(promptAsync).mockImplementation(() => ({ fcmApiKey: 'blah' }));
describe(CreateFcm, () => {
it('creates an fcm api key in Interactive Mode', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
@@ -7,7 +8,7 @@ import { CreateGoogleServiceAccountKey } from '../CreateGoogleServiceAccountKey'
jest.mock('../../../../prompts');
jest.mock('fs');
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
filePath: '/google-service-account-key.json',
}));
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { jester as mockJester } from '../../../../credentials/__tests__/fixtures-constants';
import { testKeystore } from '../../../__tests__/fixtures-android';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -14,7 +15,7 @@ jest.mock('../../utils/keystore', () => ({ generateRandomKeystoreAsync: jest.fn(
describe('CreateKeystore', () => {
beforeEach(() => {
jest.mocked(generateRandomKeystoreAsync).mockReset();
asMock(generateRandomKeystoreAsync).mockReset();
});
it('creates a keystore in Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: false });
@@ -27,7 +28,7 @@ describe('CreateKeystore', () => {
expect(generateRandomKeystoreAsync).toHaveBeenCalled();
});
it('creates a keystore by uploading', async () => {
jest.mocked(askForUserProvidedAsync).mockImplementationOnce(async () => testKeystore);
asMock(askForUserProvidedAsync).mockImplementationOnce(() => testKeystore);
const ctx = createCtxMock({ nonInteractive: false });
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
const createKeystoreAction = new CreateKeystore(appLookupParams.account);
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -8,7 +9,7 @@ import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { RemoveFcm } from '../RemoveFcm';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveFcm, () => {
it('removes an FCM API Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -7,7 +8,7 @@ import { createCtxMock } from '../../../__tests__/fixtures-context';
import { RemoveGoogleServiceAccountKey } from '../RemoveGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveGoogleServiceAccountKey, () => {
it('removes an Google Service Account Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { testAndroidBuildCredentialsFragment } from '../../../__tests__/fixtures-android';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -6,7 +7,7 @@ import { RemoveKeystore } from '../RemoveKeystore';
jest.mock('fs-extra');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveKeystore, () => {
it('removes a keystore', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import {
getNewAndroidApiMock,
testAndroidBuildCredentialsFragment,
@@ -18,7 +19,7 @@ jest.mock('../../utils/keystore', () => ({ generateRandomKeystoreAsync: jest.fn(
describe('SetUpBuildCredentials', () => {
beforeEach(() => {
jest.mocked(generateRandomKeystoreAsync).mockReset();
asMock(generateRandomKeystoreAsync).mockReset();
});
it('skips setup when there are prior credentials', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { AndroidKeystoreType } from '../../../../graphql/generated';
import { confirmAsync } from '../../../../prompts';
import {
@@ -12,14 +13,13 @@ import {
SelectAndroidBuildCredentials,
SelectAndroidBuildCredentialsResultType,
} from '../../../manager/SelectAndroidBuildCredentials';
import { AppLookupParams } from '../../api/GraphqlClient';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { SetUpBuildCredentialsFromCredentialsJson } from '../SetUpBuildCredentialsFromCredentialsJson';
jest.mock('fs');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
jest.mock('../../../manager/SelectAndroidBuildCredentials');
beforeEach(() => {
@@ -28,7 +28,7 @@ beforeEach(() => {
describe(SetUpBuildCredentialsFromCredentialsJson, () => {
it('sets up a new build configuration from credentials.json upon user request', async () => {
jest.mocked(SelectAndroidBuildCredentials).mockImplementation((() => {
asMock(SelectAndroidBuildCredentials).mockImplementation(() => {
return {
runAsync: () => {
return {
@@ -37,7 +37,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
};
},
};
}) as any as (app: AppLookupParams) => SelectAndroidBuildCredentials);
});
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -78,7 +78,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
expect(ctx.android.createAndroidAppBuildCredentialsAsync).toHaveBeenCalledTimes(1);
});
it('uses an existing build configuration upon user request', async () => {
jest.mocked(SelectAndroidBuildCredentials).mockImplementation((() => {
asMock(SelectAndroidBuildCredentials).mockImplementation(() => {
return {
runAsync: () => {
return {
@@ -87,7 +87,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
};
},
};
}) as any as (app: AppLookupParams) => SelectAndroidBuildCredentials);
});
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -13,7 +14,7 @@ import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest.mock('fs');
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
filePath: '/google-service-account-key.json',
}));
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -8,9 +9,7 @@ import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { UseExistingGoogleServiceAccountKey } from '../UseExistingGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest
.mocked(promptAsync)
.mockImplementation(async () => ({ chosenKey: testGoogleServiceAccountKeyFragment }));
asMock(promptAsync).mockImplementation(() => ({ chosenKey: testGoogleServiceAccountKeyFragment }));
describe(UseExistingGoogleServiceAccountKey, () => {
it('uses an existing Google Service Account Key in Interactive Mode', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { detectGoogleServiceAccountKeyPathAsync } from '../googleServiceAccountKey';
@@ -24,13 +25,13 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
jest.mocked(confirmAsync).mockClear();
asMock(promptAsync).mockClear();
asMock(confirmAsync).mockClear();
});
describe('Google Service Account Key path detection', () => {
it('detects a single google-services file and prompts for confirmation', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
asMock(confirmAsync).mockResolvedValueOnce(true);
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir/subdir');
expect(confirmAsync).toHaveBeenCalledWith(
expect.objectContaining({ message: 'Would you like to use this file?' })
@@ -45,7 +46,7 @@ describe('Google Service Account Key path detection', () => {
});
it('returns null, when user rejects to use detected file', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(false);
asMock(confirmAsync).mockResolvedValueOnce(false);
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir/subdir');
expect(confirmAsync).toHaveBeenCalledTimes(1);
expect(confirmAsync).toHaveBeenCalledWith(
@@ -55,7 +56,7 @@ describe('Google Service Account Key path detection', () => {
});
it('displays a chooser, when multiple files are found', async () => {
jest.mocked(promptAsync).mockResolvedValueOnce({
asMock(promptAsync).mockResolvedValueOnce({
selectedPath: '/project_dir/another-service-account.json',
});
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir'); // should find 2 files here
@@ -75,7 +76,7 @@ describe('Google Service Account Key path detection', () => {
});
it('returns null, when user selects the "None of above"', async () => {
jest.mocked(promptAsync).mockResolvedValueOnce({
asMock(promptAsync).mockResolvedValueOnce({
selectedPath: false,
});
@@ -1,5 +1,6 @@
import mockdate from 'mockdate';
import { asMock } from '../../../../__tests__/utils';
import Log from '../../../../log';
import {
testAndroidAppCredentialsFragment,
@@ -24,9 +25,7 @@ describe('print credentials', () => {
appLookupParams,
appCredentials: testAndroidAppCredentialsFragment,
});
const loggedSoFar = jest
.mocked(Log.log)
.mock.calls.reduce((acc, mockValue) => acc + mockValue.toString(), '');
const loggedSoFar = asMock(Log.log).mock.calls.reduce((acc, mockValue) => acc + mockValue);
expect(loggedSoFar).toMatchSnapshot();
});
});
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import prompts from 'prompts';
import { asMock } from '../../../__tests__/utils';
import { IosDistributionType } from '../../../graphql/generated';
import {
testKeystore,
@@ -24,8 +25,8 @@ jest.mock('prompts');
describe('update credentials.json', () => {
beforeEach(() => {
vol.reset();
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -175,7 +176,6 @@ describe('update credentials.json', () => {
{
targetName: 'testapp',
bundleIdentifier: 'com.bundle.id',
entitlements: {},
},
];
@@ -11,6 +11,7 @@ import { ensureAllTargetsAreConfigured } from '../credentialsJson/utils';
import { getAppFromContext } from './actions/BuildCredentialsUtils';
import { SetUpBuildCredentials } from './actions/SetUpBuildCredentials';
import { SetUpPushKey } from './actions/SetUpPushKey';
import { IosCapabilitiesOptions } from './appstore/ensureAppExists';
import { App, IosCredentials, Target } from './types';
import { isAdHocProfile } from './utils/provisioningProfile';
@@ -19,6 +20,7 @@ interface Options {
targets: Target[];
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export default class IosCredentialsProvider {
@@ -61,6 +63,7 @@ export default class IosCredentialsProvider {
targets: this.options.targets,
distribution: this.options.distribution,
enterpriseProvisioning: this.options.enterpriseProvisioning,
iosCapabilitiesOptions: this.options.iosCapabilitiesOptions,
}).runAsync(this.ctx);
}
@@ -58,13 +58,7 @@ describe(IosCredentialsProvider, () => {
},
projectName: appLookupParams.projectName,
},
targets: [
{
targetName: 'testapp',
bundleIdentifier: appLookupParams.bundleIdentifier,
entitlements: {},
},
],
targets: [{ targetName: 'testapp', bundleIdentifier: appLookupParams.bundleIdentifier }],
distribution: 'store',
});
await expect(provider.getCredentialsAsync(CredentialsSource.REMOTE)).rejects.toThrowError(
@@ -101,13 +95,7 @@ describe(IosCredentialsProvider, () => {
},
projectName: appLookupParams.projectName,
},
targets: [
{
targetName: 'testapp',
bundleIdentifier: appLookupParams.bundleIdentifier,
entitlements: {},
},
],
targets: [{ targetName: 'testapp', bundleIdentifier: appLookupParams.bundleIdentifier }],
distribution: 'store',
});
@@ -4,6 +4,7 @@ import nullthrows from 'nullthrows';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
import { App, IosAppBuildCredentialsMap, IosCredentials, Target } from '../types';
import { displayProjectCredentials } from '../utils/printCredentials';
import { SetUpTargetBuildCredentials } from './SetUpTargetBuildCredentials';
@@ -13,6 +14,7 @@ interface Options {
targets: Target[];
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export class SetUpBuildCredentials {
@@ -48,9 +50,7 @@ export class SetUpBuildCredentials {
Log.newLine();
}
const action = new SetUpTargetBuildCredentials({
enterpriseProvisioning: this.options.enterpriseProvisioning,
distribution: this.options.distribution,
entitlements: target.entitlements,
...this.options,
app: {
...this.options.app,
bundleIdentifier: target.bundleIdentifier,
@@ -1,5 +1,4 @@
import { DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
import { JSONObject } from '@expo/json-file';
import {
IosDistributionType as GraphQLIosDistributionType,
@@ -8,6 +7,7 @@ import {
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams as GraphQLAppLookupParams } from '../api/GraphqlClient';
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
import { SetUpInternalProvisioningProfile } from './SetUpInternalProvisioningProfile';
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
@@ -16,13 +16,13 @@ interface Options {
app: GraphQLAppLookupParams;
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
entitlements: JSONObject;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export class SetUpTargetBuildCredentials {
constructor(private options: Options) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const { app, entitlements } = this.options;
const { app, iosCapabilitiesOptions } = this.options;
await ctx.bestEffortAppStoreAuthenticateAsync();
@@ -33,7 +33,7 @@ export class SetUpTargetBuildCredentials {
bundleIdentifier: app.bundleIdentifier,
projectName: app.projectName,
},
{ entitlements }
iosCapabilitiesOptions
);
}
try {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -17,8 +18,8 @@ function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
}
afterEach(() => {
jest.mocked(promptAsync).mockClear();
jest.mocked(getCredentialsFromUserAsync).mockClear();
asMock(promptAsync).mockClear();
asMock(getCredentialsFromUserAsync).mockClear();
});
describe(getAscApiKeyName, () => {
@@ -34,10 +35,10 @@ describe(getAscApiKeyName, () => {
describe(promptForAscApiKeyPathAsync, () => {
it('prompts for keyId, keyP8Path and issuerId when user is not authenticated to Apple', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -61,10 +62,10 @@ describe(promptForAscApiKeyPathAsync, () => {
expect(getCredentialsFromUserAsync).toHaveBeenCalledTimes(2); // keyId, issuerId
});
it('prompts for keyId, keyP8Path and detects issuerId when user is authenticated to Apple', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
}));
const ctx = createCtxMock({
@@ -84,6 +85,6 @@ describe(promptForAscApiKeyPathAsync, () => {
});
expect(promptAsync).toHaveBeenCalledTimes(1); // keyP8Path
expect(getCredentialsFromUserAsync).toHaveBeenCalledTimes(1); // keyId
expect(jest.mocked(ctx.appStore.getAscApiKeyAsync).mock.calls.length).toBe(1); // issuerId
expect(asMock(ctx.appStore.getAscApiKeyAsync).mock.calls.length).toBe(1); // issuerId
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -14,7 +15,7 @@ import { ConfigureProvisioningProfile } from '../ConfigureProvisioningProfile';
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('ConfigureProvisioningProfile', () => {
it('configures a Provisioning Profile in Interactive Mode', async () => {
@@ -42,9 +43,9 @@ describe('ConfigureProvisioningProfile', () => {
await provProfConfigurator.runAsync(ctx);
// expect provisioning profile not to be updated on expo servers
expect(jest.mocked(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be updated on apple portal
expect(jest.mocked(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -61,8 +62,8 @@ describe('ConfigureProvisioningProfile', () => {
);
// expect provisioning profile not to be updated on expo servers
expect(jest.mocked(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be updated on apple portal
expect(jest.mocked(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -8,7 +9,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreateAscApiKey } from '../CreateAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(CreateAscApiKey, () => {
it('creates a App Store API Key in Interactive Mode', async () => {
@@ -26,9 +27,9 @@ describe(CreateAscApiKey, () => {
await createAscApiKeyAction.runAsync(ctx, AppStoreApiKeyPurpose.SUBMISSION_SERVICE);
// expect api key to be created on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
// expect api key to be created on apple portal
expect(jest.mocked(ctx.appStore.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createAscApiKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -12,7 +13,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreateProvisioningProfile } from '../CreateProvisioningProfile';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('CreateProvisioningProfile', () => {
it('creates a Provisioning Profile in Interactive Mode', async () => {
@@ -33,9 +34,9 @@ describe('CreateProvisioningProfile', () => {
await createProvProfAction.runAsync(ctx);
// expect provisioning profile to be created on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be created on apple portal
expect(jest.mocked(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -51,8 +52,8 @@ describe('CreateProvisioningProfile', () => {
);
// expect provisioning profile not to be created on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be created on apple portal
expect(jest.mocked(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -7,7 +8,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreatePushKey } from '../CreatePushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(CreatePushKey, () => {
it('creates a Push Key in Interactive Mode', async () => {
@@ -24,9 +25,9 @@ describe(CreatePushKey, () => {
await createPushKeyAction.runAsync(ctx);
// expect push key to be created on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
// expect push key to be created on apple portal
expect(jest.mocked(ctx.appStore.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createPushKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testAscApiKeyFragment } from '../../../__tests__/fixtures-ios';
import { RemoveAscApiKey } from '../RemoveAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveAscApiKey, () => {
it('removes an Asc API Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -10,7 +11,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { RemoveDistributionCertificate } from '../RemoveDistributionCertificate';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('RemoveDistributionCertificate', () => {
it('deletes the distribution certificate on Expo and Apple servers when there are no App Dependencies in Interactive Mode', async () => {
@@ -23,11 +24,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect dist cert to be deleted on apple portal
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile deletion to be skipped because there arent any associated with the dist cert
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('deletes the distribution certificate on Expo servers when there are no App Dependencies in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -39,11 +40,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// not supported in non-interactive mode
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(0);
// expect provisioning profile deletion to be skipped because there arent any associated with the dist cert
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('deletes the distribution certificate and its provisioning profile on Expo and Apple servers when there are App Dependencies in Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: false });
@@ -55,11 +56,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect dist cert to be deleted on apple portal
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile deletion to be invoked on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
it('errors when the distribution certificate has App Dependencies in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testTargets } from '../../../__tests__/fixtures-ios';
@@ -19,6 +20,6 @@ describe('RemoveProvisioningProfile', () => {
await removeProfilesAction.runAsync(ctx);
// expect provisioning profile to be deleted from servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
});
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testPushKey } from '../../../__tests__/fixtures-ios';
import { RemovePushKey } from '../RemovePushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemovePushKey, () => {
it('deletes the push key on Expo and Apple servers in Interactive Mode', async () => {
@@ -13,9 +14,9 @@ describe(RemovePushKey, () => {
await removePushKeyAction.runAsync(ctx);
// expect push key to be deleted on expo servers
expect(jest.mocked(ctx.ios.deletePushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deletePushKeyAsync).mock.calls.length).toBe(1);
// expect push key to be revoked on apple portal
expect(jest.mocked(ctx.appStore.revokePushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokePushKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -14,7 +15,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpAscApiKey, SetupAscApiKeyChoice } from '../SetUpAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpAscApiKey, () => {
it('skips setting up a ASC API Key if it is already configured', async () => {
@@ -35,9 +36,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
});
it('sets up a ASC API Key, creating a new one if there are no existing ASC API Keys', async () => {
const ctx = createCtxMock({
@@ -60,9 +61,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect to create a new ASC API Key on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a ASC API Key, use autoselected ASC API Key when there are existing keys', async () => {
const ctx = createCtxMock({
@@ -86,9 +87,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a ASC API Key, allowing user to choose existing keys', async () => {
const ctx = createCtxMock({
@@ -98,7 +99,7 @@ describe(SetUpAscApiKey, () => {
getAscApiKeysForAccountAsync: jest.fn(() => [testAscApiKeyFragment]),
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
choice: SetupAscApiKeyChoice.USE_EXISTING,
chosenAscApiKey: testAscApiKeyFragment,
}));
@@ -110,9 +111,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('works in Non Interactive Mode if ASC Key is configured', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { IosAppBuildCredentialsFragment, IosDistributionType } from '../../../../graphql/generated';
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -13,10 +14,10 @@ jest.mock('../SetUpProvisioningProfile');
jest.mock('../BuildCredentialsUtils', () => ({ getAllBuildCredentialsAsync: jest.fn() }));
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
jest.mocked(getAllBuildCredentialsAsync).mockReset();
jest.mocked(getAllBuildCredentialsAsync).mockImplementation(() => {
asMock(getAllBuildCredentialsAsync).mockReset();
asMock(getAllBuildCredentialsAsync).mockImplementation(() => {
throw new Error(
`unhandled getAllBuildCredentialsAsync call - this shouldn't happen - fix tests!`
);
@@ -36,7 +37,7 @@ describe(SetUpInternalProvisioningProfile, () => {
describe('interactive mode', () => {
describe('when authenticated with apple', () => {
it('runs the SetUpAdhocProvisioningProfile action for non-enterprise team', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -63,10 +64,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('asks the user for an action to run when they have access to an enterprise team', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -98,10 +99,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
describe('when not authenticated with apple', () => {
it('asks the user for an action to run when both adhoc and universal distribution credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testAdhocBuildCredentials,
testEnterpriseBuildCredentials,
@@ -132,10 +133,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testAdhocBuildCredentials];
return buildCredentials;
});
@@ -163,10 +164,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testEnterpriseBuildCredentials,
];
@@ -196,10 +197,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('forces the apple authentication when neither adhoc nor enterprise credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -227,7 +228,7 @@ describe(SetUpInternalProvisioningProfile, () => {
describe('non-interactive mode', () => {
it('throws an error when both adhoc and enterprise credentials are set up', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testAdhocBuildCredentials,
testEnterpriseBuildCredentials,
@@ -248,7 +249,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('throws an error when neither adhoc nor enterprise credentials are set up', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -264,7 +265,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testAdhocBuildCredentials];
return buildCredentials;
});
@@ -286,7 +287,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testEnterpriseBuildCredentials];
return buildCredentials;
});
@@ -1,5 +1,6 @@
import nullthrows from 'nullthrows';
import { asMock } from '../../../../__tests__/utils';
import { IosDistributionType } from '../../../../graphql/generated';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
@@ -18,7 +19,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpProvisioningProfile } from '../SetUpProvisioningProfile';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
jest.mock('../SetUpDistributionCertificate');
jest.mock('../ConfigureProvisioningProfile');
jest.mock('../CreateProvisioningProfile');
@@ -26,7 +27,7 @@ jest.mock('../../validators/validateProvisioningProfile');
describe('SetUpProvisioningProfile', () => {
it('repairs existing Provisioning Profile with bad build credentials in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -60,14 +61,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('sets up a new Provisioning Profile with bad build credentials in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -92,14 +91,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
it('skips setting up a Provisioning Profile with prior build credentials configured properly in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => true);
asMock(validateProvisioningProfileAsync).mockImplementation(() => true);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -124,14 +121,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('sets up a Provisioning Profile with no prior build credentials configured in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -152,14 +147,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('errors in Non Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: true,
});
@@ -173,10 +166,8 @@ describe('SetUpProvisioningProfile', () => {
);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -7,7 +8,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpPushKey } from '../SetUpPushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpPushKey, () => {
it('skips setting up a Push Key if it is already configured', async () => {
@@ -23,9 +24,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect not to create a new push key on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
});
it('sets up a Push Key, creating a new one if there are no existing push keys', async () => {
const ctx = createCtxMock({
@@ -41,9 +42,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect to create a new push key on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
// expect configuration to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a Push Key, use autoselected push key when there are existing keys', async () => {
const ctx = createCtxMock({
@@ -65,9 +66,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect not to create a new push key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -14,16 +15,15 @@ import {
} from '../SetUpSubmissionCredentials';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpSubmissionCredentials, () => {
it('allows user to enter an App Specific Password', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
choice: PROMPT_FOR_APP_SPECIFIC_PASSWORD,
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
appSpecificPassword: 'super secret',
}));
const ctx = createCtxMock({
@@ -37,7 +37,7 @@ describe(SetUpSubmissionCredentials, () => {
const asp = await setupAscApiKeyAction.runAsync(ctx);
// prompt to choose ASP, then prompt to input ASP
expect(jest.mocked(promptAsync).mock.calls.length).toBe(2);
expect(asMock(promptAsync).mock.calls.length).toBe(2);
expect(asp).toBe('super secret');
});
it('returns an ASC API key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { IosDistributionType } from '../../../../graphql/generated';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
@@ -20,8 +21,8 @@ jest.mock('../../../../prompts');
jest.mock('../../../credentialsJson/read');
beforeEach(() => {
jest.mocked(confirmAsync).mockReset();
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockReset();
asMock(confirmAsync).mockImplementation(() => true);
});
describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
@@ -60,13 +61,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect distribution certificate not to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
});
it('sets up build credentials with no prior configuration in Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -91,13 +90,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('sets up build credentials with different prior configuration in Interactive Mode', async () => {
// create deep clone the quick and dirty way
@@ -126,13 +123,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('works in Non Interactive Mode - sets up build credentials with different prior configuration', async () => {
// create deep clone the quick and dirty way
@@ -162,14 +157,12 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect no prompts to be called
expect(jest.mocked(confirmAsync).mock.calls.length).toBe(0);
expect(asMock(confirmAsync).mock.calls.length).toBe(0);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
});
@@ -1,5 +1,6 @@
import { ApiKey, ApiKeyType, UserRole } from '@expo/apple-utils';
import { asMock } from '../../../../__tests__/utils';
import {
createAscApiKeyAsync,
getAscApiKeyAsync,
@@ -56,7 +57,7 @@ beforeEach(() => {
test(`listAscApiKeysAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'getAsync').mockImplementation(async () => []);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await listAscApiKeysAsync(mockAuthCtx);
expect(ApiKey.getAsync).toHaveBeenLastCalledWith(mockRequestContext);
expect(result).toEqual([]);
@@ -65,7 +66,7 @@ test(`listAscApiKeysAsync`, async () => {
test(`getAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'infoAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await getAscApiKeyAsync(mockAuthCtx, 'test-key-id');
expect(ApiKey.infoAsync).toHaveBeenLastCalledWith(mockRequestContext, { id: 'test-key-id' });
expect(result).toEqual(mockAscApiKeyInfo);
@@ -74,7 +75,7 @@ test(`getAscApiKeyAsync`, async () => {
test(`createAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'createAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await createAscApiKeyAsync(mockAuthCtx, { nickname: 'test-name' });
expect(ApiKey.createAsync).toHaveBeenLastCalledWith(mockRequestContext, {
nickname: 'test-name',
@@ -88,7 +89,7 @@ test(`createAscApiKeyAsync`, async () => {
test(`revokeAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'infoAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await revokeAscApiKeyAsync(mockAuthCtx, 'test-key-id');
expect(ApiKey.infoAsync).toHaveBeenLastCalledWith(mockRequestContext, {
id: 'test-key-id',
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { AppleTeamType } from '../authenticateTypes';
import { resolveAppleTeamAsync, resolveAscApiKeyAsync } from '../resolveCredentials';
@@ -16,7 +17,7 @@ const testAscApiKey = {
beforeEach(() => {
vol.reset();
process.env = {};
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(resolveAscApiKeyAsync, () => {
@@ -43,7 +44,7 @@ describe(resolveAscApiKeyAsync, () => {
vol.fromJSON({
'/test-asc-key.p8': testAscApiKey.keyP8,
});
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
ascApiKeyPath: '/test-asc-key.p8',
ascIssuerId: testAscApiKey.issuerId,
ascApiKeyId: testAscApiKey.keyId,
@@ -77,7 +78,7 @@ describe(resolveAppleTeamAsync, () => {
});
});
it(`prompts the user if it can't find anything else`, async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
appleTeamId: testTeam.teamId,
appleTeamType: testTeam.teamType,
}));
@@ -1,14 +1,11 @@
import { IOSConfig, compileModsAsync } from '@expo/config-plugins';
import { Workflow } from '@expo/eas-build-job';
import { JSONObject } from '@expo/json-file';
import { getPrebuildConfigAsync } from '@expo/prebuild-config';
import { readPlistAsync } from '../../utils/plist';
import { readPlistAsync } from '../../../utils/plist';
interface Target {
buildConfiguration?: string;
targetName: string;
}
export async function getManagedApplicationTargetEntitlementsAsync(
export async function getManagedEntitlementsJsonAsync(
projectDir: string,
env: Record<string, string>
): Promise<JSONObject> {
@@ -31,11 +28,22 @@ export async function getManagedApplicationTargetEntitlementsAsync(
}
}
export async function getNativeTargetEntitlementsAsync(
export async function resolveEntitlementsJsonAsync(
projectDir: string,
target: Target
): Promise<JSONObject | null> {
const entitlementsPath = IOSConfig.Entitlements.getEntitlementsPath(projectDir, target);
workflow: Workflow,
env: Record<string, string>
): Promise<JSONObject> {
if (workflow === Workflow.GENERIC) {
return (await getEntitlementsJsonAsync(projectDir)) || {};
} else if (workflow === Workflow.MANAGED) {
return await getManagedEntitlementsJsonAsync(projectDir, env);
} else {
throw new Error(`Unknown workflow: ${workflow}`);
}
}
async function getEntitlementsJsonAsync(projectDir: string): Promise<JSONObject | null> {
const entitlementsPath = IOSConfig.Paths.getEntitlementsPath(projectDir);
if (entitlementsPath) {
const plist = await readPlistAsync(entitlementsPath);
return plist ? (plist as unknown as JSONObject) : null;
@@ -1,5 +1,3 @@
import { JSONObject } from '@expo/json-file';
import {
CommonIosAppCredentialsFragment,
IosAppBuildCredentialsFragment,
@@ -16,7 +14,6 @@ export interface Target {
buildConfiguration?: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
entitlements: JSONObject;
}
export interface TargetCredentials {
@@ -1,7 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`print credentials prints the IosAppCredentials map 1`] = `
"iOS Credentials
Array [
"iOS Credentials
Project @quinlanj/test52
Bundle Identifier com.quinlanj.test52
Apple Team test-apple-identifier
@@ -50,5 +51,6 @@ Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
"
",
]
`;
@@ -1,6 +1,7 @@
import mockdate from 'mockdate';
import nullthrows from 'nullthrows';
import { asMock } from '../../../../__tests__/utils';
import Log from '../../../../log';
import { IosAppCredentialsQuery } from '../../api/graphql/queries/IosAppCredentialsQuery';
import { App, Target } from '../../types';
@@ -28,13 +29,9 @@ describe('print credentials', () => {
const appCredentials = {
test52: nullthrows(testIosAppCredentialsData),
};
const targets: Target[] = [
{ targetName: 'test52', bundleIdentifier: 'com.quinlanj.test52', entitlements: {} },
];
const targets: Target[] = [{ targetName: 'test52', bundleIdentifier: 'com.quinlanj.test52' }];
displayIosCredentials(app, appCredentials, targets);
const loggedSoFar = jest
.mocked(Log.log)
.mock.calls.reduce((acc, mockValue) => acc + mockValue, '');
const loggedSoFar = asMock(Log.log).mock.calls.reduce((acc, mockValue) => acc + mockValue);
expect(loggedSoFar).toMatchSnapshot();
});
});
@@ -123,7 +123,7 @@ export function displayIosCredentials(
export function displayProjectCredentials(
app: App,
appBuildCredentials: IosAppBuildCredentialsMap,
targets: Omit<Target, 'entitlements'>[]
targets: Target[]
): void {
const projectFullName = `@${app.account.name}/${app.projectName}`;
const targetToBundleId = targets.reduce<Record<string, string>>((acc, target) => {
@@ -43,6 +43,7 @@ import { SetUpProvisioningProfile } from '../ios/actions/SetUpProvisioningProfil
import { SetUpPushKey } from '../ios/actions/SetUpPushKey';
import { UpdateCredentialsJson } from '../ios/actions/UpdateCredentialsJson';
import { AppLookupParams } from '../ios/api/GraphqlClient';
import { getManagedEntitlementsJsonAsync } from '../ios/appstore/entitlements';
import { App, IosAppCredentialsMap, Target } from '../ios/types';
import { displayIosCredentials } from '../ios/utils/printCredentials';
import { ActionInfo, IosActionType, Scope } from './Actions';
@@ -191,12 +192,10 @@ export class ManageIos {
},
buildProfile
);
const targets = await resolveTargetsAsync({
exp: ctx.exp,
projectDir: ctx.projectDir,
xcodeBuildContext,
env: buildProfile.env,
});
const targets = await resolveTargetsAsync(
{ exp: ctx.exp, projectDir: ctx.projectDir },
xcodeBuildContext
);
return {
app,
targets,
@@ -236,6 +235,12 @@ export class ManageIos {
targets,
distribution: buildProfile.distribution,
enterpriseProvisioning: buildProfile.enterpriseProvisioning,
iosCapabilitiesOptions: {
entitlements: await getManagedEntitlementsJsonAsync(
ctx.projectDir,
buildProfile.env ?? {}
),
},
}).runAsync(ctx);
return;
}
@@ -1,3 +1,4 @@
import { asMock } from '../../../__tests__/utils';
import { promptAsync } from '../../../prompts';
import {
getNewAndroidApiMock,
@@ -14,12 +15,12 @@ const TEST_STRING = 'TEST_STRING';
jest.mock('../../../prompts');
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(SelectAndroidBuildCredentials, () => {
it('returns a request to make default build credentials when there are no credentials', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
providedName: TEST_STRING,
}));
const ctx = createCtxMock({
@@ -37,7 +38,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns a request to make build credentials when the user chooses to make a new one', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew:
SelectAndroidBuildCredentialsResultType.CREATE_REQUEST,
providedName: TEST_STRING,
@@ -59,7 +60,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns a request to make default build credentials when the user chooses to make a new one, and if they have no existing credentials', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew:
SelectAndroidBuildCredentialsResultType.CREATE_REQUEST,
providedName: TEST_STRING,
@@ -79,7 +80,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns buildCredentials of the users choice', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew: testAndroidBuildCredentialsFragment,
}));
const ctx = createCtxMock({
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../__tests__/utils';
import Log from '../../log';
import { Actor } from '../../user/User';
import { AccountResolver } from '../manager';
@@ -14,8 +15,8 @@ jest.mock('../../project/projectUtils', () => {
jest.mock('../../credentials/ios/api/graphql/queries/AppleTeamQuery');
beforeEach(() => {
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -37,7 +38,7 @@ describe(AccountResolver, () => {
const exp = {} as any;
it('returns the account defined in app.json/app.config.js if user confirms', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
value: true,
}));
@@ -47,10 +48,10 @@ describe(AccountResolver, () => {
});
it('asks the user to choose the account from his account list if he rejects to use the one defined in app.json / app.config.js', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
useProjectAccount: false,
}));
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -60,7 +61,7 @@ describe(AccountResolver, () => {
});
it(`asks the user to choose the account from his account list if he doesn't have access to the account defined in app.json / app.config.js`, async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -79,7 +80,7 @@ describe(AccountResolver, () => {
describe('when outside project dir', () => {
it('asks the user to choose the account from his account list', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../../../__tests__/utils';
import { Account } from '../../../../user/Account';
import DeviceCreateAction, { RegistrationMethod } from '../action';
import { runInputMethodAsync } from '../inputMethod';
@@ -10,19 +11,19 @@ jest.mock('../registrationUrlMethod');
jest.mock('../inputMethod');
beforeEach(() => {
const promptsMock = jest.mocked(prompts);
const promptsMock = asMock(prompts);
promptsMock.mockReset();
promptsMock.mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(runRegistrationUrlMethodAsync).mockClear();
jest.mocked(runInputMethodAsync).mockClear();
asMock(runRegistrationUrlMethodAsync).mockClear();
asMock(runInputMethodAsync).mockClear();
});
describe(DeviceCreateAction, () => {
describe('#runAsync', () => {
it('calls runRegistrationUrlMethodAsync if user chooses the website method', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
method: RegistrationMethod.WEBSITE,
}));
@@ -42,7 +43,7 @@ describe(DeviceCreateAction, () => {
});
it('calls runInputMethodAsync if user chooses the input method', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
method: RegistrationMethod.INPUT,
}));
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../../../__tests__/utils';
import { AppleDeviceMutation } from '../../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import { AppleDeviceClass, AppleTeam } from '../../../../graphql/generated';
import { runInputMethodAsync } from '../inputMethod';
@@ -8,19 +9,19 @@ jest.mock('../../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation
jest.mock('../../../../ora');
beforeEach(() => {
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(AppleDeviceMutation.createAppleDeviceAsync).mockClear();
asMock(AppleDeviceMutation.createAppleDeviceAsync).mockClear();
});
describe(runInputMethodAsync, () => {
it('should allow for multiple device registration', async () => {
mockDeviceData('00001111-001122334455662E', 'my iPhone', AppleDeviceClass.Iphone);
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: true }));
asMock(prompts).mockImplementationOnce(() => ({ value: true }));
mockDeviceData('b12cba9856d89c932ab7a4b813c4d932534e1679', 'my iPad', AppleDeviceClass.Ipad);
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: false }));
asMock(prompts).mockImplementationOnce(() => ({ value: false }));
const accountId = 'account-id';
// @ts-expect-error appleTeam is missing properties of AppleTeam GraphQL type
@@ -37,8 +38,8 @@ describe(runInputMethodAsync, () => {
});
function mockDeviceData(udid: string, name: string, deviceClass: AppleDeviceClass): void {
jest.mocked(prompts).mockImplementationOnce(async () => ({ udid }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ name }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ deviceClass }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: true }));
asMock(prompts).mockImplementationOnce(() => ({ udid }));
asMock(prompts).mockImplementationOnce(() => ({ name }));
asMock(prompts).mockImplementationOnce(() => ({ deviceClass }));
asMock(prompts).mockImplementationOnce(() => ({ value: true }));
}
@@ -1,41 +0,0 @@
import * as PackageManager from '@expo/package-manager';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import * as path from 'path';
import semver from 'semver';
import Log from '../log';
export async function installNodeDependenciesAsync(
projectRoot: string,
packageManager: 'yarn' | 'npm'
): Promise<void> {
const options = { cwd: projectRoot };
if (packageManager === 'yarn') {
const yarn = new PackageManager.YarnPackageManager(options);
const version = await yarn.versionAsync();
const nodeLinker = await yarn.getConfigAsync('nodeLinker');
if (semver.satisfies(version, '>=2.0.0-rc.24') && nodeLinker !== 'node-modules') {
const yarnRc = path.join(projectRoot, '.yarnrc.yml');
let yamlString = '';
try {
yamlString = await fs.readFile(yarnRc, 'utf8');
} catch (error: any) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const config: any = yamlString ? yaml.safeLoad(yamlString) : {};
config.nodeLinker = 'node-modules';
Log.warn(
`Yarn v${version} detected, enabling experimental Yarn v2 support using the node-modules plugin.`
);
Log.log(`Writing ${yarnRc}...`);
await fs.writeFile(yarnRc, yaml.safeDump(config));
}
await yarn.installAsync();
} else {
await new PackageManager.NpmPackageManager(options).installAsync();
}
}
@@ -1,66 +0,0 @@
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import { Transform } from 'stream';
import Log from '../log';
function ansiRegex({ onlyFirst = false } = {}): RegExp {
const pattern = [
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
].join('|');
return new RegExp(pattern, onlyFirst ? undefined : 'g');
}
const ansi = `(?:${ansiRegex().source})*`;
const yarnPeerDependencyWarningPattern = new RegExp(
`${ansi}warning${ansi} "[^"]+" has (?:unmet|incorrect) peer dependency "[^"]+"\\.\n`,
'g'
);
class YarnStderrTransform extends Transform {
_transform(
chunk: Buffer,
_encoding: string,
callback: (error?: Error | null, data?: any) => void
): void {
this.push(chunk.toString().replace(yarnPeerDependencyWarningPattern, ''));
callback();
}
}
export class YarnPackageManager {
options: SpawnOptions;
constructor({ cwd, silent }: { cwd: string; silent?: boolean }) {
this.options = {
env: {
...process.env,
},
cwd,
...(silent
? { ignoreStdio: true }
: {
stdio: ['inherit', 'inherit', 'pipe'],
}),
};
}
async runAsync(command: string): Promise<void> {
await this.runPrivateAsync([command]);
}
// Private
private async runPrivateAsync(args: string[]): Promise<SpawnResult> {
if (!this.options.ignoreStdio) {
Log.log(`> yarn ${args.join(' ')}`);
}
// Have spawnAsync consume stdio but we don't actually do anything with it if it's ignored
const promise = spawnAsync('yarnpkg', args, { ...this.options, ignoreStdio: false });
if (promise.child.stderr && !this.options.ignoreStdio) {
promise.child.stderr.pipe(new YarnStderrTransform()).pipe(process.stderr);
}
return promise;
}
}
@@ -1,123 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
const friendlySyntaxErrorLabel = 'Syntax error:';
function isLikelyASyntaxError(message: string | string[]): boolean {
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
}
// Cleans up webpack error messages.
function formatMessage(message: any): any {
let lines = [];
if (typeof message === 'string') {
lines = message.split('\n');
} else if ('message' in message) {
lines = message['message'].split('\n');
} else if (Array.isArray(message)) {
message.forEach(message => {
if ('message' in message) {
lines = message['message'].split('\n');
}
});
}
// Strip webpack-added headers off errors/warnings
// https://github.com/webpack/webpack/blob/master/lib/ModuleError.js
lines = lines.filter((line: string) => !/Module [A-z ]+\(from/.test(line));
// Transform parsing error into syntax error
// TODO: move this to our ESLint formatter?
lines = lines.map((line: string) => {
const parsingError = /Line (\d+):(?:(\d+):)?\s*Parsing error: (.+)$/.exec(line);
if (!parsingError) {
return line;
}
const [, errorLine, errorColumn, errorMessage] = parsingError;
return `${friendlySyntaxErrorLabel} ${errorMessage} (${errorLine}:${errorColumn})`;
});
message = lines.join('\n');
// Smoosh syntax errors (commonly found in CSS)
message = message.replace(
/SyntaxError\s+\((\d+):(\d+)\)\s*(.+?)\n/g,
`${friendlySyntaxErrorLabel} $3 ($1:$2)\n`
);
// Clean up export errors
message = message.replace(
/^.*export '(.+?)' was not found in '(.+?)'.*$/gm,
`Attempted import error: '$1' is not exported from '$2'.`
);
message = message.replace(
/^.*export 'default' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm,
`Attempted import error: '$2' does not contain a default export (imported as '$1').`
);
message = message.replace(
/^.*export '(.+?)' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm,
`Attempted import error: '$1' is not exported from '$3' (imported as '$2').`
);
lines = message.split('\n');
// Remove leading newline
if (lines.length > 2 && lines[1].trim() === '') {
lines.splice(1, 1);
}
// Clean up file name
lines[0] = lines[0].replace(/^(.*) \d+:\d+-\d+$/, '$1');
// Cleans up verbose "module not found" messages for files and packages.
if (lines[1] && lines[1].indexOf('Module not found: ') === 0) {
lines = [
lines[0],
lines[1]
.replace('Error: ', '')
.replace('Module not found: Cannot find file:', 'Cannot find file:'),
];
}
// Add helpful message for users trying to use Sass for the first time
if (lines[1] && lines[1].match(/Cannot find module.+sass/)) {
lines[1] = 'To import Sass files, you first need to install sass.\n';
lines[1] += 'Run `npm install sass` or `yarn add sass` inside your workspace.';
}
message = lines.join('\n');
// Internal stacks are generally useless so we strip them... with the
// exception of stacks containing `webpack:` because they're normally
// from user code generated by webpack. For more information see
// https://github.com/facebook/create-react-app/pull/1050
message = message.replace(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, ''); // at ... ...:x:y
message = message.replace(/^\s*at\s<anonymous>(\n|$)/gm, ''); // at <anonymous>
lines = message.split('\n');
// Remove duplicated newlines
lines = lines.filter(
(line: string, index: number, arr: string[]) =>
index === 0 || line.trim() !== '' || line.trim() !== arr[index - 1].trim()
);
// Reassemble the message
message = lines.join('\n');
return message.trim();
}
export function formatWebpackMessages(json: any): {
errors: any[];
warnings: any[];
} {
const formattedErrors = json.errors.map(formatMessage);
const formattedWarnings = json.warnings.map(formatMessage);
const result = { errors: formattedErrors, warnings: formattedWarnings };
if (result.errors.some(isLikelyASyntaxError)) {
// If there are any syntax errors, show just them.
result.errors = result.errors.filter(isLikelyASyntaxError);
}
return result;
}
-132
View File
@@ -1,132 +0,0 @@
import chalk from 'chalk';
import fs from 'fs/promises';
import path from 'path';
import PnpWebpackPlugin from 'pnp-webpack-plugin';
import { Compiler, Configuration, EntryObject, webpack } from 'webpack';
import Log from '../log';
import { formatWebpackMessages } from './formatWebpackMessages';
async function getWebpackFunctionConfigurationAsync(
projectDir: string,
entry: EntryObject,
outputPath: string
): Promise<Configuration> {
const webpackConfig = {
entry,
target: 'node',
mode: 'development' as 'development',
// Disable file info logs.
//stats: 'none',
// https://webpack.js.org/configuration/other-options/#bail
// Fail out on the first error instead of tolerating it.
bail: true,
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: require.resolve('ts-loader'),
options: {
configFile: require.resolve('../../functions.tsconfig.webpack.json'),
context: projectDir,
},
},
exclude: /node_modules/,
},
],
},
resolve: {
plugins: [PnpWebpackPlugin],
extensions: ['.tsx', '.ts', '.js'],
},
resolveLoader: {
plugins: [PnpWebpackPlugin.moduleLoader(module)],
},
output: {
filename: '[name].js',
path: outputPath,
library: {
type: 'commonjs',
},
},
};
return webpackConfig;
}
async function compileWebAppAsync(compiler: Compiler): Promise<any> {
// We generate the stats.json file in the webpack-config
const { warnings } = await new Promise((resolve, reject) =>
compiler.run((error, stats) => {
let messages;
if (error) {
if (!error.message) {
return reject(error);
}
messages = formatWebpackMessages({
errors: [error.message],
warnings: [],
_showErrors: true,
_showWarnings: true,
});
} else {
messages = formatWebpackMessages(
stats?.toJson({ all: false, warnings: true, errors: true })
);
}
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(messages.errors.join('\n\n'));
}
resolve({
warnings: messages.warnings,
});
})
);
return { warnings };
}
export async function bundleFunctionsAsync(
projectDir: string,
entry: EntryObject,
outputPath: string
): Promise<void> {
const config = await getWebpackFunctionConfigurationAsync(projectDir, entry, outputPath);
const compiler = webpack(config);
try {
const { warnings } = await compileWebAppAsync(compiler);
if (warnings.length) {
Log.warn(chalk.yellow('Compiled with warnings.\n'));
Log.warn(warnings.join('\n\n'));
} else {
Log.log(chalk.green('Compiled successfully.\n'));
}
} catch (error) {
Log.error(chalk.red('Failed to compile.\n'));
Log.error(error);
throw error;
}
}
export async function prepareFunctionEntriesAsync(functionsPath: string): Promise<EntryObject> {
const files = await fs.readdir(functionsPath);
if (files.length === 0) {
throw new Error(`No functions found in ${functionsPath}`);
}
const entries = {} as EntryObject;
for (const file of files) {
const filenameNoExtension = path.parse(file).name;
entries[filenameNoExtension] = path.join(functionsPath, file);
}
return entries;
}
-75
View File
@@ -401,16 +401,10 @@ export type AccountUsageMetric = {
export type AccountUsageMetrics = {
__typename?: 'AccountUsageMetrics';
byBillingPeriod: UsageMetricTotal;
metricsForServiceMetric: Array<AccountUsageMetric>;
};
export type AccountUsageMetricsByBillingPeriodArgs = {
date: Scalars['DateTime'];
};
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
granularity: UsageMetricsGranularity;
metric: EasServiceMetric;
@@ -2025,11 +2019,6 @@ export type DeleteWebhookResult = {
id: Scalars['ID'];
};
export type DeployFunctionResult = {
__typename?: 'DeployFunctionResult';
success: Scalars['Boolean'];
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type Deployment = {
__typename?: 'Deployment';
@@ -2160,40 +2149,6 @@ export enum Feature {
Teams = 'TEAMS'
}
export type FunctionMutation = {
__typename?: 'FunctionMutation';
createFunction: DeployFunctionResult;
getFunctionStatus: FunctionStatusResult;
getSignedFunctionUploadSpecification: GetFunctionUploadUrlResult;
};
export type FunctionMutationCreateFunctionArgs = {
appId: Scalars['String'];
bucketKey: Scalars['String'];
};
export type FunctionMutationGetFunctionStatusArgs = {
appId: Scalars['String'];
};
export type FunctionMutationGetSignedFunctionUploadSpecificationArgs = {
appId: Scalars['String'];
};
export type FunctionStatusResult = {
__typename?: 'FunctionStatusResult';
logUrls: Array<Scalars['String']>;
status: Scalars['String'];
};
export type GetFunctionUploadUrlResult = {
__typename?: 'GetFunctionUploadUrlResult';
specification: Scalars['String'];
};
export type GetSignedAssetUploadSpecificationsResult = {
__typename?: 'GetSignedAssetUploadSpecificationsResult';
specifications: Array<Scalars['String']>;
@@ -2911,7 +2866,6 @@ export type RootMutation = {
emailSubscription: EmailSubscriptionMutation;
/** Mutations that create and delete EnvironmentSecrets */
environmentSecret: EnvironmentSecretMutation;
function: FunctionMutation;
/** Mutations that modify a Google Service Account Key */
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
/** Mutations that modify the build credentials for an iOS app */
@@ -3441,13 +3395,6 @@ export enum UploadSessionType {
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE'
}
export type UsageMetricTotal = {
__typename?: 'UsageMetricTotal';
billingPeriod: BillingPeriod;
metrics: Array<AccountUsageMetric>;
total: Scalars['Float'];
};
export enum UsageMetricType {
Request = 'REQUEST'
}
@@ -4344,28 +4291,6 @@ export type DeleteEnvironmentSecretMutationVariables = Exact<{
export type DeleteEnvironmentSecretMutation = { __typename?: 'RootMutation', environmentSecret: { __typename?: 'EnvironmentSecretMutation', deleteEnvironmentSecret: { __typename?: 'DeleteEnvironmentSecretResult', id: string } } };
export type GetSignedUploadFunctionMutationVariables = Exact<{
appId: Scalars['String'];
}>;
export type GetSignedUploadFunctionMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', getSignedFunctionUploadSpecification: { __typename?: 'GetFunctionUploadUrlResult', specification: string } } };
export type CreateFunctionMutationVariables = Exact<{
bucketKey: Scalars['String'];
appId: Scalars['String'];
}>;
export type CreateFunctionMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', createFunction: { __typename?: 'DeployFunctionResult', success: boolean } } };
export type GetFunctionStatusMutationVariables = Exact<{
appId: Scalars['String'];
}>;
export type GetFunctionStatusMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', getFunctionStatus: { __typename?: 'FunctionStatusResult', status: string, logUrls: Array<string> } } };
export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>;
@@ -1,79 +0,0 @@
import gql from 'graphql-tag';
import { graphqlClient, withErrorHandlingAsync } from '../client';
import {
CreateFunctionMutation,
CreateFunctionMutationVariables,
GetFunctionStatusMutation,
GetFunctionStatusMutationVariables,
GetSignedUploadFunctionMutation,
GetSignedUploadFunctionMutationVariables,
} from '../generated';
export const FunctionMutation = {
async uploadAsync(appId: string): Promise<string> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<GetSignedUploadFunctionMutation, GetSignedUploadFunctionMutationVariables>(
gql`
mutation GetSignedUploadFunctionMutation($appId: String!) {
function {
getSignedFunctionUploadSpecification(appId: $appId) {
specification
}
}
}
`,
{
appId,
}
)
.toPromise()
);
return data.function.getSignedFunctionUploadSpecification.specification;
},
async createAsync(bucketKey: string, appId: string): Promise<boolean> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<CreateFunctionMutation, CreateFunctionMutationVariables>(
gql`
mutation CreateFunctionMutation($bucketKey: String!, $appId: String!) {
function {
createFunction(bucketKey: $bucketKey, appId: $appId) {
success
}
}
}
`,
{
bucketKey,
appId,
}
)
.toPromise()
);
return data.function.createFunction.success;
},
async getStatusAsync(appId: string): Promise<{ status: string; logUrls: string[] }> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<GetFunctionStatusMutation, GetFunctionStatusMutationVariables>(
gql`
mutation GetFunctionStatusMutation($appId: String!) {
function {
getFunctionStatus(appId: $appId) {
status
logUrls
}
}
}
`,
{
appId,
}
)
.toPromise()
);
return data.function.getFunctionStatus;
},
};
+12 -1
View File
@@ -1 +1,12 @@
export { run } from '@oclif/core';
import { run } from '@oclif/core';
/**
* Invokes the `testcli-modern` CLI with args programmatically.
*
* @param {...string} args - args to pass to CLI.
*
* @returns {Promise<void>}
*/
export default async function eascliAsync(...args: any[]): Promise<unknown> {
return run(args, import.meta.url);
}
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../__tests__/utils';
import { confirmAsync } from '../../prompts';
import { Actor, getUserAsync } from '../../user/User';
import {
@@ -122,58 +123,52 @@ describe(getProjectAccountNameAsync, () => {
const expWithoutOwner: any = {};
beforeEach(() => {
jest.mocked(getUserAsync).mockReset();
asMock(getUserAsync).mockReset();
});
it(`returns the owner field's value from app.json / app.config.js`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
const projectAccountName = await getProjectAccountNameAsync(expWithOwner);
expect(projectAccountName).toBe('dominik');
});
it(`returns the username if owner field is not set in app.json / app.config.js`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
const projectAccountName = await getProjectAccountNameAsync(expWithoutOwner);
expect(projectAccountName).toBe('notnotbrent');
});
it(`throws when project owner is undefined for robot actors`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'Robot',
id: 'user_id',
firstName: 'GLaDOS',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'Robot',
id: 'user_id',
firstName: 'GLaDOS',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
await expect(getProjectAccountNameAsync(expWithoutOwner)).rejects.toThrow(
'manifest property is required'
);
@@ -182,67 +177,57 @@ describe(getProjectAccountNameAsync, () => {
describe(promptToCreateProjectIfNotExistsAsync, () => {
beforeEach(() => {
jest.mocked(getUserAsync).mockReset();
jest.mocked(findProjectIdByAccountNameAndSlugNullableAsync).mockReset();
asMock(getUserAsync).mockReset();
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockReset();
});
it('returns the existing project', async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(
() => 'already-existing-project-id'
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => 'already-existing-project-id');
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe('already-existing-project-id');
});
it('makes a new project if none exists', async () => {
jest.mock('../../prompts');
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => null);
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(() => null);
asMock(ensureProjectExistsAsync).mockImplementation(() => 'new-project-id');
// user agrees to make new project
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe('new-project-id');
});
it('aborts making a new project if the user declines', async () => {
jest.mock('../../prompts');
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => null);
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(() => null);
asMock(ensureProjectExistsAsync).mockImplementation(() => 'new-project-id');
// user doesnt agree to make new project
jest.mocked(confirmAsync).mockImplementation(async () => false);
asMock(confirmAsync).mockImplementation(() => false);
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe(null);
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import {
@@ -20,7 +21,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(getApplicationIdAsync, () => {
@@ -121,7 +122,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
packageName: 'com.expo.notdominik',
}));
@@ -138,7 +139,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
packageName: 'com.expo.notdominik',
}));
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import { resolveGradleBuildContextAsync } from '../gradle';
@@ -16,7 +17,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(resolveGradleBuildContextAsync, () => {
@@ -3,6 +3,7 @@ import { vol } from 'memfs';
import os from 'os';
import path from 'path';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import {
@@ -22,7 +23,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
const originalFs = jest.requireActual('fs') as typeof fs;
@@ -104,7 +105,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
bundleIdentifier: 'com.expo.notdominik',
}));
@@ -121,7 +122,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
bundleIdentifier: 'com.expo.notdominik',
}));
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { promptAsync } from '../../../prompts';
import { selectSchemeAsync } from '../scheme';
@@ -15,7 +16,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(selectSchemeAsync, () => {
@@ -57,7 +58,7 @@ describe(selectSchemeAsync, () => {
},
projectDir
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
selectedScheme: 'scheme3',
}));
+66 -92
View File
@@ -1,30 +1,17 @@
import { ExpoConfig } from '@expo/config';
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 { Target } from '../../credentials/ios/types';
import { resolveWorkflowAsync } from '../workflow';
import { getBundleIdentifierAsync } from './bundleIdentifier';
import {
getManagedApplicationTargetEntitlementsAsync,
getNativeTargetEntitlementsAsync,
} from './entitlements';
import { XcodeBuildContext } from './scheme';
interface UserDefinedTarget {
targetName: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
entitlements?: JSONObject;
}
interface ResolveTargetOptions {
projectDir: string;
exp: ExpoConfig;
env?: Record<string, string>;
xcodeBuildContext: XcodeBuildContext;
}
const AppExtensionsConfigSchema = Joi.array().items(
@@ -32,85 +19,27 @@ const AppExtensionsConfigSchema = Joi.array().items(
targetName: Joi.string().required(),
bundleIdentifier: Joi.string().required(),
parentBundleIdentifier: Joi.string(),
entitlements: Joi.object(),
})
);
export async function resolveMangedProjectTargetsAsync({
exp,
projectDir,
xcodeBuildContext,
env,
}: ResolveTargetOptions): Promise<Target[]> {
const { buildScheme, buildConfiguration } = xcodeBuildContext;
const applicationTargetName = buildScheme;
const applicationTargetBundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, {
targetName: applicationTargetName,
buildConfiguration,
});
const applicationTargetEntitlements = await getManagedApplicationTargetEntitlementsAsync(
projectDir,
env ?? {}
);
const appExtensions: UserDefinedTarget[] =
exp.extra?.eas?.build?.experimental?.ios?.appExtensions ?? [];
const { error } = AppExtensionsConfigSchema.validate(appExtensions, {
allowUnknown: false,
abortEarly: false,
});
if (error) {
throw new Error(
`Failed to validate "extra.eas.build.experimental.ios.appExtensions" in you app config.\n${error.message}`
);
}
const extensionsTargets: Target[] = appExtensions.map(extension => ({
targetName: extension.targetName,
buildConfiguration,
bundleIdentifier: extension.bundleIdentifier,
parentBundleIdentifier: extension.parentBundleIdentifier ?? applicationTargetBundleIdentifier,
entitlements: extension.entitlements ?? {},
}));
return [
{
targetName: applicationTargetName,
bundleIdentifier: applicationTargetBundleIdentifier,
buildConfiguration,
entitlements: applicationTargetEntitlements,
},
...extensionsTargets,
];
}
export async function resolveBareProjectTargetsAsync({
exp,
projectDir,
xcodeBuildContext,
}: ResolveTargetOptions): Promise<Target[]> {
const { buildScheme, buildConfiguration } = xcodeBuildContext;
export async function resolveTargetsAsync(
{ exp, projectDir }: { exp: ExpoConfig; projectDir: string },
{ buildConfiguration, buildScheme }: XcodeBuildContext
): Promise<Target[]> {
const result: Target[] = [];
const applicationTarget = await IOSConfig.Target.findApplicationTargetWithDependenciesAsync(
projectDir,
buildScheme
);
const applicationTarget = await readApplicationTargetForSchemeAsync(projectDir, buildScheme);
const bundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, {
targetName: applicationTarget.name,
buildConfiguration,
});
const entitlements = await getNativeTargetEntitlementsAsync(projectDir, {
targetName: applicationTarget.name,
buildConfiguration,
});
result.push({
targetName: applicationTarget.name,
bundleIdentifier,
buildConfiguration,
entitlements: entitlements ?? {},
});
const dependencies = await resolveBareProjectDependenciesAsync({
const dependencies = await resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -121,21 +50,55 @@ export async function resolveBareProjectTargetsAsync({
result.push(...dependencies);
}
result.push(
...(await resolveManagedAppExtensionsAsync({
exp,
projectDir,
buildConfiguration,
applicationTargetBundleIdentifier: bundleIdentifier,
}))
);
return result;
}
export async function resolveTargetsAsync(opts: ResolveTargetOptions): Promise<Target[]> {
const workflow = await resolveWorkflowAsync(opts.projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
return await resolveBareProjectTargetsAsync(opts);
} else if (workflow === Workflow.MANAGED) {
return await resolveMangedProjectTargetsAsync(opts);
} else {
throw new Error(`Unknown workflow: ${workflow}`);
async function resolveManagedAppExtensionsAsync({
exp,
projectDir,
buildConfiguration,
applicationTargetBundleIdentifier,
}: {
exp: ExpoConfig;
projectDir: string;
buildConfiguration?: string;
applicationTargetBundleIdentifier: string;
}): Promise<Target[]> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const managedAppExtensions: UserDefinedTarget[] =
exp.extra?.eas?.build?.experimental?.ios?.appExtensions;
if (workflow === Workflow.GENERIC || !managedAppExtensions) {
return [];
}
const { error } = AppExtensionsConfigSchema.validate(managedAppExtensions, {
allowUnknown: false,
abortEarly: false,
});
if (error) {
throw new Error(
`Failed to validate "extra.eas.build.experimental.ios.appExtensions" in you app config\n${error.message}`
);
}
return managedAppExtensions.map(extension => ({
targetName: extension.targetName,
buildConfiguration,
bundleIdentifier: extension.bundleIdentifier,
parentBundleIdentifier: extension.parentBundleIdentifier ?? applicationTargetBundleIdentifier,
}));
}
async function resolveBareProjectDependenciesAsync({
async function resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -156,18 +119,13 @@ async function resolveBareProjectDependenciesAsync({
targetName: dependency.name,
buildConfiguration,
});
const entitlements = await getNativeTargetEntitlementsAsync(projectDir, {
targetName: target.name,
buildConfiguration,
});
result.push({
targetName: dependency.name,
buildConfiguration,
bundleIdentifier: dependencyBundleIdentifier,
parentBundleIdentifier: bundleIdentifier,
entitlements: entitlements ?? {},
});
const dependencyDependencies = await resolveBareProjectDependenciesAsync({
const dependencyDependencies = await resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -183,6 +141,22 @@ async function resolveBareProjectDependenciesAsync({
return result;
}
async function readApplicationTargetForSchemeAsync(
projectDir: string,
scheme: string
): Promise<IOSConfig.Target.Target> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
return await IOSConfig.Target.findApplicationTargetWithDependenciesAsync(projectDir, scheme);
} else {
return {
name: scheme,
type: IOSConfig.Target.TargetType.APPLICATION,
dependencies: [],
};
}
}
export function findApplicationTarget(targets: Target[]): Target {
const applicationTarget = targets.find(({ parentBundleIdentifier }) => !parentBundleIdentifier);
if (!applicationTarget) {
+4 -4
View File
@@ -54,7 +54,7 @@ export async function validateMetroConfigForManagedWorkflowAsync(
}
}
function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
async function importMetroConfigFromProjectAsync(projectDir: string): Promise<typeof MetroConfig> {
const resolvedPath = resolveFrom.silent(projectDir, 'metro-config');
if (!resolvedPath) {
throw new MetroConfigPackageMissingError(
@@ -64,12 +64,12 @@ function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
'and run `yarn` or `npm install`.'
);
}
return require(resolvedPath);
return await import(resolvedPath);
}
async function configExistsAsync(projectRoot: string): Promise<boolean> {
try {
const MetroConfig = importMetroConfigFromProject(projectRoot);
const MetroConfig = await importMetroConfigFromProjectAsync(projectRoot);
const result = await MetroConfig.resolveConfig(undefined, projectRoot);
return !result.isEmpty;
} catch (err) {
@@ -82,7 +82,7 @@ async function configExistsAsync(projectRoot: string): Promise<boolean> {
}
async function loadConfigAsync(projectDir: string): Promise<MetroConfig.ConfigT> {
const MetroConfig = importMetroConfigFromProject(projectDir);
const MetroConfig = await importMetroConfigFromProjectAsync(projectDir);
return await MetroConfig.loadConfig({ cwd: projectDir }, {});
}
@@ -3,6 +3,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../__tests__/utils';
import { AppPlatform, BuildFragment, UploadSessionType } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
@@ -50,13 +51,13 @@ describe(getArchiveAsync, () => {
beforeEach(() => {
vol.reset();
jest.mocked(promptAsync).mockReset();
jest.mocked(promptAsync).mockImplementation(async () => {
asMock(promptAsync).mockReset();
asMock(promptAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(confirmAsync).mockReset();
jest.mocked(confirmAsync).mockImplementation(async () => {
asMock(confirmAsync).mockReset();
asMock(confirmAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -72,8 +73,7 @@ describe(getArchiveAsync, () => {
});
it('prompts again if provided URL is invalid', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -88,8 +88,8 @@ describe(getArchiveAsync, () => {
});
it('asks the user if use build id instead of build details page url', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
jest.mocked(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT as BuildFragment);
asMock(confirmAsync).mockResolvedValueOnce(true);
asMock(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -103,8 +103,7 @@ describe(getArchiveAsync, () => {
});
it('handles prompt source', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -118,7 +117,7 @@ describe(getArchiveAsync, () => {
it('handles Build ID source', async () => {
const buildId = uuidv4();
jest.mocked(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT as BuildFragment);
asMock(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -131,9 +130,8 @@ describe(getArchiveAsync, () => {
});
it('prompts again if build with provided ID doesnt exist', async () => {
jest.mocked(BuildQuery.byIdAsync).mockRejectedValue(new Error('Build doesnt exist'));
jest
.mocked(promptAsync)
asMock(BuildQuery.byIdAsync).mockRejectedValue(new Error('Build doesnt exist'));
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -148,9 +146,7 @@ describe(getArchiveAsync, () => {
it('handles latest build source', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -166,9 +162,8 @@ describe(getArchiveAsync, () => {
});
it('prompts again if no builds exists when selected latest', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([]);
jest
.mocked(promptAsync)
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([]);
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -182,10 +177,8 @@ describe(getArchiveAsync, () => {
it('handles build-list-select source', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
jest.mocked(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
asMock(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -202,14 +195,13 @@ describe(getArchiveAsync, () => {
});
it('prompts again if all builds have expired', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
{
...MOCK_BUILD_FRAGMENT,
updatedAt: new Date(Date.now() - 31 * 24 * 3600 * 1000),
} as BuildFragment,
},
]);
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -222,11 +214,8 @@ describe(getArchiveAsync, () => {
});
it('falls back to prompt if user selected "None of the above"', async () => {
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
jest
.mocked(promptAsync)
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
asMock(promptAsync)
.mockResolvedValueOnce({ selectedBuild: null })
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -242,7 +231,7 @@ describe(getArchiveAsync, () => {
it('handles path source', async () => {
const path = '/archive.apk';
await fs.writeFile(path, 'some content');
jest.mocked(uploadAsync).mockResolvedValueOnce({ url: ARCHIVE_URL, bucketKey: 'wat' });
asMock(uploadAsync).mockResolvedValueOnce({ url: ARCHIVE_URL });
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -259,8 +248,7 @@ describe(getArchiveAsync, () => {
});
it('prompts again if provided path doesnt exist', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -3,6 +3,7 @@ import { AndroidReleaseStatus, AndroidReleaseTrack } from '@expo/eas-json';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import {
AppPlatform,
@@ -73,7 +74,7 @@ describe(AndroidSubmitCommand, () => {
});
afterEach(() => {
jest.mocked(getProjectIdAsync).mockClear();
asMock(getProjectIdAsync).mockClear();
});
describe('non-interactive mode', () => {
@@ -135,9 +136,7 @@ describe(AndroidSubmitCommand, () => {
it('assigns the build ID to submission', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([fakeBuildFragment as BuildFragment]);
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([fakeBuildFragment]);
const ctx = await createSubmissionContextAsync({
platform: Platform.ANDROID,
@@ -3,6 +3,7 @@ import { AndroidReleaseStatus, AndroidReleaseTrack } from '@expo/eas-json';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { testAndroidAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-android';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { SetUpGoogleServiceAccountKey } from '../../../credentials/android/actions/SetUpGoogleServiceAccountKey';
@@ -59,14 +60,14 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
asMock(promptAsync).mockClear();
jest.restoreAllMocks();
});
describe(getServiceAccountKeyPathAsync, () => {
describe('when source is ServiceAccountSourceType.path', () => {
it("prompts for path if the provided file doesn't exist", async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -99,7 +100,7 @@ describe(getServiceAccountKeyPathAsync, () => {
describe('when source is ServiceAccountSourceType.prompt', () => {
it('prompts for path', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -111,15 +112,14 @@ describe(getServiceAccountKeyPathAsync, () => {
});
it('prompts for path until the user provides an existing file', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
filePath: '/doesnt-exist.json',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
filePath: '/googl-service-account.json',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -166,7 +166,7 @@ describe(getServiceAccountKeyResultAsync, () => {
});
it('returns a local Service Account Key file with a ServiceAccountSourceType.prompt source', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/project_dir/subdir/service-account.json',
}));
const ctx = await createSubmissionContextAsync({
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { getCredentialsFromUserAsync } from '../../../credentials/utils/promptForCredentials';
import { createTestProject } from '../../../project/__tests__/project-utils';
@@ -62,16 +63,16 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
asMock(promptAsync).mockClear();
});
describe(getAscApiKeyPathAsync, () => {
describe('when source is AscApiKeySourceType.path', () => {
it("prompts for path if the provided file doesn't exist", async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -116,10 +117,10 @@ describe(getAscApiKeyPathAsync, () => {
describe('when source is AscApiKeySourceType.prompt', () => {
it('prompts for path', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -137,18 +138,17 @@ describe(getAscApiKeyPathAsync, () => {
});
it('prompts for path until the user provides an existing file', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
keyP8Path: '/doesnt-exist.p8',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
keyP8Path: '/blah.p8',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -190,10 +190,10 @@ describe(getAscApiKeyLocallyAsync, () => {
});
it('returns a local Asc API Key file with a AscApiKeySourceType.prompt source', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementationOnce(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementationOnce(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -1,6 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { testCommonIosAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-ios';
import { SetUpSubmissionCredentials } from '../../../credentials/ios/actions/SetUpSubmissionCredentials';
@@ -56,8 +57,8 @@ describe(getFromCredentialsServiceAsync, () => {
jest
.spyOn(SetUpSubmissionCredentials.prototype, 'runAsync')
.mockImplementation(async _ctx => 'super secret');
jest.mocked(getBundleIdentifierAsync).mockImplementation(async () => 'com.hello.world');
jest.mocked(promptAsync).mockImplementationOnce(async () => ({ appleId: '[email protected]' }));
asMock(getBundleIdentifierAsync).mockImplementation(() => 'com.hello.world');
asMock(promptAsync).mockImplementationOnce(() => ({ appleId: '[email protected]' }));
const result = await getFromCredentialsServiceAsync(ctx, {
sourceType: CREDENTIALS_SERVICE_SOURCE,
@@ -83,7 +84,7 @@ describe(getFromCredentialsServiceAsync, () => {
jest
.spyOn(SetUpSubmissionCredentials.prototype, 'runAsync')
.mockImplementation(async _ctx => testCommonIosAppCredentialsFragment);
jest.mocked(getBundleIdentifierAsync).mockImplementation(async () => 'com.hello.world');
asMock(getBundleIdentifierAsync).mockImplementation(() => 'com.hello.world');
const result = await getFromCredentialsServiceAsync(ctx, {
sourceType: CREDENTIALS_SERVICE_SOURCE,
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { SubmissionMutation } from '../../../graphql/mutations/SubmissionMutation';
import { createTestProject } from '../../../project/__tests__/project-utils';
@@ -49,7 +50,7 @@ describe(IosSubmitCommand, () => {
});
afterEach(() => {
jest.mocked(getProjectIdAsync).mockClear();
asMock(getProjectIdAsync).mockClear();
});
describe('non-interactive mode', () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../__tests__/utils';
import { ApiV2Error } from '../../api';
import { promptAsync } from '../../prompts';
import { Actor, loginAsync } from '../User';
@@ -15,12 +16,12 @@ jest.mock('../User', () => ({
}));
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
jest.mocked(promptAsync).mockImplementation(async () => {
asMock(promptAsync).mockReset();
asMock(promptAsync).mockImplementation(() => {
throw new Error('Should not be called');
});
jest.mocked(loginAsync).mockReset();
asMock(loginAsync).mockReset();
});
const userStub: Actor = {
@@ -51,15 +52,13 @@ describe('ensureActorHasUsername', () => {
describe(showLoginPromptAsync, () => {
it('prompts for OTP when 2FA is enabled', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ username: 'hello', password: 'world' }))
.mockImplementationOnce(async () => ({ otp: '123456' }))
.mockImplementation(async () => {
asMock(promptAsync)
.mockImplementationOnce(() => ({ username: 'hello', password: 'world' }))
.mockImplementationOnce(() => ({ otp: '123456' }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(loginAsync)
asMock(loginAsync)
.mockImplementationOnce(async () => {
throw new ApiV2Error({
message: 'An OTP is required',
@@ -77,7 +76,7 @@ describe(showLoginPromptAsync, () => {
},
});
})
.mockImplementation(async () => {});
.mockImplementation(() => {});
await showLoginPromptAsync();
+40 -52
View File
@@ -1,3 +1,4 @@
import { asMock } from '../../__tests__/utils';
import { api } from '../../api';
import Log from '../../log';
import { promptAsync, selectAsync } from '../../prompts';
@@ -12,25 +13,24 @@ jest.mock('../User', () => ({
jest.mock('../../log');
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
jest.mocked(promptAsync).mockImplementation(async () => {
asMock(promptAsync).mockReset();
asMock(promptAsync).mockImplementation(() => {
throw new Error('Should not be called');
});
jest.mocked(selectAsync).mockReset();
jest.mocked(selectAsync).mockImplementation(() => {
asMock(selectAsync).mockReset();
asMock(selectAsync).mockImplementation(() => {
throw new Error('Should not be called');
});
jest.mocked(loginAsync).mockReset();
jest.mocked(Log.log).mockReset();
asMock(loginAsync).mockReset();
asMock(Log.log).mockReset();
});
describe(retryUsernamePasswordAuthWithOTPAsync, () => {
it('shows SMS OTP prompt when SMS is primary and code was automatically sent', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: 'hello' }))
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: 'hello' }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -51,13 +51,12 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
'One-time password was sent to the phone number ending in testphone.'
);
expect(jest.mocked(loginAsync)).toHaveBeenCalledTimes(1);
expect(asMock(loginAsync)).toHaveBeenCalledTimes(1);
});
it('shows authenticator OTP prompt when authenticator is primary', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: 'hello' }))
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: 'hello' }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -75,21 +74,19 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
});
expect(Log.log).toHaveBeenCalledWith('One-time password from authenticator required.');
expect(jest.mocked(loginAsync)).toHaveBeenCalledTimes(1);
expect(asMock(loginAsync)).toHaveBeenCalledTimes(1);
});
it('shows menu when user bails on primary', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: null }))
.mockImplementationOnce(async () => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: null }))
.mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(selectAsync)
.mockImplementationOnce(async () => -1)
asMock(selectAsync)
.mockImplementationOnce(() => -1)
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -112,14 +109,13 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
smsAutomaticallySent: false,
});
expect(jest.mocked(selectAsync).mock.calls.length).toEqual(1);
expect(jest.mocked(loginAsync)).toHaveBeenCalledTimes(1);
expect(asMock(selectAsync).mock.calls.length).toEqual(1);
expect(asMock(loginAsync)).toHaveBeenCalledTimes(1);
});
it('shows a warning when when user bails on primary and does not have any secondary set up', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: null }))
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: null }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -142,17 +138,15 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
});
it('prompts for authenticator OTP when user selects authenticator secondary', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: null }))
.mockImplementationOnce(async () => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: null }))
.mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(selectAsync)
.mockImplementationOnce(async () => -1)
asMock(selectAsync)
.mockImplementationOnce(() => -1)
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -175,28 +169,24 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
smsAutomaticallySent: false,
});
expect(jest.mocked(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP
expect(asMock(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP
});
it('requests SMS OTP and prompts for SMS OTP when user selects SMS secondary', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: null }))
.mockImplementationOnce(async () => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: null }))
.mockImplementationOnce(() => ({ otp: 'hello' })) // second time it is prompted after selecting backup code
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(selectAsync)
.mockImplementationOnce(async () => 0)
asMock(selectAsync)
.mockImplementationOnce(() => 0)
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(api.postAsync)
.mockReturnValueOnce(Promise.resolve({ sessionSecret: 'SESSION_SECRET' }));
asMock(api.postAsync).mockReturnValueOnce(Promise.resolve({ sessionSecret: 'SESSION_SECRET' }));
await retryUsernamePasswordAuthWithOTPAsync('blah', 'blah', {
secondFactorDevices: [
@@ -216,8 +206,8 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
smsAutomaticallySent: false,
});
expect(jest.mocked(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP
expect(jest.mocked(api.postAsync).mock.calls[0]).toEqual([
expect(asMock(promptAsync).mock.calls.length).toBe(2); // first OTP, second OTP
expect(asMock(api.postAsync).mock.calls[0]).toEqual([
'auth/send-sms-otp',
{
body: {
@@ -230,16 +220,14 @@ describe(retryUsernamePasswordAuthWithOTPAsync, () => {
});
it('exits when user bails on primary and backup', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ otp: null }))
asMock(promptAsync)
.mockImplementationOnce(() => ({ otp: null }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(selectAsync)
.mockImplementationOnce(async () => -2)
asMock(selectAsync)
.mockImplementationOnce(() => -2)
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
@@ -1,6 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJsonReader, errors } from '@expo/eas-json';
import { asMock } from '../../__tests__/utils';
import { selectAsync } from '../../prompts';
import { getProfilesAsync } from '../profiles';
@@ -26,7 +27,7 @@ describe(getProfilesAsync, () => {
afterEach(() => {
getBuildProfileAsync.mockReset();
getBuildProfileNamesAsync.mockReset();
jest.mocked(selectAsync).mockReset();
asMock(selectAsync).mockReset();
});
it('defaults to production profile', async () => {
@@ -75,7 +76,7 @@ describe(getProfilesAsync, () => {
throw new errors.MissingProfileError();
});
getBuildProfileNamesAsync.mockImplementation(() => Promise.resolve(['foo', 'bar']));
jest.mocked(selectAsync).mockImplementation(async () => 'foo');
asMock(selectAsync).mockImplementation(() => 'foo');
const result = await getProfilesAsync({
projectDir: '/fake',
+1 -1
View File
@@ -1,3 +1,3 @@
const packageJSON = require('../../package.json');
import packageJSON from '../../package.json';
export const easCliVersion: string = packageJSON.version;
+1 -5
View File
@@ -7,15 +7,11 @@ import Log from '../log';
export async function expoCommandAsync(
projectDir: string,
args: string[],
{ silent = false, env }: { silent?: boolean; env?: { [key: string]: string } } = {}
{ silent = false }: { silent?: boolean } = {}
): Promise<void> {
const expoCliPath = resolveFrom(projectDir, 'expo/bin/cli.js');
const spawnPromise = spawnAsync(expoCliPath, args, {
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so user can install a missing expo-cli from inside this command
env: {
...process.env,
...env,
},
});
const {
child: { stdout, stderr },
+3 -4
View File
@@ -78,12 +78,11 @@ export async function makeShallowCopyAsync(src: string, dst: string): Promise<vo
const ignore = new Ignore(src);
await ignore.initIgnoreAsync();
await fs.copy(src, dst, {
filter: (_srcFilePath: string) => {
return true; // TODO: handle ignored build folder
/* if (srcFilePath === src) {
filter: (srcFilePath: string) => {
if (srcFilePath === src) {
return true;
}
return !ignore.ignores(path.relative(src, srcFilePath)); */
return !ignore.ignores(path.relative(src, srcFilePath));
},
});
}
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../nLine truncated
+7 -4
View File
@@ -2,20 +2,23 @@
"compilerOptions": {
"declaration": true,
"importHelpers": true,
"module": "commonjs",
"module": "es2020",
"moduleResolution": "node",
"composite": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noUnusedParameters":true,
"resolveJsonModule": true,
"noUnusedLocals":true,
"strict": true,
"target": "ES2019",
"target": "ES2020",
"outDir": "build",
"rootDir": "src",
// "rootDir": "src",
"typeRoots": [
"../../node_modules/@types",
"../../ts-declarations",
"./node_modules/@types"
]
},
"include": ["src/**/*"]
"include": ["src/**/*", "package.json"]
}
+1
View File
@@ -0,0 +1 @@
{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../node_modules/typescript/lib/lib.es2020.full.d.ts","../../node_modules/tslib/tslib.d.ts","../../node_modules/@expo/json-file/build/JsonFile.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/assert/strict.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/dns/promises.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/stream/promises.d.ts","../../node_modules/@types/node/stream/consumers.d.ts","../../node_modules/@types/node/stream/web.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/timers/promises.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../nLine truncated
+11 -2
View File
@@ -1,5 +1,14 @@
module.exports = {
...require('../../jest/jest.config.js'),
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import baseConfig from '../../jest/jest.config.js';
const filename = fileURLToPath(import.meta.url);
// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(filename);
export default {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
};
+5 -4
View File
@@ -1,11 +1,12 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "0.52.0",
"version": "0.51.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"type": "module",
"dependencies": {
"@expo/eas-build-job": "0.2.71",
"@expo/eas-build-job": "0.2.68",
"@expo/json-file": "8.2.34",
"chalk": "4.1.2",
"env-string": "1.0.1",
@@ -21,11 +22,11 @@
"typescript": "4.6.3"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.16.0"
},
"homepage": "https://github.com/expo/eas-cli",
"license": "MIT",
"main": "build/index.js",
"exports": "./build/index.js",
"types": "build/index.d.ts",
"repository": "expo/eas-cli",
"scripts": {
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more