Compare commits

...
40 Commits
Author SHA1 Message Date
Dominik Sokal fedfdb3364 v0.28.2 2021-09-23 11:36:29 +02:00
Dominik Sokal 028f0ab74b fix auto-submitting in fresh project (#640)
* fix auto-submitting in fresh project

* update CHANGELOG
2021-09-23 11:34:22 +02:00
Dominik Sokal cd278155e2 clean up tests output - mock console.log 2021-09-23 11:07:54 +02:00
Dominik Sokal 1e419ab49d use log-symbols to unify green ticks style (#639)
* use log-symbols to unify green ticks style

* update CHANGELOG
2021-09-23 10:14:29 +02:00
Dominik Sokal 2f57fd199d update CHANGELOG 2021-09-22 15:40:42 +02:00
Dominik Sokal 5932775cd4 v0.28.1 2021-09-22 15:33:39 +02:00
Dominik Sokal d68d6c38d6 do not fail in case of transient network errors (#638)
* do not fail in case of transient network errors

* update CHANGELOG
2021-09-22 14:29:52 +02:00
Evan BaconandEvan Bacon d690e10caf Forward props (#636)
Co-authored-by: Evan Bacon <[email protected]>
2021-09-22 13:16:33 +02:00
Quinlan JungandDominik Sokal 99ea613b39 [eas-cli] add google service account key fragment (#632)
* [eas-cli] add google service account key fragment

* fix lint

* update changelog

* Update CHANGELOG.md

Co-authored-by: Dominik Sokal <[email protected]>

Co-authored-by: Dominik Sokal <[email protected]>
2021-09-21 17:02:16 -07:00
Quinlan JungandDominik Sokal 13715317bd [eas-cli] remove graphql support for app specific password (#633)
* [eas-cli] remove graphql support for app specific password

* fix tests

* changelog stuff

* Update CHANGELOG.md

Co-authored-by: Dominik Sokal <[email protected]>

Co-authored-by: Dominik Sokal <[email protected]>
2021-09-21 17:01:08 -07:00
Dominik Sokal 18d1ed441b add CODEOWNERS 2021-09-21 11:39:54 +02:00
Dominik Sokal db067c4757 rename directory submissions -> submit (#631) 2021-09-20 16:55:46 +02:00
Dominik Sokal 51388f6adb add PR comment with code coverage (#629) 2021-09-20 16:36:42 +02:00
fcbf363538 use custom ora instance (#575)
* use custom ora instance

* Update CHANGELOG.md

* Update ora.ts

* pr feedback

* address PR feedback

* update CHANGELOG

* add eslint rule to prevent using ora directly

Co-authored-by: Evan Bacon <[email protected]>
Co-authored-by: Dominik Sokal <[email protected]>
2021-09-20 16:36:28 +02:00
Dominik Sokal b4d7795006 update CHANGELOG 2021-09-20 11:15:30 +02:00
Dominik Sokal 2364ed74d7 v0.28.0 2021-09-20 10:56:59 +02:00
Dominik Sokal 87fb74dd5a print meaningful error message when not logged in in non-interactive mode (#628)
* print meaningful error message when not logged in in non-interactive mode

* update CHANGELOG

* maybe fix tests
2021-09-20 10:53:55 +02:00
Dominik Sokal eef0c6ae6e [ENG-1792] Validate that project includes expo-dev-client when building with developmentClient flag (#627)
* move reviewAndCommitChangesAsync to utils

* move expo-cli runner to utils

* add expo-cli and expo-dev-client to diagnostics

* check for expo-dev-client when building with developmentClient = true

* update CHANGELOG

* add installation instructions
2021-09-20 10:36:55 +02:00
kgc00 5ee905a977 [eas-cli] Removes Amplitude (#621)
- Deleted references to Amplitude, uninstalled packages.
- Renamed `UserSettings` involving `amplitude...` => `analytics...`
2021-09-17 15:23:25 -07:00
Bartłomiej Klocek ca3e083995 [eas-cli] eas submit: Fix error message not displayed (#626)
* [eas-cli] eas submit: Fix error message not displayed

* Fix error display

* Use `platforDisplayNames`
2021-09-17 13:33:56 +02:00
Dominik Sokal b7ae91892c eas submit: warn about turtle v1 build ids (#624)
* eas submit: warn about turtle v1 build ids

* update CHANGELOG

* id -> ID

* address PR feedback
2021-09-17 10:45:33 +02:00
Dominik Sokal 508ca745c1 [ENG-1915] show syntax errors in eas.json (#618)
* show syntax errors in eas.json

* update CHANGELOG
2021-09-16 12:13:10 +02:00
Dominik Sokal 95e8a38c85 [ENG-1347] eas submit: suggest using build id instead of build details page (#620)
* eas submit: suggest using build id instead of build details page

* update CHANGELOG

* fix lint
2021-09-16 12:05:39 +02:00
Dominik Sokal 441f3eaf4c enforce Async suffix for async functions (#623)
* enforce Async suffix for async functions

* update CHANGELOG

* fix tests
2021-09-16 11:46:15 +02:00
Wojciech Kozyra 9edb10ca55 enforce return type in all functions (#619)
* enforce return type in all functions

* update CHANGLOG
2021-09-15 16:28:09 +02:00
Wojciech Kozyra 7f094bc605 [eas-cli] support multiflavor android projects (#595)
* [eas-cli] support multiflavor android projects

* move utils from expo-cli

* update CHANGELOG

* review feedback
2021-09-15 13:51:28 +02:00
Dominik Sokal 2463e958aa add eslint rule to reduce node-fetch usage 2021-09-14 10:44:31 +02:00
Dominik Sokal 390e6fef9e upgrade eslint-config-universe to 8.0.0 2021-09-13 17:44:24 +02:00
Dominik Sokal 6b416c3dfb use envs from build profile for resolving app config when auto-submiting (#614)
* use envs from build profile for resolving app config when auto-submitting

* update CHANGELOG
2021-09-13 17:34:25 +02:00
dependabot[bot] 60d2daf4c8 Bump handlebars from 4.7.6 to 4.7.7 (#613)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

---
updated-dependencies:
- dependency-name: handlebars
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 17:03:33 +02:00
dependabot[bot] 9528f25ff2 Bump hosted-git-info from 2.8.8 to 2.8.9 (#612)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

---
updated-dependencies:
- dependency-name: hosted-git-info
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 17:03:17 +02:00
dependabot[bot] 5da03dfca7 Bump glob-parent from 5.1.1 to 5.1.2 (#611)
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 17:02:34 +02:00
dependabot[bot] 8b3b0376c3 Bump trim-newlines from 3.0.0 to 3.0.1 (#608)
Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sindresorhus/trim-newlines/releases)
- [Commits](https://github.com/sindresorhus/trim-newlines/commits)

---
updated-dependencies:
- dependency-name: trim-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 16:53:26 +02:00
dependabot[bot] 5f3c4756d2 Bump path-parse from 1.0.6 to 1.0.7 (#610)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 16:53:05 +02:00
dependabot[bot] 3b34a64893 Bump ws from 5.2.2 to 5.2.3 (#609)
Bumps [ws](https://github.com/websockets/ws) from 5.2.2 to 5.2.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/5.2.2...5.2.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 16:52:36 +02:00
Dominik Sokal 243cf8d630 reduce eas-cli size (#605)
* don't use lodash

* upgrade @expo/json-file

* upgrade @expo/config to 5.0.9

* add tests for utils

* address PR feedback

* more tests

* move tempy to dev dependencies

* replace timeago.js with @expo/timeago.js

* add eslint rule to forbid using lodash

* update CHANGELOG
2021-09-13 16:47:49 +02:00
Juwan Wheatley 671ce30ed4 update CHANGELOG 2021-09-10 19:14:14 -04:00
Juwan Wheatley 9021887072 v0.27.1 2021-09-10 18:59:11 -04:00
Juwan Wheatley 6a80d4c8e9 [eas-cli] fix secrets mutations (#606)
* [eas-cli] fix secrets mutations

* add changelog entry

* fix typo
2021-09-10 18:42:10 -04:00
Dominik Sokal 471e26817a update CHANGELOG 2021-09-10 14:42:07 +02:00
223 changed files with 4081 additions and 5196 deletions

No files matched your search

+35 -12
View File
@@ -1,14 +1,7 @@
module.exports = {
root: true,
extends: ['universe/node'],
overrides: [
{
files: ['**/__tests__/*'],
},
],
globals: {
jasmine: false,
},
plugins: ['async-protect'],
rules: {
'no-console': 'warn',
'no-constant-condition': ['warn', { checkLoops: false }],
@@ -20,11 +13,41 @@ module.exports = {
],
'flowtype/no-types-missing-file-annotation': 'off',
curly: 'warn',
'import/no-cycle': 'error',
'import/no-extraneous-dependencies': [
'error',
{ devDependencies: ['**/__tests__/**/*', '**/__mocks__/**/*'] },
],
'import/no-relative-packages': 'error',
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'lodash',
message: "Don't use lodash, it's heavy!",
},
{
name: 'node-fetch',
message: 'Use got instead, node-fetch is used only for the GraphQL client.',
},
],
},
],
'@typescript-eslint/explicit-function-return-type': [
'warn',
{
allowExpressions: true,
},
],
'async-protect/async-suffix': 'error',
},
settings: {
react: {
version: 'detect',
overrides: [
{
files: ['**/__tests__/**/*.ts', '**/__mocks__/**/*.ts'],
rules: {
'async-protect/async-suffix': 'off',
},
},
},
],
};
+48 -3
View File
@@ -8,17 +8,62 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features
### 🐛 Bug fixes
- Use `log-symbols` to unify green ticks style. ([#639](https://github.com/expo/eas-cli/pull/639) by [@dsokal](https://github.com/dsokal))
- Fix `eas build --auto-submit` in fresh projects. ([#640](https://github.com/expo/eas-cli/pull/640) by [@dsokal](https://github.com/dsokal))
### 🧹 Chores
## [0.28.1](https://github.com/expo/eas-cli/releases/tag/v0.28.1) - 2021-09-22
### 🐛 Bug fixes
- Prevent ora spinners breaking when debug logs are enabled. ([#575](https://github.com/expo/eas-cli/pull/575) by [@EvanBacon](https://github.com/EvanBacon))
- Do not fail `eas build` when transient network errors occur. ([#638](https://github.com/expo/eas-cli/pull/638) by [@dsokal](https://github.com/dsokal))
### 🧹 Chores
- Add GraphQL support for GoogleServiceAccountKey. ([#632](https://github.com/expo/eas-cli/pull/632) by [@quinlanj](https://github.com/quinlanj))
- Remove GraphQL support for the App Specific Password. ([#633](https://github.com/expo/eas-cli/pull/633) by [@quinlanj](https://github.com/quinlanj))
## [0.28.0](https://github.com/expo/eas-cli/releases/tag/v0.28.0) - 2021-09-20
### 🎉 New features
- Use envs from build profile to resolve app config when auto-submitting. ([#614](https://github.com/expo/eas-cli/pull/614) by [@dsokal](https://github.com/dsokal))
- Support multi flavor Android projects. ([#595](https://github.com/expo/eas-cli/pull/595) by [@wkozyra95](https://github.com/wkozyra95))
- Improve experience when using the build details page as a build artifact URL in `eas submit`. ([#620](https://github.com/expo/eas-cli/pull/620) by [@dsokal](https://github.com/dsokal))
- Better error message when eas.json is invalid JSON. ([#618](https://github.com/expo/eas-cli/pull/618) by [@dsokal](https://github.com/dsokal))
- Add warning about the legacy build service IDs in `eas submit`. ([#624](https://github.com/expo/eas-cli/pull/624) by [@dsokal](https://github.com/dsokal))
- Validate that project includes `expo-dev-client` when building with `developmentClient` flag. ([#627](https://github.com/expo/eas-cli/pull/627) by [@dsokal](https://github.com/dsokal))
- Better experience when not logged in in non-interactive mode. ([#628](https://github.com/expo/eas-cli/pull/628) by [@dsokal](https://github.com/dsokal))
### 🧹 Chores
- Reduce `eas-cli` size by almost getting rid of `lodash` from dependencies. ([#605](https://github.com/expo/eas-cli/pull/605) by [@dsokal](https://github.com/dsokal))
- Enforce explicit return type in all functions. ([#619](https://github.com/expo/eas-cli/pull/619) by [@wkozyra95](https://github.com/wkozyra95))
- Enforce `Async` suffix for async functions. ([#623](https://github.com/expo/eas-cli/pull/623) by [@dsokal](https://github.com/dsokal))
## [0.27.1](https://github.com/expo/eas-cli/releases/tag/v0.27.1) - 2021-09-10
### 🐛 Bug fixes
- Fixed force-creation, app secret creation, and secret deletion mutations. ([#606](https://github.com/expo/eas-cli/pull/606) by [@fiberjw](https://github.com/fiberjw))
## [0.27.0](https://github.com/expo/eas-cli/releases/tag/v0.27.0) - 2021-09-10
### 🎉 New features
- Implement support for `--platform all` in `eas submit`. ([#598](https://github.com/expo/eas-cli/pull/598) by [@dsokal](https://github.com/dsokal))
- Implement support for `--non-interactive` in `eas submit`. ([#600](https://github.com/expo/eas-cli/pull/600) by [@dsokal](https://github.com/dsokal))
- Add auto-submit feature. Run `eas build --submit` to submit automatically on build complete. ([#603](https://github.com/expo/eas-cli/pull/603) by [@dsokal](https://github.com/dsokal))
- Add auto-submit feature. Run `eas build --auto-submit` to submit automatically on build complete. ([#603](https://github.com/expo/eas-cli/pull/603) by [@dsokal](https://github.com/dsokal))
- Allow using env var for `android.serviceAccountKeyPath` in submit profiles. ([#604](https://github.com/expo/eas-cli/pull/604) by [@dsokal](https://github.com/dsokal))
### 🐛 Bug fixes
- Fix detecting `googleServicesFile` on Windows. ([#602](https://github.com/expo/eas-cli/pull/602) by [@wkozyra95](https://github.com/wkozyra95))
### 🧹 Chores
## [0.26.0](https://github.com/expo/eas-cli/releases/tag/v0.26.0) - 2021-09-08
### 🛠 Breaking changes
+7
View File
@@ -0,0 +1,7 @@
packages/eas-cli/src/build @dsokal @wkozyra95
packages/eas-cli/src/commands/build @dsokal @wkozyra95
packages/eas-cli/src/submit @barthap @dsokal
packages/eas-cli/src/commands/submit.ts @barthap @dsokal
packages/eas-json @dsokal @wkozyra95
-1
View File
@@ -1,4 +1,3 @@
comment: false
coverage:
round: up
precision: 2
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.27.0"
"version": "0.28.2"
}
+2 -1
View File
@@ -19,7 +19,8 @@
"@types/jest": "27.0.1",
"@types/node": "16.7.8",
"eslint": "7.32.0",
"eslint-config-universe": "7.0.1",
"eslint-config-universe": "8.0.0",
"eslint-plugin-async-protect": "1.1.0",
"jest": "27.1.0",
"jest-watch-typeahead": "0.6.4",
"lerna": "4.0.0",
+12 -3
View File
@@ -1,9 +1,18 @@
const rootEslintrc = require('../../.eslintrc.js');
module.exports = {
rules: {
'import/no-cycle': 'error',
'import/no-extraneous-dependencies': [
'no-restricted-imports': [
'error',
{ devDependencies: ['**/__tests__/**/*', '**/__mocks__/**/*'] },
{
paths: [
...rootEslintrc.rules['no-restricted-imports'][1].paths,
{
name: 'ora',
message: 'Import ora from src/ora.ts instead.',
},
],
},
],
'graphql/template-strings': [
'error',
+26 -26
View File
@@ -73,7 +73,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/account/login.js)_
_See code: [src/commands/account/login.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/account/login.js)_
## `eas account:logout`
@@ -87,7 +87,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/account/logout.js)_
_See code: [src/commands/account/logout.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/account/logout.js)_
## `eas account:view`
@@ -101,7 +101,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/account/view.js)_
_See code: [src/commands/account/view.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/account/view.js)_
## `eas analytics [STATUS]`
@@ -112,7 +112,7 @@ USAGE
$ eas analytics [STATUS]
```
_See code: [src/commands/analytics.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/analytics.js)_
_See code: [src/commands/analytics.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/analytics.js)_
## `eas build`
@@ -145,7 +145,7 @@ OPTIONS
--[no-]wait Wait for build(s) to complete
```
_See code: [src/commands/build/index.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/build/index.js)_
_See code: [src/commands/build/index.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/build/index.js)_
## `eas build:cancel [BUILD_ID]`
@@ -156,7 +156,7 @@ USAGE
$ eas build:cancel [BUILD_ID]
```
_See code: [src/commands/build/cancel.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/build/cancel.js)_
_See code: [src/commands/build/cancel.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/build/cancel.js)_
## `eas build:configure`
@@ -170,7 +170,7 @@ OPTIONS
-p, --platform=(android|ios|all) Platform to configure
```
_See code: [src/commands/build/configure.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/build/configure.js)_
_See code: [src/commands/build/configure.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/build/configure.js)_
## `eas build:list`
@@ -203,7 +203,7 @@ OPTIONS
--status=(new|in-queue|in-progress|errored|finished|canceled)
```
_See code: [src/commands/build/list.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/build/list.js)_
_See code: [src/commands/build/list.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/build/list.js)_
## `eas build:view [BUILD_ID]`
@@ -217,7 +217,7 @@ OPTIONS
--json Enable JSON output, non-JSON messages will be printed to stderr
```
_See code: [src/commands/build/view.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/build/view.js)_
_See code: [src/commands/build/view.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/build/view.js)_
## `eas config`
@@ -232,7 +232,7 @@ OPTIONS
--profile=profile
```
_See code: [src/commands/config.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/config.js)_
_See code: [src/commands/config.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/config.js)_
## `eas credentials`
@@ -243,7 +243,7 @@ USAGE
$ eas credentials
```
_See code: [src/commands/credentials.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/credentials.js)_
_See code: [src/commands/credentials.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/credentials.js)_
## `eas device:create`
@@ -254,7 +254,7 @@ USAGE
$ eas device:create
```
_See code: [src/commands/device/create.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/device/create.js)_
_See code: [src/commands/device/create.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/device/create.js)_
## `eas device:list`
@@ -268,7 +268,7 @@ OPTIONS
--apple-team-id=apple-team-id
```
_See code: [src/commands/device/list.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/device/list.js)_
_See code: [src/commands/device/list.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/device/list.js)_
## `eas device:view [UDID]`
@@ -279,7 +279,7 @@ USAGE
$ eas device:view [UDID]
```
_See code: [src/commands/device/view.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/device/view.js)_
_See code: [src/commands/device/view.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/device/view.js)_
## `eas diagnostics`
@@ -290,7 +290,7 @@ USAGE
$ eas diagnostics
```
_See code: [src/commands/diagnostics.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/diagnostics.js)_
_See code: [src/commands/diagnostics.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/diagnostics.js)_
## `eas help [COMMAND]`
@@ -318,7 +318,7 @@ USAGE
$ eas project:info
```
_See code: [src/commands/project/info.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/project/info.js)_
_See code: [src/commands/project/info.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/project/info.js)_
## `eas project:init`
@@ -332,7 +332,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/project/init.js)_
_See code: [src/commands/project/init.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/project/init.js)_
## `eas secret:create`
@@ -349,7 +349,7 @@ OPTIONS
--value=value Value of the secret
```
_See code: [src/commands/secret/create.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/secret/create.js)_
_See code: [src/commands/secret/create.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/secret/create.js)_
## `eas secret:delete`
@@ -366,7 +366,7 @@ DESCRIPTION
Unsure where to find the secret's ID? Run eas secrets:list
```
_See code: [src/commands/secret/delete.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/secret/delete.js)_
_See code: [src/commands/secret/delete.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/secret/delete.js)_
## `eas secret:list`
@@ -377,7 +377,7 @@ USAGE
$ eas secret:list
```
_See code: [src/commands/secret/list.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/secret/list.js)_
_See code: [src/commands/secret/list.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/secret/list.js)_
## `eas submit`
@@ -410,7 +410,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/submit.js)_
_See code: [src/commands/submit.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/submit.js)_
## `eas webhook:create`
@@ -429,7 +429,7 @@ OPTIONS
--url=url Webhook URL
```
_See code: [src/commands/webhook/create.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/webhook/create.js)_
_See code: [src/commands/webhook/create.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/webhook/create.js)_
## `eas webhook:delete [ID]`
@@ -443,7 +443,7 @@ ARGUMENTS
ID ID of the webhook to delete
```
_See code: [src/commands/webhook/delete.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/webhook/delete.js)_
_See code: [src/commands/webhook/delete.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/webhook/delete.js)_
## `eas webhook:list`
@@ -457,7 +457,7 @@ OPTIONS
--event=(BUILD) Event type that triggers the webhook
```
_See code: [src/commands/webhook/list.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/webhook/list.js)_
_See code: [src/commands/webhook/list.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/webhook/list.js)_
## `eas webhook:update`
@@ -477,7 +477,7 @@ OPTIONS
--url=url Webhook URL
```
_See code: [src/commands/webhook/update.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/webhook/update.js)_
_See code: [src/commands/webhook/update.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/webhook/update.js)_
## `eas webhook:view ID`
@@ -491,5 +491,5 @@ ARGUMENTS
ID ID of the webhook to view
```
_See code: [src/commands/webhook/view.js](https://github.com/expo/eas-cli/blob/v0.27.0/packages/eas-cli/src/commands/webhook/view.js)_
_See code: [src/commands/webhook/view.js](https://github.com/expo/eas-cli/blob/v0.28.2/packages/eas-cli/src/commands/webhook/view.js)_
<!-- commandsstop -->
+828 -2392
View File
File diff suppressed because it is too large. Load diff
+11 -12
View File
@@ -1,28 +1,28 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "0.27.0",
"version": "0.28.2",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@amplitude/identify": "1.7.0",
"@amplitude/node": "1.7.0",
"@expo/apple-utils": "0.0.0-alpha.25",
"@expo/config": "3.3.42",
"@expo/config": "5.0.9",
"@expo/config-plugins": "3.1.0",
"@expo/eas-build-job": "0.2.46",
"@expo/eas-json": "^0.27.0",
"@expo/json-file": "8.2.25",
"@expo/eas-json": "^0.28.2",
"@expo/json-file": "8.2.33",
"@expo/pkcs12": "0.0.4",
"@expo/plist": "0.0.14",
"@expo/plugin-warn-if-update-available": "1.7.0",
"@expo/prebuild-config": "2.1.0",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.0.7",
"@expo/sdk-runtime-versions": "1.0.0",
"@expo/spawn-async": "1.5.0",
"@expo/timeago.js": "1.0.0",
"@oclif/command": "1.8.0",
"@oclif/config": "1.17.0",
"@oclif/errors": "1.3.5",
@@ -40,6 +40,7 @@
"fs-extra": "10.0.0",
"getenv": "1.0.0",
"got": "11.8.2",
"gradle-to-js": "2.0.0",
"graphql": "15.5.1",
"graphql-tag": "2.12.5",
"ignore": "5.1.8",
@@ -47,7 +48,7 @@
"jks-js": "1.0.1",
"joi": "17.4.2",
"keychain": "1.3.0",
"lodash": "4.17.21",
"log-symbols": "4.1.0",
"mime": "2.5.2",
"minimatch": "3.0.4",
"nanoid": "3.1.25",
@@ -62,9 +63,7 @@
"semver": "7.3.5",
"strip-ansi": "6.0.0",
"tar": "6.1.11",
"tempy": "0.7.0",
"terminal-link": "2.1.1",
"timeago.js": "4.0.2",
"tslib": "2.3.1",
"turndown": "7.1.1",
"untildify": "4.0.0",
@@ -81,7 +80,6 @@
"@types/form-data": "^2.5.0",
"@types/fs-extra": "9.0.12",
"@types/getenv": "^1.0.0",
"@types/lodash": "4.14.172",
"@types/mime": "2.0.3",
"@types/node-fetch": "^2.5.7",
"@types/node-forge": "^0.9.5",
@@ -95,7 +93,9 @@
"eslint-plugin-graphql": "4.0.0",
"memfs": "3.2.2",
"mockdate": "^3.0.2",
"nock": "^13.0.5"
"nock": "^13.0.5",
"tempy": "0.7.0",
"ts-deepmerge": "1.0.8"
},
"engines": {
"node": ">=12.0.0"
@@ -120,7 +120,6 @@
"@oclif/plugin-help",
"@expo/plugin-warn-if-update-available"
],
"helpClass": "./build/help",
"topics": {
"account": {
"description": "manage your account"
@@ -1,7 +1,9 @@
export default jest.fn().mockReturnValue({
export const ora = jest.fn().mockReturnValue({
start: jest.fn().mockReturnValue({
fail: jest.fn(),
succeed: jest.fn(),
}),
stop: jest.fn(),
stopAndPersist: jest.fn(),
succeed: jest.fn(),
});
+35 -67
View File
@@ -1,5 +1,3 @@
import { Identify } from '@amplitude/identify';
import * as Amplitude from '@amplitude/node';
import RudderAnalytics from '@expo/rudder-sdk-node';
import os from 'os';
import { URL } from 'url';
@@ -20,40 +18,46 @@ const PLATFORM_TO_ANALYTICS_PLATFORM: { [platform: string]: string } = {
linux: 'Linux',
};
let amplitudeClient: Amplitude.NodeClient | null = null;
let rudderstackClient: RudderAnalytics | null = null;
let userIdentified = false;
let identifyData: {
userId: string;
deviceId: string;
identify: Identify;
traits: Record<string, any>;
} | null = null;
export async function initAsync(): Promise<void> {
if (process.env.DISABLE_EAS_ANALYTICS) {
await UserSettings.setAsync('amplitudeEnabled', false);
// TODO: remove after some time
const amplitudeEnabled = await UserSettings.getAsync('amplitudeEnabled', null);
if (amplitudeEnabled !== null) {
await UserSettings.setAsync('analyticsEnabled', amplitudeEnabled);
await UserSettings.deleteKeyAsync('amplitudeEnabled');
}
const amplitudeEnabled = await UserSettings.getAsync('amplitudeEnabled', true);
if (amplitudeEnabled) {
const amplitudeDeviceId = await UserSettings.getAsync('amplitudeDeviceId', null);
if (amplitudeDeviceId !== null) {
await UserSettings.setAsync('analyticsDeviceId', amplitudeDeviceId);
await UserSettings.deleteKeyAsync('amplitudeDeviceId');
}
// TODO: cut here
if (process.env.DISABLE_EAS_ANALYTICS) {
await UserSettings.setAsync('analyticsEnabled', false);
}
const analyticsEnabled = await UserSettings.getAsync('analyticsEnabled', true);
if (analyticsEnabled) {
const config =
process.env.EXPO_STAGING || process.env.EXPO_LOCAL
? {
// staging environment
amplitudeWriteKey: 'cdebbc678931403439486c4750781544',
rudderstackWriteKey: '1wpX20Da4ltFGSXbPFYUL00Chb7',
rudderstackDataPlaneURL: 'https://cdp.expo.dev',
}
: {
// prod environment
amplitudeWriteKey: '4ac443afd5073c0df6169291db1d3495',
rudderstackWriteKey: '1wpXLFxmujq86etH6G6cc90hPcC',
rudderstackDataPlaneURL: 'https://cdp.expo.dev',
};
amplitudeClient = Amplitude.init(config.amplitudeWriteKey, {
retryClass: new Amplitude.OfflineRetryHandler(config.amplitudeWriteKey),
});
rudderstackClient = new RudderAnalytics(
config.rudderstackWriteKey,
new URL('/v1/batch', config.rudderstackDataPlaneURL).toString(),
@@ -65,24 +69,17 @@ export async function initAsync(): Promise<void> {
}
}
export async function setUserDataAsync(
userId: string,
properties: Record<string, any>
): Promise<void> {
const identify = new Identify();
Object.entries(properties).forEach(([property, value]) => {
identify.set(property, value);
});
const savedDeviceId = await UserSettings.getAsync('amplitudeDeviceId', null);
export async function setUserDataAsync(userId: string, traits: Record<string, any>): Promise<void> {
const savedDeviceId = await UserSettings.getAsync('analyticsDeviceId', null);
const deviceId = savedDeviceId ?? uuidv4();
if (!savedDeviceId) {
await UserSettings.setAsync('amplitudeDeviceId', deviceId);
await UserSettings.setAsync('analyticsDeviceId', deviceId);
}
identifyData = {
userId,
deviceId,
identify,
traits,
};
ensureUserIdentified();
@@ -92,14 +89,10 @@ export async function flushAsync(): Promise<void> {
if (rudderstackClient) {
rudderstackClient.flush();
}
if (amplitudeClient) {
await amplitudeClient.flush();
}
}
export function logEvent(name: string, properties: Record<string, any> = {}) {
if (!amplitudeClient && !rudderstackClient) {
export function logEvent(name: string, properties: Record<string, any> = {}): void {
if (!rudderstackClient) {
return;
}
ensureUserIdentified();
@@ -107,56 +100,31 @@ export function logEvent(name: string, properties: Record<string, any> = {}) {
const { userId, deviceId } = identifyData ?? {};
const commonEventProperties = { source_version: packageJSON?.version, source: 'eas cli' };
if (amplitudeClient) {
amplitudeClient.logEvent({
event_type: name,
event_properties: { ...properties, ...commonEventProperties },
...(userId && { user_id: userId }),
...(deviceId && { device_id: deviceId }),
...getAmplitudeContext(),
});
}
if (rudderstackClient) {
const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() };
rudderstackClient.track({
event: name,
properties: { ...properties, ...commonEventProperties },
...identity,
context: getRudderStackContext(),
});
}
const identity = { userId: userId ?? undefined, anonymousId: deviceId ?? uuidv4() };
rudderstackClient.track({
event: name,
properties: { ...properties, ...commonEventProperties },
...identity,
context: getRudderStackContext(),
});
}
function ensureUserIdentified() {
if (!(rudderstackClient || amplitudeClient) || userIdentified || !identifyData) {
function ensureUserIdentified(): void {
if (!rudderstackClient || userIdentified || !identifyData) {
return;
}
if (amplitudeClient) {
amplitudeClient.identify(identifyData.userId, identifyData.deviceId, identifyData.identify);
}
if (rudderstackClient) {
rudderstackClient.identify({
userId: identifyData.userId,
anonymousId: identifyData.deviceId,
traits: identifyData.traits,
});
}
userIdentified = true;
}
function getAmplitudeContext() {
const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform();
return {
os_name: platform,
os_version: os.release(),
device_brand: platform,
device_model: platform,
};
}
function getRudderStackContext() {
function getRudderStackContext(): Record<string, any> {
const platform = PLATFORM_TO_ANALYTICS_PLATFORM[os.platform()] || os.platform();
return {
os: { name: platform, version: os.release() },
@@ -1,9 +1,11 @@
import { ExpoConfig } from '@expo/config';
import { Platform } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { readVersionCodeAsync, readVersionNameAsync } from '../version';
import { maybeResolveVersionsAsync } from '../version';
jest.mock('fs');
@@ -20,36 +22,29 @@ beforeEach(() => {
fs.mkdirpSync(os.tmpdir());
});
describe(readVersionCodeAsync, () => {
describe(maybeResolveVersionsAsync, () => {
describe('generic project', () => {
it('reads the version code from native code', async () => {
const exp = initGenericProject();
const versionCode = await readVersionCodeAsync('/repo', exp);
expect(versionCode).toBe(123);
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/repo',
exp,
{} as BuildProfile<Platform.ANDROID>
);
expect(appVersion).toBe('2.0');
expect(appBuildVersion).toBe('123');
});
});
describe('managed project', () => {
it('reads the version code from expo config', async () => {
const exp = initManagedProject();
const versionCode = await readVersionCodeAsync('/repo', exp);
expect(versionCode).toBe(123);
});
});
});
describe(readVersionNameAsync, () => {
describe('generic project', () => {
it('reads the version name from native code', async () => {
const exp = initGenericProject();
const versionName = await readVersionNameAsync('/repo', exp);
expect(versionName).toBe('1.0');
});
});
describe('managed project', () => {
it('reads the version from expo config', async () => {
const exp = initManagedProject();
const versionName = await readVersionNameAsync('/repo', exp);
expect(versionName).toBe('1.0.0');
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/repo',
exp,
{} as BuildProfile<Platform.ANDROID>
);
expect(appVersion).toBe('5.0.0');
expect(appBuildVersion).toBe('126');
});
});
});
@@ -69,7 +64,7 @@ function initGenericProject(): ExpoConfig {
defaultConfig {
applicationId "com.expo.testapp"
versionCode 123
versionName "1.0"
versionName "2.0"
}
}`,
'./android/app/src/main/AndroidManifest.xml': 'fake',
@@ -80,9 +75,9 @@ function initGenericProject(): ExpoConfig {
const fakeExp: ExpoConfig = {
name: 'myproject',
slug: 'myproject',
version: '1.0.0',
version: '3.0.0',
android: {
versionCode: 123,
versionCode: 124,
},
};
return fakeExp;
@@ -93,9 +88,9 @@ function initManagedProject(): ExpoConfig {
{
'./app.json': JSON.stringify({
expo: {
version: '1.0.0',
version: '4.0.0',
android: {
versionCode: 123,
versionCode: 125,
},
},
}),
@@ -106,9 +101,9 @@ function initManagedProject(): ExpoConfig {
const fakeExp: ExpoConfig = {
name: 'myproject',
slug: 'myproject',
version: '1.0.0',
version: '5.0.0',
android: {
versionCode: 123,
versionCode: 126,
},
};
return fakeExp;
+24 -6
View File
@@ -1,4 +1,4 @@
import { Android, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
@@ -12,11 +12,13 @@ import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
} from '../../project/android/applicationId';
import { GradleBuildContext, resolveGradleBuildContextAsync } from '../../project/android/gradle';
import { toggleConfirmAsync } from '../../prompts';
import { findAccountByName } from '../../user/Account';
import {
BuildRequestSender,
CredentialsResult,
JobData,
prepareBuildRequestForPlatformAsync,
} from '../build';
import { BuildContext } from '../context';
@@ -51,20 +53,31 @@ This means that it will most likely produce an AAB and you will not be able to i
checkNodeEnvVariable(ctx);
await checkGoogleServicesFileAsync(ctx);
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
if (ctx.workflow === Workflow.MANAGED) {
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp);
}
return await prepareBuildRequestForPlatformAsync({
ctx,
ensureCredentialsAsync: ensureAndroidCredentialsAsync,
ensureCredentialsAsync: async (ctx: BuildContext<Platform.ANDROID>) => {
return await ensureAndroidCredentialsAsync(ctx, gradleContext);
},
ensureProjectConfiguredAsync: async () => {
if (ctx.workflow === Workflow.GENERIC) {
await validateAndSyncProjectConfigurationAsync(ctx.projectDir, ctx.exp);
}
},
getMetadataContext: () => ({}),
prepareJobAsync,
getMetadataContext: () => ({
gradleContext,
}),
prepareJobAsync: async (
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData<AndroidCredentials>
): Promise<Job> => {
return await prepareJobAsync(ctx, jobData);
},
sendBuildRequestAsync: async (
appId: string,
job: Android.Job,
@@ -86,12 +99,17 @@ function shouldProvideCredentials(ctx: BuildContext<Platform.ANDROID>): boolean
}
async function ensureAndroidCredentialsAsync(
ctx: BuildContext<Platform.ANDROID>
ctx: BuildContext<Platform.ANDROID>,
gradleContext?: GradleBuildContext
): Promise<CredentialsResult<AndroidCredentials> | undefined> {
if (!shouldProvideCredentials(ctx)) {
return;
}
const androidApplicationIdentifier = await getApplicationIdAsync(ctx.projectDir, ctx.exp);
const androidApplicationIdentifier = await getApplicationIdAsync(
ctx.projectDir,
ctx.exp,
gradleContext
);
const provider = new AndroidCredentialsProvider(
await createCredentialsContextAsync(ctx.projectDir, {
exp: ctx.exp,
+28 -37
View File
@@ -1,50 +1,41 @@
import { ExpoConfig } from '@expo/config';
import { AndroidConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import fs from 'fs-extra';
import { BuildProfile } from '@expo/eas-json';
import {
getAppBuildGradleAsync,
parseGradleCommand,
resolveConfigValue,
} from '../../project/android/gradleUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
export async function readVersionCodeAsync(
export async function maybeResolveVersionsAsync(
projectDir: string,
exp: ExpoConfig
): Promise<number | undefined> {
exp: ExpoConfig,
buildProfile: BuildProfile<Platform.ANDROID>
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
if (workflow === Workflow.GENERIC) {
const buildGradle = readBuildGradle(projectDir);
const matchResult = buildGradle?.match(/versionCode (.*)/);
if (matchResult) {
return Number(matchResult[1]);
} else {
return undefined;
const buildGradle = await getAppBuildGradleAsync(projectDir);
try {
const parsedGradleCommand = buildProfile.gradleCommand
? parseGradleCommand(buildProfile.gradleCommand, buildGradle)
: undefined;
return {
appVersion:
resolveConfigValue(buildGradle, 'versionName', parsedGradleCommand?.flavor) ?? '1.0.0',
appBuildVersion:
resolveConfigValue(buildGradle, 'versionCode', parsedGradleCommand?.flavor) ?? '1',
};
} catch (err: any) {
return {};
}
} else {
return AndroidConfig.Version.getVersionCode(exp) ?? 1;
return {
appBuildVersion: String(AndroidConfig.Version.getVersionCode(exp)),
appVersion: exp.version,
};
}
}
export async function readVersionNameAsync(
projectDir: string,
exp: ExpoConfig
): Promise<string | undefined> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
if (workflow === Workflow.GENERIC) {
const buildGradle = readBuildGradle(projectDir);
const matchResult = buildGradle?.match(/versionName ["'](.*)["']/);
if (matchResult) {
return matchResult[1];
} else {
return undefined;
}
} else {
return exp.version;
}
}
function readBuildGradle(projectDir: string): string | undefined {
const buildGradlePath = AndroidConfig.Paths.getAppBuildGradleFilePath(projectDir);
if (!fs.pathExistsSync(buildGradlePath)) {
return undefined;
}
return fs.readFileSync(buildGradlePath, 'utf8');
}
+42 -89
View File
@@ -2,27 +2,25 @@ import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo
import { CredentialsSource } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import nullthrows from 'nullthrows';
import ora from 'ora';
import { BuildFragment, BuildStatus, UploadSessionType } from '../graphql/generated';
import { BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import Log, { learnMore } from '../log';
import { ora } from '../ora';
import { requestedPlatformDisplayNames } from '../platform';
import { promptAsync } from '../prompts';
import { uploadAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { createProgressTracker } from '../utils/progress';
import { sleep } from '../utils/promise';
import { sleepAsync } from '../utils/promise';
import vcs from '../vcs';
import { BuildContext } from './context';
import { runLocalBuildAsync } from './local';
import { MetadataContext, collectMetadata } from './metadata';
import { MetadataContext, collectMetadataAsync } from './metadata';
import { TrackingContext } from './types';
import Analytics, { Event } from './utils/analytics';
import { printDeprecationWarnings } from './utils/printBuildInfo';
import { commitPromptAsync, makeProjectTarballAsync } from './utils/repository';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
export interface CredentialsResult<Credentials> {
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
@@ -89,7 +87,7 @@ export async function prepareBuildRequestForPlatformAsync<
} as const);
const metadataContext = builder.getMetadataContext();
const metadata = await collectMetadata(ctx, metadataContext);
const metadata = await collectMetadataAsync(ctx, metadataContext);
const job = await builder.prepareJobAsync(ctx, {
projectArchive,
credentials: credentialsResult?.credentials,
@@ -230,58 +228,6 @@ async function withAnalyticsAsync<Result>(
}
}
enum ShouldCommitChanges {
Yes,
ShowDiffFirst,
Abort,
}
async function reviewAndCommitChangesAsync(
initialCommitMessage: string,
{ nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean }
): Promise<void> {
if (process.env.EAS_BUILD_AUTOCOMMIT) {
await vcs.commitAsync({ commitMessage: initialCommitMessage, commitAllFiles: false });
Log.withTick('Committed changes.');
return;
}
if (nonInteractive) {
throw new Error(
'Cannot commit changes when --non-interactive is specified. Run the command in interactive mode or set EAS_BUILD_AUTOCOMMIT=1 in your environment.'
);
}
const { selected } = await promptAsync({
type: 'select',
name: 'selected',
message: 'Can we commit these changes to git for you?',
choices: [
{ title: 'Yes', value: ShouldCommitChanges.Yes },
...(askedFirstTime
? [{ title: 'Show the diff and ask me again', value: ShouldCommitChanges.ShowDiffFirst }]
: []),
{
title: 'Abort build process',
value: ShouldCommitChanges.Abort,
},
],
});
if (selected === ShouldCommitChanges.Abort) {
throw new Error(
"Aborting, run the command again once you're ready. Make sure to commit any changes you've made."
);
} else if (selected === ShouldCommitChanges.Yes) {
await commitPromptAsync({ initialCommitMessage });
Log.withTick('Committed changes.');
} else if (selected === ShouldCommitChanges.ShowDiffFirst) {
await vcs.showDiffAsync();
await reviewAndCommitChangesAsync(initialCommitMessage, {
nonInteractive,
askedFirstTime: false,
});
}
}
export async function waitForBuildEndAsync(
buildIds: string[],
{ timeoutSec = 3600, intervalSec = 30 } = {}
@@ -298,39 +244,46 @@ export async function waitForBuildEndAsync(
try {
return await BuildQuery.byIdAsync(buildId, { useCache: false });
} catch (err) {
Log.debug('Failed to fetch the build status', err);
return null;
}
})
);
if (builds.length === 1) {
const build = nullthrows(builds[0]);
switch (build.status) {
case BuildStatus.Finished:
spinner.succeed('Build finished');
return builds;
case BuildStatus.New:
spinner.text = 'Build created';
break;
case BuildStatus.InQueue:
spinner.text = 'Build queued...';
break;
case BuildStatus.Canceled:
spinner.text = 'Build canceled';
spinner.stopAndPersist();
return builds;
case BuildStatus.InProgress:
spinner.text = 'Build in progress...';
break;
case BuildStatus.Errored:
spinner.fail('Build failed');
if (build.error) {
const [build] = builds;
if (build !== null) {
switch (build.status) {
case BuildStatus.Finished:
spinner.succeed('Build finished');
return builds;
} else {
throw new Error(`Standalone build failed!`);
}
default:
spinner.warn('Unknown status.');
throw new Error(`Unknown status: ${builds} - aborting!`);
case BuildStatus.New:
spinner.text = 'Build created';
break;
case BuildStatus.InQueue:
spinner.text = 'Build queued...';
break;
case BuildStatus.Canceled:
spinner.text = 'Build canceled';
spinner.stopAndPersist();
return builds;
case BuildStatus.InProgress:
spinner.text = 'Build in progress...';
break;
case BuildStatus.Errored:
spinner.fail('Build failed');
if (build.error) {
return builds;
} else {
throw new Error(`Standalone build failed!`);
}
default:
spinner.warn('Unknown status');
throw new Error(`Unknown build status: ${build.status} - aborting!`);
}
} else {
if (!spinner.text) {
spinner.text = 'Could not fetch the build status. Check your network connection.';
}
}
} else {
if (builds.filter(build => build?.status === BuildStatus.Finished).length === builds.length) {
@@ -354,7 +307,7 @@ export async function waitForBuildEndAsync(
const errored = builds.filter(build => build?.status === BuildStatus.Errored).length;
const finished = builds.filter(build => build?.status === BuildStatus.Finished).length;
const canceled = builds.filter(build => build?.status === BuildStatus.Canceled).length;
const unknownState = builds.length - newBuilds - inQueue - inProgress - errored - finished;
const unknown = builds.length - newBuilds - inQueue - inProgress - errored - finished;
spinner.text = [
newBuilds && `Builds created: ${newBuilds}`,
inQueue && `Builds in queue: ${inQueue}`,
@@ -362,14 +315,14 @@ export async function waitForBuildEndAsync(
canceled && `Builds canceled: ${canceled}`,
errored && chalk.red(`Builds failed: ${errored}`),
finished && chalk.green(`Builds finished: ${finished}`),
unknownState && chalk.red(`Builds in unknown state: ${unknownState}`),
unknown && chalk.red(`Builds with unknown status: ${unknown}`),
]
.filter(i => i)
.join('\t');
}
}
time = new Date().getTime();
await sleep(intervalSec * 1000);
await sleepAsync(intervalSec * 1000);
}
spinner.warn('Timed out');
throw new Error(
@@ -9,6 +9,7 @@ import {
BumpStrategy,
bumpVersionAsync,
bumpVersionInAppJsonAsync,
evaluateTemplateString,
readBuildNumberAsync,
readShortVersionAsync,
} from '../version';
@@ -28,6 +29,12 @@ beforeEach(() => {
fs.mkdirpSync(os.tmpdir());
});
describe(evaluateTemplateString, () => {
it('evaluates the template string', () => {
expect(evaluateTemplateString('$(BLAH_BLAH)', { BLAH_BLAH: 123 })).toBe('123');
});
});
// generic workflow
describe(bumpVersionAsync, () => {
it('bumps expo.ios.buildNumber and CFBundleVersion when strategy = BumpStrategy.BUILD_NUMBER', async () => {
+5 -1
View File
@@ -67,7 +67,11 @@ export async function prepareIosBuildAsync(
buildConfiguration: applicationTarget.buildConfiguration,
});
const buildSettings = xcBuildConfiguration?.buildSettings ?? {};
return { buildSettings };
return {
buildSettings,
targetName: applicationTarget.targetName,
buildConfiguration: applicationTarget.buildConfiguration,
};
},
prepareJobAsync: async (
ctx: BuildContext<Platform.IOS>,
+23 -3
View File
@@ -10,7 +10,6 @@ import Log from '../../log';
import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { evaluateString } from '../utils/template';
import { readPlistAsync, writePlistAsync } from './plist';
export enum BumpStrategy {
@@ -114,7 +113,7 @@ export async function readShortVersionAsync(
const infoPlist = await readInfoPlistAsync(projectDir);
return (
infoPlist.CFBundleShortVersionString &&
evaluateString(infoPlist.CFBundleShortVersionString, buildSettings)
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings)
);
} else {
return exp.version;
@@ -129,12 +128,29 @@ export async function readBuildNumberAsync(
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
const infoPlist = await readInfoPlistAsync(projectDir);
return infoPlist.CFBundleVersion && evaluateString(infoPlist.CFBundleVersion, buildSettings);
return (
infoPlist.CFBundleVersion && evaluateTemplateString(infoPlist.CFBundleVersion, buildSettings)
);
} else {
return IOSConfig.Version.getBuildNumber(exp);
}
}
export async function maybeResolveVersionsAsync(
projectDir: string,
exp: ExpoConfig,
buildSettings: XCBuildConfiguration['buildSettings']
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
try {
return {
appBuildVersion: await readBuildNumberAsync(projectDir, exp, buildSettings),
appVersion: await readShortVersionAsync(projectDir, exp, buildSettings),
};
} catch {
return {};
}
}
async function writeVersionsToInfoPlistAsync({
projectDir,
exp,
@@ -172,3 +188,7 @@ function ensureStaticConfigExists(projectDir: string): void {
throw new Error('autoIncrement option is not supported when using app.config.js');
}
}
export function evaluateTemplateString(s: string, vars: Record<string, any>): string {
return s.replace(/\$\((\w+)\)/g, (match, key) => (vars.hasOwnProperty(key) ? vars[key] : match));
}
+2 -2
View File
@@ -11,7 +11,7 @@ const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
export async function runLocalBuildAsync(job: Job): Promise<void> {
const execNameOrPath =
process.env.EAS_LOCAL_BUILD_PLUGIN_PATH ??
(await findWithNodeResolution()) ??
(await findWithNodeResolutionAsync()) ??
PLUGIN_PACKAGE_NAME;
try {
const arg = Buffer.from(JSON.stringify({ job })).toString('base64');
@@ -31,7 +31,7 @@ export async function runLocalBuildAsync(job: Job): Promise<void> {
}
}
async function findWithNodeResolution(): Promise<string | undefined> {
async function findWithNodeResolutionAsync(): Promise<string | undefined> {
try {
const resolvedPath = path.resolve(
path.dirname(require.resolve(PLUGIN_PACKAGE_NAME)),
+35 -30
View File
@@ -3,6 +3,7 @@ import { IosEnterpriseProvisioning } from '@expo/eas-json';
import type { XCBuildConfiguration } from 'xcode';
import { getApplicationIdAsync } from '../project/android/applicationId';
import { GradleBuildContext } from '../project/android/gradle';
import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier';
import { getUsername } from '../project/projectUtils';
import { ensureLoggedInAsync } from '../user/actions';
@@ -11,13 +12,13 @@ import {
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
} from './android/UpdatesModule';
import { readVersionCodeAsync, readVersionNameAsync } from './android/version';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import {
readChannelSafelyAsync as readIosChannelSafelyAsync,
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
} from './ios/UpdatesModule';
import { readBuildNumberAsync, readShortVersionAsync } from './ios/version';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
import { isExpoUpdatesInstalled } from './utils/updates';
/**
@@ -28,16 +29,21 @@ import { isExpoUpdatesInstalled } from './utils/updates';
const packageJSON = require('../../package.json');
export type MetadataContext<T extends Platform> = T extends Platform.ANDROID
? object
? AndroidMetadataContext
: IosMetadataContext;
export interface AndroidMetadataContext {
gradleContext?: GradleBuildContext;
}
export interface IosMetadataContext {
buildSettings: XCBuildConfiguration['buildSettings'];
targetName?: string;
buildConfiguration?: string;
}
export async function collectMetadata<T extends Platform>(
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>,
platformContext?: MetadataContext<T>
platformContext: MetadataContext<T>
): Promise<Metadata> {
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
@@ -46,8 +52,7 @@ export async function collectMetadata<T extends Platform>(
: ctx.buildProfile.distribution) ?? 'store';
const metadata = {
trackingContext: ctx.trackingCtx,
appVersion: await resolveAppVersionAsync(ctx, platformContext),
appBuildVersion: await resolveAppBuildVersionAsync(ctx, platformContext),
...(await maybeResolveVersionsAsync(ctx, platformContext)),
cliVersion: packageJSON.version,
workflow: ctx.workflow,
credentialsSource: ctx.buildProfile.credentialsSource,
@@ -56,7 +61,7 @@ export async function collectMetadata<T extends Platform>(
...channelOrReleaseChannel,
distribution,
appName: ctx.exp.name,
appIdentifier: await resolveAppIdentifierAsync(ctx),
appIdentifier: await resolveAppIdentifierAsync(ctx, platformContext),
buildProfile: ctx.buildProfileName,
gitCommitHash: await vcs.getCommitHashAsync(),
username: getUsername(ctx.exp, await ensureLoggedInAsync()),
@@ -69,38 +74,38 @@ export async function collectMetadata<T extends Platform>(
return sanitizeMetadata(metadata);
}
async function resolveAppVersionAsync<T extends Platform>(
async function maybeResolveVersionsAsync<T extends Platform>(
ctx: BuildContext<T>,
platformContext?: MetadataContext<T>
): Promise<string | undefined> {
platformContext: MetadataContext<T>
): Promise<{ appBuildVersion?: string; appVersion?: string }> {
if (ctx.platform === Platform.IOS) {
const iosContext = platformContext as IosMetadataContext | undefined;
return await readShortVersionAsync(ctx.projectDir, ctx.exp, iosContext?.buildSettings ?? {});
const iosContext = platformContext as IosMetadataContext;
return await maybeResolveIosVersionsAsync(
ctx.projectDir,
ctx.exp,
iosContext?.buildSettings ?? {}
);
} else if (ctx.platform === Platform.ANDROID) {
const androidCtx = ctx as BuildContext<Platform.ANDROID>;
return await maybeResolveAndroidVersionsAsync(ctx.projectDir, ctx.exp, androidCtx.buildProfile);
} else {
return await readVersionNameAsync(ctx.projectDir, ctx.exp);
}
}
async function resolveAppBuildVersionAsync<T extends Platform>(
ctx: BuildContext<T>,
platformContext?: MetadataContext<T>
): Promise<string | undefined> {
if (ctx.platform === Platform.IOS) {
const iosContext = platformContext as IosMetadataContext | undefined;
return await readBuildNumberAsync(ctx.projectDir, ctx.exp, iosContext?.buildSettings ?? {});
} else {
const versionCode = await readVersionCodeAsync(ctx.projectDir, ctx.exp);
return versionCode !== undefined ? String(versionCode) : undefined;
throw new Error(`Unsupported platform ${ctx.platform}`);
}
}
async function resolveAppIdentifierAsync<T extends Platform>(
ctx: BuildContext<T>
ctx: BuildContext<T>,
platformContext: MetadataContext<T>
): Promise<string> {
if (ctx.platform === Platform.IOS) {
return await getBundleIdentifierAsync(ctx.projectDir, ctx.exp);
const iosContext = platformContext as IosMetadataContext;
return await getBundleIdentifierAsync(ctx.projectDir, ctx.exp, {
targetName: iosContext.targetName,
buildConfiguration: iosContext.buildConfiguration,
});
} else {
return await getApplicationIdAsync(ctx.projectDir, ctx.exp);
const androidContext = platformContext as AndroidMetadataContext;
return await getApplicationIdAsync(ctx.projectDir, ctx.exp, androidContext.gradleContext);
}
}
@@ -5,7 +5,10 @@ import chalk from 'chalk';
import Log from '../../log';
import { requestedPlatformDisplayNames } from '../../platform';
export function logCredentialsSource(credentialsSource: CredentialsSource, platform: Platform) {
export function logCredentialsSource(
credentialsSource: CredentialsSource,
platform: Platform
): void {
let message = `Using ${credentialsSource} ${requestedPlatformDisplayNames[platform]} credentials`;
if (credentialsSource === CredentialsSource.LOCAL) {
message += ` ${chalk.dim('(credentials.json)')}`;
@@ -0,0 +1,116 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { EasJsonReader } from '@expo/eas-json';
import { error } from '@oclif/errors';
import chalk from 'chalk';
import resolveFrom from 'resolve-from';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames } from '../../platform';
import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCommand';
import zipObject from '../../utils/expodash/zipObject';
import { reviewAndCommitChangesAsync } from './repository';
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
projectDir,
platforms,
profile,
nonInteractive = false,
}: {
projectDir: string;
platforms: Platform[];
profile: string;
nonInteractive?: boolean;
}): Promise<void> {
if (await isExpoDevClientInstalledAsync(projectDir)) {
return;
}
const easJsonReader = new EasJsonReader(projectDir);
const devClientPerPlatformList = await Promise.all(
platforms.map(async platform => {
const buildProfile = await easJsonReader.readBuildProfileAsync(platform, profile);
return buildProfile.developmentClient ?? false;
})
);
const isDevClientRequired = devClientPerPlatformList.some(i => i);
if (!isDevClientRequired) {
return;
}
const devClientPerPlatform = zipObject(platforms, devClientPerPlatformList);
const platformsToCheck = platforms.filter(platform => devClientPerPlatform[platform]);
const workflowPerPlatformList = await Promise.all(
platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform))
);
Log.newLine();
Log.error(
`You want to build a development client build for platforms: ${platformsToCheck
.map(i => chalk.bold(appPlatformDisplayNames[toAppPlatform(i)]))
.join(', ')}`
);
Log.error(
`However, we detected that you don't have ${chalk.bold(
'expo-dev-client'
)} installed for your project.`
);
const areAllManaged = workflowPerPlatformList.every(i => i === Workflow.MANAGED);
if (areAllManaged) {
const install = await confirmAsync({
message: 'Do you want EAS CLI to install expo-dev-client for you?',
instructions: 'The command will abort unless you agree.',
});
if (install) {
await installExpoDevClientAsync(projectDir, { nonInteractive });
} else {
error(`Install ${chalk.bold('expo-dev-client')} on your own and come back later.`, {
exit: 1,
});
}
} else {
Log.warn(`Unfortunately, you need to install ${chalk.bold('expo-dev-client')} on your own.`);
Log.warn(
learnMore('https://docs.expo.dev/clients/installation/', {
learnMoreMessage: 'See installation instructions on how to do it',
dim: false,
})
);
Log.warn('If you proceed anyway, you might not get the build you want.');
Log.newLine();
const shouldContinue = await confirmAsync({
message: 'Do you want to proceed anyway?',
initial: false,
});
if (!shouldContinue) {
error('Come back later', { exit: 1 });
}
}
}
async function isExpoDevClientInstalledAsync(projectDir: string): Promise<boolean> {
try {
resolveFrom(projectDir, 'expo-dev-client/package.json');
return true;
} catch (err: any) {
Log.debug(err);
return false;
}
}
async function installExpoDevClientAsync(
projectDir: string,
{ nonInteractive }: { nonInteractive: boolean }
): Promise<void> {
Log.newLine();
Log.log(`Running ${chalk.bold('expo install expo-dev-client')}`);
Log.newLine();
await expoCommandAsync(projectDir, ['install', 'expo-dev-client']);
Log.newLine();
await reviewAndCommitChangesAsync('Install expo-dev-client', {
nonInteractive,
});
}
@@ -9,7 +9,7 @@ import { appPlatformDisplayNames } from '../../platform';
import formatFields from '../../utils/formatFields';
import { getBuildLogsUrl } from './url';
export function formatGraphQLBuild(build: BuildFragment) {
export function formatGraphQLBuild(build: BuildFragment): string {
const actor = getActorName(build);
const fields: { label: string; value?: string | null }[] = [
{ label: 'ID', value: build.id },
+53 -1
View File
@@ -1,11 +1,11 @@
import chalk from 'chalk';
import fs from 'fs-extra';
import ora from 'ora';
import path from 'path';
import tar from 'tar';
import { v4 as uuidv4 } from 'uuid';
import Log from '../../log';
import { ora } from '../../ora';
import { confirmAsync, promptAsync } from '../../prompts';
import { formatBytes } from '../../utils/files';
import { getTmpDirectory } from '../../utils/paths';
@@ -121,3 +121,55 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n
return { size, path: tarPath };
}
enum ShouldCommitChanges {
Yes,
ShowDiffFirst,
Abort,
}
export async function reviewAndCommitChangesAsync(
initialCommitMessage: string,
{ nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean }
): Promise<void> {
if (process.env.EAS_BUILD_AUTOCOMMIT) {
await vcs.commitAsync({ commitMessage: initialCommitMessage, commitAllFiles: false });
Log.withTick('Committed changes.');
return;
}
if (nonInteractive) {
throw new Error(
'Cannot commit changes when --non-interactive is specified. Run the command in interactive mode or set EAS_BUILD_AUTOCOMMIT=1 in your environment.'
);
}
const { selected } = await promptAsync({
type: 'select',
name: 'selected',
message: 'Can we commit these changes to git for you?',
choices: [
{ title: 'Yes', value: ShouldCommitChanges.Yes },
...(askedFirstTime
? [{ title: 'Show the diff and ask me again', value: ShouldCommitChanges.ShowDiffFirst }]
: []),
{
title: 'Abort build process',
value: ShouldCommitChanges.Abort,
},
],
});
if (selected === ShouldCommitChanges.Abort) {
throw new Error(
"Aborting, run the command again once you're ready. Make sure to commit any changes you've made."
);
} else if (selected === ShouldCommitChanges.Yes) {
await commitPromptAsync({ initialCommitMessage });
Log.withTick('Committed changes.');
} else if (selected === ShouldCommitChanges.ShowDiffFirst) {
await vcs.showDiffAsync();
await reviewAndCommitChangesAsync(initialCommitMessage, {
nonInteractive,
askedFirstTime: false,
});
}
}
@@ -1,5 +0,0 @@
import template from 'lodash/template';
export function evaluateString(s: string, vars: Record<string, any>): string {
return template(s, { interpolate: /\$\(([\s\S]+?)\)/g })(vars);
}
+1 -1
View File
@@ -1,6 +1,6 @@
import { ExpoConfig, getPackageJson } from '@expo/config';
export function isExpoUpdatesInstalled(projectDir: string) {
export function isExpoUpdatesInstalled(projectDir: string): boolean {
const packageJson = getPackageJson(projectDir);
return packageJson.dependencies && 'expo-updates' in packageJson.dependencies;
}
@@ -16,11 +16,16 @@ export default abstract class EasCommand extends Command {
*/
protected requiresAuthentication = true;
async init() {
protected abstract runAsync(): Promise<any>;
// eslint-disable-next-line async-protect/async-suffix
async run(): Promise<any> {
await initAnalyticsAsync();
if (this.requiresAuthentication) {
await ensureLoggedInAsync();
const { flags } = this.parse();
const nonInteractive = (flags as any)['non-interactive'] ?? false;
await ensureLoggedInAsync({ nonInteractive });
} else {
await getUserAsync();
}
@@ -29,9 +34,11 @@ export default abstract class EasCommand extends Command {
// commands/submit === submit, commands/build/list === build:list
action: `eas ${this.id}`,
});
return this.runAsync();
}
async finally(err: Error) {
// eslint-disable-next-line async-protect/async-suffix
async finally(err: Error): Promise<any> {
await flushAnalyticsAsync();
return super.finally(err);
}
@@ -19,6 +19,17 @@ jest.mock('../../analytics', () => {
};
});
let originalProcessArgv: string[];
beforeAll(() => {
originalProcessArgv = process.argv;
process.argv = [];
});
afterAll(() => {
process.argv = originalProcessArgv;
});
beforeEach(() => {
(getUserAsync as jest.Mock).mockReset().mockImplementation(() => mockJester);
(ensureLoggedInAsync as jest.Mock).mockReset().mockImplementation(() => mockJester);
@@ -3,11 +3,11 @@ import EasCommand from '../EasCommand';
export default class TestEasCommand extends EasCommand {
requiresAuthentication = this.authValue();
authValue() {
authValue(): boolean {
return false;
}
async run() {}
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,15 +1,14 @@
import { Command } from '@oclif/command';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { showLoginPromptAsync } from '../../user/actions';
export default class AccountLogin extends Command {
export default class AccountLogin extends EasCommand {
static description = 'log in with your Expo account';
static aliases = ['login'];
async run() {
Log.log('Log in to EAS');
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
await showLoginPromptAsync();
Log.log('Logged in');
}
@@ -1,14 +1,14 @@
import { Command } from '@oclif/command';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { logoutAsync } from '../../user/User';
export default class AccountLogout extends Command {
export default class AccountLogout extends EasCommand {
static description = 'log out';
static aliases = ['logout'];
async run() {
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
await logoutAsync();
Log.log('Logged out');
}
@@ -1,15 +1,16 @@
import { Command } from '@oclif/command';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { getActorDisplayName, getUserAsync } from '../../user/User';
export default class AccountView extends Command {
export default class AccountView extends EasCommand {
static description = 'show the username you are logged in as';
static aliases = ['whoami'];
async run() {
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const user = await getUserAsync();
if (user) {
Log.log(chalk.green(getActorDisplayName(user)));
+8 -7
View File
@@ -1,23 +1,24 @@
import { Command } from '@oclif/command';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import UserSettings from '../user/UserSettings';
export default class AnalyticsView extends Command {
export default class AnalyticsView extends EasCommand {
static description = 'view or change analytics settings';
static args = [{ name: 'STATUS', options: ['on', 'off'] }];
async run() {
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const { STATUS: status } = this.parse(AnalyticsView).args;
if (status) {
await UserSettings.setAsync('amplitudeEnabled', status === 'on');
await UserSettings.setAsync('analyticsEnabled', status === 'on');
Log.withTick(`${status === 'on' ? 'Enabling' : 'Disabling'} analytics.`);
} else {
const amplitudeEnabled = await UserSettings.getAsync('amplitudeEnabled', null);
const analyticsEnabled = await UserSettings.getAsync('analyticsEnabled', null);
Log.log(
`Analytics are ${
amplitudeEnabled === false ? 'disabled' : 'enabled'
analyticsEnabled === false ? 'disabled' : 'enabled'
} on this eas-cli installation.`
);
}
@@ -1,8 +1,9 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
CreateUpdateBranchForAppMutation,
@@ -49,7 +50,7 @@ export async function createUpdateBranchOnAppAsync({
return newBranch;
}
export default class BranchCreate extends Command {
export default class BranchCreate extends EasCommand {
static hidden = true;
static description = 'Create a branch on the current project.';
@@ -68,7 +69,7 @@ export default class BranchCreate extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name },
flags,
@@ -1,8 +1,9 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateBranchMutation,
@@ -73,7 +74,7 @@ async function deleteBranchOnAppAsync({
return data.updateBranch.deleteUpdateBranch;
}
export default class BranchDelete extends Command {
export default class BranchDelete extends EasCommand {
static hidden = true;
static description = 'Republish an update group';
@@ -91,7 +92,7 @@ export default class BranchDelete extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name },
flags: { json: jsonFlag },
+18 -24
View File
@@ -1,18 +1,29 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import CliTable from 'cli-table3';
import { print } from 'graphql';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { BranchesByAppQuery, BranchesByAppQueryVariables } from '../../graphql/generated';
import {
BranchesByAppQuery,
BranchesByAppQueryVariables,
UpdateBranchFragment,
} from '../../graphql/generated';
import { UpdateBranchFragmentNode } from '../../graphql/types/UpdateBranch';
import Log from '../../log';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
const BRANCHES_LIMIT = 10_000;
export async function listBranchesAsync({ projectId }: { projectId: string }) {
export async function listBranchesAsync({
projectId,
}: {
projectId: string;
}): Promise<UpdateBranchFragment[]> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<BranchesByAppQuery, BranchesByAppQueryVariables>(
@@ -23,29 +34,12 @@ export async function listBranchesAsync({ projectId }: { projectId: string }) {
id
updateBranches(offset: 0, limit: $limit) {
id
name
updates(offset: 0, limit: 10) {
id
actor {
__typename
id
... on User {
username
}
... on Robot {
firstName
}
}
createdAt
message
runtimeVersion
group
platform
}
...UpdateBranchFragment
}
}
}
}
${print(UpdateBranchFragmentNode)}
`,
{
appId: projectId,
@@ -58,7 +52,7 @@ export async function listBranchesAsync({ projectId }: { projectId: string }) {
return data?.app?.byId.updateBranches ?? [];
}
export default class BranchList extends Command {
export default class BranchList extends EasCommand {
static hidden = true;
static description = 'List all branches on this project.';
@@ -70,7 +64,7 @@ export default class BranchList extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const { flags } = this.parse(BranchList);
const projectDir = await findProjectRootAsync(process.cwd());
@@ -1,13 +1,12 @@
import { ExpoConfig, getConfig, getDefaultTarget } from '@expo/config';
import { getRuntimeVersionForSDKVersion } from '@expo/sdk-runtime-versions';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import assert from 'assert';
import chalk from 'chalk';
import dateFormat from 'dateformat';
import gql from 'graphql-tag';
import { uniqBy } from 'lodash';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetUpdateGroupAsyncQuery,
@@ -20,6 +19,7 @@ import {
} from '../../graphql/generated';
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import Log from '../../log';
import { ora } from '../../ora';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import {
PublishPlatform,
@@ -30,6 +30,7 @@ import {
} from '../../project/publish';
import { promptAsync, selectAsync } from '../../prompts';
import { formatUpdate } from '../../update/utils';
import uniqBy from '../../utils/expodash/uniqBy';
import formatFields from '../../utils/formatFields';
import vcs from '../../vcs';
import { createUpdateBranchOnAppAsync } from './create';
@@ -66,7 +67,7 @@ async function getUpdateGroupAsync({
return updatesByGroup;
}
async function ensureBranchExists({
async function ensureBranchExistsAsync({
appId,
name: branchName,
}: {
@@ -93,7 +94,7 @@ async function ensureBranchExists({
return { id: newUpdateBranch.id, updates: [] };
}
export default class BranchPublish extends Command {
export default class BranchPublish extends EasCommand {
static hidden = true;
static description = 'Publish an update group to a branch.';
@@ -144,7 +145,7 @@ export default class BranchPublish extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name: branchName },
flags: {
@@ -201,7 +202,7 @@ export default class BranchPublish extends Command {
assert(branchName, 'branch name must be specified.');
}
const { id: branchId, updates } = await ensureBranchExists({
const { id: branchId, updates } = await ensureBranchExistsAsync({
appId: projectId,
name: branchName,
});
@@ -1,8 +1,9 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
EditUpdateBranchInput,
@@ -49,7 +50,7 @@ async function renameUpdateBranchOnAppAsync({
return data.updateBranch.editUpdateBranch;
}
export default class BranchRename extends Command {
export default class BranchRename extends EasCommand {
static hidden = true;
static description = 'Rename a branch.';
@@ -68,7 +69,7 @@ export default class BranchRename extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
flags: { json: jsonFlag, from: currentName, to: newName },
} = this.parse(BranchRename);
+5 -4
View File
@@ -1,16 +1,17 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import Table from 'cli-table3';
import gql from 'graphql-tag';
import { groupBy } from 'lodash';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { ViewBranchQuery, ViewBranchQueryVariables } from '../../graphql/generated';
import Log from '../../log';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { UPDATE_COLUMNS, formatUpdate, getPlatformsForGroup } from '../../update/utils';
import groupBy from '../../utils/expodash/groupBy';
import formatFields from '../../utils/formatFields';
const PAGE_LIMIT = 10_000;
@@ -64,7 +65,7 @@ export async function viewUpdateBranchAsync({
return data;
}
export default class BranchView extends Command {
export default class BranchView extends EasCommand {
static hidden = true;
static description = 'View a branch.';
@@ -83,7 +84,7 @@ export default class BranchView extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name },
flags: { json: jsonFlag },
@@ -1,9 +1,8 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
Build,
@@ -13,6 +12,7 @@ import {
} from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { appPlatformEmojis } from '../../platform';
import {
findProjectRootAsync,
@@ -109,12 +109,12 @@ async function ensureBuildExistsAsync(buildId: string): Promise<void> {
}
}
export default class BuildCancel extends Command {
export default class BuildCancel extends EasCommand {
static description = 'Cancel a build.';
static args = [{ name: 'BUILD_ID' }];
async run() {
async runAsync(): Promise<void> {
const { BUILD_ID: buildIdFromArg } = this.parse(BuildCancel).args;
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
@@ -1,14 +1,14 @@
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import { configureAsync } from '../../build/configure';
import EasCommand from '../../commandUtils/EasCommand';
import Log, { learnMore } from '../../log';
import { RequestedPlatform } from '../../platform';
import { findProjectRootAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { ensureLoggedInAsync } from '../../user/actions';
export default class BuildConfigure extends Command {
export default class BuildConfigure extends EasCommand {
static description = 'Configure the project to support EAS Build.';
static flags = {
@@ -19,7 +19,7 @@ export default class BuildConfigure extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const { flags } = this.parse(BuildConfigure);
Log.log(
'💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.'
@@ -29,7 +29,6 @@ export default class BuildConfigure extends Command {
const platform =
(flags.platform as RequestedPlatform | undefined) ?? (await promptForPlatformAsync());
await ensureLoggedInAsync();
await configureAsync({
platform,
projectDir: (await findProjectRootAsync()) ?? process.cwd(),
@@ -40,7 +39,7 @@ export default class BuildConfigure extends Command {
}
}
function logSuccess(platform: RequestedPlatform) {
function logSuccess(platform: RequestedPlatform): void {
let platformsText = 'iOS and Android projects are';
let storesText = 'the Apple App Store or Google Play Store';
+14 -4
View File
@@ -14,6 +14,7 @@ import { BuildRequestSender, waitForBuildEndAsync } from '../../build/build';
import { ensureProjectConfiguredAsync } from '../../build/configure';
import { BuildContext, createBuildContextAsync } from '../../build/context';
import { prepareIosBuildAsync } from '../../build/ios/build';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from '../../build/utils/devClient';
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
import EasCommand from '../../commandUtils/EasCommand';
@@ -34,12 +35,12 @@ import {
import { validateMetroConfigForManagedWorkflowAsync } from '../../project/metroConfig';
import { findProjectRootAsync } from '../../project/projectUtils';
import { confirmAsync } from '../../prompts';
import { createSubmissionContext } from '../../submissions/context';
import { createSubmissionContext } from '../../submit/context';
import {
submitAsync,
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
} from '../../submissions/submit';
import { printSubmissionDetailsUrls } from '../../submissions/utils/urls';
} from '../../submit/submit';
import { printSubmissionDetailsUrls } from '../../submit/utils/urls';
import { enableJsonOutput } from '../../utils/json';
import vcs from '../../vcs';
@@ -127,7 +128,7 @@ export default class Build extends EasCommand {
private metroConfigValidated = false;
async run(): Promise<void> {
async runAsync(): Promise<void> {
const { flags: rawFlags } = this.parse(Build);
if (rawFlags.json) {
enableJsonOutput();
@@ -144,6 +145,12 @@ export default class Build extends EasCommand {
await ensureProjectConfiguredAsync(projectDir, requestedPlatform);
const platforms = toPlatforms(requestedPlatform);
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
platforms,
projectDir,
profile: flags.profile,
nonInteractive: flags.nonInteractive,
});
const startedBuilds: BuildFragment[] = [];
for (const platform of platforms) {
@@ -259,6 +266,7 @@ export default class Build extends EasCommand {
}): Promise<BuildFragment | undefined> {
const easJsonReader = new EasJsonReader(projectDir);
const buildProfile = await easJsonReader.readBuildProfileAsync(platform, flags.profile);
const buildCtx = await createBuildContextAsync({
buildProfileName: flags.profile,
clearCache: flags.clearCache,
@@ -315,6 +323,7 @@ export default class Build extends EasCommand {
}): Promise<SubmissionFragment> {
const easJsonReader = new EasJsonReader(projectDir);
const platform = toPlatform(build.platform);
const buildProfile = await easJsonReader.readBuildProfileAsync(platform, flags.profile);
const submitProfile = await easJsonReader.readSubmitProfileAsync(platform, flags.submitProfile);
const submissionCtx = createSubmissionContext({
platform,
@@ -323,6 +332,7 @@ export default class Build extends EasCommand {
profile: submitProfile,
archiveFlags: { id: build.id },
nonInteractive: flags.nonInteractive,
env: buildProfile.env,
});
if (moreBuilds) {
+5 -4
View File
@@ -1,10 +1,10 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import ora from 'ora';
import { BuildDistributionType, BuildStatus } from '../../build/types';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import {
AppPlatform,
DistributionType,
@@ -12,6 +12,7 @@ import {
} from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import {
findProjectRootAsync,
@@ -20,7 +21,7 @@ import {
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildList extends Command {
export default class BuildList extends EasCommand {
static description = 'list all builds for your project';
static flags = {
@@ -58,7 +59,7 @@ export default class BuildList extends Command {
limit: flags.integer(),
};
async run() {
async runAsync(): Promise<void> {
const { flags } = this.parse(BuildList);
const {
json,
+5 -4
View File
@@ -1,11 +1,12 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import ora from 'ora';
import { flags } from '@oclif/command';
import { formatGraphQLBuild } from '../../build/utils/formatBuild';
import EasCommand from '../../commandUtils/EasCommand';
import { BuildFragment } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import Log from '../../log';
import { ora } from '../../ora';
import {
findProjectRootAsync,
getProjectFullNameAsync,
@@ -13,7 +14,7 @@ import {
} from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class BuildView extends Command {
export default class BuildView extends EasCommand {
static description = 'view a build for your project';
static args = [{ name: 'BUILD_ID' }];
@@ -24,7 +25,7 @@ export default class BuildView extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const {
args: { BUILD_ID: buildId },
flags,
@@ -1,8 +1,9 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
CreateUpdateChannelOnAppMutation,
@@ -57,7 +58,7 @@ async function createUpdateChannelOnAppAsync({
);
}
export default class ChannelCreate extends Command {
export default class ChannelCreate extends EasCommand {
static hidden = true;
static description = 'Create a channel on the current project.';
@@ -77,7 +78,7 @@ export default class ChannelCreate extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name: channelName },
flags: { json: jsonFlag },
@@ -1,8 +1,9 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetChannelByNameToEditQuery,
@@ -92,7 +93,7 @@ export async function updateChannelBranchMappingAsync({
return data.updateChannel.editUpdateChannel!;
}
export default class ChannelEdit extends Command {
export default class ChannelEdit extends EasCommand {
static hidden = true;
static description = 'Point a channel at a new branch.';
@@ -114,7 +115,7 @@ export default class ChannelEdit extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name: channelName },
flags: { branch: branchName, json: jsonFlag },
@@ -1,7 +1,8 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetAllChannelsForAppQuery,
@@ -63,7 +64,7 @@ async function getAllUpdateChannelForAppAsync({
);
}
export default class ChannelList extends Command {
export default class ChannelList extends EasCommand {
static hidden = true;
static description = 'List all channels on the current project.';
@@ -74,7 +75,7 @@ export default class ChannelList extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const {
flags: { json: jsonFlag },
} = this.parse(ChannelList);
@@ -1,7 +1,8 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { GetChannelByNameForAppQuery, UpdateBranch } from '../../graphql/generated';
import Log from '../../log';
import {
@@ -305,7 +306,7 @@ async function endRolloutAsync({
return { newChannelInfo, logMessage };
}
export default class ChannelRollout extends Command {
export default class ChannelRollout extends EasCommand {
static hidden = true;
static description = 'Rollout a new branch out to a channel incrementally.';
@@ -336,7 +337,7 @@ export default class ChannelRollout extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const {
args: { channel: channelName },
flags: { json: jsonFlag, end: endFlag },
@@ -1,10 +1,11 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import assert from 'assert';
import chalk from 'chalk';
import Table from 'cli-table3';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
GetChannelByNameForAppQuery,
@@ -189,7 +190,7 @@ export function logChannelDetails(channel: {
Log.log(table.toString());
}
export default class ChannelView extends Command {
export default class ChannelView extends EasCommand {
static hidden = true;
static description = 'View a channel on the current project.';
@@ -208,7 +209,7 @@ export default class ChannelView extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
let {
args: { name: channelName },
flags: { json: jsonFlag },
+6 -3
View File
@@ -1,15 +1,16 @@
import { getProjectConfigDescription } from '@expo/config';
import { Platform } from '@expo/eas-build-job';
import { EasJsonReader } from '@expo/eas-json';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import { getExpoConfig } from '../project/expoConfig';
import { findProjectRootAsync } from '../project/projectUtils';
import { selectAsync } from '../prompts';
import { handleDeprecatedEasJsonAsync } from './build';
export default class Config extends Command {
export default class Config extends EasCommand {
static description = 'show the eas.json config';
static flags = {
@@ -17,7 +18,9 @@ export default class Config extends Command {
profile: flags.string(),
};
async run() {
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const { flags } = this.parse(Config);
const { platform: maybePlatform, profile: maybeProfile } = flags as {
platform?: Platform;
+1 -1
View File
@@ -5,7 +5,7 @@ import { SelectPlatform } from '../credentials/manager/SelectPlatform';
export default class Credentials extends EasCommand {
static description = 'manage your credentials';
async run() {
async runAsync(): Promise<void> {
const ctx = await createCredentialsContextAsync(process.cwd(), {});
await new SelectPlatform().runAsync(ctx);
}
@@ -1,17 +1,16 @@
import { Command } from '@oclif/command';
import EasCommand from '../../commandUtils/EasCommand';
import AppStoreApi from '../../credentials/ios/appstore/AppStoreApi';
import { createContext } from '../../devices/context';
import { createContextAsync } from '../../devices/context';
import DeviceManager from '../../devices/manager';
import { ensureLoggedInAsync } from '../../user/actions';
export default class DeviceCreate extends Command {
export default class DeviceCreate extends EasCommand {
static description = 'register new Apple Devices to use for internal distribution';
async run() {
async runAsync(): Promise<void> {
const user = await ensureLoggedInAsync();
const ctx = await createContext({ appStore: new AppStoreApi(), user });
const ctx = await createContextAsync({ appStore: new AppStoreApi(), user });
const manager = new DeviceManager(ctx);
await manager.createAsync();
}
+6 -5
View File
@@ -1,31 +1,32 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import assert from 'assert';
import chalk from 'chalk';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import { AppleTeamQuery } from '../../credentials/ios/api/graphql/queries/AppleTeamQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { Ora, ora } from '../../ora';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
export default class BuildList extends Command {
export default class BuildList extends EasCommand {
static description = 'list all registered devices for your account';
static flags = {
'apple-team-id': flags.string(),
};
async run() {
async runAsync(): Promise<void> {
let appleTeamIdentifier = this.parse(BuildList).flags['apple-team-id'];
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
const accountName = await getProjectAccountNameAsync(exp);
let spinner: ora.Ora;
let spinner: Ora;
if (!appleTeamIdentifier) {
spinner = ora().start('Fetching the list of teams for the project…');
+5 -5
View File
@@ -1,18 +1,18 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { AppleDeviceQuery } from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
import formatDevice from '../../devices/utils/formatDevice';
import Log from '../../log';
import { ora } from '../../ora';
import { findProjectRootAsync, getProjectAccountNameAsync } from '../../project/projectUtils';
export default class DeviceView extends Command {
export default class DeviceView extends EasCommand {
static description = 'view a device for your project';
static args = [{ name: 'UDID' }];
async run() {
async runAsync(): Promise<void> {
const { UDID } = this.parse(DeviceView).args;
if (!UDID) {
@@ -36,7 +36,7 @@ If you are not sure what is the UDID of the device you are looking for, run:
const spinner = ora().start(`Fetching device details for ${UDID}…`);
try {
const device = await AppleDeviceQuery.getByDeviceIdentifier(accountName, UDID);
const device = await AppleDeviceQuery.getByDeviceIdentifierAsync(accountName, UDID);
if (device) {
spinner.succeed('Fetched device details');
+8 -4
View File
@@ -1,14 +1,16 @@
import { Command } from '@oclif/command';
import envinfo from 'envinfo';
import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
const packageJSON = require('../../package.json');
export default class Diagnostics extends Command {
export default class Diagnostics extends EasCommand {
static description = 'log environment info to the console';
async run() {
protected requiresAuthentication = false;
async runAsync(): Promise<void> {
const info = await envinfo.run(
{
System: ['OS', 'Shell'],
@@ -16,14 +18,16 @@ export default class Diagnostics extends Command {
Utilities: ['Git'],
npmPackages: [
'expo',
'expo-cli',
'react',
'react-dom',
'react-native',
'react-native-web',
'react-navigation',
'@expo/webpack-config',
'expo-dev-client',
],
npmGlobalPackages: ['eas-cli'],
npmGlobalPackages: ['eas-cli', 'expo-cli'],
},
{
title: `EAS CLI ${packageJSON.version} environment info`,
@@ -1,7 +1,7 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { AppInfoQuery, AppInfoQueryVariables } from '../../graphql/generated';
import Log from '../../log';
@@ -30,10 +30,10 @@ async function projectInfoByIdAsync(appId: string): Promise<AppInfoQuery> {
return data;
}
export default class ProjectInfo extends Command {
export default class ProjectInfo extends EasCommand {
static description = 'information about the current project';
async run() {
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync(process.cwd());
if (!projectDir) {
throw new Error('Please run this command inside a project directory.');
@@ -1,15 +1,15 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { findProjectRootAsync, setProjectIdAsync } from '../../project/projectUtils';
export default class ProjectInit extends Command {
export default class ProjectInit extends EasCommand {
static description = 'create or link an EAS project';
static aliases = ['init'];
async run() {
async runAsync(): Promise<void> {
const projectDir = await findProjectRootAsync(process.cwd());
if (!projectDir) {
throw new Error('Please run this command inside a project directory.');
+11 -8
View File
@@ -1,7 +1,8 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
EnvironmentSecretScope,
@@ -22,7 +23,7 @@ import { findAccountByName } from '../../user/Account';
import { getActorDisplayName } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretCreate extends Command {
export default class EnvironmentSecretCreate extends EasCommand {
static description = 'Create an environment secret on the current project or owner account.';
static flags = {
@@ -43,7 +44,7 @@ export default class EnvironmentSecretCreate extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const actor = await ensureLoggedInAsync();
let {
flags: { name, value: secretValue, scope, force },
@@ -123,7 +124,7 @@ export default class EnvironmentSecretCreate extends Command {
const existingSecret = existingSecrets.find(secret => secret.name === name);
if (existingSecret) {
await EnvironmentSecretMutation.delete(existingSecret.id);
await EnvironmentSecretMutation.deleteAsync(existingSecret.id);
Log.withTick(
`Deleting existing secret ${chalk.bold(name)} on project ${chalk.bold(
`@${accountName}/${slug}`
@@ -132,7 +133,7 @@ export default class EnvironmentSecretCreate extends Command {
}
}
const secret = await EnvironmentSecretMutation.createForApp(
const secret = await EnvironmentSecretMutation.createForAppAsync(
{ name, value: secretValue },
projectId
);
@@ -149,6 +150,7 @@ export default class EnvironmentSecretCreate extends Command {
);
} else if (scope === EnvironmentSecretScope.ACCOUNT) {
const ownerAccount = findAccountByName(actor.accounts, accountName);
if (!ownerAccount) {
Log.warn(
`Your account (${getActorDisplayName(actor)}) doesn't have access to the ${chalk.bold(
@@ -159,11 +161,12 @@ export default class EnvironmentSecretCreate extends Command {
}
if (force) {
const existingSecrets = await EnvironmentSecretsQuery.byAcccountNameAsync(projectId);
const existingSecrets = await EnvironmentSecretsQuery.byAccountNameAsync(ownerAccount.name);
const existingSecret = existingSecrets.find(secret => secret.name === name);
if (existingSecret) {
await EnvironmentSecretMutation.delete(existingSecret.id);
await EnvironmentSecretMutation.deleteAsync(existingSecret.id);
Log.withTick(
`Deleting existing secret ${chalk.bold(name)} on account ${chalk.bold(
ownerAccount.name
@@ -172,7 +175,7 @@ export default class EnvironmentSecretCreate extends Command {
}
}
const secret = await EnvironmentSecretMutation.createForAccount(
const secret = await EnvironmentSecretMutation.createForAccountAsync(
{ name, value: secretValue },
ownerAccount.id
);
@@ -1,7 +1,8 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretMutation } from '../../graphql/mutations/EnvironmentSecretMutation';
import {
EnvironmentSecretScope,
@@ -19,9 +20,8 @@ import {
getProjectIdAsync,
} from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretDelete extends Command {
export default class EnvironmentSecretDelete extends EasCommand {
static description = `Delete an environment secret by ID.
Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
@@ -31,9 +31,7 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
}),
};
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
const projectId = await getProjectIdAsync(exp);
@@ -93,7 +91,7 @@ Unsure where to find the secret's ID? Run ${chalk.bold('eas secrets:list')}`;
process.exit(1);
}
await EnvironmentSecretMutation.delete(id);
await EnvironmentSecretMutation.deleteAsync(id);
Log.withTick(`️Deleted secret${secret?.name ? ` "${secret?.name}"` : ''} with id "${id}".`);
}
+3 -6
View File
@@ -1,9 +1,9 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import chalk from 'chalk';
import Table from 'cli-table3';
import dateFormat from 'dateformat';
import EasCommand from '../../commandUtils/EasCommand';
import { EnvironmentSecretsQuery } from '../../graphql/queries/EnvironmentSecretsQuery';
import Log from '../../log';
import {
@@ -15,14 +15,11 @@ import {
getProjectAccountNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
export default class EnvironmentSecretList extends Command {
export default class EnvironmentSecretList extends EasCommand {
static description = 'Lists environment secrets available for your current app';
async run(): Promise<void> {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const projectDir = (await findProjectRootAsync()) ?? process.cwd();
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
const projectId = await getProjectIdAsync(exp);
+4 -4
View File
@@ -17,9 +17,9 @@ import {
} from '../platform';
import { isEasEnabledForProjectAsync, warnEasUnavailable } from '../project/isEasEnabledForProject';
import { findProjectRootAsync, getProjectIdAsync } from '../project/projectUtils';
import { SubmitArchiveFlags, createSubmissionContext } from '../submissions/context';
import { submitAsync, waitToCompleteAsync } from '../submissions/submit';
import { printSubmissionDetailsUrls } from '../submissions/utils/urls';
import { SubmitArchiveFlags, createSubmissionContext } from '../submit/context';
import { submitAsync, waitToCompleteAsync } from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
interface RawFlags {
platform?: string;
@@ -89,7 +89,7 @@ See how to configure submits with eas.json: ${learnMore('https://docs.expo.dev/s
}),
};
async run(): Promise<void> {
async runAsync(): Promise<void> {
const { flags: rawFlags } = this.parse(Submit);
const flags = await this.sanitizeFlagsAsync(rawFlags);
@@ -1,7 +1,8 @@
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateGroupMutation,
@@ -33,7 +34,7 @@ async function deleteUpdateGroupAsync({
);
}
export default class UpdateDelete extends Command {
export default class UpdateDelete extends EasCommand {
static hidden = true;
static description = 'Delete all the updates in an update Group.';
@@ -52,7 +53,7 @@ export default class UpdateDelete extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const {
args: { groupId: group },
flags: { json: jsonFlag },
+4 -3
View File
@@ -1,7 +1,8 @@
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import Table from 'cli-table3';
import gql from 'graphql-tag';
import EasCommand from '../../commandUtils/EasCommand';
import { graphqlClient, withErrorHandlingAsync } from '../../graphql/client';
import { UpdatesByGroupQuery, UpdatesByGroupQueryVariables } from '../../graphql/generated';
import Log from '../../log';
@@ -47,7 +48,7 @@ export async function viewUpdateAsync({
}
return data;
}
export default class UpdateView extends Command {
export default class UpdateView extends EasCommand {
static hidden = true;
static description = 'Update group details.';
@@ -66,7 +67,7 @@ export default class UpdateView extends Command {
}),
};
async run() {
async runAsync(): Promise<void> {
const {
args: { groupId },
flags: { json: jsonFlag },
@@ -1,14 +1,14 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import ora from 'ora';
import { flags } from '@oclif/command';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookType } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { ora } from '../../ora';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookCreate extends Command {
export default class WebhookCreate extends EasCommand {
static description = 'Create a webhook on the current project.';
static flags = {
@@ -26,8 +26,7 @@ export default class WebhookCreate extends Command {
}),
};
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const { flags } = this.parse(WebhookCreate);
const webhookInputParams = await prepareInputParamsAsync(flags);
@@ -1,20 +1,19 @@
import { getConfig } from '@expo/config';
import { Command } from '@oclif/command';
import assert from 'assert';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookFragment } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import Log from '../../log';
import { ora } from '../../ora';
import { findProjectRootAsync, getProjectIdAsync } from '../../project/projectUtils';
import { promptAsync, toggleConfirmAsync } from '../../prompts';
import { ensureLoggedInAsync } from '../../user/actions';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookDelete extends Command {
export default class WebhookDelete extends EasCommand {
static description = 'Delete a webhook on the current project.';
static args = [
@@ -25,8 +24,7 @@ export default class WebhookDelete extends Command {
},
];
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
let {
args: { ID: webhookId },
} = this.parse(WebhookDelete);
@@ -1,20 +1,20 @@
import { getConfig } from '@expo/config';
import { Command, flags } from '@oclif/command';
import { flags } from '@oclif/command';
import chalk from 'chalk';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookType } from '../../graphql/generated';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import Log from '../../log';
import { ora } from '../../ora';
import {
findProjectRootAsync,
getProjectFullNameAsync,
getProjectIdAsync,
} from '../../project/projectUtils';
import { ensureLoggedInAsync } from '../../user/actions';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookList extends Command {
export default class WebhookList extends EasCommand {
static description = 'List webhooks on the current project.';
static flags = {
@@ -24,8 +24,7 @@ export default class WebhookList extends Command {
}),
};
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const {
flags: { event },
} = this.parse(WebhookList);
@@ -1,14 +1,14 @@
import { Command, flags } from '@oclif/command';
import pick from 'lodash/pick';
import ora from 'ora';
import { flags } from '@oclif/command';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookType } from '../../graphql/generated';
import { WebhookMutation } from '../../graphql/mutations/WebhookMutation';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import { ensureLoggedInAsync } from '../../user/actions';
import { ora } from '../../ora';
import pick from '../../utils/expodash/pick';
import { prepareInputParamsAsync } from '../../webhooks/input';
export default class WebhookUpdate extends Command {
export default class WebhookUpdate extends EasCommand {
static description = 'Create a webhook on the current project.';
static flags = {
@@ -30,8 +30,7 @@ export default class WebhookUpdate extends Command {
}),
};
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const { flags } = this.parse(WebhookUpdate);
const webhookId = flags.id;
@@ -1,12 +1,10 @@
import { Command } from '@oclif/command';
import ora from 'ora';
import EasCommand from '../../commandUtils/EasCommand';
import { WebhookQuery } from '../../graphql/queries/WebhookQuery';
import Log from '../../log';
import { ensureLoggedInAsync } from '../../user/actions';
import { ora } from '../../ora';
import { formatWebhook } from '../../webhooks/formatWebhook';
export default class WebhookView extends Command {
export default class WebhookView extends EasCommand {
static description = 'View a webhook on the current project.';
static args = [
@@ -17,8 +15,7 @@ export default class WebhookView extends Command {
},
];
async run() {
await ensureLoggedInAsync();
async runAsync(): Promise<void> {
const {
args: { ID: webhookId },
} = this.parse(WebhookView);
@@ -7,6 +7,7 @@ import {
AppFragment,
CommonAndroidAppCredentialsFragment,
} from '../../graphql/generated';
import * as AndroidGraphqlClient from '../android/api/GraphqlClient';
import { Keystore } from '../android/credentials';
import {
testKeystore2Base64,
@@ -115,7 +116,7 @@ export const testAndroidAppCredentialsFragment: CommonAndroidAppCredentialsFragm
androidAppBuildCredentialsList: [testLegacyAndroidBuildCredentialsFragment],
};
export function getNewAndroidApiMock() {
export function getNewAndroidApiMock(): { [key in keyof typeof AndroidGraphqlClient]?: any } {
return {
getAndroidAppCredentialsWithCommonFieldsAsync: jest.fn(),
getAndroidAppBuildCredentialsListAsync: jest.fn(() => []),
@@ -1,3 +1,4 @@
import AppStoreApi from '../ios/appstore/AppStoreApi';
import { AuthCtx } from '../ios/appstore/authenticate';
export const testAuthCtx: AuthCtx = {
@@ -6,7 +7,7 @@ export const testAuthCtx: AuthCtx = {
team: { id: 'test-team-id', name: 'test-team-name', inHouse: false },
};
export function getAppstoreMock() {
export function getAppstoreMock(): AppStoreApi {
return {
ensureAuthenticatedAsync: jest.fn(),
ensureBundleIdExistsAsync: jest.fn(),
@@ -21,5 +22,5 @@ export function getAppstoreMock() {
createProvisioningProfileAsync: jest.fn(),
revokeProvisioningProfileAsync: jest.fn(),
createOrReuseAdhocProvisioningProfileAsync: jest.fn(),
};
} as any;
}
@@ -1,4 +1,4 @@
import merge from 'lodash/merge';
import merge from 'ts-deepmerge';
import { Context } from '../context';
import { getNewAndroidApiMock } from './fixtures-android';
@@ -9,6 +9,7 @@ import {
IosAppBuildCredentialsFragment,
IosDistributionType,
} from '../../graphql/generated';
import * as IosGraphqlClient from '../ios/api/GraphqlClient';
import { DistributionCertificate, ProvisioningProfile } from '../ios/appstore/Credentials.types';
import { Target } from '../ios/types';
import { testProvisioningProfileBase64 } from './fixtures-base64-data';
@@ -117,7 +118,7 @@ export const testAppleTeam = {
id: 'test-team-id',
};
export function getNewIosApiMock() {
export function getNewIosApiMock(): { [key in keyof typeof IosGraphqlClient]?: any } {
return {
getIosAppCredentialsWithCommonFieldsAsync: jest.fn(),
createOrGetIosAppCredentialsWithCommonFieldsAsync: jest.fn(),
@@ -34,7 +34,7 @@ export default class AndroidCredentialsProvider {
}
private async getRemoteAsync(): Promise<AndroidCredentials> {
const setupBuildCredentialsAction = await new SetupBuildCredentials({ app: this.options.app });
const setupBuildCredentialsAction = new SetupBuildCredentials({ app: this.options.app });
const buildCredentials = await setupBuildCredentialsAction.runAsync(this.ctx);
return this.toAndroidCredentials(buildCredentials);
}
@@ -1,9 +1,10 @@
import assert from 'assert';
import { nanoid } from 'nanoid';
import ora from 'ora';
import { AndroidAppBuildCredentialsFragment } from '../../../graphql/generated';
import { ora } from '../../../ora';
import { getApplicationIdAsync } from '../../../project/android/applicationId';
import { GradleBuildContext } from '../../../project/android/gradle';
import { getProjectAccountName, getProjectConfigDescription } from '../../../project/projectUtils';
import { promptAsync } from '../../../prompts';
import { findAccountByName } from '../../../user/Account';
@@ -83,7 +84,10 @@ export async function promptUserAndCopyLegacyCredentialsAsync(
spinner.succeed('Credentials copied to EAS.');
}
export async function getAppLookupParamsFromContextAsync(ctx: Context): Promise<AppLookupParams> {
export async function getAppLookupParamsFromContextAsync(
ctx: Context,
gradleContext?: GradleBuildContext
): Promise<AppLookupParams> {
ctx.ensureProjectContext();
const projectName = ctx.exp.slug;
const accountName = getProjectAccountName(ctx.exp, ctx.user);
@@ -92,7 +96,11 @@ export async function getAppLookupParamsFromContextAsync(ctx: Context): Promise<
throw new Error(`You do not have access to account: ${accountName}`);
}
const androidApplicationIdentifier = await getApplicationIdAsync(ctx.projectDir, ctx.exp);
const androidApplicationIdentifier = await getApplicationIdAsync(
ctx.projectDir,
ctx.exp,
gradleContext
);
if (!androidApplicationIdentifier) {
throw new Error(
`android.package needs to be defined in your ${getProjectConfigDescription(
@@ -41,7 +41,7 @@ export class RemoveKeystore {
Log.succeed('Keystore removed');
}
displayWarning() {
displayWarning(): void {
Log.newLine();
Log.warn(
`Clearing your Android build credentials from our build servers is a ${chalk.bold(
@@ -1,11 +1,11 @@
import nullthrows from 'nullthrows';
import ora from 'ora';
import {
AndroidAppBuildCredentialsFragment,
AndroidKeystoreFragment,
} from '../../../graphql/generated';
import Log from '../../../log';
import { ora } from '../../../ora';
import { Context } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
@@ -14,6 +14,7 @@ import {
promptUserAndCopyLegacyCredentialsAsync,
} from '../BuildCredentialsUtils';
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
(confirmAsync as jest.Mock).mockImplementation(() => true);
@@ -11,6 +11,7 @@ import { generateRandomKeystoreAsync } from '../../utils/keystore';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { SetupBuildCredentials } from '../SetupBuildCredentials';
jest.mock('../../../../ora');
jest.mock('../../../../project/ensureProjectExists');
jest.mock('../../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
jest.mock('../../../../prompts', () => ({ confirmAsync: jest.fn(() => true) }));
@@ -184,7 +184,7 @@ export async function createOrUpdateAndroidAppBuildCredentialsByNameAsync(
});
}
export async function createOrUpdateDefaultIosAppBuildCredentialsAsync() {
export async function createOrUpdateDefaultIosAppBuildCredentialsAsync(): Promise<void> {
throw new Error('This requires user prompting. Look for me in BuildCredentialsUtils');
}
@@ -192,7 +192,7 @@ export async function createKeystoreAsync(
account: Account,
keystore: KeystoreWithType
): Promise<AndroidKeystoreFragment> {
return await AndroidKeystoreMutation.createAndroidKeystore(
return await AndroidKeystoreMutation.createAndroidKeystoreAsync(
{
base64EncodedKeystore: keystore.keystore,
keystorePassword: keystore.keystorePassword,
@@ -205,7 +205,7 @@ export async function createKeystoreAsync(
}
export async function deleteKeystoreAsync(keystore: AndroidKeystoreFragment): Promise<void> {
return await AndroidKeystoreMutation.deleteAndroidKeystore(keystore.id);
return await AndroidKeystoreMutation.deleteAndroidKeystoreAsync(keystore.id);
}
export async function createFcmAsync(
@@ -213,11 +213,14 @@ export async function createFcmAsync(
fcmApiKey: string,
version: AndroidFcmVersion
): Promise<AndroidFcmFragment> {
return await AndroidFcmMutation.createAndroidFcm({ credential: fcmApiKey, version }, account.id);
return await AndroidFcmMutation.createAndroidFcmAsync(
{ credential: fcmApiKey, version },
account.id
);
}
export async function deleteFcmAsync(fcm: AndroidFcmFragment): Promise<void> {
return await AndroidFcmMutation.deleteAndroidFcm(fcm.id);
return await AndroidFcmMutation.deleteAndroidFcmAsync(fcm.id);
}
async function getAppAsync(appLookupParams: AppLookupParams): Promise<AppFragment> {
@@ -12,7 +12,7 @@ import {
import { AndroidFcmFragmentNode } from '../../../../../graphql/types/credentials/AndroidFcm';
const AndroidFcmMutation = {
async createAndroidFcm(
async createAndroidFcmAsync(
androidFcmInput: AndroidFcmInput,
accountId: string
): Promise<AndroidFcmFragment> {
@@ -43,7 +43,7 @@ const AndroidFcmMutation = {
);
return data.androidFcm.createAndroidFcm;
},
async deleteAndroidFcm(androidFcmId: string): Promise<void> {
async deleteAndroidFcmAsync(androidFcmId: string): Promise<void> {
await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteAndroidFcmMutation>(
@@ -12,7 +12,7 @@ import {
import { AndroidKeystoreFragmentNode } from '../../../../../graphql/types/credentials/AndroidKeystore';
const AndroidKeystoreMutation = {
async createAndroidKeystore(
async createAndroidKeystoreAsync(
androidKeystoreInput: AndroidKeystoreInput,
accountId: string
): Promise<AndroidKeystoreFragment> {
@@ -49,7 +49,7 @@ const AndroidKeystoreMutation = {
);
return data.androidKeystore.createAndroidKeystore;
},
async deleteAndroidKeystore(androidKeystoreId: string): Promise<void> {
async deleteAndroidKeystoreAsync(androidKeystoreId: string): Promise<void> {
await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteAndroidKeystoreMutation>(
@@ -91,7 +91,7 @@ function displayAndroidBuildCredentials(
Log.newLine();
}
export function displayAndroidKeystore(keystore: AndroidKeystoreFragment) {
export function displayAndroidKeystore(keystore: AndroidKeystoreFragment): void {
const {
keyAlias,
type,
+3 -3
View File
@@ -1,6 +1,5 @@
import { ExpoConfig, getConfig } from '@expo/config';
import chalk from 'chalk';
import pick from 'lodash/pick';
import Log from '../log';
import { getExpoConfig } from '../project/expoConfig';
@@ -8,6 +7,7 @@ import { getProjectAccountName } from '../project/projectUtils';
import { confirmAsync } from '../prompts';
import { Actor, getActorDisplayName } from '../user/User';
import { ensureLoggedInAsync } from '../user/actions';
import pick from '../utils/expodash/pick';
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
import * as IosGraphqlClient from './ios/api/GraphqlClient';
import AppStoreApi from './ios/appstore/AppStoreApi';
@@ -85,7 +85,7 @@ class CredentialsContext implements Context {
return this._exp!;
}
public ensureProjectContext() {
public ensureProjectContext(): void {
if (this.hasProjectContext) {
return;
}
@@ -93,7 +93,7 @@ class CredentialsContext implements Context {
getConfig(this.projectDir, { skipSDKVersionRequirement: true });
}
public logOwnerAndProject() {
public logOwnerAndProject(): void {
if (this.hasProjectContext) {
const owner = getProjectAccountName(this.exp, this.user);
// Figure out if User A is configuring credentials as admin for User B's project
@@ -1,11 +1,11 @@
import fs from 'fs-extra';
import zipObject from 'lodash/zipObject';
import nullthrows from 'nullthrows';
import path from 'path';
import { AndroidAppBuildCredentialsFragment, IosDistributionType } from '../../graphql/generated';
import Log from '../../log';
import { findApplicationTarget, findTargetByName } from '../../project/ios/target';
import zipObject from '../../utils/expodash/zipObject';
import vcs from '../../vcs';
import GitClient from '../../vcs/git';
import { Context } from '../context';
@@ -300,7 +300,7 @@ async function isFileUntrackedAsync(path: string): Promise<boolean> {
return false;
}
function displayUntrackedFilesWarning(newFilePaths: string[]) {
function displayUntrackedFilesWarning(newFilePaths: string[]): void {
if (newFilePaths.length === 1) {
Log.warn(
`File ${newFilePaths[0]} is currently untracked, remember to add it to .gitignore, or to encrypt it (e.g. with git-crypt).`
@@ -1,12 +1,12 @@
import assert from 'assert';
import chalk from 'chalk';
import ora from 'ora';
import {
AppleDistributionCertificateFragment,
AppleProvisioningProfileFragment,
} from '../../../graphql/generated';
import Log from '../../../log';
import { ora } from '../../../ora';
import { Context } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
@@ -2,12 +2,12 @@ import { AppleDevice, AppleDeviceFragment } from '../../../graphql/generated';
import { APPLE_DEVICE_CLASS_LABELS } from '../../../graphql/types/credentials/AppleDevice';
import { promptAsync } from '../.././../prompts';
export async function chooseDevices(
export async function chooseDevicesAsync(
allDevices: AppleDeviceFragment[],
preselectedDeviceIdentifiers: string[] = []
): Promise<AppleDevice[]> {
const preselectedDeviceIdentifierSet = new Set(preselectedDeviceIdentifiers);
const isSelected = (device: AppleDeviceFragment) =>
const isSelected = (device: AppleDeviceFragment): boolean =>
preselectedDeviceIdentifierSet.size === 0 ||
preselectedDeviceIdentifierSet.has(device.identifier);
const { devices } = await promptAsync({
@@ -8,7 +8,7 @@ import { fromNow } from '../../../utils/date';
import { Context } from '../../context';
import { askForUserProvidedAsync } from '../../utils/promptForCredentials';
import { PushKey, PushKeyStoreInfo } from '../appstore/Credentials.types';
import { filterRevokedAndUntrackedPushKeysFromEasServers } from '../appstore/CredentialsUtils';
import { filterRevokedAndUntrackedPushKeysFromEasServersAsync } from '../appstore/CredentialsUtils';
import { APPLE_KEYS_TOO_MANY_GENERATED_ERROR } from '../appstore/pushKey';
import { pushKeySchema } from '../credentials';
import { isPushKeyValidAndTrackedAsync } from '../validators/validatePushKey';
@@ -137,7 +137,7 @@ export async function getValidAndTrackedPushKeysOnEasServersAsync(
pushKeysForAccount: ApplePushKeyFragment[]
): Promise<ApplePushKeyFragment[]> {
const pushInfoFromApple = await ctx.appStore.listPushKeysAsync();
return await filterRevokedAndUntrackedPushKeysFromEasServers(
return await filterRevokedAndUntrackedPushKeysFromEasServersAsync(
pushKeysForAccount,
pushInfoFromApple
);
@@ -1,7 +1,5 @@
import assert from 'assert';
import chalk from 'chalk';
import differenceBy from 'lodash/differenceBy';
import isEqual from 'lodash/isEqual';
import nullthrows from 'nullthrows';
import DeviceCreateAction, { RegistrationMethod } from '../../../devices/actions/create/action';
@@ -15,13 +13,14 @@ import {
} from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync, pressAnyKeyToContinueAsync } from '../../../prompts';
import differenceBy from '../../../utils/expodash/differenceBy';
import { Context } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
import { assignBuildCredentialsAsync, getBuildCredentialsAsync } from './BuildCredentialsUtils';
import { chooseDevices, formatDeviceLabel } from './DeviceUtils';
import { chooseDevicesAsync, formatDeviceLabel } from './DeviceUtils';
import { SetupDistributionCertificate } from './SetupDistributionCertificate';
export class SetupAdhocProvisioningProfile {
@@ -98,7 +97,10 @@ export class SetupAdhocProvisioningProfile {
const provisionedDeviceIdentifiers = (
currentBuildCredentials?.provisioningProfile?.appleDevices ?? []
).map(i => i.identifier);
const chosenDevices = await chooseDevices(registeredAppleDevices, provisionedDeviceIdentifiers);
const chosenDevices = await chooseDevicesAsync(
registeredAppleDevices,
provisionedDeviceIdentifiers
);
// 4. Reuse or create the profile on Apple Developer Portal
const provisioningProfileStoreInfo =
@@ -230,6 +232,17 @@ export class SetupAdhocProvisioningProfile {
}
}
function doUDIDsMatch(udidsA: string[], udidsB: string[]): boolean {
return isEqual(new Set(udidsA), new Set(udidsB));
export function doUDIDsMatch(udidsA: string[], udidsB: string[]): boolean {
const setA = new Set(udidsA);
const setB = new Set(udidsB);
if (setA.size !== setB.size) {
return false;
}
for (const a of setA) {
if (!setB.has(a)) {
return false;
}
}
return true;
}
@@ -1,5 +1,4 @@
import assert from 'assert';
import sortBy from 'lodash/sortBy';
import {
AppleDistributionCertificate,
@@ -8,6 +7,7 @@ import {
} from '../../../graphql/generated';
import Log from '../../../log';
import { confirmAsync, promptAsync } from '../../../prompts';
import sortBy from '../../../utils/expodash/sortBy';
import { Context } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
@@ -71,7 +71,7 @@ export class SetupDistributionCertificate {
const validDistCertsOnFile = await this.getValidDistCertsAsync(ctx);
return validDistCertsOnFile.length === 0
? await this.createNewDistCertAsync(ctx)
: await this.createOrReuseDistCert(ctx);
: await this.createOrReuseDistCertAsync(ctx);
}
private async isCurrentCertificateValidAsync(
@@ -107,7 +107,9 @@ export class SetupDistributionCertificate {
return isValid;
}
private async createOrReuseDistCert(ctx: Context): Promise<AppleDistributionCertificateFragment> {
private async createOrReuseDistCertAsync(
ctx: Context
): Promise<AppleDistributionCertificateFragment> {
const validDistCerts = await this.getValidDistCertsAsync(ctx);
const autoselectedDistCert = validDistCerts[0];
@@ -37,16 +37,16 @@ export class SetupPushKey {
if (pushKeysForAccount.length === 0) {
pushKey = await new CreatePushKey(this.app.account).runAsync(ctx);
} else {
pushKey = await this.createOrReusePushKey(ctx);
pushKey = await this.createOrReusePushKeyAsync(ctx);
}
return await new AssignPushKey(this.app).runAsync(ctx, pushKey);
}
private async createOrReusePushKey(ctx: Context): Promise<ApplePushKeyFragment> {
private async createOrReusePushKeyAsync(ctx: Context): Promise<ApplePushKeyFragment> {
const pushKeysForAccount = await ctx.ios.getPushKeysForAccountAsync(this.app.account);
assert(
pushKeysForAccount.length > 0,
'createOrReusePushKey: There are no Push Keys available in your EAS account.'
'createOrReusePushKeyAsync: There are no Push Keys available in your EAS account.'
);
if (ctx.appStore.authCtx) {
@@ -37,14 +37,14 @@ export class SetupTargetBuildCredentials implements Action<IosAppBuildCredential
);
}
try {
return await this.setupBuildCredentials(ctx);
return await this.setupBuildCredentialsAsync(ctx);
} catch (error) {
Log.error('Failed to setup credentials.');
throw error;
}
}
async setupBuildCredentials(ctx: Context): Promise<IosAppBuildCredentialsFragment> {
async setupBuildCredentialsAsync(ctx: Context): Promise<IosAppBuildCredentialsFragment> {
const { app, distribution, enterpriseProvisioning } = this.options;
if (distribution === 'internal') {
if (enterpriseProvisioning === 'adhoc') {
@@ -170,7 +170,7 @@ export class SetupTargetBuildCredentialsFromCredentialsJson {
function displaySingleTargetProjectCredentials(
app: AppLookupParams,
buildCredentials: IosAppBuildCredentialsFragment
) {
): void {
const targetName = app.projectName;
displayProjectCredentials(app, { [targetName]: buildCredentials }, [
{ targetName, bundleIdentifier: app.bundleIdentifier },
@@ -11,6 +11,8 @@ import {
import { MissingCredentialsNonInteractiveError } from '../../../errors';
import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { ConfigureProvisioningProfile } from '../ConfigureProvisioningProfile';
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
(confirmAsync as jest.Mock).mockImplementation(() => true);
@@ -14,7 +14,7 @@ mockdate.set(new Date('4/20/2021'));
describe('select credentials', () => {
it('select an AppleDistributionCertificate fragment', async () => {
const testDistCerts = (
await AppleDistributionCertificateQuery.getAllForAccount('quinAccount')
await AppleDistributionCertificateQuery.getAllForAccountAsync('quinAccount')
).sort((a, b) => (a.serialNumber > b.serialNumber ? 1 : -1));
const loggedSoFar = testDistCerts
.map(cert => formatDistributionCertificate(cert))
@@ -0,0 +1,14 @@
import { doUDIDsMatch } from '../SetupAdhocProvisioningProfile';
describe(doUDIDsMatch, () => {
it('return false if UDIDs do not match', () => {
const udidsA: string[] = ['00001111-001122334455662E', '11110000-771122334455662E'];
const udidsB: string[] = ['34330000-771122334455662E', '00001111-001122334455662E'];
expect(doUDIDsMatch(udidsA, udidsB)).toBe(false);
});
it('return true if UDIDs match', () => {
const udidsA: string[] = ['00001111-001122334455662E', '11110000-771122334455662E'];
const udidsB: string[] = ['11110000-771122334455662E', '00001111-001122334455662E'];
expect(doUDIDsMatch(udidsA, udidsB)).toBe(true);
});
});
@@ -347,7 +347,7 @@ export async function getDistributionCertificateForAppAsync(
export async function getDistributionCertificatesForAccountAsync(
account: Account
): Promise<AppleDistributionCertificateFragment[]> {
return await AppleDistributionCertificateQuery.getAllForAccount(account.name);
return await AppleDistributionCertificateQuery.getAllForAccountAsync(account.name);
}
export async function createDistributionCertificateAsync(
@@ -358,7 +358,7 @@ export async function createDistributionCertificateAsync(
appleTeamIdentifier: distCert.teamId,
appleTeamName: distCert.teamName,
});
return await AppleDistributionCertificateMutation.createAppleDistributionCertificate(
return await AppleDistributionCertificateMutation.createAppleDistributionCertificateAsync(
{
certP12: distCert.certP12,
certPassword: distCert.certPassword,
@@ -373,7 +373,7 @@ export async function createDistributionCertificateAsync(
export async function deleteDistributionCertificateAsync(
distributionCertificateId: string
): Promise<void> {
return await AppleDistributionCertificateMutation.deleteAppleDistributionCertificate(
return await AppleDistributionCertificateMutation.deleteAppleDistributionCertificateAsync(
distributionCertificateId
);
}
@@ -386,7 +386,7 @@ export async function createPushKeyAsync(
appleTeamIdentifier: pushKey.teamId,
appleTeamName: pushKey.teamName,
});
return await ApplePushKeyMutation.createApplePushKey(
return await ApplePushKeyMutation.createApplePushKeyAsync(
{
keyP8: pushKey.apnsKeyP8,
keyIdentifier: pushKey.apnsKeyId,
@@ -399,7 +399,7 @@ export async function createPushKeyAsync(
export async function getPushKeysForAccountAsync(
account: Account
): Promise<ApplePushKeyFragment[]> {
return await ApplePushKeyQuery.getAllForAccount(account.name);
return await ApplePushKeyQuery.getAllForAccountAsync(account.name);
}
export async function getPushKeyForAppAsync(
@@ -410,7 +410,7 @@ export async function getPushKeyForAppAsync(
}
export async function deletePushKeyAsync(pushKeyId: string): Promise<void> {
return await ApplePushKeyMutation.deleteApplePushKey(pushKeyId);
return await ApplePushKeyMutation.deleteApplePushKeyAsync(pushKeyId);
}
const formatProjectFullName = ({ account, projectName }: AppLookupParams): string =>
@@ -18,7 +18,7 @@ export type AppleDistributionCertificateMutationResult = AppleDistributionCertif
};
const AppleDistributionCertificateMutation = {
async createAppleDistributionCertificate(
async createAppleDistributionCertificateAsync(
appleDistributionCertificateInput: AppleDistributionCertificateInput,
accountId: string
): Promise<AppleDistributionCertificateMutationResult> {
@@ -60,7 +60,9 @@ const AppleDistributionCertificateMutation = {
);
return data.appleDistributionCertificate.createAppleDistributionCertificate;
},
async deleteAppleDistributionCertificate(appleDistributionCertificateId: string): Promise<void> {
async deleteAppleDistributionCertificateAsync(
appleDistributionCertificateId: string
): Promise<void> {
await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteAppleDistributionCertificateMutation>(
@@ -12,7 +12,7 @@ import {
import { ApplePushKeyFragmentNode } from '../../../../../graphql/types/credentials/ApplePushKey';
const ApplePushKeyMutation = {
async createApplePushKey(
async createApplePushKeyAsync(
applePushKeyInput: ApplePushKeyInput,
accountId: string
): Promise<ApplePushKeyFragment> {
@@ -46,7 +46,7 @@ const ApplePushKeyMutation = {
);
return data.applePushKey.createApplePushKey;
},
async deleteApplePushKey(applePushKeyId: string): Promise<void> {
async deleteApplePushKeyAsync(applePushKeyId: string): Promise<void> {
await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteApplePushKeyMutation>(
@@ -119,7 +119,7 @@ const AppleDeviceQuery = {
return data.account.byName.appleTeams[0];
},
async getByDeviceIdentifier(
async getByDeviceIdentifierAsync(
accountName: string,
identifier: string
): Promise<AppleDevicesByIdentifierQueryResult | null> {
Loaded 100 of 223 files, more files were not shown because too many files have changed in this diff. Show more