Compare commits

...
670 Commits
Author SHA1 Message Date
Szymon Dziedzic 987c23fc2d v10.2.4 2024-08-19 14:46:04 +02:00
Szymon Dziedzic 2bf5819075 upgrade eas-cli-local-build-plugin to 1.0.131 2024-08-19 14:45:56 +02:00
Expo Bot fdbeb862ec Upgrade eas-build dependencies (#2496) 2024-08-15 23:20:52 +02:00
Piotr Szeremeta 9a05359dcb Implement eas env commands (#2350)
* [ENG-11913] Implement `eas env` commands

* Implement review suggestions

* implement review suggestions

* EnvironmentVariables use `visibility` instead of `sensitive` field

* implement review suggestions

* implement review suggestions

* Add --force option to env:create

* implement update

* Implement review suggestions

* Implement review suggestions

* Implement review suggestions
2024-08-14 16:37:53 +02:00
Expo CI ce76d4d7d0 update CHANGELOG.md 2024-08-13 10:33:51 +00:00
Szymon Dziedzic 3c58bd4515 v10.2.3 2024-08-13 12:29:10 +02:00
Szymon Dziedzic f4f4756e07 upgrade eas-cli-local-build-plugin to 1.0.130 2024-08-13 12:29:03 +02:00
Szymon Dziedzic 31b3d81b1a [eas-cli] add support for EAS_DANGEROUS_OVERRIDE_IOS_BUNDLE_IDENTIFIER for bare workflow iOS builds (#2469)
* [eas-cli] add support for `EAS_DANGEROUS_OVERRIDE_IOS_BUNDLE_IDENTIFIER` for bare workflow

* add changelog

* fix changelog
2024-08-13 12:27:56 +02:00
Piotr SzeremetaandSzymon Dziedzic 5cf526592c Add environment to Metadata (#2478)
* Add environment to Metadata

* Update packages/eas-json/src/build/types.ts

Co-authored-by: Szymon Dziedzic <[email protected]>

* Update packages/eas-json/src/build/types.ts

Co-authored-by: Szymon Dziedzic <[email protected]>

* bump yarn.lock

* bump eas-build-job

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-08-07 09:37:44 +02:00
dependabot[bot] 8646dc81d9 Bump tar from 6.1.11 to 6.2.1 (#2479)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.11 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.11...v6.2.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 16:24:31 +02:00
Szymon Dziedzic 54ae76e85e [eas-cli][eas-json] update and deprecate images (#2450)
* [eas-cli][eas-json] update and deprecate images

* improve warn

* add changelog entry
2024-07-31 12:30:48 +02:00
Expo CI 63381b8ee4 update CHANGELOG.md 2024-07-31 09:31:57 +00:00
Szymon Dziedzic 52ab2d5792 v10.2.2 2024-07-31 10:27:02 +02:00
Szymon Dziedzic ade09fe6dc upgrade eas-cli-local-build-plugin to 1.0.129 2024-07-31 10:26:52 +02:00
Expo Bot 71261b5b26 Upgrade eas-build dependencies (#2474) 2024-07-30 13:18:50 +02:00
Expo BotandSzymon Dziedzic 37cec329d4 Upgrade eas-build dependencies (#2473)
* Upgrade eas-build dependencies

* fix lint

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-07-30 12:56:47 +02:00
Szymon Dziedzic fc88d65691 [eas-cli] pass correct path to vcsClient.isFileIgnoredAsync check for custom build config for monorepos (#2470)
* [eas-cli] pass correct path to `vcsClient.isFileIgnoredAsync` check for custom build config for monorepos

* add readme
2024-07-25 18:11:02 +02:00
Expo Bot 4699ae51f0 Upgrade eas-build dependencies (#2468) 2024-07-25 14:08:41 +02:00
Quinlan Jung aabe1ff806 [eas-cli] bump asset upload timeout from 90 to 180 seconds (#2466)
* Temporary Commit at 7/19/2024, 1:39:06 PM

* Temporary Commit at 7/19/2024, 3:55:09 PM
2024-07-22 13:43:51 -07:00
dependabot[bot] c8bd95535e Bump axios from 0.28.0 to 0.28.1 (#2464)
Bumps [axios](https://github.com/axios/axios) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-18 13:12:00 +02:00
dependabot[bot] 71184cbcd5 Bump axios from 0.27.2 to 0.28.0 in /packages/eas-cli (#2239)
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 0.28.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.28.0/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v0.28.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-18 13:05:21 +02:00
Expo CI 1629e2b585 update CHANGELOG.md 2024-07-18 10:01:35 +00:00
Szymon Dziedzic 4a82ac90d3 v10.2.1 2024-07-18 11:56:22 +02:00
Szymon DziedzicandWill Schurman 5745639bb2 [ENG-12793][eas-cli] make eas login and eas whoami behave more clearly when one is authenticated using EXPO_TOKEN (#2461)
* eas login and eas whoami improvements

* apply suggested change

* Update packages/eas-cli/src/commands/account/view.ts

Co-authored-by: Will Schurman <[email protected]>

* add changelog

* fix changelog

---------

Co-authored-by: Will Schurman <[email protected]>
2024-07-18 11:51:55 +02:00
Expo CI 17ecdb2a95 update CHANGELOG.md 2024-07-15 22:34:05 +00:00
Szymon Dziedzic 42b105c869 v10.2.0 2024-07-16 00:28:27 +02:00
Szymon Dziedzic ac04891b28 upgrade eas-cli-local-build-plugin to 1.0.124 2024-07-16 00:28:15 +02:00
Szymon Dziedzic 14a454edfa [eas-cli] bump @expo/apple-utils version to use the fallback Apple Developer Portal domain on every internal server error (#2459)
* [eas-cli] bump `@expo/apple-utils`

* changelog
2024-07-16 00:26:33 +02:00
Renato c0651a880c [eas-cli] Add flag to emit build meta (#2451) 2024-07-15 08:19:31 -07:00
Rinku Chaudhari c39f76392d fix: readme learn more link (#2456) 2024-07-13 21:41:24 +02:00
Expo CI f2ed434828 update CHANGELOG.md 2024-07-04 21:27:18 +00:00
Szymon Dziedzic 9344fba85f v10.1.1 2024-07-04 23:22:22 +02:00
Szymon Dziedzic e5bfcf4e90 upgrade eas-cli-local-build-plugin to 1.0.122 2024-07-04 23:22:13 +02:00
Expo CI 40a7b144ea update CHANGELOG.md 2024-07-02 12:24:22 +00:00
Kadi Kraman 97d1e25908 v10.1.0 2024-07-02 13:19:37 +01:00
Kadi Kraman 2b23c11d6e [eas-cli] [ENG-12650] add fingerprint sources to build metadata (#2422)
* Add fingerprint sources to build metadata

* Upload fingerprint to google cloud

* Rename fingerprintGCSLocation -> fingerprintGCSBucketKey

* Add fingerprint source in an object

* Update dependencies

* Add comment about fingerprintSources

* Add a changelog entry

* Add support for local build plugin

* Add safety check that a temp directory exists
2024-07-02 12:58:52 +01:00
Expo CI 1c61465c5f update CHANGELOG.md 2024-06-26 09:33:51 +00:00
Szymon Dziedzic 0516b507bc v10.0.3 2024-06-26 11:19:54 +02:00
Szymon Dziedzic 2e14870b60 [eas-cli] track how much is local build mode used in analytics (#2445)
* [eas-cli] track how much is `local` build mode used in analytics

* add changelog entry
2024-06-26 11:18:30 +02:00
Expo Bot 559baf9150 Upgrade eas-build dependencies (#2444) 2024-06-25 14:45:57 +02:00
Szymon Dziedzic bd0a696b4e [eas-cli] remove old Xcode images entirerly (#2438)
* [eas-cli] remove old Xcode images entirerly

* add changelog
2024-06-18 10:53:18 +02:00
dependabot[bot] 230e364379 Bump ws from 5.2.3 to 5.2.4 (#2440)
Bumps [ws](https://github.com/websockets/ws) from 5.2.3 to 5.2.4.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/5.2.3...5.2.4)

---
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>
2024-06-18 10:42:49 +02:00
Expo CI eaebb72813 update CHANGELOG.md 2024-06-17 14:39:38 +00:00
Szymon Dziedzic cf39201de3 v10.0.2 2024-06-17 16:34:24 +02:00
Szymon Dziedzic bb3e815716 [eas-cli] make sure we filter out any comments in build.gradle file before passing to to gradle-to-js parser (#2435)
* [eas-cli] make sure we filter out any comments in `build.gradle` file before passing to to `gradle-to-js` parser

* use trim

* add test

* add changelog
2024-06-17 16:30:19 +02:00
Expo CI beec6ad305 update CHANGELOG.md 2024-06-17 12:28:19 +00:00
Szymon Dziedzic 84b47924b7 v10.0.1 2024-06-17 14:23:43 +02:00
Szymon Dziedzic 3ff62a9c79 upgrade eas-cli-local-build-plugin to 1.0.118 2024-06-17 14:10:32 +02:00
Expo Bot 2751a7e0de Upgrade eas-build dependencies (#2436) 2024-06-17 14:09:41 +02:00
Quinlan Jung 7139e4f576 [eas-cli] add clarification for codesigning private key flag (#2432)
* Temporary Commit at 6/14/2024, 3:59:20 PM

* pr feedback

* Temporary Commit at 6/14/2024, 5:11:26 PM
2024-06-14 17:35:00 -07:00
dependabot[bot] ecbe49abaa Bump braces from 3.0.2 to 3.0.3 (#2428)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-13 12:15:57 +02:00
Expo CI 6af86b545f update CHANGELOG.md 2024-06-13 10:01:58 +00:00
Szymon Dziedzic a9a77b6fcb v10.0.0 2024-06-13 11:56:32 +02:00
Szymon Dziedzic 82fccc8193 upgrade eas-cli-local-build-plugin to 1.0.117 2024-06-13 11:56:16 +02:00
Szymon Dziedzic 5f800b5be6 [eas-cli] resolve correct submit profile configuration for eas build --auto-submit-with-profile (#2425)
* set `specifiedProfile` in submission context when triggering a submission from build profile

* add changelog
2024-06-12 18:43:01 +02:00
Expo BotandSzymon Dziedzic 3817e2d387 Upgrade eas-build dependencies (#2416)
* Upgrade eas-build dependencies

* add changelog entry

* fix changelog

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-06-10 13:14:37 +02:00
Cedric van Putten eb4c5e073d chore!: drop support for Node 16 (#2413)
* chore: update node versions to test with to 18, 20, 22

* update CHANGELOG.md

* chore(eas-cli)!: bump minimum supported Node version to 18

This makes the PR a breaking change.

* docs(eas-cli): add breaking change(log) entry

* chore: update `engines.node` to 18 everywhere
2024-06-08 15:37:56 +02:00
Cedric van Putten eefc133eda fix: update lockfile to only include @types/[email protected] (#2412)
* fix: update lockfile to only include `@types/[email protected].0`

* update CHANGELOG.md
2024-06-07 18:55:21 +02:00
Szymon Dziedzic 7f9969429d fix changelog 2024-06-07 13:53:56 +02:00
louix 26ff083b7c fix looking up process.env[process.env.EXPO_APPLE_PROVIDER_ID] (#2349) 2024-06-07 13:52:24 +02:00
Expo CI 6414361d4c update CHANGELOG.md 2024-06-06 16:58:05 +00:00
Szymon Dziedzic 28ce6b7dc7 v9.2.0 2024-06-06 18:53:10 +02:00
Szymon Dziedzic 4772f1aea5 upgrade eas-cli-local-build-plugin to 1.0.116 2024-06-06 18:53:02 +02:00
Szymon Dziedzic 2a1fd290b9 [eas-cli] add target-profile and source-profile flags to eas build:resign (#2410)
* [eas-cli] add `target-profile` and `source-profile` flags to `eas build:resign`

* update CHANGELOG.md

* fix target profile description
2024-06-06 18:47:11 +02:00
Szymon Dziedzic e83565c518 [eas-cli] make flags for eas build:list command more aligned with flags for rest of the commands (#2409)
* [eas-cli] make flags for `eas build:list` command more aligned with flags for rest of the commands

* update CHANGELOG.md
2024-06-06 18:30:29 +02:00
Szymon Dziedzic f38a739858 [eas-cli] display build profile in eas build:list command details (#2408)
* [eas-cli] display build profile in `eas build:list` command details

* update CHANGELOG.md
2024-06-06 15:06:05 +02:00
Szymon Dziedzic 3fcbcf5f6b [eas-cli] print network error message if present (#2407)
* [eas-cli] print network error message if present

* update CHANGELOG.md
2024-06-05 12:41:50 +02:00
Stanisław Chmiela 30936911ad Fix expiration date check for build list source (#2406) 2024-06-04 16:08:29 +02:00
Piotr Szeremeta a0606adde9 Remove logging of metadata file upload (#2394) 2024-06-03 14:42:42 +02:00
Szymon Dziedzic f0e3b648a1 [eas-cli] use BuildMode.REPACK for --repack builds (#2390) 2024-05-28 11:52:24 +02:00
Szymon Dziedzic 3ed5e7821e [eas-cli] use correct app config for no gh init:onboarding flow (#2397)
* [eas-cli] use correct app config for no gh `init:onboarding` flow

* update CHANGELOG.md
2024-05-28 11:51:41 +02:00
Expo CI 466339b90e update CHANGELOG.md 2024-05-23 17:20:14 +00:00
Szymon Dziedzic 1d07e08a70 v9.1.0 2024-05-23 19:15:26 +02:00
Szymon Dziedzic c21270e062 [eas-cli] make init:onboarding command public (#2399)
* [eas-cli] make `init:onboarding` command public

* update CHANGELOG.md
2024-05-23 19:11:41 +02:00
Expo CI afb548599e update CHANGELOG.md 2024-05-22 19:23:20 +00:00
Szymon Dziedzic 0b54169631 v9.0.10 2024-05-22 21:14:53 +02:00
Szymon Dziedzic af13f3987e [eas-cli] pass cwd to findProjectRootAsync in onboarding (#2398) 2024-05-22 21:13:57 +02:00
Expo CI 3ea71e57cb update CHANGELOG.md 2024-05-22 16:57:47 +00:00
Szymon Dziedzic 261dbb4be4 v9.0.9 2024-05-22 18:51:46 +02:00
Szymon Dziedzic 5dd02e2b40 log detected onboarding clone method 2024-05-22 18:50:53 +02:00
Szymon Dziedzic 9447cbc478 [eas-cli] auto detect preferred git clone method (#2396)
* [eas-cli] ask user for prefered `git clone` method when running onboarding

* use `git ls-remote` to detect whether one has ssh auth set up
2024-05-22 18:49:28 +02:00
Stanisław Chmiela 562bd3fe9c [eas-cli] Update default onboarding eas.json (#2393) 2024-05-22 11:34:18 +02:00
Expo CI d6840de401 update CHANGELOG.md 2024-05-21 13:49:48 +00:00
Szymon Dziedzic d9bd20a47b v9.0.8 2024-05-21 15:42:39 +02:00
Szymon Dziedzic 13da8d4bfc [eas-cli] display device registration qr code better (#2391)
* [eas-cli] display device registration qr code better

* update CHANGELOG.md
2024-05-21 15:06:56 +02:00
Expo BotandSzymon Dziedzic a99fe9aef5 Upgrade eas-build dependencies (#2387)
* Upgrade eas-build dependencies

* update CHANGELOG.md

* fix types

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-05-21 01:46:37 +02:00
Expo CI 61109ea2c1 update CHANGELOG.md 2024-05-17 07:32:39 +00:00
Szymon Dziedzic f889929f5f v9.0.7 2024-05-17 09:27:12 +02:00
Szymon Dziedzic 4bb314192e upgrade eas-cli-local-build-plugin to 1.0.112 2024-05-17 09:27:05 +02:00
Szymon Dziedzic 1a170283db [eas-cli] add --repack flag to eas build command (#2384)
* [eas-cli] add `--repack` flag to `eas build` command

* update Android prepare job
2024-05-17 09:18:38 +02:00
Expo CI ca803eed3f update CHANGELOG.md 2024-05-15 10:57:34 +00:00
Stanisław Chmiela 4b26472975 v9.0.6 2024-05-15 12:52:40 +02:00
Stanisław Chmiela 70b854abb1 [eas-cli] Add turtleJobRunId to UpdatePublishMutation input (#2381)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

We want to save the relationship between a job run and an update.

# How

See https://github.com/expo/universe/pull/15329.

# Test Plan

Tested with staging.
2024-05-15 12:51:49 +02:00
Expo CI 2576a97704 update CHANGELOG.md 2024-05-13 15:59:00 +00:00
Szymon Dziedzic 15e50b1536 v9.0.5 2024-05-13 17:53:24 +02:00
Szymon Dziedzic d6f3784cc0 [eas-cli] install dependencies before resolving expo config (#2380) 2024-05-13 17:52:23 +02:00
Expo CI a6f7d37d71 update CHANGELOG.md 2024-05-13 14:54:54 +00:00
Szymon Dziedzic 675c36a157 v9.0.4 2024-05-13 16:49:55 +02:00
Bartosz Kaszubowski b2961a275b [eas-cli] diagnostics: add loader and progress information (#2378)
* [eas-cli] diagnostics: add loader and progress information

* add changelog entry
2024-05-13 16:47:13 +02:00
Szymon Dziedzic 57928d1fd6 [eas-cli] create keystore for Android emulator builds in init:onboarding (#2379)
* [eas-cli] create keystore for Android emulator builds in `init:onboarding`

* use correct build profile
2024-05-13 16:46:32 +02:00
Szymon Dziedzic ff9d533248 [eas-cli] add init:onboarding flow for no gh repo connected (#2373)
* [eas-cli] add `init:onboarding` flow for no gh repo connected

* fix types

* don't wait
2024-05-13 16:43:13 +02:00
Expo CI e1ebcc0b8a update CHANGELOG.md 2024-05-10 17:38:09 +00:00
Stanisław Chmiela 628a7262cf v9.0.3 2024-05-10 19:33:08 +02:00
Stanisław Chmiela 05be162ec1 [eas-cli] Prevent ora flooding logs (#2375)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

I noticed logs look off in CI when running `eas update --auto`.

<img width="1000" alt="Zrzut ekranu 2024-05-10 o 17 50 10" src="https://github.com/expo/eas-cli/assets/1151041/ed50d014-62f1-4342-bde2-0673f49d52b4">

# How

I pinpointed it to Ora being restarted on every log.

# Test Plan

Ran `CI=1 easd update --auto` and got ⬇️ which I think is much more readable.

```
CI=1 easd update --auto
- Exporting...
[expo-cli] Starting Metro Bundler
[expo-cli] Android Bundling complete 3492ms
[expo-cli] iOS Bundling complete 3493ms
[expo-cli] iOS Building Hermes bytecode for the bundle
[expo-cli] Android Building Hermes bytecode for the bundle
[expo-cli] 
[expo-cli] Bundle                      Size
[expo-cli] ┌ index.ios.hbc           858 kB
[expo-cli] ├ index.android.hbc       864 kB
[expo-cli] ├ index.ios.hbc.map      3.45 MB
[expo-cli] └ index.android.hbc.map  3.48 MB
[expo-cli] 
[expo-cli] 💡 JavaScript bundle sizes affect startup time. Learn more: https://expo.fyi/javascript-bundle-sizes
[expo-cli] Finished saving JS Bundles
[expo-cli] Dumping asset map
[expo-cli] Dumping source maps
[expo-cli] Preparing additional debugging files
[expo-cli] Export was successful. Your exported files can be found in dist
✔ Exported bundle(s)
- Uploading...
✔ Uploaded 2 app bundles
✔ Uploading assets skipped - no new assets found
ℹ 1 iOS asset, 1 Android asset (maximum: 20000 total per update). Learn more about asset limits: https://expo.fyi/eas-update-asset-limits
- Publishing...
✔ Published!
```
2024-05-10 19:31:55 +02:00
Bartosz Kaszubowski ff2fa425bd [eas-cli] fix typo in onboarding commit message (#2376) 2024-05-10 19:19:13 +02:00
Expo Bot a621e395fc Upgrade eas-build dependencies (#2372) 2024-05-10 17:53:25 +02:00
Stanisław Chmiela 2260f74434 s/Website link/EAS Dashboard when displaying update info (#2374)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Implementing @tchayen's suggestion.

# How

Changed wording

# Test Plan

None.
2024-05-10 17:47:31 +02:00
Szymon Dziedzic 6dc9253d2d [eas-cli] improve prompt asking user to specify target clone dir in eas init:onboarding (#2371) 2024-05-10 14:56:51 +02:00
Expo CI b6570bdfcf update CHANGELOG.md 2024-05-09 17:44:21 +00:00
Szymon Dziedzic 4d1675c96f v9.0.2 2024-05-09 19:39:29 +02:00
Szymon Dziedzic 00d9009967 [eas-cli] make sure that spinner is not used for interactive git clone command in inint:onboarding and fallback to cloning via https (#2368)
* [eas-cli] make sure that spinner is not used for interactive `git clone` command in `inint:onboarding`

* fallback to https
2024-05-09 19:38:48 +02:00
Szymon Dziedzic b68840b76b fix eas-json bump workflow 2024-05-09 18:47:49 +02:00
Expo CI 6d8be295bf update CHANGELOG.md 2024-05-09 14:12:44 +00:00
Piotr Szeremeta 40c1cbe9f6 v9.0.1 2024-05-09 16:07:32 +02:00
Szymon Dziedzic efae679089 [eas-cli] improvements to eas onboarding after dogfooding (#2367) 2024-05-09 15:05:32 +02:00
Szymon Dziedzic 6a2a3fe037 [eas-json] update image tags in eas.schema.json (#2363)
* [eas-json] update image tags in `eas.schema.json`

* update CHANGELOG.md

* [eas-json] delete detailed image descriptions for `sdk-xy` tags
2024-05-09 14:55:35 +02:00
Tomasz Czajęcki 917987a41e [eas-cli] fix onboarding mutation (#2365)
* fix onboarding mutation

* Skip passing __typename
2024-05-09 14:48:09 +03:00
Bartosz Kaszubowski bbe8fb9698 [eas-cli] onboarding logs content tweaks (#2366) 2024-05-09 13:09:59 +02:00
Szymon Dziedzic d656103357 fix update eas-json in universe workflow 2024-05-08 17:04:42 +02:00
Expo CI 4b7ad9f23a update CHANGELOG.md 2024-05-08 15:01:08 +00:00
Szymon Dziedzic 4db3979ab5 v9.0.0 2024-05-08 16:56:18 +02:00
Szymon Dziedzic 63e0bf2a24 upgrade eas-cli-local-build-plugin to 1.0.107 2024-05-08 16:55:54 +02:00
Szymon Dziedzic a740aa174b [ENG-12115][eas-cli] add eas onboarding flow (#2338)
* [eas-cli] add eas onboarding flow

* mark command as hidden and prompt user before starting any action

* add gql queries

* regenerate gql schema

* fix type issues

* mark isCLIDone as true

* apply suggested changes

* improvements after cr

* more improvements
2024-05-08 16:52:14 +02:00
Szymon Dziedzic f9ef83ba52 [eas-cli] don't allow to specify custom global version of deprecated expo-cli (#2361)
* [eas-cli] don't allow to specify custom global version of deprecated `expo-cli`

* update CHANGELOG.md

* update dependencies

* print a warning vs throwing an error

* mark as deprecated

* print warning nicer

* fix changelog entry

* fix changelog
2024-05-08 12:48:26 +02:00
Expo BotandStanisław Chmiela de8956c2ec Upgrade eas-build dependencies (#2360)
* Upgrade eas-build dependencies

* update CHANGELOG.md

* Fix typecheck

---------

Co-authored-by: Stanisław Chmiela <[email protected]>
2024-05-08 10:21:20 +02:00
Will Schurman 41622c0cd3 [eas-cli] Pass env from process.env and build profile to expo-updates CLI calls where applicable (#2359) 2024-05-06 09:06:27 -07:00
Quinlan Jung d54abee7c3 [ENG-8801][eas-cli] allow modification of provisioning profile in CI, add --freeze-credentials flag (#2347)
* freeze creds flag

* allow profile to be modified in non interactive mode

* Temporary Commit at 4/24/2024, 4:15:30 PM

* Temporary Commit at 4/25/2024, 4:33:46 PM

* Temporary Commit at 4/25/2024, 5:24:12 PM

* update CHANGELOG.md

* Temporary Commit at 4/25/2024, 6:30:26 PM

* Temporary Commit at 5/2/2024, 1:10:17 PM
2024-05-02 13:38:42 -07:00
Will Schurman cdd046b647 [eas-cli] Remove more classic updates code (#2357) 2024-05-02 11:01:48 -07:00
dependabot[bot] 4577cf957d Bump ejs from 3.1.8 to 3.1.10 (#2356)
Bumps [ejs](https://github.com/mde/ejs) from 3.1.8 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.8...v3.1.10)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 13:48:50 +02:00
Expo CI 84b56f86eb update CHANGELOG.md 2024-05-01 10:24:12 +00:00
Szymon Dziedzic b2580d0cc2 v8.0.0 2024-05-01 12:19:32 +02:00
Szymon Dziedzic 0f18ea01a9 upgrade eas-cli-local-build-plugin to 1.0.102 2024-05-01 12:19:22 +02:00
Szymon Dziedzic fe9927862c [ENG-12122][eas-cli] don't prompt users to set push notifications by default if they don't have the expo-notifications installed (#2343)
* [eas-cli] don't prompt to set up push notifications if `promptToConfigurePushNotifications` is `undefined` and `expo-notifications` is not present

* update eas.schema.json

* update CHANGELOG.md
2024-05-01 10:23:48 +02:00
lzkb bc2f6e60f4 [eas-cli] Show the -s, --sso option in the login command help. (#2353)
* Show the -s, --sso option in the login command help.

* Update CHANGELOG
2024-04-29 19:54:03 -07:00
Will Schurman 6ac1e54f67 [eas-cli] Improve login info message for other login options (#2352) 2024-04-29 08:56:10 -07:00
Expo Bot 479e8d0b5d Upgrade eas-build dependencies (#2351)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-04-29 16:45:27 +02:00
Quinlan Jung 7ed62ec7f1 [ENG-11658][eas-cli] dont generate channel on eas update without flag (#2346)
* Temporary Commit at 4/25/2024, 2:50:35 PM

* update CHANGELOG.md
2024-04-26 11:23:26 -07:00
Expo CI 87000c36c1 update CHANGELOG.md 2024-04-26 16:40:41 +00:00
Szymon Dziedzic a9d08f39ad v7.8.5 2024-04-26 18:35:33 +02:00
Szymon Dziedzic 55c156dd00 Revert "v7.8.5"
This reverts commit 85de580d84.
2024-04-26 18:33:59 +02:00
Szymon Dziedzic 736d414cd6 [ci] fix build macos workflow (#2348) 2024-04-26 18:33:00 +02:00
Szymon Dziedzic 85de580d84 v7.8.5 2024-04-26 17:07:23 +02:00
Szymon Dziedzic 1a65b2cc4e [ENG-12118][eas-cli] improve error message if server returns UNAUTHORIZED_ERROR (#2345)
* [eas-cli] improve error message if server returns `UNAUTHORIZED_ERROR`

* update CHANGELOG.md
2024-04-26 12:35:26 +02:00
Will Schurman b3fca978d0 [eas-cli] Fill in min expo-updates version for expo-updates CLI (#2344) 2024-04-25 09:18:13 -07:00
Szymon Dziedzic ba4c4e5954 fix changelog 2024-04-24 18:38:13 +02:00
Will Schurman 383caa0354 [eas-cli] Add explicit workflow arg to expo-update CLI calls (#2340) 2024-04-24 09:15:37 -07:00
Szymon Dziedzic 2e43d4bba0 v7.8.4 2024-04-24 18:13:54 +02:00
Szymon Dziedzic b995d1d713 upgrade eas-cli-local-build-plugin to 1.0.101 2024-04-24 18:13:47 +02:00
Szymon Dziedzic d307f2343b [ENG-12120][eas-cli] make multi-select for revoking distribution certificates more readable (#2342)
* [eas-cli] make multi-select for revoking distribution certificates more readable

* update CHANGELOG.md
2024-04-24 18:03:07 +02:00
Szymon Dziedzic 9eeaee544a [ENG-12116][eas-cli] improve error message displayed when EAS CLI version doesn't satisfy minimal version required specified in eas.json (#2341)
* [eas-cli] improve error message displayed when EAS CLI version doesn't satisfy minimal version required specified in `eas.json`

* fix typo

* update CHANGELOG.md
2024-04-24 17:58:55 +02:00
Radosław Krzemień d9092e15f2 [eas-cli] [ENG-11602] Update eas.schema.json (#2337)
* [eas-cli] Update eas.schema.json

Updated the eas.schema.json with the current image values for Android images

See: https://linear.app/expo/issue/ENG-11310/fix-warnings-errors-that-come-up-when-adding-eas-to-a-react-native-cli

* [eas-cli] Update eas.schema.json

Updated the eas.schema.json with the current image values for Android images

See: https://linear.app/expo/issue/ENG-11602/preinstall-gradle-and-ndks-used-in-the-3-latests-sdks-on-android

* [eas-cli] Update eas.schema.json

Returned the auto option

See: https://linear.app/expo/issue/ENG-11602/preinstall-gradle-and-ndks-used-in-the-3-latests-sdks-on-android

* [eas-cli] Update eas.schema.json

Updated descriptions

See: https://linear.app/expo/issue/ENG-11602/preinstall-gradle-and-ndks-used-in-the-3-latests-sdks-on-android

* [eas-cli] Update eas.schema.json

Removed stable option

See: https://linear.app/expo/issue/ENG-11602/preinstall-gradle-and-ndks-used-in-the-3-latests-sdks-on-android

* update CHANGELOG.md
2024-04-23 20:17:30 +02:00
Expo CI f90f085dcc update CHANGELOG.md 2024-04-23 15:33:25 +00:00
Szymon Dziedzic 61bb001059 v7.8.3 2024-04-23 17:25:28 +02:00
Szymon Dziedzic 9ef86f51fa [ENG-12057][eas-cli] don't prompt user to set android.package and ios.bundleIdentifier value when runing in non-interactive mode (#2336)
* [eas-cli] don't prompt user to set `android.package` and `ios.bundleIdentifier` value when runing in non-interactive mode

* update CHANGELOG.md

* rename test
2024-04-23 17:16:29 +02:00
Quinlan Jung 44cd3b2669 [eas-cli] amend credential removal wording (#2334)
* Temporary Commit at 4/18/2024, 8:40:55 PM

* update CHANGELOG.md
2024-04-19 10:41:18 -07:00
Szymon Dziedzic 0c706048be [ENG-9817][ci] bump @expo/eas-json version in universe after the release (#2142)
* [ci] bump `@expo/eas-json` version in universe after the release

* Update .github/workflows/release.yml

* test

* edit eas.json

* Revert "edit eas.json"

This reverts commit 3979a1b1fb.

* Revert "test"

This reverts commit 69db39f548.
2024-04-19 10:22:44 +02:00
Expo CI 211a3fb6bf update CHANGELOG.md 2024-04-15 18:29:07 +00:00
Szymon Dziedzic baeb88f5be v7.8.2 2024-04-15 20:23:12 +02:00
Szymon Dziedzic ec3c27f744 upgrade eas-cli-local-build-plugin to 1.0.100 2024-04-15 20:21:47 +02:00
Szymon Dziedzic 054732c245 Revert "v7.8.2"
This reverts commit 0f45f938b1.
2024-04-15 20:18:21 +02:00
Szymon Dziedzic 70c876776d [ci] use correct workingdir for getting x86 macos build (#2331) 2024-04-15 20:12:05 +02:00
Quinlan Jung 0f45f938b1 v7.8.2 2024-04-15 10:32:58 -07:00
Quinlan Jung c8313f36e7 [ENG-11823][2/2] move credentials endpoints to paginated (#2327)
* [eas-cli] move credentials endpoints to paginated

* switch over android creds to paginated endpoints

* update CHANGELOG.md
2024-04-15 10:19:15 -07:00
Quinlan Jung a8cde5e81d [1/2][eas-cli] paginated progress bar (#2326)
* [eas-cli] paginated progress bar

* update CHANGELOG.md
2024-04-15 10:11:29 -07:00
Will Schurman d05142f9fc [eas-cli] Fix display of errors when expo-updates CLI command fails (#2324) 2024-04-12 13:36:54 -07:00
Szymon Dziedzic 8e26c7c508 [ci] build darwin-arm64 executable as well when releasing new EAS CLI version (#2323) 2024-04-12 18:43:58 +02:00
Expo CI c29018df20 update CHANGELOG.md 2024-04-11 10:04:02 +00:00
Szymon Dziedzic 8b7d977b5b v7.8.1 2024-04-11 11:56:23 +02:00
Szymon Dziedzic 12f98c2471 upgrade eas-cli-local-build-plugin to 1.0.97 2024-04-11 11:56:17 +02:00
Szymon Dziedzic d6186edb69 [eas-cli] stop querying Build.resourceClass (#2320)
* [eas-cli] stop querying `Build.resourceClass`

* update CHANGELOG.md
2024-04-11 11:45:20 +02:00
dependabot[bot] b20cf472bf Bump tar from 6.2.0 to 6.2.1 (#2319)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 11:42:01 +02:00
Szymon Dziedzic 0ab64d5eaa [eas-cli] fix README.md command files URLs (#2318)
* [eas-cli] fix `README.md` command files URLs

* update CHANGELOG.md
2024-04-11 11:41:36 +02:00
Expo Bot 40afed198f Upgrade eas-build dependencies (#2316)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-04-08 21:13:53 +02:00
Expo CI 5337929696 update CHANGELOG.md 2024-04-08 12:37:01 +00:00
Szymon Dziedzic 4c97fc31f8 v7.8.0 2024-04-08 14:29:06 +02:00
Szymon Dziedzic 42252cd355 upgrade eas-cli-local-build-plugin to 1.0.96 2024-04-08 14:28:59 +02:00
Szymon Dziedzic 784b52bddb [eas-cli] add --build-logger-level flag to eas build command (#2313)
* [eas-cli] add `--build-logger-level` flag to `eas build` command

* update CHANGELOG.md

* fix types

* fix changelog

* apply suggestions from cr

* fix changelog
2024-04-08 14:11:47 +02:00
Szymon Dziedzic d85b3283f0 [eas-cli] mark default image tag and old images as deprecated and add new image tags to eas.schema.json (#2298)
* [eas-cli] mark `default` image tag as deprecated

* add new image tags to `eas.schema.json`

* update CHANGELOG.md

* update CHANGELOG.md

* deprecate JDK 8 and NDK r19c images

* update CHANGELOG.md

* use better warn message for Xcode < 14 images and link to our changelog post

* update warn message

* don't deprecate Androdi images in this PR

* fix changelog

* use correct latest image
2024-04-08 12:15:03 +02:00
Expo CI 0c332eb6d3 update CHANGELOG.md 2024-04-05 19:24:01 +00:00
Szymon Dziedzic 26bb7052ef v7.7.0 2024-04-05 21:16:01 +02:00
Szymon Dziedzic 8333d3361a upgrade eas-cli-local-build-plugin to 1.0.95 2024-04-05 21:15:52 +02:00
Szymon Dziedzic f62a8cad27 [eas-json] add Xcode 15.3 image (#2312)
* [eas-json] add Xcode 15.3 image

* update CHANGELOG.md

* fix changelog
2024-04-05 20:30:22 +02:00
Piotr Szeremeta b52145c0a7 Fix project metadata spinner persisting after upload (#2311) 2024-04-03 17:18:01 +02:00
Piotr SzeremetaandSzymon Dziedzic e06fc189aa [ENG-11445] Add '--verbose-fastlane' flag for verbose fastlane logs (#2276)
* [ENG-11445] Add verbose-fastlane flag for verbose fastlane logs

Make isVerboseFastlaneEnabled required parameter

Co-authored-by: Szymon Dziedzic <[email protected]>

* update CHANGELOG.md

* [ENG-11445] Add verbose-fastlane for android builds

* [ENG-11445] Update graphql

* [ENG-11445] Fix tests

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-04-03 12:05:12 +02:00
Piotr Szeremeta b346f96c63 [ENG-10412] Upload projectMetadata file (#2149)
* [ENG-10412] Upload projectMetadata file

* Lint

* [ENG-10412] Apply review suggestions

* update graphql
2024-04-03 11:47:44 +02:00
Juwan Wheatley 26ba0719ce [eas-cli] add a credentials:configure-build subcommand that prepares a project for EAS Build without executing the build from the CLI (#2282)
* [eas-cli] create new `init-build` command under the `credentials` topic

* get hacky version of cli command working

* add all option to flag

* extend the Manage<Platform> actions to reduce code duplication

* prompt user for platform if it doesn't exist

* prompt the user to choose a profile

* add a changelog entry

* call SetUpBuildCredentials for android
2024-03-28 15:55:19 -04:00
dependabot[bot] c33dff99d2 Bump express from 4.18.2 to 4.19.2 (#2302)
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-28 08:48:50 +01:00
Expo BotandStanisław Chmiela 3d7d851d07 Upgrade eas-build dependencies (#2304)
Co-authored-by: Stanisław Chmiela <[email protected]>
2024-03-27 21:29:08 +01:00
Expo CI fad3f1346c update CHANGELOG.md 2024-03-27 16:02:19 +00:00
Stanisław Chmiela dece2e5242 v7.6.2 2024-03-27 16:55:26 +01:00
Expo Bot f579441e05 Upgrade eas-build dependencies (#2301) 2024-03-27 16:53:41 +01:00
Expo CI e40634297b update CHANGELOG.md 2024-03-25 14:02:09 +00:00
Szymon Dziedzic 17ea5d4f01 v7.6.1 2024-03-25 14:55:03 +01:00
Szymon Dziedzic d803e52085 upgrade eas-cli-local-build-plugin to 1.0.89 2024-03-25 14:54:43 +01:00
Expo Bot e400515fa7 Upgrade eas-build dependencies (#2293)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-03-22 17:04:55 +01:00
Szymon Dziedzic e6233b3028 [eas-cli] use GitNoCommitClient for eas build:internal command (#2295)
* [eas-cli] use `GitNoCommitClient` for `eas build:internal` command

* Revert "[eas-cli] use `GitNoCommitClient` for `eas build:internal` command"

This reverts commit d83a9a5a5e.

* fix resolving vcs client in context

* update CHANGELOG.md

* fix tests
2024-03-22 16:55:49 +01:00
Quinlan Jung eec975c171 [eas-cli] fix asset limit punctuation (#2296)
* Temporary Commit at 3/21/2024, 12:10:10 PM

* update CHANGELOG.md
2024-03-21 13:08:55 -07:00
Expo Bot 08ea07bf5c Upgrade eas-build dependencies (#2291)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-03-18 17:52:07 +01:00
Expo CI e5d94ad15c update CHANGELOG.md 2024-03-18 16:23:58 +00:00
Szymon Dziedzic a51fbd37ca v7.6.0 2024-03-18 17:16:25 +01:00
Szymon Dziedzic 1c78e09676 upgrade eas-cli-local-build-plugin to 1.0.86 2024-03-18 17:16:11 +01:00
Szymon Dziedzic d1edec6549 [eas-cli] bump @expo/apple-utils to switch between developer.apple.com and developer-mdn.apple.com domains when one of them doesn't work (#2290)
* [eas-cli] bump `@expo/apple-utils` to switch between `developer.apple.com` and `developer-mdn.apple.com` domains when one of them is not working

* update CHANGELOG.md

* bump version
2024-03-18 17:12:06 +01:00
Szymon Dziedzic ab6483f7a5 [eas-cli] send a path to custom build config to EAS Build server with posix separator for Windows (#2285)
* [eas-cli] send a path to custom build config to EAS Build server with posix separator for Windows

* add process.platform to if statement

* update CHANGELOG.md

* apply suggestions from cr

* remove unnecessary check

* apply suggested change
2024-03-18 17:00:49 +01:00
Stanisław Chmiela 32848f6255 Log dirty files in non-interactive when bailing (#2288) 2024-03-18 13:03:42 +01:00
dependabot[bot] 7e2aae333d Bump follow-redirects from 1.15.4 to 1.15.6 (#2284)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-15 10:29:05 +01:00
Expo Bot 191cb2396c Upgrade eas-build dependencies (#2283) 2024-03-14 21:54:34 +01:00
Expo Bot 33fb5fad03 Upgrade eas-build dependencies (#2277)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-03-12 10:59:51 +01:00
Expo CI 3ec6e5dd14 update CHANGELOG.md 2024-03-11 11:38:53 +00:00
Szymon Dziedzic b6acbf05d8 v7.5.0 2024-03-11 12:31:11 +01:00
Szymon Dziedzic 957874d8cc upgrade eas-cli-local-build-plugin to 1.0.83 2024-03-11 12:31:01 +01:00
Expo Bot 1a5fbc2342 Upgrade eas-build dependencies (#2274)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-03-11 12:29:22 +01:00
Szymon Dziedzic 68cd7e6fba [ENG-11416][eas-cli] add --auto-submit flag to eas build:internal command (#2271)
* [eas-cli] add `--auto-submit` option to `eas build:internal` command

fix types

revert unnecessary changes

one more revert

* update CHANGELOG.md

* try to fix changelog

* try again

* fix changelog
2024-03-10 17:24:50 +01:00
Expo CI 628a3f708a update CHANGELOG.md 2024-03-10 02:57:28 +00:00
Brent Vatne 3ac45f08ba v7.4.0 2024-03-09 18:50:50 -08:00
Brent Vatne 73ba19de66 [eas-cli] Bump @expo/apple-utils 2024-03-09 18:50:16 -08:00
Will Schurman ef6623cd15 [eas-cli] Use new expo-updates configuration:syncnative for versioned native sync (#2269) 2024-03-08 14:27:12 -08:00
Will Schurman d78917b947 Revert "Revert "[eas-cli] Use expo-updates runtime version CLI to generate runtime ve…" (#2264)" (#2265) 2024-03-07 08:55:53 -08:00
Will Schurman 2b59a5e2ee Revert "[eas-cli] Use expo-updates runtime version CLI to generate runtime ve…" (#2264)
This reverts commit 561e3bbb5a.
2024-03-06 09:13:20 -08:00
Expo Bot e6465cd3e5 Upgrade eas-build dependencies (#2259)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-03-05 15:07:24 +01:00
Will Schurman 561e3bbb5a [eas-cli] Use expo-updates runtime version CLI to generate runtime versions (#2251) 2024-02-28 09:27:14 -08:00
dependabot[bot] 64a6e3a0f7 Bump es5-ext from 0.10.53 to 0.10.63 (#2255)
Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.53 to 0.10.63.
- [Release notes](https://github.com/medikoo/es5-ext/releases)
- [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
- [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.53...v0.10.63)

---
updated-dependencies:
- dependency-name: es5-ext
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-27 09:07:36 +01:00
Will Schurman 33c965b236 [vscode] Include src/**/build directories in search and replace (#2250) 2024-02-26 10:19:35 -08:00
Expo Bot 831e69e5c6 Upgrade eas-build dependencies (#2253)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-02-26 17:09:09 +01:00
Stanisław Chmiela 7d47060b12 Add config property to eas.schema.json (#2248)
# Why

We want users to feel like they're adding something we support.

# How

Added `config` property to build profile level and platform-specific profile properties levels.

Also prettified the file according to my JSON Tools VS Code extension. Let me know if it should be deprettified.

# Test Plan

None.
2024-02-23 17:24:35 +01:00
Will Schurman b6b22b79c3 [eas-cli] Fix expo-updates package version detection for canaries (#2243) 2024-02-22 08:14:24 -08:00
Expo Bot 6770cd99c9 Upgrade eas-build dependencies (#2240)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-02-21 11:48:12 +01:00
dependabot[bot] 704745b829 Bump ip from 1.1.5 to 1.1.9 (#2238)
Bumps [ip](https://github.com/indutny/node-ip) from 1.1.5 to 1.1.9.
- [Commits](https://github.com/indutny/node-ip/compare/v1.1.5...v1.1.9)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-20 21:37:49 +01:00
Quinlan Jung 3780e2ae1c [eas-cli] reword the update configure instruction (#2234)
* [eas-cli] reword the update configure instruction

* nits

* update CHANGELOG.md
2024-02-20 10:58:11 -07:00
Expo Bot e93854d039 Upgrade eas-build dependencies (#2237)
⬆️ Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.
2024-02-20 14:23:24 +01:00
Expo CI 89354f5932 update CHANGELOG.md 2024-02-19 16:25:41 +00:00
Szymon Dziedzic a074ce0cff v7.3.0 2024-02-19 17:18:11 +01:00
Szymon Dziedzic 31f583442a upgrade eas-cli-local-build-plugin to 1.0.71 2024-02-19 17:18:03 +01:00
Radosław Krzemień 9bf1f383e5 [eas-cli] [ENG-11310] Don't require expo on fresh react-native project (#2235)
* [eas-cli] Don't require expo

Do not require expo to be installed and configured for a fresh react-native project

See: https://linear.app/expo/issue/ENG-11310/fix-warnings-errors-that-come-up-when-adding-eas-to-a-react-native-cli

* update CHANGELOG.md

* [eas-cli] Throw other errors

Forgot to rethrow other types of errors

See: https://linear.app/expo/issue/ENG-11310/fix-warnings-errors-that-come-up-when-adding-eas-to-a-react-native-cli
2024-02-19 16:44:07 +01:00
Will Schurman e157473f41 [eas-cli] fix expo-updates fingerprinting during update (#2231) 2024-02-16 14:09:40 -08:00
Expo Bot ba87bb6e29 Upgrade eas-build dependencies (#2230)
* Upgrade eas-build dependencies

* update CHANGELOG.md
2024-02-16 11:12:42 +01:00
Expo Bot efd9213fab Upgrade eas-build dependencies (#2229) 2024-02-14 17:33:56 +01:00
Expo CI a3d9bc0eca update CHANGELOG.md 2024-02-11 04:48:40 +00:00
Brent Vatne 39dc6299d2 v7.2.0 2024-02-10 20:42:08 -08:00
Brent Vatne 37573bed50 Bump @xpo/apple-utils 2024-02-10 20:40:58 -08:00
Will Schurman 2e49f6348e Revert "feat(eas-cli): expose expo export dev flag as an option in eas update (#2050)" (#2214) 2024-02-08 12:38:16 -08:00
Expo BotandSzymon Dziedzic 3a7ddbd025 Upgrade eas-build dependencies (#2223)
* Upgrade eas-build dependencies

* update CHANGELOG.md

* fix type errors

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-02-08 10:40:58 +01:00
Chris Walter a5c1100ce1 [fcmv1] Manage FCM V1 Google Service Account Key in CLI (#2197)
* [fcmv1] Manage FCM V1 Google Service Account Key in CLI

# Why
NOTE: DO NOT LAND THIS UNTIL GRAPHQL CHANGES LAND IN WWW

We're adding support for FCM V1 credentials, as Google is shutting down
the FCM Legacy API for sending Android notifications in June.

# How
Add new prompts and refactor existing prompts to match this schematic:

# Test Plan
Verified all functionality e2e:

* update CHANGELOG.md
2024-02-07 15:26:56 -08:00
Expo CI 0b71812694 update CHANGELOG.md 2024-02-07 12:34:16 +00:00
Stanisław Chmiela caf2403dfd v7.1.3 2024-02-07 13:24:16 +01:00
Expo Bot 09fe5dd1a3 Upgrade eas-build dependencies (#2220) 2024-02-07 12:39:31 +01:00
Stanisław Chmiela 048460f323 Add workflow for bumping eas-build dependencies (#2216)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

It is currently not possible to use `if: ${ always() }` in Custom Build workflows due to outdated `@expo/steps` dependency.

# How

Added a GitHub workflow, similar to the one we have in `turtle-v2`, which will automatically upgrade all dependencies from the `eas-build` repo.

# Test Plan

https://github.com/expo/eas-cli/pull/2220
2024-02-07 12:39:19 +01:00
Radosław Krzemień 69b0edae8d [eas-cli] [ENG-9957] Reapply changes after fixing query in www (#2209)
* Revert "Revert "[eas-cli] [ENG-9957] Don't overwrite distribution for simulator build…" (#2207)"

This reverts commit a6e281b6

* [eas-cli] Generate schema

Re-generated gql schema

See: https://linear.app/expo/issue/ENG-11247/fix-console-output-when-creating-asc-key
2024-02-05 11:37:39 +01:00
Szymon Dziedzic 9ee9ea39bb [ci] auto update EAS CLI version used on production for builds triggered using GitHub trigger (#2211)
* [ci] add update eas cli used on production for builds triggered with GitHub trigger once new version is published

* remove unnecessary docs from releasing
2024-02-01 23:26:17 +01:00
Radosław Krzemień 204a8b7eb4 [eas-cli] [ENG-11247] Remove duplicate log (#2208)
* [eas-cli] Remove duplicate log

Information about App Store Connect API key being created was displayed twice to the user. This removes the duplicate

See: https://linear.app/expo/issue/ENG-11247/fix-console-output-when-creating-asc-key

* update CHANGELOG.md
2024-02-01 12:28:45 +01:00
Szymon Dziedzic a6e281b6c8 Revert "[eas-cli] [ENG-9957] Don't overwrite distribution for simulator build…" (#2207)
This reverts commit 315b5b23f8.
2024-01-31 21:06:37 +01:00
Radosław Krzemień 315b5b23f8 [eas-cli] [ENG-9957] Don't overwrite distribution for simulator builds (#2073)
* [eas-cli] Update dependencies

Updated eas-build-job to new version

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Don't overwrite distribution

Stops overwriting distribution type for simulator builds, and sets simulator field in metadata instead

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Merge main

Merged changes from main branch

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Check for simulator flag

Updated build:run command to use simulator flag instead of overwritten distribution type

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Update schema

Update graphql schema with simulator fields

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Add flag

Add simulator flag to build:list command

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Fix check

Fixed check function with correct field name

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Fix tests

Fixed failing tests after BuildFragment structure changed

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Add warning

Added warning to build:list command that the distribution=simulator is deprecated

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Add description

Added description to the new simulator flag

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Add validation

Added validation so the simulator flag can only be used with --platofrm=ios

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Change message

Updated the warning displayed when using deprecated --distribution=simulator flag value

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Replace string

Replaced hardcoded string with variable

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Remove redundant check

Removed no longer necessary check for simulator build

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Update schema

Updated graphql schema after recent changes

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* [eas-cli] Update schema

Updated graphql schema after recent changes

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds

* update CHANGELOG.md

* [eas-cli] Update schema

Updated graphql schema after recent changes

See: https://linear.app/expo/issue/ENG-9957/fix-internal-distribution-pages-for-simulator-builds
2024-01-31 17:21:05 +01:00
Szymon Dziedzic ceb561a4af add sjchmiela as codeowner (#2205) 2024-01-31 15:23:00 +01:00
Expo CI 63743cae65 update CHANGELOG.md 2024-01-30 10:26:13 +00:00
Szymon Dziedzic e8e5eb622c v7.1.2 2024-01-30 11:19:50 +01:00
Szymon DziedzicandStanisław Chmiela fe8f1b32b3 Revert "Revert "[eas-json] validate EAS Submit inputs better"" (#2202)
* Revert "Revert "[eas-json] validate EAS Submit inputs better""

* make validation constraints more loose where necessary

* update CHANGELOG.md

* fix message

* Apply suggestions from code review

Co-authored-by: Stanisław Chmiela <[email protected]>

* apply suggested changes

---------

Co-authored-by: Stanisław Chmiela <[email protected]>
2024-01-30 11:13:41 +01:00
Expo CI 3a1e5298a4 update CHANGELOG.md 2024-01-26 20:19:03 +00:00
Szymon Dziedzic 92c1256e8b v7.1.1 2024-01-26 21:11:12 +01:00
Szymon Dziedzic 61543c7983 Revert "[eas-json] validate EAS Submit inputs better" (#2200)
* Revert "[eas-json] validate EAS Submit inputs better"

* revert changelog revert

* update CHANGELOG.md
2024-01-26 21:03:43 +01:00
Expo CI 62d149ae60 update CHANGELOG.md 2024-01-26 09:58:59 +00:00
Szymon Dziedzic bbf14e2ee8 v7.1.0 2024-01-26 10:53:35 +01:00
Szymon DziedzicandStanisław Chmiela 126c5627eb [eas-json] validate EAS Submit inputs better (#2198)
* [eas-json] sanitize EAS Submit inputs better

* add link to docs

* update CHANGELOG.md

* validate app specific password

* use Joi schema for validation

* fix typo

* minor improvements

* Update packages/eas-cli/src/submit/ios/__tests__/IosSubmitCommand-test.ts

Co-authored-by: Stanisław Chmiela <[email protected]>

* make uuid validation less strict

* apply suggested changes

* fix tests

---------

Co-authored-by: Stanisław Chmiela <[email protected]>
2024-01-26 10:48:01 +01:00
Will Schurman b5f2f5286a [eas-cli] Support requireCommit for EAS Update (#2196) 2024-01-22 10:38:13 -08:00
Will Schurman 8ea05793dd [eas-cli] Remove support for classic updates release channel in 50+ (#2189) 2024-01-19 10:53:52 -08:00
Expo CI 02c8dcaa5b update CHANGELOG.md 2024-01-19 14:57:46 +00:00
Szymon Dziedzic fe1a1f14d1 v7.0.0 2024-01-19 15:51:27 +01:00
Szymon Dziedzic df7b18ca03 upgrade eas-cli-local-build-plugin to 1.0.60 2024-01-19 15:46:42 +01:00
Quinlan Jung 41142a9486 [eas-cli] Revert "[1/1][eas-cli] generate eas-update-metadata.json on publish … (#2187)
* Revert "[1/1][eas-cli] generate eas-update-metadata.json  on publish (#2158)"

This reverts commit 183fe8712b.

* lint

* update CHANGELOG.md
2024-01-18 13:59:19 -08:00
Quinlan Jung 72c28d4259 [eas-cli] use branch mapping utility fn (#2186)
* [eas-cli] use branch mapping utility fn

* update CHANGELOG.md
2024-01-18 13:58:43 -08:00
Expo CI da42962151 update CHANGELOG.md 2024-01-18 21:28:12 +00:00
Brent Vatne bbebe90876 v6.1.0 2024-01-18 13:21:18 -08:00
Will Schurman ea57d6383a [eas-cli] Upgrade packages from the expo/expo repo (#2145) 2024-01-18 11:01:00 -08:00
Szymon Dziedzic f5a99cea6b [ENG-11092][eas-json] update eas.schema.json to add support for Xcode 15.2 image (#2184)
* [eas-json] update `eas.schema.json` to add support for Xcode 15.2 image

* update CHANGELOG.md
2024-01-18 17:57:31 +01:00
dependabot[bot] e9064b7d25 Bump jsonwebtoken from 8.5.1 to 9.0.0 (#2183)
Bumps [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) from 8.5.1 to 9.0.0.
- [Changelog](https://github.com/auth0/node-jsonwebtoken/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/node-jsonwebtoken/compare/v8.5.1...v9.0.0)

---
updated-dependencies:
- dependency-name: jsonwebtoken
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-17 11:32:48 +01:00
dependabot[bot] 10ab6724e1 Bump jsonwebtoken from 8.5.1 to 9.0.0 in /packages/eas-cli (#2182)
Bumps [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) from 8.5.1 to 9.0.0.
- [Changelog](https://github.com/auth0/node-jsonwebtoken/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/node-jsonwebtoken/compare/v8.5.1...v9.0.0)

---
updated-dependencies:
- dependency-name: jsonwebtoken
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-17 11:28:14 +01:00
Szymon Dziedzic ab8115b6d3 [ENG-11071][eas-json][eas-cli] bump dependencies and fix errors/warnings (#2176)
* bump dependencies and fix errors/warnings

* use lerna compatible with node 16

* fix tests

* update CHANGELOG.md

* run `yarn lint --fix` to fix prettier errors

* fix lint warnings

* fix weird syntax
2024-01-17 11:19:02 +01:00
Radosław Krzemień ccc8d5ac02 [eas-cli] [ENG-11026] Add build:delete command (#2178)
* [eas-cli] Add command to delete build

Added a new command build:delete. Shares some code with build:cancel, so I have moved the redundancies to a utils file at src/commandUtils/builds

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Add tests

Added a test file based on existing tests for build:cancel command. Adding more tests

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Sort builds

Explicitly sorts fetched builds by descending order of `created_at`. In case multiple results were combined (for different statuses) they would by already sorted, but grouped by status also

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Use object arg

Changed `fetchBuildsAsync` arguments into single object argument for readability

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Update msg

Changed the message displayed when no builds are found for deletion/cancellation to be more human-like

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Update msg

Changed the message displayed when no build ID is provided for non-interactive mode, to not include the argument name but rather its description

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Parallelize

Changed for loop with async calls into await promise.all()

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Add filter flags

When not providing an ID of the build to delete, the user can now filter the listed builds by platform and build profile using new flags. The same change has also been applied to `build:cancel` command

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* update CHANGELOG.md

* update CHANGELOG.md

* [eas-cli] Rephrase

Using imperative form in the changelog

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Validate args

Disallow using both the build ID and filter flags when using `build:delete` and `build:cancel` commands

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Update descriptions

Updated the descriptions of the filter flags as per suggestion during review

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Add mapping

Added function for mapping platform flag into generated graphQL value

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Update mapping

Replaced function with a mapping object

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Use Flags.enum

Replaced strings with object enums

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command

* [eas-cli] Fix type

Removed undefined

See: https://linear.app/expo/issue/ENG-11026/add-eas-builddelete-command
2024-01-16 16:12:14 +01:00
Szymon Dziedzic 4c07b89eb6 [ENG-11090][eas-cli] prompt users if they want to continue if provisioning the devices fails (#2181)
* [eas-cli] prompt users if they want to continue if provisioning the devices fails

* update CHANGELOG.md

* fix tests

* fix imports
2024-01-16 16:06:33 +01:00
Stanisław Chmiela 189d163a41 [eas-cli] Remove bare-specific eas.json template (#2179)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Simplification, unification.

# How

Removed bare-specific template.

# Test Plan

None.
2024-01-15 21:00:56 +01:00
Expo CI 098b1665bc update CHANGELOG.md 2024-01-12 08:24:27 +00:00
Szymon Dziedzic 51a01d10e9 v6.0.0 2024-01-12 09:17:24 +01:00
Szymon Dziedzic 28042ef5a6 upgrade eas-cli-local-build-plugin to 1.0.56 2024-01-12 09:16:43 +01:00
Szymon Dziedzic 14dd3d29ea [ENG-10799][ci][eas-cli][eas-json] test against node 20 and drop support for node 14 (#2175)
* test against node 20 and drop support for node 14

* update CHANGELOG.md

* update @types/node

* bump yarn version

* Revert "update @types/node"

This reverts commit 027e26b002.

* update @types/node

* try to debug

* Revert "try to debug"

This reverts commit 0d557d02f8.

* test sth

* update lock

* Revert "update lock"

This reverts commit 184b412655.

* Revert "test sth"

This reverts commit 77606cf5be.

* diff once again

* fix issues

* Revert "diff once again"

This reverts commit 526ab57453.
2024-01-12 09:05:41 +01:00
dependabot[bot] b1fd4e8e3a Bump follow-redirects from 1.15.2 to 1.15.4 (#2172)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-10 10:07:47 +01:00
Szymon Dziedzic bcc5f2b3ec [ci] make CI tsconfig settings match the default local tsconfig settings (#2167)
* Revert "[ENG-8956][eas-build]  Improve EAS CLI tsconfig.json for local development (#1933)"

This reverts commit e963558ef6.

* add start-allow-unused option

* update CONTRIBUTING.md

* fix type error
2024-01-10 10:06:33 +01:00
Stanisław Chmiela 0aea5a3dc6 Remove S3 archive support (#2165)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

x-ref https://github.com/expo/eas-build/pull/302

# How

Upgraded `@expo/eas-build-job` and fixed the only TS error.

# Test Plan

None.
2024-01-05 16:41:27 +01:00
Stanisław Chmiela 493cf56c32 [ci] Cancel in progress jobs when new are run (#2166)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

I've noticed tests ran for an old commit even though I pushed a new one.

# How

Added `concurrency` where I thought it was applicable.

# Test Plan

None.
2024-01-05 11:48:07 +01:00
Will Schurman eeb143af8a [eas-cli][eas-update] Allow undefined update message when no VCS (#2148) 2024-01-04 12:10:39 -08:00
dependabot[bot] 2dab7846be Bump tj-actions/changed-files from 35 to 41 in /.github/workflows (#2163)
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 35 to 41.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v35...v41)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-03 10:55:11 +01:00
Quinlan Jung 32b8d7dc77 fix lint 2023-12-19 15:18:19 -08:00
Quinlan Jung 183fe8712b [1/1][eas-cli] generate eas-update-metadata.json on publish (#2158)
* [eas-cli] publish: patch metadata.json with eas info

* feedback
2023-12-19 10:57:54 -08:00
Expo CI 06466e5967 update CHANGELOG.md 2023-12-19 13:45:27 +00:00
Szymon Dziedzic b5c905734a v5.9.3 2023-12-19 14:29:07 +01:00
Szymon Dziedzic 7e74829f8f [ENG-10922][eas-json] add new Xcode 15.1 image to eas.schema.json (#2155)
* [eas-json] add new Xcode 15.1 image to `eas.schema.json`

* update CHANGELOG.md

* update default
2023-12-19 13:02:02 +01:00
Quinlan Jung 655f6c8a84 [0/1][eas-cli] rename getUpdateGroupJsonInfo (#2157)
* [eas-cli] rename getUpdateGroupJsonInfo

* update CHANGELOG.md

* feedback
2023-12-18 17:51:34 -08:00
Expo CI 43180c0ebe update CHANGELOG.md 2023-12-15 10:40:44 +00:00
Szymon Dziedzic 1a5df5ce55 v5.9.2 2023-12-15 11:34:04 +01:00
Szymon Dziedzic 533bde2442 upgrade eas-cli-local-build-plugin to 1.0.55 2023-12-15 11:33:27 +01:00
Szymon Dziedzic 899a563c1a fix lint changelog and use node 18.19.0 by default (#2153) 2023-12-15 01:12:27 +01:00
Stanisław Chmiela f0012f59a4 [eas-cli] Pass build mode only in job (#2138)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

We use two build modes meaning the same thing. In https://github.com/expo/universe/pull/14035 I'm merging them into the single `job.mode`.

# How

Updated `eas-build-job` which already removed `buildMode` from metadata. Then adjusted code so it:
- does not send `buildMode` in metadata
- transforms internal `buildMode` to the GraphQL `BuildMode`.

# Test Plan

In a test project, triggered:
- [a custom build](https://staging.expo.dev/accounts/sjchmiela/projects/staging-app/builds/3abbbe06-7e64-449c-9fa3-05a4b14652f5) with `EXPO_STAGING=1 easd build -p ios -e demo`
- [a regular build](https://staging.expo.dev/accounts/sjchmiela/projects/staging-app/builds/15dc26e2-3e89-486d-be32-8ee12d81693d) with `EXPO_STAGING=1 easd build -p ios -e simulator`

they both ran as expected.
2023-12-12 17:06:47 +01:00
Radosław Krzemień b44ba6b22b [eas-cli] [ENG-9175] Update oclif version (#2008)
* [eas-cli] Update oclif version

Updated oclif version

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Update dependencies

Updated dependencies of cli that also use oclif and used to depend on older versions

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Merge main

Updated the branch after long inactivity

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags

Fixed flags for build command

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags

Fixed flags for build:version:set and build:version:get commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Add getters

Added getters for build status and distribution type from string flag values

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix args

Fixed args in `branch` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags

Fixed flags in `build:version` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `build` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `channel` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `device` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `secret` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `update` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `webhook` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix flags and args

Fixed flags and args in `analytics`, `config`, `credentials` and `submit` commands

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Fix test

Fixed EasCommand-test

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Add getters

Added getters for environment secret scope and secret type from string flag value

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Add getters

Added getters for end outcome from string flag value

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Add getters

Added getters for requested platform from string flag value

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Update oclif

Use newest version of oclif packages where possible

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Downgrade oclif

Oclif v4 needs node >= v18, so had to downgrade it to v3

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* Revert "[eas-cli] Add getters"

This reverts commit 74f6f06d5f.

* Revert "[eas-cli] Add getters"

This reverts commit bdfcd6c03c.

* Revert "[eas-cli] Add getters"

This reverts commit ab634e426a.

* Revert "[eas-cli] Fix test"

This reverts commit 06831102be.

* Revert "[eas-cli] Fix flags and args"

This reverts commit c057738a1c.

* Revert "[eas-cli] Fix flags and args"

This reverts commit 5d4f380352.

* Revert "[eas-cli] Fix flags and args"

This reverts commit a32ee869f3.

* Revert "[eas-cli] Fix flags and args"

This reverts commit 8e46cfc431.

* Revert "[eas-cli] Fix flags and args"

This reverts commit 00d3322ff9.

* Revert "[eas-cli] Fix flags and args"

This reverts commit 26af526636.

* Revert "[eas-cli] Fix flags and args"

This reverts commit d091288945.

* Revert "[eas-cli] Fix flags"

This reverts commit 669b21229f.

* Revert "[eas-cli] Fix args"

This reverts commit f24e7e5fed.

* Revert "[eas-cli] Add getters"

This reverts commit 766d48e1ef.

* Revert "[eas-cli] Fix flags"

This reverts commit 928d826be1.

* Revert "[eas-cli] Fix flags"

This reverts commit f9d8e2438c.

* [eas-cli] Downgrade oclif

Oclif v4 and v3 need node >= v18, so had to downgrade it to v2. v2 has a bug however when importing one of the dependencies which makes it unusable so had to go back to v1

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* [eas-cli] Downgrade oclif/plugin-autocomplete

oclif/plugin-autocomplete v3 needs oclif/core v3 which needs node >= v18, so had to downgrade it to v2

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli

* update CHANGELOG.md
2023-12-04 12:07:55 +01:00
Szymon DziedzicandStanisław Chmiela 0cd0c8c5bd [eas-json] move ubuntu 18 images to ubuntu 20 (#2137)
* [eas-json] move ubuntu 18 images to ubuntu 20

* update CHANGELOG.md

* Update CHANGELOG.md

Co-authored-by: Stanisław Chmiela <[email protected]>

---------

Co-authored-by: Stanisław Chmiela <[email protected]>
2023-11-28 13:58:03 +01:00
Szymon Dziedzic ffa45bbefa [eas-cli] bump @expo/steps version (#2130)
* [eas-cli] bump @expo/steps version

* update CHANGELOG.md
2023-11-27 18:16:25 +01:00
Szymon Dziedzic 8c8b354bee [ENG-10464][eas-cli] throw error if custom build config is gitignored (#2123)
* [eas-cli] throw error if custom build config is gitignored

* update CHANGELOG.md
2023-11-22 18:28:51 +01:00
Expo CI 5b46476175 update CHANGELOG.md 2023-11-20 11:25:56 +00:00
Szymon Dziedzic 8817842b72 v5.9.1 2023-11-20 12:18:46 +01:00
Radosław Krzemień 1db76a1b2d [eas-cli] Always refresh provisioning profile (#2125)
* [eas-cli] Always refresh provisioning profile

There are other cases when the provisioning profile needs to be refreshed before use and which cannot be easily validated at runtime without bigger changes in the code. It is simpler (at least for the time being) to just refresh existing profile each time

See: https://exponent-internal.slack.com/archives/C9PRD479V/p1700236895524379
https://exponent-internal.slack.com/archives/C9PRD479V/p1700229660146479

* [eas-cli] Fix test

Added missing mock

See: https://exponent-internal.slack.com/archives/C9PRD479V/p1700236895524379
https://exponent-internal.slack.com/archives/C9PRD479V/p1700229660146479

* update CHANGELOG.md
2023-11-20 12:17:24 +01:00
Szymon Dziedzic 50d78b04d8 [ENG-9772][eas-cli] don't ask user to install dev client if running in non-interactive mode (#2124)
* [eas-cli] don't ask user to install dev client if running in non ineractive mode

* update CHANGELOG.md
2023-11-17 12:41:00 +01:00
Expo CI a541a2e534 update CHANGELOG.md 2023-11-15 10:28:35 +00:00
Szymon Dziedzic 97c6df81af v5.9.0 2023-11-15 11:20:45 +01:00
Szymon Dziedzic e300d7a118 upgrade eas-cli-local-build-plugin to 1.0.49 2023-11-15 11:20:36 +01:00
Szymon Dziedzic 7c48b2bd35 [ENG-9927][eas-cli] add profile flag to eas build:run command (#2035)
* [eas-cli] add `profile` flag to `eas build:run` command

* update CHANGELOG.md
2023-11-15 10:35:16 +01:00
Expo CI fea791300d update CHANGELOG.md 2023-11-13 12:52:45 +00:00
Radoslaw Krzemien 54b1028cd6 v5.8.0 2023-11-13 13:43:01 +01:00
Radosław Krzemień 8e7c668f47 [eas-cli] [ENG-10555] Fix device provsioning (#2119)
* [eas-cli] Fix provisioning

Updates provisioning profile in EAS before using it if the profile id matches the one from apple but the list of devices does not

See: https://linear.app/expo/issue/ENG-10555/ios-devices-get-stuck-in-failed-to-provision-state-until-the-developer

* [eas-cli] Add test

Added test for a newly added case

See: https://linear.app/expo/issue/ENG-10555/ios-devices-get-stuck-in-failed-to-provision-state-until-the-developer

* update CHANGELOG.md

* [eas-cli] Remove unused import

Remove unused import

See: https://linear.app/expo/issue/ENG-10555/ios-devices-get-stuck-in-failed-to-provision-state-until-the-developer
2023-11-13 13:40:51 +01:00
Szymon Dziedzic cd6a0c49f3 [eas-cli] update @expo/package-manager to 1.1.2 (#2118)
* [eas-cli] update `@expo/package-manager` to `1.1.2`

* update CHANGELOG.md
2023-11-10 17:46:01 +01:00
Szymon Dziedzic 5e31a9d55d remove unused Log import to fix typecheck 2023-11-10 14:37:13 +01:00
Quinlan Jung cd3a62ec80 [eas-cli] move rollouts out of dev preview (#2114)
* [eas-cli] move rollouts out of dev preview

* update CHANGELOG.md
2023-11-08 12:25:54 -08:00
Expo CI abda123286 update CHANGELOG.md 2023-11-08 12:14:17 +00:00
Szymon Dziedzic 5815b8b3a8 v5.7.0 2023-11-08 12:34:03 +01:00
Szymon Dziedzic 1bdab63799 fix changelog 2023-11-08 12:33:34 +01:00
Szymon Dziedzic c33334bbee upgrade eas-cli-local-build-plugin to 1.0.48 2023-11-08 12:31:42 +01:00
Szymon Dziedzic 9f4e39b21f [ENG-10566][eas-cli] add selectedImage and customNodeVersion to build metadata (#2113)
* [eas-cli] add `selectedImage` to build metadata

* bump eas-build-job

* update gql schema

* update CHANGELOG.md
2023-11-08 12:29:09 +01:00
Will Schurman f8cfa009c1 [eas-cli] Add link to SDK upgrade page for SDK-gated command error (#2106) 2023-10-31 14:20:51 -07:00
Evan Bacon 2600805da2 Add EXPO_APPLE_TEAM_ID and EXPO_APPLE_PROVIDER_ID support (#2091) 2023-10-30 09:36:50 -07:00
Expo CI 335fb1f55e update CHANGELOG.md 2023-10-27 11:32:31 +00:00
Stanisław Chmiela d2bb189cf3 v5.6.0 2023-10-27 13:25:22 +02:00
Stanisław Chmiela dc25b207b9 [eas-cli] Add buildArtifactsUrl to eas-cli build:view --json (#2102)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

It's nice to be able to download both application archive and "build artifact".

# How

Added `buildArtifactsUrl` to GraphQL query. It contains the thing I'm looking for.

# Test Plan

Ran `easd build:view 7bab0c66-d917-4873-9241-c5fa0b01dec1 --json` and confirmed it has the new field.
2023-10-27 12:53:07 +02:00
Radosław Krzemień 5bf7f279fd [eas-cli] [ENG-8973] Use selected builds profile name if not specified (#2101)
* [eas-cli] Use selected build's profile if possible

When user calls `eas submit` command without specified profile name, the command used to default to `production` profile. If the user then selects one of their builds from EAS which was built with another build profile, then using the `production` submit profile may be confusing. Now, after selecting the build from EAS, command checks if a corresponding submit profile with the same name as seleted build's build profile exists, and if so uses that submit profile

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* [eas-cli] Add tests

Added tests for IosSubmitCommand and AndroidSubmitCommand checking what params are passed to SubmissionMutation when user selects build from EAS and corresponding submit profile exists/does not exist

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* [eas-cli] Add tests

Added tests for new `refreshContextSubmitProfileAsync` function

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* [eas-cli] Remove unused imports

Removed unused imports

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* update CHANGELOG.md

* [eas-cli] Don't refresh submit profile if profile specified by the user

Just noticed that if the user does specify the profile and then selects the build from EAS, that specified profile would then be overwritten by the profile corresponding with the one from selected build. This shouldn't be the case and this commit fixes that

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* [eas-cli] Add tests

Added tests for cases when matching submit profile exists for selected build but the user explicitly specifies another profile when calling the command

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile

* [eas-cli] Adjust error message

Function should not be called when user specifies the profile name so the message is adjusted to not include such a case

See: https://linear.app/expo/issue/ENG-8973/eas-submit-doesnt-use-asc-configuration-from-selected-build-profile
2023-10-26 15:20:05 +02:00
Szymon Dziedzic fe710d03fa [ci] fix release-changelog workflow (#2100) 2023-10-25 12:38:12 +02:00
Szymon Dziedzic 7c7d6ec118 update changelog 2023-10-25 12:32:44 +02:00
Szymon Dziedzic b9064287fc v5.5.0 2023-10-25 12:00:01 +02:00
Szymon DziedzicandStanisław Chmiela 239ad4a88e [ENG-10384][eas-json] update eas.schema.json to include new JDK 17 image (#2099)
* [eas-json] update `eas.schema.json` to include new JDK 17 image

* Update packages/eas-json/schema/eas.schema.json

Co-authored-by: Stanisław Chmiela <[email protected]>

* update CHANGELOG.md

---------

Co-authored-by: Stanisław Chmiela <[email protected]>
2023-10-25 11:55:02 +02:00
dependabot[bot] 7374a93f03 Bump undici from 5.19.1 to 5.26.3 (#2088)
Bumps [undici](https://github.com/nodejs/undici) from 5.19.1 to 5.26.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.19.1...v5.26.3)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 12:05:43 +02:00
dependabot[bot] b2bab3a523 Bump @babel/traverse from 7.12.5 to 7.23.2 (#2089)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.12.5 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 12:02:52 +02:00
Szymon Dziedzic e8df9cdeb6 [eas-cli][eas-json] remove Intel resource class related code (#2093)
* [eas-cli][eas-json] remove Intel resource class related code

* fix types

* update CHANGELOG.md
2023-10-19 12:13:04 +02:00
Will Schurman 14980fe6e8 [expo-updates] Gate roll back to embedded to expo-updates >= 0.19.0 (#2094) 2023-10-18 15:39:01 -07:00
Josh McFarlin 743ae7c648 [eas-cli] VCS Context Fields to replace helper functions (#2086)
* [eas-cli] Add context fields for vcs clients

* [eas-cli] Replace vcs client helper with new context field

* [eas-cli] Pass instance of vcs client in helpers instead of re-calling vcs helper

* update CHANGELOG.md
2023-10-18 14:18:20 -07:00
Will Schurman 5ed155e67a [ci] Try to fix changelog-bot usage of hub CLI (#2095) 2023-10-18 12:05:11 -07:00
Radosław Krzemień aa14f21597 [eas-cli] [ENG-10334] Display device creation date (#2092)
* [eas-cli] Add createdAt to AppleDeviceFragment

Added a new field to gql type to fetch it later

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt

* [eas-cli] Generate gql schema

Generated a new graphql schema based on updated local version of www

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt

* [eas-cli] Fix mutation

Fixed mutation that started to fail after schema update

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt

* [eas-cli] Display creation date

Added creation date to the AppleDevice formatting function used to display details in cli

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt

* [eas-cli] Add changelog

Manually added the changelog entry since the automation failed for some reason

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt
2023-10-18 17:52:04 +02:00
Will Schurman 91787e1f76 [eas-update] Increase asset upload timeout to 90s and reset on upload retry for slow connections (#2085) 2023-10-16 10:30:28 -07:00
Radoslaw Krzemien 474c95a7b7 Revert "[eas-cli] Add createdAt to AppleDeviceFragment"
This reverts commit 1948fd6e81.
2023-10-16 16:03:49 +02:00
Radoslaw Krzemien 0e4f00df2f Revert "[eas-cli] Generate gql schema"
This reverts commit 8df2a08b20.
2023-10-16 16:03:36 +02:00
Radoslaw Krzemien c5d75568ef Revert "[eas-cli] Fix mutation"
This reverts commit 606ce9a1ff.
2023-10-16 16:03:25 +02:00
Radoslaw Krzemien 4982e12bf6 Revert "[eas-cli] Display creation date"
This reverts commit 41c039c428.
2023-10-16 16:03:09 +02:00
Radoslaw Krzemien 41c039c428 [eas-cli] Display creation date
Added creation date to the AppleDevice formatting function used to display details in cli

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt
2023-10-16 15:31:07 +02:00
Radoslaw Krzemien 606ce9a1ff [eas-cli] Fix mutation
Fixed mutation that started to fail after schema update

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt
2023-10-16 15:30:19 +02:00
Radoslaw Krzemien 8df2a08b20 [eas-cli] Generate gql schema
Generated a new graphql schema based on updated local version of www

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt
2023-10-16 15:29:27 +02:00
Radoslaw Krzemien 1948fd6e81 [eas-cli] Add createdAt to AppleDeviceFragment
Added a new field to gql type to fetch it later

See: https://linear.app/expo/issue/ENG-10334/show-creation-time-of-device-record-in-ad-hoc-device-selection-prompt
2023-10-16 15:21:33 +02:00
Alan Hughes 49b6e1f9e4 [eas-cli] add account type to project owner prompt (#2083) 2023-10-11 09:56:27 +01:00
dependabot[bot] a56abe33dd Bump graphql from 16.6.0 to 16.8.1 (#2060)
Bumps [graphql](https://github.com/graphql/graphql-js) from 16.6.0 to 16.8.1.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v16.6.0...v16.8.1)

---
updated-dependencies:
- dependency-name: graphql
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 11:13:56 -07:00
Kadi Kraman feaedbe634 [eas-cli] [ENG-10225] add requiredPackageManager to build metadata (#2067)
* Add requiredPackageManager to build metadata

* Use correct version in changelog

* Fix changelog

* Update @expo/eas-build-job library

* Update graphql schema

* Update gql schema
2023-10-03 11:28:38 +01:00
Expo CI 0fba735f7a update CHANGELOG.md 2023-09-28 22:12:53 +00:00
Brent Vatne 480bc30468 v5.4.0 2023-09-28 15:01:57 -07:00
Jakov Glavina c1c9e06f76 feat: add tap to pay on iphone entitlement (#2069) 2023-09-28 14:44:06 -07:00
Expo CI 5fb68a652b update CHANGELOG.md 2023-09-28 19:31:07 +00:00
Brent Vatne 056d0b1017 v5.3.1 2023-09-28 12:22:45 -07:00
Szymon Dziedzic cc99794bbd [ENG-10176][eas-json] update images info in eas.schema.json (#2068) 2023-09-28 11:59:43 -07:00
Expo CI 04049a8d1f update CHANGELOG.md 2023-09-25 16:41:24 +00:00
Szymon Dziedzic 5bdd46a2a9 v5.3.0 2023-09-25 18:27:59 +02:00
Szymon Dziedzic a7e88bd7cb upgrade eas-cli-local-build-plugin to 1.0.44 2023-09-25 18:27:06 +02:00
Kadi KramanandSzymon Dziedzic 78b75c6224 [eas-cli] [eas-json] [ENG-10069] add support for bun (#2055)
* Update eas-build-job dependency

* Add bun to eas-cli

* Add bun to eas-json

* Add changelog

* Use correct PR in changelog

* Update packages/eas-json/schema/eas.schema.json

Co-authored-by: Szymon Dziedzic <[email protected]>

* Update gql schema

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2023-09-25 16:54:23 +01:00
Szymon Dziedzic 52f54fc7ae fix tests 2023-09-18 10:26:36 +02:00
Piotr Szeremeta 7ddd630cfd [FIX] Remove semver validation from node version read from .nvmrc (#2052)
* [FIX] Remove semver validation from node version read from .nvmrc

Node version can be specified as an alias (such as `iojs`, `lts/hydrogen` etc), so validating it as semver may cause errors for some users.

* update CHANGELOG.md
2023-09-18 08:28:39 +02:00
_nderscore 8751a2835f feat(eas-cli): expose expo export dev flag as an option in eas update (#2050) 2023-09-13 13:00:01 -07:00
Piotr Szeremeta da9829a853 [ENG-5266] Suggest correct bundleId when account name not available (#2027) 2023-09-11 14:56:53 +02:00
Quinlan Jung cc822b18f3 [eas-cli] rollouts: more robust printing function (#2047)
* [eas-cli] rollouts: more robust printing function

* update CHANGELOG.md
2023-09-06 11:10:44 -07:00
Expo CI bcab3bcb09 update CHANGELOG.md 2023-09-05 20:58:51 +00:00
Quinlan Jung a5dc0bd27c v5.2.0 2023-09-05 13:50:41 -07:00
Quinlan Jung 54127c4e37 [ENG-9828][eas-cli] rollouts: view action for CI (#2040)
* [eas-cli] rollouts: non-interactive view

* update CHANGELOG.md

* rebase
2023-09-05 13:49:12 -07:00
Quinlan Jung 9097ab2537 [ENG-9602][eas-cli] rollouts: json output for ci (#2037)
* [eas-cli] rollouts: json output for ci

* update CHANGELOG.md

* fix tests
2023-09-05 13:01:20 -07:00
Quinlan Jung 4e2029a5c5 [eas-cli] rollout tests (#2042)
* [eas-cli] rollout tests

* update CHANGELOG.md
2023-09-05 12:30:31 -07:00
Quinlan Jung ad02a056d7 [eas-cli] remove unreachable codesigning option (#2041)
* [eas-cli] remove unreachable option

* update CHANGELOG.md
2023-09-05 12:29:23 -07:00
Quinlan Jung ea9f734b11 [eas-cli] fix graphql (#2039)
* [eas-cli] fix graphql

* update CHANGELOG.md
2023-09-05 12:27:54 -07:00
Piotr Szeremeta 7db9d79c55 [Fix] Remove doubled definition of pnpm field from eas.schema.json (#2038) 2023-09-04 07:44:39 +02:00
Quinlan Jung 2e31b42b76 [eas-cli] only print channel-branch pairing if we created a channel (#2036)
* [eas-cli] only print channel-branch pairing if we created a channel

* update CHANGELOG.md
2023-09-01 12:38:42 -07:00
Expo CI 66d02d9724 update CHANGELOG.md 2023-09-01 14:14:32 +00:00
Piotr Szeremeta 7c96cc10cf v5.1.0 2023-09-01 16:05:53 +02:00
Piotr Szeremeta 523a781ba5 upgrade eas-cli-local-build-plugin to 1.0.42 2023-09-01 16:05:39 +02:00
Piotr Szeremeta 6cf8bd5c5a [ENG-9473] Support installing selected pnpm version (#1988)
* [wip][ENG-9473]

* [ENG-9473] Add `pnpm` option to `builderEnvironment`.

* update CHANGELOG.md

* Fix changelog
2023-09-01 15:26:28 +02:00
dependabot[bot] 67e9ea706b Bump word-wrap from 1.2.3 to 1.2.5 (#2033)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-30 17:04:07 +02:00
Szymon Dziedzic c22f410889 [eas-cli][eas-json] bump semver version (#2030)
* [eas-cli][eas-json] bump `semver` version

* update scripts yarn.lock
2023-08-30 16:00:49 +02:00
Radosław Krzemień 9ff47af4fa [eas-cli] [ENG-7536] Stop app config error repeating (#2020)
* [eas-cli] Stop app config error repeating

If app config error is thrown it is re-thrown with modified message instead of printing it to the as a "cryptic error" and repeating with 0% chances of success (since the config is incorrect)

See: https://linear.app/expo/issue/ENG-7536/make-app-config-error-on-credentials-command-not-repeat-indefinitely

* [eas-cli] Create custom error

Replaced generic error with specific message with a custom error class, which allows for not relying on a message to be exactly as expected

See: https://linear.app/expo/issue/ENG-7536/make-app-config-error-on-credentials-command-not-repeat-indefinitely

* update CHANGELOG.md

* [eas-cli] Remove blank line

Removed blank line from changelog

See: https://linear.app/expo/issue/ENG-7536/make-app-config-error-on-credentials-command-not-repeat-indefinitely
2023-08-30 09:11:00 +02:00
Quinlan Jung 324dce8fc0 [eas-cli] fix printing bug: branch with no update (#2023)
* [eas-cli] fix branch with no update printing bug

* tests

* update CHANGELOG.md
2023-08-29 14:07:35 -07:00
Expo CI b30c86f0c0 update CHANGELOG.md 2023-08-29 11:51:19 +00:00
Szymon Dziedzic 008279e1dd v5.0.2 2023-08-29 13:15:39 +02:00
Cedric van Putten d00814f84b fix(eas-cli): implement variadic --platform flag when creating EAS updates (#1939)
* fix(eas-cli): implement variadic `--platform` flag when creating EAS updates

* fix(eas-cli): finish todo and set minimum `@expo/[email protected]` version for multiple `--platform` flags during export

* update CHANGELOG.md

* fix(eas-cli): be verbose about `--platform=<flag>` in bundle script
2023-08-29 13:11:49 +02:00
Expo CI 81aa127e28 update CHANGELOG.md 2023-08-28 19:05:12 +00:00
Szymon Dziedzic d321a0bccb v5.0.1 2023-08-28 20:54:10 +02:00
Szymon Dziedzic a1d86549ed [eas-cli] fix eas update passing platform argument incorrectly to expo-cli (#2028)
* [eas-cli] fix `eas update` passing `platform` argument incorrectly to `expo-cli`

* update CHANGELOG.md
2023-08-28 20:51:18 +02:00
Expo CI f7e9af913c update CHANGELOG.md 2023-08-28 14:00:12 +00:00
Szymon Dziedzic 137f9d4ac7 v5.0.0 2023-08-28 14:20:36 +02:00
Szymon Dziedzic 2ac863a9e3 upgrade eas-cli-local-build-plugin to 1.0.39 2023-08-28 14:19:20 +02:00
Quinlan Jung 11be9eba49 [ENG-9685][eas-cli] create a linked channel on build if not exists (#2017)
* [eas-cli] create linked channel on build

* refactor

* update CHANGELOG.md

* amend doc block
2023-08-25 12:07:59 -07:00
Will Schurman b36db15601 [eas-cli][updates] configure updates as well when somebody tries to run a build with channel set (#2016) 2023-08-25 09:14:29 -07:00
Piotr Szeremeta 93c5dc1a32 [ENG-9422] nvmrc support for setting node version (#1998)
* [ENG-9422][eas-cli] Use .nvmrc to set node version

* update CHANGELOG.md
2023-08-25 15:46:55 +02:00
Szymon Dziedzic c86550c0c8 [eas-cli][eas-json] add developmentClient to metadata (#2015)
* [eas-cli][eas-json] add `developmentClient` to metadata

* update CHANGELOG.md
2023-08-25 13:26:33 +02:00
Brent Vatne 36b4296d3c [.github] Add issue template config 2023-08-24 17:09:47 -07:00
Brent Vatne 8c08541ca3 Create SECURITY.md 2023-08-24 17:06:03 -07:00
Will Schurman 5a1406d39c [eas-cli] Add info to EAS Update asset upload process about asset counts and limits (#2013) 2023-08-24 09:22:30 -07:00
Radosław Krzemień 78ea88cc5f [eas-cli] [ENG-7591] Detect not provisioned devices (#2011)
* [eas-cli] Detect not provisioned devices

After selecting devices to be provisioned and making a call to App Store Connect API the result is compared with the list of chosen devices. If any are missing, a message is displayed to the user

See: https://linear.app/expo/issue/ENG-7591/detect-processing-devices-on-apple-developer-portal-and-dont-allow-the

* [eas-cli] Adjust message

Updated the message with the link to the devices page in the Apple Developer portal

See: https://linear.app/expo/issue/ENG-7591/detect-processing-devices-on-apple-developer-portal-and-dont-allow-the

* update CHANGELOG.md
2023-08-24 15:35:48 +02:00
Will Schurman 6cb02d0ccd [eas-cli][eas-update] Support republishing roll back to embedded updates (#2006) 2023-08-15 11:39:24 -04:00
Will Schurman d6c7623718 [eas-cli][eas-update] Add rollback disambiguation command (#2004) 2023-08-15 11:36:18 -04:00
Quinlan Jung 32142b4fe8 [eas-cli] more branch mapping utils (#2003)
* [eas-cli] more branch mapping utils

* update CHANGELOG.md
2023-08-14 14:51:29 -07:00
Will Schurman 6bdee0f395 [eas-cli][update] Only export at most ios and android dist (#2002) 2023-08-14 17:28:06 -04:00
Quinlan Jung baf94a21c3 [eas-cli] more branch map utility functions (#2001)
* [eas-cli] more branch map utility functions

* update CHANGELOG.md

* feedback
2023-08-11 11:35:47 -07:00
Radoslaw Krzemien 162d64bce6 Revert "[eas-cli] Update oclif version"
This reverts commit d16c1fc757.
2023-08-11 12:22:56 +02:00
Radoslaw Krzemien d16c1fc757 [eas-cli] Update oclif version
Updated oclif version

See: https://linear.app/expo/issue/ENG-9175/update-oclif-in-eas-cli
2023-08-11 12:22:17 +02:00
Expo CI e42ea7e2ec update CHANGELOG.md 2023-08-10 10:26:10 +00:00
Szymon Dziedzic 5fea1ad16e v4.1.2 2023-08-10 12:12:49 +02:00
Szymon Dziedzic 2a57a2eb3d upgrade eas-cli-local-build-plugin to 1.0.38 2023-08-10 12:12:41 +02:00
Quinlan Jung 5628b78523 [eas-cli] make branch mapping utility files have no dependencies (#1993)
* [eas-cli] generalize branch mapping utils

* make standalone

* update CHANGELOG.md
2023-08-09 15:32:19 -07:00
Expo CI 19d1e6bffd update CHANGELOG.md 2023-08-08 18:36:49 +00:00
Quinlan Jung 88b068511c v4.1.1 2023-08-08 11:28:16 -07:00
Quinlan Jung 842efcf3ab upgrade eas-cli-local-build-plugin to 1.0.35 2023-08-08 11:28:02 -07:00
Quinlan Jung f5f08aa506 [eas-cli] rollouts websupport (#1997)
* [eas-cli] rollouts websupport

* update CHANGELOG.md

* fix changelog
2023-08-08 11:26:18 -07:00
Expo CI 9f7f412cfb update CHANGELOG.md 2023-08-08 13:19:03 +00:00
Szymon Dziedzic e369ec1e25 v4.1.0 2023-08-08 15:10:39 +02:00
Szymon Dziedzic b353441f31 upgrade eas-cli-local-build-plugin to 1.0.34 2023-08-08 15:10:24 +02:00
Szymon Dziedzic 4db35e73c6 [eas-cli] bump @expo/steps version (#1995) 2023-08-08 15:02:33 +02:00
Szymon Dziedzic 10bc872dc9 [eas-json][eas-cli] add global withoutCredentials property to eas.json (#1994)
* [eas-json][eas-cli] add global `withoutCredentials` property to `eas.json`

* update CHANGELOG.md

* fix tests
2023-08-08 14:13:25 +02:00
Szymon Dziedzic fedc39ea48 [eas-cli] pass iOS credentials to custom builds (#1989)
* [eas-cli] pass iOS credentials to custom builds

* update CHANGELOG.md
2023-08-08 13:55:18 +02:00
Expo CI 6046723769 update CHANGELOG.md 2023-08-08 08:39:19 +00:00
Quinlan Jung afaee9df38 v4.0.1 2023-08-07 16:28:14 -07:00
Quinlan Jung de39d6f00d [eas-cli] account for empty branch mapping (#1992)
* [eas-cli] account for empty branch mapping

* update CHANGELOG.md
2023-08-07 16:24:32 -07:00
Expo CI 401751fffa update CHANGELOG.md 2023-08-07 20:40:01 +00:00
Quinlan Jung 7f7f1c6a9a v4.0.0 2023-08-07 13:31:43 -07:00
Quinlan Jung 279d6716d2 [ENG-9617][eas-cli] release rollout into developer preview (#1986)
* [eas-cli] release rollout into developer preview

* update CHANGELOG.md
2023-08-07 13:14:52 -07:00
Quinlan Jung 92977d9a53 [ENG-9666][eas-cli] rollouts: link fyi (#1991)
* [eas-cli] rollouts: link fyi:

* update CHANGELOG.md
2023-08-07 13:14:25 -07:00
Quinlan Jung 31871a9eda [ENG-9614][eas-cli] rollout ux improvements (#1984)
* [eas-cli] rollout ux improvements

* more efficient 'only one' detection

* update CHANGELOG.md

* fix tsc

* amend tests to account for new behaviour
2023-08-07 12:59:16 -07:00
Quinlan Jung f122f42149 [eas-ci] rollouts: fix fp precision (#1985)
* [eas-ci] rollouts: fix fp precision

* update CHANGELOG.md
2023-08-07 10:51:26 -07:00
Quinlan Jung a9a49d6b97 [eas-cli] more grammar fixes (#1980)
* replace 'route', no more plate emoji

replace plate emoji

more grammar nits

* update CHANGELOG.md

* szdziedzic feedback

* wschurman feedback

* send -> put

* put -> remain
2023-08-04 12:59:58 -07:00
Quinlan Jung 1d94414334 [ENG-9603][eas-cli] rollout edit edge case (#1981)
* [eas-cli] rollout edit edge case

* update CHANGELOG.md

* warn only

* warn only

* lint
2023-08-04 12:27:01 -07:00
Quinlan Jung 0b73dc32f6 [eas-cli] use 'roll out' instead of rollout for verbs (#1979)
* [eas-cli] use 'roll out' instead of rollout for verbs

* update CHANGELOG.md
2023-08-04 11:30:03 -07:00
Radosław Krzemień ef8ad24639 [eas-cli] [ENG-8475] Add option to add current device automatically (#1943)
* [eas-cli] Add option to add current device automatically

Added a new option to `device:create` command to add a current device by automatically grabbing the provisioning UDID of the current Apple Silicon machine. If the current machine is not Apple Silicon, the command fails with proper message. If the user does not proceed when prompted it also fails since trying again with the same UDID does not make much sense

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Adjust log messages

Improved the log message from testing phase

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Fix test

The `currentMachineMethod` tests used to be platform dependent, since the function checks for the architecture of the machine. This check is now mocked to make the tests platform agnostic

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Adjust message

On failed arch check the message was displayed twice, now it is displayed once

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Fix tests

The tests were still platform dependent since the system profiler is only viable for Macs. This should fix it

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Remove timeout

Jest.timeout was leftover from testing

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Re-generate gql schema

Generated schema again from production

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Re-generate gql schema

Generated schema again from production

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Fix test

Fix test that was failing after re-generating gql schema

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Add mac device class

Added the mac apple device class and made it default for current machine method

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Generate gql schema

Generated the schema again after merge from main

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Remove `arch` check

More systems than Apple Silicon can have `arch` of `arm64`

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* update CHANGELOG.md

* [eas-cli] Set mac device class automatically

It doesn't make sense to give the user choice of device class if we previously check if the current machine is an Apple Silicon Mac

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Remove unused import

Import became redundant after last changes

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Check for platform

Updated the apple silicon check to check the platform as well

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Update command description

The command description had a typo and mentioned only iPhones and iPads

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli

* [eas-cli] Remove unused import

Remove unused import

See: https://linear.app/expo/issue/ENG-8475/investigate-grabbing-registering-macos-provisioning-udid-from-eas-cli
2023-08-04 14:28:13 +02:00
Will Schurman 24dbe6e7cc [eas-cli] Fix rollout-preview ending with republish with code signing (#1978) 2023-08-03 15:30:04 -07:00
Expo CI aad026febb update CHANGELOG.md 2023-08-03 18:07:08 +00:00
Quinlan Jung 6ae8125c0e v3.18.3 2023-08-03 10:58:19 -07:00
Quinlan Jung 87d143b310 [ENG-8715][eas-cli] get channel:{view,list,edit} to play nice with rollouts (#1974)
* [eas-cli] get channel:{view,list,edit} to play nice with rollouts

* tests

* update CHANGELOG.md
2023-08-03 10:55:20 -07:00
Will Schurman 346dee9ea5 [eas-cli] Fix republishing with code signing (#1973) 2023-08-03 10:48:47 -07:00
Quinlan Jung 4308d21927 [eas-cli] rollout relationships (#1972)
* [eas-cli] rollout relationships

* update CHANGELOG.md
2023-08-03 10:18:18 -07:00
Expo CI 207f623338 update CHANGELOG.md 2023-08-03 12:53:11 +00:00
Szymon Dziedzic 601101844c v3.18.2 2023-08-03 14:43:28 +02:00
Szymon Dziedzic 7027eaa974 upgrade eas-cli-local-build-plugin to 1.0.33 2023-08-03 14:43:17 +02:00
Szymon Dziedzic b3203345ea Revert "[ENG-9422][eas-cli] Use .nvmrc to set node version (#1954)" (#1976)
* Revert "[ENG-9422][eas-cli] Use .nvmrc to set node version (#1954)"

This reverts commit 0b5194b1a5.

* update CHANGELOG.md
2023-08-03 14:35:28 +02:00
Szymon Dziedzic 5ed2d01146 [eas-cli][metadata] use just a comma to concatenate keywords (#1967)
* [eas-cli][metadata] use just a comma to concatenate keywords

* update CHANGELOG.md
2023-08-03 14:30:24 +02:00
Szymon Dziedzic 6fc64d337f fix changelog once again 2023-08-03 14:22:49 +02:00
Szymon Dziedzic f7ed4cfa20 fix changelog 2023-08-03 13:22:09 +02:00
Expo CI 47968a1b8b update CHANGELOG.md 2023-08-03 02:14:06 +00:00
Quinlan Jung 7c779b3dc0 v3.18.1 2023-08-02 19:04:22 -07:00
Quinlan Jung 50474ec5e9 [eas-cli] fix update:republish group id (#1971)
* [eas-cli] fix update:republish group id

* update CHANGELOG.md
2023-08-02 15:20:33 -07:00
Quinlan Jung 46ff97f173 [ENG-8616][eas-cli] change update default runtime policy (#1968)
* [eas-cli] change update default runtime policy

* update CHANGELOG.md
2023-08-02 14:59:09 -07:00
Szymon Dziedzic b263dffbde update and fix changelog 2023-08-02 23:53:24 +02:00
Quinlan Jung 8d57828f7e [ENG-9441][eas-cli] channel:rollout-unstable (#1966)
* [eas-cli] eas channel:rollout-unstable

* json unsupport

* update CHANGELOG.md

* fix tsc

* fix test

* fix lint

* new selectors using filter pagination

* replace selectors

* remove unused stuff

* branch mapping tests

* add tests for relay utilities and selectors

* at is not shipped in node14

* pr feedback
2023-08-02 14:40:40 -07:00
Szymon Dziedzic fd9561cfc3 v3.18.0 2023-08-02 23:33:31 +02:00
Szymon Dziedzic 93ce451bc7 upgrade eas-cli-local-build-plugin to 1.0.31 2023-08-02 23:33:14 +02:00
Szymon Dziedzic 554e433548 update @expo/steps and @expo/eas-build-job (#1970) 2023-08-02 23:24:35 +02:00
Szymon Dziedzic 12a1cb7011 [ENG-9482][eas-cli] provide credentials for custom Android builds (#1969)
* [eas-cli] provide credentials for custom Android builds

* update CHANGELOG.md
2023-08-02 18:06:41 +02:00
Patryk FrydaandPatryk Fryda 360de90eea [eas-cli] fix 'manged' typo in ios target (#1961)
Co-authored-by: Patryk Fryda <[email protected]>
2023-07-31 17:08:12 +02:00
Quinlan Jung ea02fcebda [eas-cli] select existing rollouts and channels (#1958)
* [eas-cli] select existing rollouts and channels

* update CHANGELOG.md
2023-07-28 09:27:17 -07:00
Piotr Szeremeta 0b5194b1a5 [ENG-9422][eas-cli] Use .nvmrc to set node version (#1954)
* [ENG-9422][eas-cli] Use .nvmrc to set node version

* update CHANGELOG.md
2023-07-28 12:26:03 +02:00
Quinlan Jung 05799aba9c [eas-cli] more branch mapping utility (#1957)
* [eas-cli] more branch mapping utility

* update CHANGELOG.md
2023-07-27 17:05:00 -07:00
Expo CI 2cece65dbf update CHANGELOG.md 2023-07-27 14:32:42 +00:00
Szymon Dziedzic 17b6b4ec45 v3.17.1 2023-07-27 15:37:26 +02:00
Szymon Dziedzic 4f892c66e7 [eas-cli] update @expo/steps version (#1956) 2023-07-27 15:15:09 +02:00
Quinlan Jung fd99365655 [eas-cli] fetch entire relay compliant dataset (#1955)
* [eas-cli] fetch entire relay compliant dataset

* update CHANGELOG.md
2023-07-26 20:48:21 -07:00
Quinlan Jung 7dca2c8021 [eas-cli] revert UpdateBranchWithCurrentGroupFragment (#1952)
* [eas-ci] revert UpdateBranchWithCurrentGroupFragment

* update CHANGELOG.md
2023-07-26 10:32:11 -07:00
Quinlan Jung 55bce8d82a [eas-cli] update channel basic tests and fixtures (#1950) 2023-07-25 09:37:47 -07:00
Quinlan Jung 1f5533e5e3 [eas-cli][1/2] unify channel graphql query types (#1949)
* [eas-cli] unify channel graphql query types

* update CHANGELOG.md
2023-07-25 09:34:12 -07:00
Expo CI 51d0555baa update CHANGELOG.md 2023-07-24 17:24:12 +00:00
Szymon Dziedzic 2137176a65 v3.17.0 2023-07-24 19:16:40 +02:00
Szymon Dziedzic 3fda52c8d7 [eas-cli] handle server side defined error for situation when intel-medium is not available and remove intel-medium for eas.schema.json (#1947)
* [eas-cli] handle server side defined error when `intel-medium` resource class is not available

* don't suggest using `intel-medium` in `eas.schema.json`

* update CHANGELOG.md

* update CHANGELOG.md
2023-07-24 19:08:10 +02:00
Quinlan Jung b252b5b582 [eas-cli] utils: amend rollouts definition (#1945)
* [eas-cli] utils: amend rollouts definition

* update CHANGELOG.md
2023-07-24 09:10:07 -07:00
Szymon Dziedzic 2befe410cf [eas-cli] allow for rollout option when configuring submission (#1938)
* [eas-cli] allow for `rollout` option when configuring submission

* update CHANGELOG.md

* add entry to `eas.schema.json`

* improve eas.schema.json

* make rollout default to 1 if not set

* Revert "make rollout default to 1 if not set"

This reverts commit 2ab689b02c.

* generate schema against staging

* update fragment
2023-07-24 14:05:10 +02:00
Quinlan Jung 1463e0de74 [eas-cli] branch mapping utility functions (#1944)
* [eas-cli] branch mapping primitives

* refactor

* extra test

* update CHANGELOG.md
2023-07-20 12:45:40 -07:00
Piotr Szeremeta e963558ef6 [ENG-8956][eas-build] Improve EAS CLI tsconfig.json for local development (#1933)
Changed noUnusedLocals and noUnusedParameters to false for easier local development and experimenting.
These checks do not run during local development. Instead, `yarn build` catches these cases on github.
2023-07-20 16:35:52 +02:00
Quinlan Jung fe57c055b6 [eas-cli] bump urql graphql client (#1936)
* [eas-cli] bump urql graphql client

* update CHANGELOG.md

* fix test
2023-07-19 10:34:04 -07:00
Expo CI 045840111e update CHANGELOG.md 2023-07-18 16:13:46 +00:00
Szymon Dziedzic 9a6068c1f2 v3.16.0 2023-07-18 18:04:52 +02:00
Szymon Dziedzic d06a77bd6d [eas-cli] fix incorrect handling of valid inProgress release status (#1934)
* [eas-cli] fix incorrect handling of valid `inProgress` release status

* update CHANGELOG.md

* add another test
2023-07-18 15:47:45 +02:00
Szymon Dziedzic 7bcf83d141 Revert "add another test"
This reverts commit cd9323e115.
2023-07-18 15:34:09 +02:00
Szymon Dziedzic cd9323e115 add another test 2023-07-18 15:33:15 +02:00
Firas Regaieg 34145c38ca [Build] Print friendly error msg in case account doesn't have required permission (#1867)
* Print friendly error msg (reviewed)

Comment: This is useful when credentials fail, if user doesn't have permissions.

* Print friendly error msg - 2nd review

* Print friendly error msg - 2nd review (reviewed)

* Replace changelog event under main section
2023-07-18 15:28:33 +02:00
Piotr SzeremetaandSzymon Dziedzic 57dd372957 [ENG-9283][eas-cli] Throw error when project tarball exceeds 1GB (#1928)
* [ENG-9283][eas-cli] Throw error when project tarball exceeds 1GB

* update CHANGELOG.md

* Update packages/eas-cli/src/build/build.ts

Co-authored-by: Szymon Dziedzic <[email protected]>

* Update packages/eas-cli/src/build/build.ts

Co-authored-by: Szymon Dziedzic <[email protected]>

* Update CHANGELOG.md

Co-authored-by: Szymon Dziedzic <[email protected]>

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2023-07-17 14:12:17 +02:00
Will Schurman 5232641217 [eas-cli] Use node server default port selection for SSO login server (#1930) 2023-07-13 09:29:51 -07:00
Cedric van Putten 3c3d3150c9 [eas-cli] Ignore the existing entitlements when ios is gitignored (#1906)
* [eas-cli] Ignore the existing entitlements when ios is gitignored

* chore: add changelog entry
2023-07-13 16:51:39 +02:00
Will Schurman fd322d4189 [eas-cli] Add styling to SSO auth redirect completion page (#1929) 2023-07-12 09:11:03 -07:00
Expo CI 77254f6be1 update CHANGELOG.md 2023-07-11 06:59:32 +00:00
Szymon Dziedzic 44fcbbaf17 v3.15.1 2023-07-11 08:51:55 +02:00
Szymon Dziedzic db841b45ee upgrade eas-cli-local-build-plugin to 1.0.27 2023-07-11 08:51:40 +02:00
Szymon Dziedzic 568669d384 [eas-cli] bump @expo/steps version (#1927) 2023-07-11 08:11:17 +02:00
Quinlan Jung aa50bf11ce [eas-cli] fix runtime logs (#1925)
* [eas-cli] fix runtime logs

* update CHANGELOG.md

* pr feedback
2023-07-10 10:37:46 -07:00
Sundeep Peswani 119133360b [eas-cli] Handle free user limit error messages (#1921)
* [eas-cli] Handle free user limit error messages

* update CHANGELOG.md
2023-07-10 13:18:20 -04:00
Radosław Krzemień 3f0f25dd46 [eas-cli] [ENG-8957] Handle multiple GraphQLErrors in CombinedError (#1924)
* [eas-cli] Handle multiple GraphQLErrors in CombinedError

CombinedError technically can have more than 1 GraphQLError inside it, even though for now we only use it with 1. In such a case it would be good for the eas-cli to be able to handle and properly log/display all the multiple error messages

See: https://linear.app/expo/issue/ENG-8957/improve-error-handling-in-eas-cli-to-support-case-when-combined-error

* [eas-cli] Revert change

Small change made for testing slipped through and got committed

See: https://linear.app/expo/issue/ENG-8957/improve-error-handling-in-eas-cli-to-support-case-when-combined-error

* update CHANGELOG.md
2023-07-10 13:07:10 +02:00
Szymon Dziedzic e821ae95e1 fix changelog (#1926) 2023-07-10 09:20:40 +02:00
Szymon Dziedzic f4e51b4fbc [eas-json] add info about new image to eas.schema.json (#1920) 2023-07-10 08:11:27 +02:00
James Ide 963bea9b03 [ENG-9093][eas-cli] Throw if useClassicUpdates is set when the developer is trying to use EAS Update (#1915)
Why
---
Fail fast with a clear message if the app config contradicts itself or the developer's behavior.

How
---
Throw if useClassicUpdates is set in the app config and the developer is running an EAS Update-related command or if the app config uses an EAS Update URL. If the developer is using a static app config (app.json), offer to modify the config file for them.

Test Plan
---
Added a unit test for ensureEASUpdateIsConfiguredAsync. Manually tested as wlel.
2023-07-05 17:32:20 -07:00
Quinlan Jung 9bf2020b2b [eas-cli] better runtime version error for bare workflow (#1910)
* [eas-cli] better runtime version error for bare workflow

* update CHANGELOG.md

* resolve conflicts
2023-07-03 14:41:27 -07:00
Brent Vatne 50e695b5a3 [eas-cli] Bump dependencies (#1911) 2023-07-03 13:08:42 -07:00
Expo CI f3577deac4 update CHANGELOG.md 2023-06-30 23:13:32 +00:00
Brent Vatne f11c0b4d34 v3.15.0 2023-06-30 16:05:25 -07:00
Szymon Dziedzic 88411e8fc0 [eas-cli] print the list of environment variables that we load in EAS CLI when evaluating app config for a build (#1909) 2023-06-30 16:04:28 -07:00
c199173704 [eas-cli] Added alias -m for --message in eas update (#1904)
* added alias -m for --message

* align with the other messages

* updated README

* moving change in update/republish to a seperate PR

* removed README comment

* update CHANGELOG.md

---------

Co-authored-by: Steven Pu <[email protected]>
Co-authored-by: Steven Pu <[email protected]>
2023-06-28 11:36:18 -07:00
800098b958 [eas-cli] Added alias -m for --message in update/republish (#1905)
* added alias in republish

* reverted change in README

* update CHANGELOG.md

---------

Co-authored-by: Steven Pu <[email protected]>
Co-authored-by: Steven Pu <[email protected]>
2023-06-28 11:16:07 -07:00
Keith Kurak 146468914f Show build profile on eas build:run (#1901)
* [eas-cli] Add build profile to build:run list items

* say "simulator" or "emulator" in warning b/c we know which one

* update CHANGELOG.md
2023-06-27 08:40:57 -04:00
Szymon Dziedzic 0e48417983 [eas-cli] print better error message when uploading the project archive tarball fails (#1897)
* [eas-cli] print better error message when uplaoding tarball failed

* use better error class

* update CHANGELOG.md
2023-06-23 11:48:08 +02:00
Wojciech Kozyra cf61f585e6 Update CODEOWNERS (#1896) 2023-06-22 13:52:23 +02:00
Quinlan Jung cc700ae263 [eas-cli] better runtime output (#1894)
* [eas-cli] better runtime output

* update CHANGELOG.md
2023-06-21 12:47:33 -07:00
Expo CI 4e94c60c7f update CHANGELOG.md 2023-06-20 09:24:16 +00:00
Szymon Dziedzic 37f8e117e1 v3.14.0 2023-06-20 11:00:45 +02:00
Szymon Dziedzic 46095d2da4 upgrade eas-cli-local-build-plugin to 1.0.24 2023-06-20 11:00:20 +02:00
Szymon Dziedzic 6c01856617 [ENG-7183][eas-cli] validate that expo-updates are in project dependencies when somebody tries to run a build with channel set (#1885)
* [eas-cli] validate that `expo-updates` are in project dependencies when somebody tries to run a build with channel set

* apply suggested changes

* remove unnecessary change

* fix warning

* improve validation logic

* improve insatlled message

* warn when expo-updates are in dev dependencies for update command

* impreve warn messsages

* improve warning messages

* one more warn message improvement

* update CHANGELOG.md
2023-06-20 10:52:21 +02:00
Wojciech Kozyra f8cd50e0ed [eas-cli] Add minimal validation for app config returned by @expo/config (#1893)
* [eas-cli] Add minimal validation for app config returned by @expo/config

* update CHANGELOG.md
2023-06-20 10:41:52 +02:00
lzkb fd0a129a4f [eas-cli] change sso flag display (#1890) 2023-06-19 13:08:27 -07:00
Quinlan Jung c44e07bfeb [eas-cli] update:configure less verbose output (#1888)
* [eas-cli] less verbose output

* update CHANGELOG.md

* pr feedback
2023-06-19 11:12:37 -07:00
Piotr Szeremeta 951583e6ea [eas-cli][ENG-8763] Select default android keystore (#1889)
* [eas-cli][ENG-8763] Select default android keystore

- Add option to change default keystore from CLI (previously available only from dashboard)
- Streamline setting up new keystore

Instead of overwriting existing keystore, new one is created every time. Also, option to immediately set it as default is added, since it is also easier to change it from CLI.

* update CHANGELOG.md
2023-06-19 16:01:14 +02:00
Quinlan Jung 3f9247ca84 [eas-cli] build:configure add channels to eas.json if using eas updates (#1887)
* [eas-cli] configure build: amend eas.json for eas updates

* update CHANGELOG.md

* pr feedback
2023-06-17 16:14:23 -07:00
lzkb c38638e94e [eas-cli] Changes for SSO user type and SSO login. (#1875)
* [eas-cli] Changes for SSO user type and SSO login.
2023-06-16 10:44:13 -07:00
Brent VatneandQuinlan Jung 3b46466a9d [eas-cli] Initialize app.json if it does not exist, add expo key if missing (#1881)
Co-authored-by: Quinlan Jung <[email protected]>
2023-06-15 19:54:31 -07:00
Brent Vatne 806085cbf1 [eas-cli] Ensure that re-thrown error includes original stack (#1882) 2023-06-15 19:45:00 -07:00
Quinlan Jung 398b41cc81 [eas-cli] updates: error gracefully if no git repo (#1884)
* [eas-cli] updates: error gracefully if no git repo

* update CHANGELOG.md
2023-06-15 12:54:37 -07:00
Quinlan Jung d23a97238b [eas-cli] error gracefully if expo pkg not installed (#1883)
* [eas-cli] error gracefully if expo pkg not installed

* update CHANGELOG.md

* remove canadian language
2023-06-15 12:52:29 -07:00
Szymon Dziedzic 5b6f312ccb [eas-cli] bump @expo/steps version (#1886) 2023-06-15 15:20:54 +02:00
Aman MittalandWojciech Kozyra 2bcab0f2b4 [eas-cli] Update eas build:configure command to show link of eas.json when generated (#1878)
Co-authored-by: Wojciech Kozyra <[email protected]>
2023-06-14 16:15:53 +05:30
Stanisław Chmiela b90ce63067 Remove direct @types/bunyan dependency (#1880)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Cleanup after https://github.com/expo/eas-build/pull/233.

# How

Upgraded `@expo/steps` package. It is the only `eas-build` package in here.

# Test Plan

It's a type-only change so CI success will be enough.
2023-06-13 17:05:37 +02:00
Szymon Dziedzic be02aced9d [eas-cli] update @expo/steps (#1876)
* [eas-cli] update `@expo/steps`

* update lockfile

* install @types/bunyan
2023-06-13 13:48:49 +02:00
Evan Bacon f788b081c6 feat(eas-cli): add new public entitlements from wwdc23 (#1870)
* feat(eas-cli): add new public entitlements from wwdc23

* update CHANGELOG.md
2023-06-07 13:29:01 -05:00
Expo CI 654f973d0e update CHANGELOG.md 2023-06-05 08:34:53 +00:00
Wojciech Kozyra 2e614cbde9 v3.13.3 2023-06-05 10:27:49 +02:00
Wojciech Kozyra b7150163d9 upgrade eas-cli-local-build-plugin to 1.0.18 2023-06-05 10:27:37 +02:00
Will Schurman 0922121e84 [ENG-8790][eas-cli] Fix updates synchronization of native files to include strings.xml (#1865) 2023-06-02 11:46:26 -07:00
Will Schurman 4ace7d20fb [eas-cli] Print assets that timed out during upload or processing (#1849) 2023-05-30 12:26:27 -07:00
Dominik Sokal 4ae9aed8f6 update CODEOWNERS 2023-05-30 09:50:59 +02:00
Dominik Sokal 54d6079406 print graphql error message (#1862)
* fix tests

* update CHANGELOG.md
2023-05-30 09:22:42 +02:00
Expo CI f08adad1e7 update CHANGELOG.md 2023-05-26 08:57:33 +00:00
Radoslaw Krzemien 346e47448a v3.13.2 2023-05-26 10:49:00 +02:00
Radosław Krzemień b0fde7b465 [eas-cli] [ENG-8506] Handle pending cancel build status in cli (#1855)
* [eas-cli] Update gql schema

Updated the graphql schema based on the changes in locally run www instance

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or

* [eas-cli] Handle new status

Updated the code to handle the pending cancel build status

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or

* [eas-cli] Regenerate gql schema

Merged main in www and cli and regenerated schema

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or

* [eas-cli] Fix failing test

Updating gql schema added a new field `App` in some places which made the test fail

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or

* [eas-cli] Reformat conditionals

Updated the way multiple status checks are done after review

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or

* update CHANGELOG.md

* [eas-cli] Regenerate gql

After merging changes in www back to main regenerated the graphql schema to make sure it is compliant with the current version

See: https://linear.app/expo/issue/ENG-8506/attempt-to-upload-xcode-logs-when-build-fails-for-unexpected-reason-or
2023-05-26 10:40:10 +02:00
Will Schurman d42efadf51 [eas-cli] Refactor getExpoConfig to remove dangerous default (#1857) 2023-05-25 08:58:34 -07:00
Expo CI e302390324 update CHANGELOG.md 2023-05-24 12:26:56 +00:00
Szymon Dziedzic 3a239f8cf9 v3.13.1 2023-05-24 14:18:36 +02:00
Szymon Dziedzic b5bc8a8f6e [eas-cli] make large resource class only available for paid tier users (#1848)
* [eas-cli] make large resource class only available for paid users

* update eas.schema.json

* Revert "update eas.schema.json"

This reverts commit 319d998b76a4c6c87d137174d6cf9f90d64b3886.

* apply suggested changes

* update CHANGELOG.md
2023-05-24 14:02:31 +02:00
Will Schurman 6cbff89b75 [eas-cli] Improve types for update display (#1851) 2023-05-23 10:46:19 -07:00
Piotr Szeremeta 118c008917 Validate CFBundleShortVersionString (#1846)
* [eas-cli][bugfix] Fix ios version-test

Tests were passing, but were not testing the right thing, essentially always defaulting to 'managed' workflow. `projectDir` should be set to the same value as EAS_PROJECT_ROOT.

* [eas-cli][ENG-8571] Validate CFBundleShortVersionString

* update CHANGELOG.md
2023-05-22 09:22:11 +02:00
Quinlan Jung 3b24d37f3c [eas-cli] remove asp prompt (#1843)
* [eas-cli] remove asp prompt

* update CHANGELOG.md
2023-05-17 11:05:06 -07:00
Szymon Dziedzic df425201b3 fix changelog 2023-05-17 15:47:14 +02:00
Piotr Szeremeta 9636507130 [eas-cli][ENG-8528] Don't print all selected devices. (#1840)
* [eas-cli][ENG-8528] Don't print all selected devices.

* update CHANGELOG.md
2023-05-17 15:04:49 +02:00
Szymon Dziedzic 2dd50ad21a v3.13.0 2023-05-17 14:28:30 +02:00
Szymon Dziedzic 30441b3db4 [eas-cli][eas-json] update @expo/steps and @expo/eas-build-job version (#1845) 2023-05-17 12:47:57 +02:00
Dominik Sokal 789c314a8d don't generate ios credentials for non-signable targets (#1835)
* don't generate ios credentials for non-signable targets

* upgrade @expo/config-plugins

* update CHANGELOG.md
2023-05-17 09:30:30 +02:00
Wojciech Kozyra 4760314ddf [eas-cli] ignore dirty workingdir when building from GitHub (#1842)
* [eas-cli] builds from GitHub are never in a dirty state

* update CHANGELOG.md
2023-05-16 16:28:44 +02:00
Quinlan Jung c8e57f000c [eas-cli] add clear cache option to updates (#1839)
* [eas-cli] add clear cache option to updates

* update CHANGELOG.md
2023-05-15 14:57:50 -07:00
Expo CI 3c82a2f8a8 update CHANGELOG.md 2023-05-15 13:26:43 +00:00
Dominik Sokal 0e7e926cec v3.12.1 2023-05-15 14:19:49 +02:00
Dominik Sokal 30364fd679 check build expiration date before submitting (#1837)
* check build expiration date before submitting

* add tests

* update CHANGELOG.md
2023-05-15 10:00:25 +02:00
Wojciech Kozyra 267fa0e43b [eas-cli] Fix random test failures (#1834)
* increase max-old-space-size

* limit number of workers
2023-05-11 12:11:45 +02:00
Wojciech Kozyra e154f19751 [eas-cli] add tests for eas build:version:* commands (#1829)
* [eas-cli] add utils for testing eas commands and write tests for build:version:*

* review feedback
2023-05-11 10:48:15 +02:00
Dominik Sokal 4bca79f828 fix CHANGELOG.md 2023-05-11 09:39:00 +02:00
dchhetri 7e01661f70 [eas-cli] Change red apple icon to green apple icon (#1828) 2023-05-11 09:29:04 +02:00
Dominik Sokal ab18e3e27e [ENG-7650] add custom workflow name to build metadata (#1830)
* generate graphql code

* upgrade @expo/eas-build-job

* add custom workflow name to build metadata
2023-05-10 10:32:55 +02:00
Expo CI 3e7a112097 update CHANGELOG.md 2023-05-08 10:26:39 +00:00
Dominik Sokal 4cd6b8d228 v3.12.0 2023-05-08 12:20:06 +02:00
Dominik Sokal 400244194b [ENG-7740][eas-cli] upgrade @expo/steps (#1824)
* [eas-cli] upgrade @expo/steps

* update CHANGELOG.md
2023-05-08 12:17:01 +02:00
Wojciech Kozyra dcf3a2d4fd [eas-cli] Add build:version:get command (#1815)
* [eas-cli] Add `build:version:get` command

* rename command class name

* review feedback

* update CHANGELOG.md
2023-05-08 11:04:45 +02:00
Expo CI 0464ac27cd update CHANGELOG.md 2023-05-05 09:48:45 +00:00
Szymon Dziedzic e0d2126f0a v3.11.0 2023-05-05 11:39:15 +02:00
Szymon Dziedzic bd46da748d [eas-cli][eas-json] add large resource class for iOS as allowed value in eas.json (#1817)
* [eas-cli][eas-json] add `large` resource class for iOS as allowed value in `eas.json`

* add deprecation warning for `m-large`

* add test

* update CHANGELOG.md
2023-05-05 11:32:22 +02:00
Radosław Krzemień d8c68561c5 [eas-cli] [ENG-8153] Print requestId and original error message on unexpected graphQL-related error (#1813)
* Change error message

Upon unexpected GraphQL-related error a new message is printed to the user. Now it also has the request ID (if returned with the error from `www`) and the original error message. This hopefully will help to identify the issues users have when unsuccessfully submitting build requests

See: https://linear.app/expo/issue/ENG-8153/if-the-build-request-fails-with-unknown-error-print-request-id

* Remove original error message

After some discussion it was decided to remove the original error message since it is not vetted against possibly leaking unintentional information to the users

See: https://linear.app/expo/issue/ENG-8153/if-the-build-request-fails-with-unknown-error-print-request-id

* Log request ID globally

After discussion under the PR it was decided to print the request ID after all graphql errors

See: https://linear.app/expo/issue/ENG-8153/if-the-build-request-fails-with-unknown-error-print-request-id

* Don't print requestId on foreseen errors

Foreseen graphQL errors with custom messages no longer print the request id to the user

See: https://linear.app/expo/issue/ENG-8153/if-the-build-request-fails-with-unknown-error-print-request-id

* update CHANGELOG.md
2023-04-28 10:22:24 +02:00
Will Schurman dfc314d5be [eas-cli] Add account list to eas whoami/eas account:view (#1814) 2023-04-27 06:52:24 -07:00
Expo CI 2285d5771a update CHANGELOG.md 2023-04-25 14:31:02 +00:00
Szymon Dziedzic aa22163688 v3.10.2 2023-04-25 16:23:04 +02:00
Szymon Dziedzic 5e5395b0c6 [ENG-7676][eas-cli] don't resolve default resource class for iOS based on metadata in CLI (#1734)
* [eas-cli] don't resolve default resource class for iOS based on metadata

* don't set m1-medium as default in `eas build:configure`

* pass sdkVersion and reactNativeVersion in buildParams

* convert it to sync function

* update gql schema

* update CHANGELOG.md
2023-04-25 15:51:41 +02:00
Szymon Dziedzic c94a1e7d9c [eas-cli] allow users to select app to run if multiple apps are found in the build tarball (#1807)
* [eas-cli] allow users to select app to run if multiple apps are found in the build tarball

* rename variable

* improve error message

* apply suggested changes

* add dot

* update CHANGELOG.md
2023-04-25 15:42:50 +02:00
Szymon Dziedzic aefadf9376 [ENG-8074][eas-json] add info about new image (#1808)
* [eas-json] add info about new image

* update eas.schema.json

* update images descriptions

* update CHANGELOG.md
2023-04-25 15:28:10 +02:00
Expo CI 7acea869ff update CHANGELOG.md 2023-04-25 13:07:56 +00:00
Radoslaw Krzemien 02af3f587f v3.10.1 2023-04-25 14:59:42 +02:00
0d0da1a47a [eas-cli] [eas-json] [ENG-8120] Use cache.paths and deprecate cache.customPaths (#1794)
* Update dependencies

Bump version of `@expo/eas-build-job` to use `cache.paths` and deprecate `cache.customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update default cache values

Updated the default cache values in `eas-cli` `prepareJobAsync` functions to use `paths` instead of `customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add tests

Added tests for `prepareJobAsync` functions both for Android and iOS to check if they handle both deprecated `customPaths` and new `paths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add `paths` and deprecate `customPaths`

Updated the schema in `eas-json` to add the new field `cache.paths` and deprecate the old `cache.customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add deprecation warning

Added a warning message to be returned when the eas.json contains deprecated field `cache.customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add tests

Added tests for `EasJsonUtils.getBuildProfileDeprecationWarnings()` checking if it correctly returns deprecation warnings for `cache.customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add tests

Added tests for printing the deprecation messages

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Uncomment and fix test

Had previously one test that was broken and commented out

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update dependencies

Updated `@expo/eas-build-job` version after removing default value from `cache.customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Stop passing `customPaths` to `www`

Remove `cache.customPaths` from the job and add its values to `paths` if present

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Refactor getting cache settings

Moved getting the cache settings to `eas-json`, changed appending elements from `customPaths` to `paths` to overwriting them, added an error thrown when both `paths` and `customPaths` are present

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Fix deprecation message

Replaced hardcoded profile name with dynamic one

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Revert "Refactor getting cache settings"

This reverts commit 4727eba210.

* Revert changes and update Joi schema

Replaced the procedural code moving values of customPaths to paths into Joi schema validation

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Check for deprecation in raw EAS json

Changed the method checking for deprecated fields in eas.json to look into the raw EAS json instead of the validated build profile, because the validated build profile will always lack the deprecated field

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update error message

Updated the message after user provides both `paths` and `customPaths` to be more understandable

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Fix broken test

One of the tests broke after the last adjustment of the error message

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* update CHANGELOG.md

* Fix import

Fixed incorrect import

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Pass accessor as argument

Passing eas json accessor as an argument instead of the results of its method. Also, removed a redundant check and made the accessor a mandatory parameter

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Check extended profile

Checking extended profile as well for deprecated `customPaths`

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update packages/eas-json/src/utils.ts

Co-authored-by: Wojciech Kozyra <[email protected]>

* Add comment

Added a comment to a function made for testing only

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update packages/eas-json/src/build/schema.ts

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

* Fix errors

Fixed failing test and a syntax error after committing a review suggestion

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Make the `profileName` mandatory

Made the `profileName` param in `getBuildProfileDeprecationWarningsAsync` mandatory and let the cli decide what the default profile name is

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Create build profile in eas-json

Instead of passing the build profile into `getBuildProfileDeprecationWarningsAsync` it is created using the already passed accessor

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add type

Added variable type

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Move doc comment

Moved the comment outside of function

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Replace try/catch

Replaced try/catch blocks in test with expect().rejects.toThrow()

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Use toMatchSnapshot

Replaced checking the calls to warn() with expect().toMatchSnapshot()

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Update snapshots

Updated the test snapshots to be up to date

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Print base profile name

When checking for deprecation messages the profile name printed is the name of the base profile not the extending one

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Force chalk color in CI

Chalk behaves differently when env var CI is set to true. It can be forced to behave normally with `FORCE_COLOR` env and this should solve the problem with CI tests failing

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Force chalk color in CI

Chalk behaves differently when env var CI is set to true. It can be forced to behave normally with `FORCE_COLOR` env and this should solve the problem with CI tests failing

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Add comment

Added a comment to explain why the `FORCE_COLOR` env is necessary in CI

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

* Move env var

Moved the `FORCE_COLOR` env var from github actions workflow to jest config

See: https://linear.app/expo/issue/ENG-8120/rename-cachecustompaths-to-cachepaths-in-easjson-and-job-schema

---------

Co-authored-by: Wojciech Kozyra <[email protected]>
Co-authored-by: Dominik Sokal <[email protected]>
2023-04-25 14:21:19 +02:00
Expo CI eec0b88258 update CHANGELOG.md 2023-04-21 07:56:25 +00:00
Dominik Sokal 98be401942 v3.10.0 2023-04-21 09:49:21 +02:00
Keith Kurak 8c2689a27b [eas-cli] Device delete not disabling on Apple side (#1803)
* [device:delete] fix matching Expo device to Apple device

* Clarify when a registered device will show up on Apple side

* update CHANGELOG.md
2023-04-20 14:31:30 -04:00
Keith KurakandDominik Sokal 1b34118d85 Add eas device:rename command (#1787)
* [device] rename stubbed out with existing graphql fns

* added update device mutation

* Device rename working on Expo side

* restore codegen endpoint

* tweaked some text output

* Update graphql

* Remove debugging log

* get inputMethod.ts off the change graph

* update CHANGELOG.md

* Update packages/eas-cli/src/commands/device/rename.ts

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

* Update packages/eas-cli/src/commands/device/rename.ts

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

* Warn when device not renamed (and fix device lookup)

* make warning slightly less wordy

---------

Co-authored-by: Dominik Sokal <[email protected]>
2023-04-20 14:24:02 -04:00
Keith Kurak 0f75fe407e [eas-cli] make ora recognize CI env variable (#1775) 2023-04-20 14:15:20 -04:00
Will Schurman efb7e534f4 [eas-cli] Fix message truncation for update message (#1801) 2023-04-20 09:25:01 -07:00
Szymon Dziedzic 53f89c0df1 [eas-cli] throw error if somebody tries to run iOS with large resource class selected using deprecated --resource-class flag (#1795)
* [eas-cli] throw error if somebody tries to run iOS with `large` resource class selected through deprecated `--resource-class` flag

* apply suggested changes

* improve error message

* update CHANGELOG.md
2023-04-20 13:52:05 +02:00
Dominik Sokal 1bbe7dfb72 [gha] add PR changelog check (#1798)
* [gha] add PR changelog check

* update PR template
2023-04-20 10:46:14 +02:00
Brent Vatne da4b56a58d Fix changelog for 3.9.3 2023-04-18 15:59:05 -07:00
Expo CI 1080600af6 update CHANGELOG.md 2023-04-18 22:47:47 +00:00
Will Schurman dcb6713f12 v3.9.3 2023-04-18 15:38:20 -07:00
Will Schurman 24a1d2d581 [eas-cli] Fix another prompt again in shared code (#1797) 2023-04-18 15:36:36 -07:00
Will Schurman 8af88c2d29 [eas-cli] Fix update message prompt (#1791) 2023-04-13 16:25:22 -07:00
Expo CI d416ac2162 update CHANGELOG.md 2023-04-13 10:11:07 +00:00
Dominik Sokal 7bf0493a4d v3.9.2 2023-04-13 12:01:04 +02:00
Dominik Sokal 91f32cc377 add missing changelog entry 2023-04-13 11:35:52 +02:00
Dominik Sokal cf54f62bac print meaningful error message if extra.eas.projectId is not a string (#1788) 2023-04-13 11:33:04 +02:00
Wojciech Kozyra 3ef3e8447c [eas-cli] always show logs from expo install (#1782)
* Always show logs from expo install

* update CHANGELOG.md
2023-04-12 12:39:03 +02:00
Jon Samp ad64e1afad [eas-cli] Fix rollout percents when running channel:rollout --end (#1781)
* fix rollout percents when running --end

* update CHANGELOG.md
2023-04-11 12:02:21 -05:00
Szymon Dziedzic a0800cf7ef [ENG-7968][eas-cli][eas-json] print deprecation warnings for deprecated eas.json fields (#1768)
* [eas-cli][eas-json] print deprecation warnings for deprecated `eas.json` fields

* fix tests

* update deprecation warning

* extract logic to separate function

* apply suggestions from code review

* fix tests

* update CHANGELOG.md
2023-04-11 13:40:00 +02:00
Expo CI 34436ee674 update CHANGELOG.md 2023-04-10 20:22:07 +00:00
Cedric van Putten 1072b75927 v3.9.1 2023-04-10 21:50:01 +02:00
Cedric van Putten c0c9d2e7df [eas-cli] Update apple utilities to avoid the maintenance error (#1780)
* [eas-cli] Update apple utilities to avoid the maintenance error

* [eas-cli] Add changelog entry

* [eas-cli] Dont change the eas-cli version

* [eas-cli] Fix changelog entry

* [eas-cli] fix it again
2023-04-10 21:48:24 +02:00
Dominik Sokal 19b831667f Update bug_report.yml (#1777) 2023-04-07 14:16:55 +02:00
Szymon Dziedzic 09db7da23b [ENG-7968][eas-cli][eas-json] don't use defaults for cacheDefaultPaths in the EAS CLI (#1769)
* [eas-cli][eas-json] don't use cacheDefaultPaths

* update deps

* update CHANGELOG.md

* update CHANGELOG.md

* fix changelog

* fix changelog again
2023-04-06 15:25:56 +02:00
Expo CI f08dde2469 update CHANGELOG.md 2023-04-06 12:49:28 +00:00
gabrieldonadel 213b5d240c v3.9.0 2023-04-06 09:28:23 -03:00
Gabriel Donadel Dall'AgnolandDominik Sokal 16ebdce913 [eas-cli] Fix expoCommandAsync to support the new cli bin path (#1772)
* [eas-cli] Fix expoCommandAsync to support the new cli bin path

* update CHANGELOG.md

* Apply suggestions from code review

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

---------

Co-authored-by: Dominik Sokal <[email protected]>
2023-04-06 09:22:04 -03:00
Dominik Sokal 121ff7a718 validate custom build config in CLI (#1770) 2023-04-06 10:34:25 +02:00
Wojciech Kozyra 360393f647 [eas-json] Add support for running on server (#1766)
* [eas-json] Add support for running on server

* review feedback

* update CHANGELOG.md
2023-04-06 09:55:04 +02:00
Wojciech Kozyra 82d66054dc [eas-cli] Do not require commits in eas build:internal (#1760)
* [eas-cli] Do not require commits in eas build:internal

* lint fix

* alternative approach

* update CHANGELOG.md
2023-03-30 12:25:10 +02:00
Will Schurman d8849dbf8c [eas-cli] Unhide channel:rollout command and modernize (#1758) 2023-03-28 09:54:45 -07:00
Dominik Sokal 4d6b5f435b [ENG-7981] use the eas-cli npm tag for eas-cli-local-build-plugin updates (#1759)
* use the eas-cli npm tag for eas-cli-local-build-plugin updates

* update CHANGELOG.md
2023-03-28 12:06:16 +02:00
Will Schurman 8fe7dfea29 [eas-cli] Add info about code signing and roll back to embedded in update info (#1755) 2023-03-24 13:42:59 -07:00
Will Schurman 8750155c25 [eas-cli] Add support for roll back to embedded updates (#1754) 2023-03-24 13:40:52 -07:00
Expo CI a0fce1c773 update CHANGELOG.md 2023-03-16 13:32:36 +00:00
Cedric van Putten cad3dca5df v3.8.1 2023-03-16 14:09:02 +01:00
Cedric van Putten 2ef6e79395 [eas-cli] Add missing changelog entry 2023-03-16 14:03:51 +01:00
Cedric van Putten 44270814a7 [eas-cli] Add test case to ensure public config is used as update (#1745)
* [eas-cli] Add test case to ensure public config is used as update

* [eas-cli] Use the public config when creating updates
2023-03-15 23:12:47 +01:00
Radosław Krzemień 95155681db [eas-cli] [ENG-6945] Don't use capitalised base error messages (#1744)
* Capitalize command id

In case the base error message is not provided and the command id starts with lower case character it will be capitalized to unify the behaviour with other cases

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Decapitalize command id

After some discussion it was agreed to decapitalize the base error messages instead, and unify the behaviour this way

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Remove redundant decapitalisation

Seems like `oclif` guarantees the command ids to be lower case in which case the explicit decapitalisation is redundant

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* update CHANGELOG.md
2023-03-14 15:42:41 +01:00
Radosław KrzemieńandWojciech Kozyra 53fd599d0a [eas-cli] [ENG-6945] Unify error display method (#1738)
* Unify error display method

Overriden the `Command`'s `catch` method to catch all the errors thrown by the commands, log the error message there the same way as it was previously handled in `src/build/build.ts` and re-throw generic `Error` instance with predefined base error message.
To avoid duplication, old `Log.error()` calls have been removed from `src/build/build.ts`.
Also added tests to see if the `catch()` method works as intended.

* Adjust base error messages

Base error messages have been adjusted after @dsokal suggested that the error may be caused by something different from a request and displaying message about a failed request is misleading

* update CHANGELOG.md

* Add test for unclean error message

Checks if the `[GraphQL] ` prefix gets removed when logging the error message

* Update handling GraphQL errors

Post-review update to how graphQL errors are handled

* Print different message for graphQL errors

When a graphQL error is caught a different base error message is printed

* Print original error to debug

When a graphQL error is caught and the message is cleaned, an original message is printed to debug (or just the same message otherwise)

* Add custom error classes

Created some custom error classes for known server side defined errors

* Log the error, not the message

Changed the debug log to log the error object, not the error message

* Fix

Fixed testing leftover

* Change phrasing

Edited the phrasing in default messages related to EAS Build

* Update if condition

Changed the if condition after review comment

* Create error base class

Created a new base class for the errors expected in build.ts, so they can be handled separately in `catch()`

* Move EasCommand properties

Moved the class properties above the class methods

* Remove helper method

Removed the helper method after review comment

* Change const type

Updated the type of SERVER_SIDE_DEFINED_ERRORS constant

* Change default base error message

Set the default base error message to `<command_name> command failed.`

* Remove comments

Removed leftover comments

* Change optional property

Changed empty string to optional property

* Post-review tweaks

Self-explanatory

* Fix failing test

One of tests started failing after changing the coalescing operators (|| -> ??). This fixes that test

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Remove error class

Removed `GenericGraphQLError` class after @wkozyra95 comment, since atm there's no need to handle it differently than a generic `Error`

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Move `EasCommandError` base class

Moved `EasCommandError` base class after @wkozyra95 comment. The class is not specific to `Build` command and so it has been moved to `commandUtils` directory

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Make message mandatory

Made `message` parameter in `EasCommandError`s mandatory, to remove need for providing a fallback string and making the server the source of truth for the errors messages

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Update packages/eas-cli/src/commandUtils/EasCommand.ts

Co-authored-by: Wojciech Kozyra <[email protected]>

* Move `baseGraphQLErrorMessage` property

Made the property into a constant in a global scope, since atm it is not changed in any of the subclasses

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Suppress ESLint warning

ESLint erroneously complains about the constructor of `EasCommandError` to be useless, which is not true is the constructor of `Error` parent class allows for optional `message` parameter and the constructor of `EasCommandError` does not

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Add error handling tests

Added a bunch of new tests related to the `handleBuildRequestError` function

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

* Directly export `handleBuildRequestError`

Dropped the `testExports` convention after team discussion

See: https://linear.app/expo/issue/ENG-6945/improve-eas-cli-error-logging

---------

Co-authored-by: Wojciech Kozyra <[email protected]>
2023-03-14 11:26:27 +01:00
dependabot[bot] b51201d010 Bump @sideway/formula from 3.0.0 to 3.0.1 (#1742)
Bumps [@sideway/formula](https://github.com/sideway/formula) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sideway/formula/releases)
- [Commits](https://github.com/sideway/formula/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: "@sideway/formula"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 12:52:53 +01:00
Expo CI f86c079733 update CHANGELOG.md 2023-03-13 11:51:39 +00:00
Szymon Dziedzic 843ec7407b v3.8.0 2023-03-13 12:44:41 +01:00
Szymon Dziedzic 537cd7872a [ENG-7709][eas-cli][eas-json] add m-medium and m-large resource classes (#1739)
* [eas-cli][eas-json] add `m-medium` and `m-large` resource classes

* add deprecation warning for m1-medium resource class

* update deprecation messages

* apply suggested changes

* update CHANGELOG.md
2023-03-13 12:33:48 +01:00
Dominik Sokal 007c40bed4 [gha] send slack notification on failed tests (#1720) 2023-02-24 16:26:23 +01:00
Dominik Sokal ba9f8d8ddd Update CHANGELOG.md 2023-02-24 09:54:39 +01:00
Expo CI e4bd933e81 update CHANGELOG.md 2023-02-24 08:27:04 +00:00
Evan Bacon a6434bf6b8 v3.7.2 2023-02-24 02:18:15 -06:00
Evan Bacon 19bad84109 [eas-cli] bump apple-utils to support hashcash for authentication (#1719)
* bump apple-utils to support hashcash for authentication

* update CHANGELOG.md
2023-02-24 02:16:06 -06:00
Expo CI 5d9f8f9d76 update CHANGELOG.md 2023-02-23 21:08:09 +00:00
Brent Vatne d9d884d211 v3.7.1 2023-02-23 12:59:33 -08:00
Brent Vatne f902bb845b Add changelog entry 2023-02-23 12:59:05 -08:00
Brent Vatne 03c76c1e0e [eas-cli] Bump apple-utils 2023-02-23 12:58:33 -08:00
Expo CI 1d0d329cdb update CHANGELOG.md 2023-02-23 10:48:34 +00:00
Dominik Sokal 7ed1efb7d6 v3.7.0 2023-02-23 11:23:55 +01:00
Dominik Sokal b7bfec0366 [ENG-7565] add basic support for custom builds (#1710)
* custom builds - wip

* upgrade @expo/eas-build-job

* [eas-json] clean up build profile schema + types

* validate if custom build config exists

* don't configure credentials for custom builds

* set buildMode in metadata

* address PR feedback

* regenerate graphql code
2023-02-23 10:16:22 +01:00
Frederick Widjaja a5308f0d37 Support variant detection for package*UniversalApk Gradle Commands (#1708)
* Support variant detection for package*UniversalApk Gradle Commands

* Update CHANGELOG.md
2023-02-23 09:31:54 +01:00
Cedric van Putten 3ad644bb9d [eas-cli] Provide more context about uploaded assets when publishing (#1488) 2023-02-22 17:39:44 -08:00
Szymon Dziedzic 233e5451a8 update changelog 2023-02-20 11:40:47 +01:00
Szymon Dziedzic ce954cdecc v3.6.1 2023-02-20 11:19:14 +01:00
Szymon Dziedzic 59bd1caf98 [ENG-7376][eas-cli] link to build phase in error message (#1699)
* [eas-cli] link to build phase in error message

* add better fallback

* use fallback for all links

* use terminalLink.isSupported

* improvements after code review

* use old fallback

* update CHANGELOG.md
2023-02-17 15:09:54 +00:00
dependabot[bot] 86877fdda3 Bump undici from 5.8.2 to 5.19.1 (#1706)
Bumps [undici](https://github.com/nodejs/undici) from 5.8.2 to 5.19.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.8.2...v5.19.1)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-17 09:00:13 +00:00
Will Schurman 9299c9b7cc [eas-cli] Clarify missing owner field error message for Robot users (#1702)
* [eas-cli] Clarify missing owner field error message for Robot users

* update CHANGELOG.md

* Address comment
2023-02-16 18:34:25 +00:00
Szymon Dziedzic 64fb2e5b3a [ci] fix release script (#1704)
* fix release script

* apply suggested changes
2023-02-16 11:19:53 +00:00
Szymon Dziedzic 91fdbac773 [eas-cli] display build message data when picking build in submit, build:run and build:resign commands (#1700)
* [eas-cli] display build message data when picking build in `submit`, `build:run` and `build:resign` commands

* display only 200 first characters of message if it exists

* print only defined values

* add '...' to message if trimmed

* use array instead of object

* update CHANGELOG.md
2023-02-16 10:36:23 +00:00
Szymon Dziedzic 507eadb914 [ci] commit release changelog update using expo bot PAT (#1697) 2023-02-15 16:09:18 +00:00
Wojciech Kozyra f1cde7a291 [eas-cli] Fix README.md (#1701) 2023-02-15 13:01:56 +00:00
Wojciech Kozyra 8b9ad47d16 [eas-cli] Validate platform for local builds earlier (#1698)
* [eas-cli] Validate platform for local builds earlier

* update CHANGELOG.md
2023-02-15 11:28:19 +00:00
Wojciech Kozyra 192984b47d [eas-cli] Disable analytics for environments behind proxy (#1696)
* [eas-cli] Disable analytics for environments behind proxy

* update CHANGELOG.md
2023-02-15 09:46:55 +00:00
Szymon Dziedzic 2c36810ce7 update changelog 2023-02-14 14:31:10 +01:00
Szymon Dziedzic bfa63f95fe v3.6.0 2023-02-14 13:32:03 +01:00
Szymon Dziedzic c414b9f975 [ENG-7389][eas-json] add Xcode 14.2 image to schema (#1693)
* add Xcode 14.2 image to schema

* update CHANGELOG.md
2023-02-14 11:21:07 +00:00
Raul Riera 6946a085cb feat: return the build message when one is available (#1691)
* Return the message when available

* Update CHANGELOG.md

* run generate-graphql-code
2023-02-13 15:56:41 +00:00
Will Schurman 317d2fcbb6 [eas-cli] Warn when project ID but no owner specified and mismatch logged in user (#1667) 2023-02-06 10:39:49 -08:00
Will Schurman dd61b4bb70 [eas-cli] Fix suggested application identifier to match owning account name (#1670) 2023-02-06 10:32:22 -08:00
Jon Samp 80fbf7cf62 Revert "[eas-cli] use appVersion instead of sdkVersion (#1588)" (#1669)
* Revert "[eas-cli] use appVersion instead of sdkVersion (#1588)"

This reverts commit 9ef8483882.

* co main changelog

* update CHANGELOG.md
2023-02-06 11:23:39 -06:00
Expo CI 26b6a17160 update CHANGELOG.md 2023-02-05 20:27:34 +00:00
Brent Vatne 7f4e2eef95 v3.5.2 2023-02-05 12:20:17 -08:00
Brent Vatne 88b683655c [eas-cli] Update subdomain to developer-mdn (#1673) 2023-02-05 12:19:31 -08:00
Will Schurman f3aa3d98d6 [eas-cli] Update test certificates (#1671) 2023-02-03 20:20:59 -08:00
dependabot[bot] f852b9329d Bump http-cache-semantics from 4.1.0 to 4.1.1 (#1668)
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-03 15:50:33 +01:00
Expo CI a2340531b0 update CHANGELOG.md 2023-02-01 12:56:13 +00:00
Szymon Dziedzic 39df3a1364 v3.5.1 2023-02-01 13:49:38 +01:00
Szymon Dziedzic 2f1d454582 [eas-cli] fix issue with the eas.json envs being not available when resolving local config (#1666)
* [eas-cli] fix issue with `eas.json` envs not available when resolving local config

* fix type definitions to prevent it from happening again

* update CHANGELOG.md
2023-02-01 13:44:32 +01:00
Expo CI e2560ea867 update CHANGELOG.md 2023-01-31 13:59:54 +00:00
Wojciech Kozyra 346d136b8b v3.5.0 2023-01-31 14:36:55 +01:00
Wojciech Kozyra dd77312510 [eas-cli] Update build metadata for internal builds (#1651) 2023-01-31 14:28:53 +01:00
Wojciech Kozyra 120a965368 [ENG-7367][eas-cli] Fix using local credentials with enterprise provisioning (#1657) 2023-01-30 12:26:22 +01:00
dependabot[bot] 050336b6fd Bump ua-parser-js from 0.7.31 to 0.7.33 (#1658)
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.31 to 0.7.33.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.31...0.7.33)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-27 12:02:48 +01:00
Szymon Dziedzic 7fc3e3fcdb [ENG-7351][eas-cli] set m1-medium resource class for builds which don't specify resource class for SDK 48 and RN 0.71 (#1655)
* [eas-cli] set `m1-medium` resource class for builds which don't specify resource class for SDK 48 and RN 0.71

* refactor resolveBuildResourceClassAsync function

* apply suggested changes

* drop warning for ios large resource classes

* call getDynamicProjectConfigAsync in create context function

* update CHANGELOG.md
2023-01-26 17:15:04 +01:00
Quinlan Jung 2d117e5b4a [ENG-7328][eas-cli] fix undefined branchMappingLogic (#1653)
* [eas-cli] fix undefined branchMappingLogic

* spaces

* update CHANGELOG.md
2023-01-25 11:28:25 -08:00
Szymon Dziedzic 131377bd2c fix [#1650](https://github.com/expo/eas-cli/pull/1650) changelog entry 2023-01-25 14:10:06 +01:00
Szymon Dziedzic ed61b0c41b [ENG-7094][eas-cli] move image validation to server side (#1650)
* [eas-cli] move image validation to server side

* update error log

* apply suggested changes

* update CHANGELOG.md
2023-01-24 13:54:43 +01:00
Expo CI 9f4d3bfc56 update CHANGELOG.md 2023-01-23 11:26:08 +00:00
490 changed files with 50475 additions and 11860 deletions

No files matched your search

+1 -1
View File
@@ -37,7 +37,7 @@ body:
required: true
attributes:
label: Environment
description: Run `npx expo-env-info` and `expo doctor`, and paste the output here.
description: Run `npx expo-env-info` and `npx expo-doctor`, and paste the output here.
- type: textarea
validations:
required: false
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Expo Developers Discord
url: https://chat.expo.dev/
about: Join other developers in discussions regarding Expo and EAS.
+2 -5
View File
@@ -1,8 +1,5 @@
<!-- Thanks for contributing to _EAS CLI_! Before you submit your pull request, please make sure to check the following boxes by putting an x in the [ ] (don't: [x ], [ x], do: [x]) -->
# Checklist
- [ ] I've added an entry to [CHANGELOG.md](https://github.com/expo/eas-cli/blob/main/CHANGELOG.md#main) if necessary. You can comment this pull request with `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]` and CHANGELOG.md will be updated automatically.
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->
# Why
@@ -0,0 +1,4 @@
{
"printWidth": 100,
"singleQuote": true
}
@@ -0,0 +1,53 @@
name: notify-slack-on-fail-or-recover
inputs:
channel:
required: true
type: string
slack_webhook:
required: true
type: string
runs:
using: 'composite'
steps:
- name: Get previous workflow run status
uses: actions/github-script@v6
id: run-status
with:
script: |
const script = require('.github/internal-actions/notify-slack-on-fail-or-recover/main.js');
return await script({github, context, core});
- name: Commit subject
shell: bash
run: |
subject="$(git show --format=%s --quiet | sed 's/"/\\"/g')"
echo "${{ fromJSON(steps.run-status.outputs.result).previous }}"
echo "SUBJECT=$subject" >> "$GITHUB_ENV"
- name: Send Slack Success Notification
if: fromJSON(steps.run-status.outputs.result).previous == 'failure' && fromJSON(steps.run-status.outputs.result).current == 'success'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ inputs.channel }}
SLACK_COLOR: good
SLACK_ICON: https://github.com/expo-bot.png?size=48
SLACK_TITLE: 'Fixed, was failing'
SLACK_MESSAGE: ${{ env.SUBJECT }}
SLACK_USERNAME: 'EAS CLI CI'
SLACK_WEBHOOK: ${{ inputs.slack_webhook }}
SLACK_FOOTER: ''
- name: Send Slack Failure Notification
if: fromJSON(steps.run-status.outputs.result).current == 'failure'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_CHANNEL: ${{ inputs.channel }}
SLACK_COLOR: danger
SLACK_ICON: https://github.com/expo-bot.png?size=48
SLACK_TITLE: 'Failed'
SLACK_MESSAGE: ${{ env.SUBJECT }}
SLACK_USERNAME: 'EAS CLI CI'
SLACK_WEBHOOK: ${{ inputs.slack_webhook }}
SLACK_FOOTER: ''
@@ -0,0 +1,155 @@
function updateOctokitInstance(github) {
updateGithubRestActions(github);
addConvenienceMethods(github);
}
function addConvenienceMethods(github) {
Object.assign(github, {
// checks if a run is a failure from the perspective of sending Slack
// notifications. Works for both runs and run attempts, which should be the
// same object shapes.
// When a run has not been completed, it fetches the jobs for the workflow
// to check if the job is "failing" instead of "failed".
async isFailedRun(run) {
console.log(`checking run: ${run.id}`);
console.log(`status: ${run.status}`);
console.log(`conclusion: ${run.conclusion}`);
console.log(`run_attempt: ${run.run_attempt}`);
if (run.status === 'completed') {
console.log('completed -- checking conclusion');
return run.conclusion === 'failure' || run.conclusion === 'timed_out';
}
const owner = run.repository.owner.login;
const repo = run.repository.name;
const jobsResp = await github.rest.actions.listJobsForWorkflowRun({
owner,
repo,
run_id: run.id,
});
console.log('checking jobs');
const jobs = jobsResp.data;
for (const job of jobs.jobs) {
if (job.conclusion === 'failure' || job.conclusion === 'timed_out') {
console.log('job failed');
return true;
} else if (job.status === 'completed') {
continue;
}
for (const step of job.steps) {
if (step.conclusion === 'failure') {
console.log('job step failed');
return true;
}
}
}
return false;
},
});
}
// github-script's Octokit/REST plugins are out of date, so we have to
// add our own custom endpoint method
// see https://octokit.github.io/rest.js/v18#custom-endpoint-methods for
// documentation
function updateGithubRestActions(github) {
Object.assign(github.rest.actions, {
getWorkflowRunAttempt: github.request.defaults({
method: 'GET',
url: '/repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}',
params: {
owner: {
required: true,
type: 'string',
},
repo: {
required: true,
type: 'string',
},
run_id: {
required: true,
type: 'number',
},
attempt_number: {
required: true,
type: 'number',
},
},
}),
});
}
// Returns { current: 'failure' } | { current: 'success', previous: 'success' | 'failure' } to indicate the failure status
// of the current and previous runs
module.exports = async ({ github }) => {
const { GITHUB_REPOSITORY, GITHUB_RUN_ID, GITHUB_REF_NAME } = process.env;
const [owner, repo] = GITHUB_REPOSITORY.split('/');
updateOctokitInstance(github);
const workflowRunResp = await github.rest.actions.getWorkflowRun({
owner,
repo,
run_id: GITHUB_RUN_ID,
});
const currentRun = workflowRunResp.data;
const listWorkflowRunsResp = await github.rest.actions.listWorkflowRuns({
owner,
repo,
workflow_id: currentRun.workflow_id,
branch: GITHUB_REF_NAME,
});
const { workflow_runs } = listWorkflowRunsResp.data;
if (workflow_runs.length === 1 && workflow_runs[0].run_attempt === 1) {
return false;
}
const previousRuns = workflow_runs.filter(
(run) => run.status === 'completed' && run.conclusion !== 'cancelled'
);
if (await github.isFailedRun(currentRun)) {
return { current: 'failure', previous: null };
}
if (currentRun.run_attempt === 1 && previousRuns.length > 0) {
return {
current: 'success',
previous: (await github.isFailedRun(previousRuns[0])) ? 'failure' : 'success',
};
}
for (let i = currentRun.run_attempt - 1; i > 0; i--) {
const previousAttemptResp = await github.rest.actions.getWorkflowRunAttempt({
owner,
repo,
run_id: currentRun.id,
attempt_number: i,
});
const previousAttempt = previousAttemptResp.data;
if (
previousAttempt.conclusion === 'success' ||
previousAttempt.conclusion === 'failure' ||
previousAttempt.conclusion === 'timed_out'
) {
return {
current: 'success',
previous: (await github.isFailedRun(previousAttempt)) ? 'failure' : 'success',
};
}
}
if (previousRuns.length > 0) {
return {
current: 'success',
previous: (await github.isFailedRun(previousRuns[0])) ? 'failure' : 'success',
};
}
return { current: 'success', previous: 'success' };
};
@@ -0,0 +1,12 @@
{
"name": "notify-slack-on-fail-or-recover",
"version": "1.0.0",
"description": "A GitHub Action to notify Expo Slack of build failures and recovery.",
"main": "main.js",
"author": "Expo",
"license": "UNLICENSED",
"private": true,
"devDependencies": {
"prettier": "^2.5.1"
}
}
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
[email protected]:
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
+48
View File
@@ -0,0 +1,48 @@
name: Changelog bot
on:
issue_comment:
types: [created]
jobs:
pr-commented:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request && github.event.issue.user.login == github.event.comment.user.login && startsWith(github.event.comment.body, '/changelog-entry ') }}
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
with:
node-version: 20
- name: Checkout Pull Request
run: gh pr checkout ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
working-directory: ./scripts
run: yarn install --frozen-lockfile --check-files
- name: Add changelog entry
working-directory: ./scripts
run: |
PARAMS=${PR_COMMENT_BODY#/changelog-entry }
if [[ ! "$PARAMS" =~ " " ]]; then
echo "Invalid /changelog-entry command"
exit 1
fi
CATEGORY=$( cut -d ' ' -f 1 <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
MESSAGE=$( cut -d ' ' -f 2- <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
yarn changelog-entry "$CATEGORY" "$MESSAGE"
env:
GITHUB_PR_AUTHOR: ${{ github.event.issue.user.login }}
GITHUB_PR_NUMBER: ${{ github.event.issue.number }}
PR_COMMENT_BODY: ${{ github.event.comment.body }}
- name: Commit and push
run: |
# use the last commit author name and email
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
git config --global user.email "$(git --no-pager log --format=format:'%ae' -n 1)"
git add CHANGELOG.md
git commit -m "update CHANGELOG.md"
git push
+33 -40
View File
@@ -1,48 +1,41 @@
on:
issue_comment:
types: [created]
name: CHANGELOG.md PR check
name: Changelog
on:
pull_request:
types:
- opened
- synchronize
- labeled
- unlabeled
concurrency:
group: changelog-${{ github.ref }}
cancel-in-progress: true
jobs:
pr-commented:
pr-changelog-entry:
name: Check CHANGELOG.md updated
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request && github.event.issue.user.login == github.event.comment.user.login && startsWith(github.event.comment.body, '/changelog-entry ') }}
steps:
- uses: actions/checkout@v2
- name: Check if CHANGELOG.md was updated
id: changelog-updated
uses: tj-actions/changed-files@v41
with:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
files: CHANGELOG.md
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
if: steps.changelog-updated.outputs.any_changed == 'false' && !contains(github.event.pull_request.labels.*.name, 'no changelog')
uses: actions/github-script@v6
with:
node-version: 18
- name: Checkout Pull Request
run: hub pr checkout ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
working-directory: ./scripts
run: yarn install --frozen-lockfile --check-files
- name: Add changelog entry
working-directory: ./scripts
run: |
PARAMS=${PR_COMMENT_BODY#/changelog-entry }
if [[ ! "$PARAMS" =~ " " ]]; then
echo "Invalid /changelog-entry command"
exit 1
fi
CATEGORY=$( cut -d ' ' -f 1 <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
MESSAGE=$( cut -d ' ' -f 2- <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
yarn changelog-entry "$CATEGORY" "$MESSAGE"
env:
GITHUB_PR_AUTHOR: ${{ github.event.issue.user.login }}
GITHUB_PR_NUMBER: ${{ github.event.issue.number }}
PR_COMMENT_BODY: ${{ github.event.comment.body }}
- name: Commit and push
run: |
# use the last commit author name and email
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
git config --global user.email "$(git --no-pager log --format=format:'%ae' -n 1)"
git add CHANGELOG.md
git commit -m "update CHANGELOG.md"
git push
script: |
core.setFailed('Please add a changelog entry!')
- uses: mshick/[email protected]
if: always()
with:
message-id: changelog-entry-check
refresh-message-position: true
message: ${{ contains(github.event.pull_request.labels.*.name, 'no changelog') && '⏩ The changelog entry check has been skipped since the "no changelog" label is present.' || '✅ Thank you for adding the changelog entry!' }}
message-failure: |
❌ It looks like a changelog entry is missing for this PR. You have two options: you can add it manually, or you can use the changelog bot to do it for you.
🤖 To use the bot, simply comment on this PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`.
⏩ If this PR doesn't require a changelog entry, such as if it's an internal change that doesn't affect the user experience, you can add the "no changelog" label to the PR.
+46 -10
View File
@@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- run: yarn install --frozen-lockfile --check-files
- run: yarn build
- name: Build tarballs
@@ -68,17 +68,23 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- run: brew install python-setuptools
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- run: yarn install --frozen-lockfile --check-files
- run: yarn build
- name: Build tarballs
working-directory: ${{ github.workspace }}/packages/eas-cli
run: |
yarn pretarball-ci
yarn oclif pack:tarballs --no-xz --targets darwin-x64
yarn oclif pack:tarballs --no-xz --targets darwin-x64,darwin-arm64
- id: x64
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
- id: arm64
run: echo "filename=$(ls eas-*-arm64.tar.gz)" >> $GITHUB_OUTPUT
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
- name: Upload darwin-x64 tarball
uses: actions/upload-release-asset@v1
env:
@@ -88,6 +94,15 @@ jobs:
asset_path: packages/eas-cli/dist/${{ steps.x64.outputs.filename }}
asset_name: ${{ steps.x64.outputs.filename }}
asset_content_type: application/gzip
- name: Upload darwin-arm64 tarball
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: packages/eas-cli/dist/${{ steps.arm64.outputs.filename }}
asset_name: ${{ steps.arm64.outputs.filename }}
asset_content_type: application/gzip
build-windows:
name: Build for Windows
runs-on: ubuntu-latest
@@ -96,7 +111,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- run: yarn install --frozen-lockfile --check-files
- run: sudo apt-get install nsis
- run: yarn build
@@ -138,7 +153,7 @@ jobs:
with:
registry-url: 'https://registry.npmjs.org/'
scope: 'expo'
node-version: 18
node-version: 20
- name: Install dependencies
run: yarn install --frozen-lockfile --check-files
- name: Build
@@ -152,7 +167,13 @@ jobs:
VERSION=$(cat lerna.json | jq -r .version)
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
npm dist-tag add eas-cli@$VERSION latest-eas-build-staging
echo "Run \"npm dist-tag add eas-cli@$VERSION latest-eas-build\" to promote it to production."
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
- name: Add latest-eas-build tag
run: |
VERSION=$(cat lerna.json | jq -r .version)
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
npm dist-tag add eas-cli@$VERSION latest-eas-build
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
release-changelog:
@@ -161,9 +182,11 @@ jobs:
needs: publish-to-npm
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- name: Install dependencies
working-directory: ./scripts
run: yarn install --frozen-lockfile --check-files
@@ -173,7 +196,7 @@ jobs:
working-directory: ./scripts
run: yarn --silent release-changelog $EAS_CLI_VERSION > /tmp/current-changelog
- name: Publish release
run: hub release edit v$EAS_CLI_VERSION --draft=false -F /tmp/current-changelog
run: gh release edit v$EAS_CLI_VERSION --draft=false -F /tmp/current-changelog
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Commit and push
@@ -197,9 +220,22 @@ jobs:
env:
SLACK_CHANNEL: eas-cli
SLACK_COLOR: ${{ job.status }}
SLACK_ICON: https://github.com/expo-ci.png?size=48
SLACK_ICON: https://github.com/expo-bot.png?size=48
SLACK_TITLE: 'Released eas-cli@${{ env.EAS_CLI_VERSION }}'
SLACK_MESSAGE: ${{ env.SLACK_CHANGELOG }}
SLACK_USERNAME: 'Expo CI'
SLACK_USERNAME: 'EAS CLI CI'
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_FOOTER: ''
open-pr-in-universe:
name: Open a PR with `@expo/eas-json` bump in universe
needs: publish-to-npm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Open a PR with `@expo/eas-json` version bump in universe
uses: benc-uk/workflow-dispatch@v1
with:
repo: expo/universe
ref: main
workflow: update-eas-json.yml
token: ${{ secrets.EXPO_BOT_PAT }}
+4 -1
View File
@@ -2,6 +2,9 @@ name: Size
on:
pull_request:
types: [synchronize, opened]
concurrency:
group: size-${{ github.ref }}
cancel-in-progress: true
jobs:
compare:
runs-on: ubuntu-latest
@@ -9,7 +12,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- uses: preactjs/compressed-size-action@v2
with:
build-script: 'build:tarballs:linux'
+5 -1
View File
@@ -13,6 +13,10 @@ on:
paths:
- 'scripts/**'
concurrency:
group: test-scripts-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
@@ -21,6 +25,6 @@ jobs:
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- run: yarn install --frozen-lockfile --check-files
- run: yarn typecheck
+24 -4
View File
@@ -1,18 +1,23 @@
name: Run tests
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: ['14', '16', '18']
node: ['18', '20', '22']
include:
- node: '18'
- node: '20'
coverage: true
name: Test with Node ${{ matrix.node }}
steps:
@@ -25,17 +30,32 @@ jobs:
- run: yarn typecheck
- run: yarn test
if: ${{ !matrix.coverage }}
env:
NODE_OPTIONS: --max-old-space-size=8192
- run: yarn test --coverage
if: ${{ matrix.coverage }}
env:
# Oclif seems to cause OOM errors when testing with coverage enabled.
# Remove this whenever we solved those OOM errors.
NODE_OPTIONS: --max-old-space-size=4096
NODE_OPTIONS: --max-old-space-size=8192
- run: yarn lint --max-warnings=0
- run: yarn install --frozen-lockfile --check-files
working-directory: ./scripts
- run: yarn lint-changelog
- uses: codecov/codecov-action@v1
if: ${{ matrix.coverage }}
with:
token: ${{ secrets.CODECOV_TOKEN }}
- run: yarn lint-changelog
notify-slack:
runs-on: ubuntu-latest
needs: test
name: Notify Slack
if: ${{ github.ref == 'refs/heads/main' && always() }}
steps:
- uses: actions/checkout@v2
- name: Notify Slack
uses: ./.github/internal-actions/notify-slack-on-fail-or-recover
with:
channel: eas-cli
slack_webhook: ${{ secrets.SLACK_WEBHOOK }}
@@ -0,0 +1,81 @@
name: Open a PR bumping eas-build modules
on: workflow_dispatch
jobs:
open-pr:
name: Open a PR bumping eas-build modules
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.EXPO_BOT_PAT }}
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 20
- run: yarn install --frozen-lockfile
- name: Configure git
run: |
git config --global user.email "[email protected]"
git config --global user.name "Expo Bot"
- name: Upgrade eas-build modules
run: |
for DIR in packages/eas-cli packages/eas-json; do
pushd $DIR >/dev/null 2>&1
# packages are ordered according to output of `lerna list --toposort`
# https://github.com/expo/turtle-v2/pull/568
for PACKAGE in "@expo/downloader" "@expo/eas-build-job" "@expo/logger" "@expo/template-file" "@expo/steps" "@expo/turtle-spawn" "@expo/build-tools"; do
if grep -q $PACKAGE package.json; then
echo "[$DIR] yarn --exact add $PACKAGE"
yarn add --exact $PACKAGE
fi
done
popd >/dev/null 2>&1
done
- name: Set a branch name
id: bump-branch-name
run: |
HASH="$(echo $RANDOM | md5sum | head -c 10)"
BRANCH_NAME="@expo-bot/eas-build-bump-$HASH"
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Create a branch, commit, and push
run: |
BRANCH_NAME="${{ steps.bump-branch-name.outputs.BRANCH_NAME }}"
git checkout -b $BRANCH_NAME
git add .
git commit -m "Upgrade eas-build dependencies"
git push origin $BRANCH_NAME
- name: Open a PR to main
uses: repo-sync/pull-request@v2
id: open-pr
with:
source_branch: '${{ steps.bump-branch-name.outputs.BRANCH_NAME }}'
destination_branch: 'main'
pr_title: 'Upgrade eas-build dependencies'
pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.'
pr_reviewer: 'szdziedzic,radoslawkrzemien,sjchmiela,khamilowicz'
github_token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/github-script@v3
with:
github-token: ${{ secrets.EXPO_BOT_PAT }}
script: |
github.issues.createComment({
issue_number: ${{ steps.open-pr.outputs.pr_number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: "/changelog-entry chore Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies."
})
+10
View File
@@ -0,0 +1,10 @@
{
"eslint.enable": true,
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.code-search": true,
"**/src/**/build/**": false,
"**/src/build/**": false,
}
}
+1004 -1
View File
File diff suppressed because it is too large. Load diff
+6 -6
View File
@@ -1,10 +1,10 @@
* @dsokal @wkozyra95
* @szdziedzic @khamilowicz @sjchmiela
packages/eas-cli/src/build @dsokal @wkozyra95
packages/eas-cli/src/commands/build @dsokal @wkozyra95
packages/eas-cli/src/build @szdziedzic @khamilowicz @sjchmiela
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz @sjchmiela
packages/eas-cli/src/submit @dsokal
packages/eas-cli/src/commands/submit.ts @dsokal
packages/eas-cli/src/submit @szdziedzic @khamilowicz @sjchmiela
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz @sjchmiela
packages/eas-cli/src/update @EvanBacon @byCedric @wschurman
packages/eas-cli/src/commands/update @EvanBacon @byCedric @wschurman
@@ -13,4 +13,4 @@ packages/eas-cli/schema @bycedric
packages/eas-cli/src/metadata @bycedric
packages/eas-cli/src/commands/metadata @bycedric
packages/eas-json @dsokal @wkozyra95
packages/eas-json @szdziedzic @khamilowicz @sjchmiela
+19
View File
@@ -18,6 +18,25 @@ If you're making changes to `packages/eas-json` or prefer/need to work with prod
yarn start
```
If it is easier for you to work with the code locally with compiler options set to:
```json
...
"compilerOptions": {
...
"noUnusedLocals": false,
"noUnusedParameters": false,
...
}
...
```
you can consider using:
```
yarn start-allow-unused
```
## Format
- Be sure to update the [`CHANGELOG.md`](./CHANGELOG.md) with changes for every PR. There is a changelog bot that can generate the proper entry for you. The instructions on how to use it are in the PR description placeholder.
-4
View File
@@ -12,7 +12,3 @@ The algorithm works as follows:
- If there are any entries in the "🛠 Breaking changes" section, bump the MAJOR version.
- Otherwise, if there are any entries in the "🎉 New features", bump the MINOR version.
- Otherwise, bump the PATCH version.
## Updating EAS CLI version on EAS Build
The release process promotes the new version to staging. After verifying it works there, you can promote it to production with `npm dist-tag add eas-cli@{version} latest-eas-build`.
+5
View File
@@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
Instead of opening a GitHub issue for security vulnerabilities, send an email to our secure inbox: secure@expo.io.
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
testPathIgnorePatterns: ['.*'],
};
+15
View File
@@ -0,0 +1,15 @@
import type { Config } from 'jest';
// eslint-disable-next-line import/no-relative-packages
import easCliConfig from './packages/eas-cli/jest.config';
// eslint-disable-next-line import/no-relative-packages
import easJsonConfig from './packages/eas-json/jest.config';
const config: Config = {
projects: [easCliConfig, easJsonConfig],
testPathIgnorePatterns: ['.*'],
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
};
export default config;
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
};
+9
View File
@@ -0,0 +1,9 @@
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
};
export default config;
+1 -2
View File
@@ -1,6 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "3.4.1",
"version": "10.2.4",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+16 -15
View File
@@ -10,31 +10,32 @@
"build-for-size-check": "yarn build && yarn workspace eas-cli pretarball-ci && CLI_SIZE_CHECK=1 yarn workspace eas-cli oclif pack:tarballs --no-xz --targets linux-x64",
"typecheck": "lerna run typecheck",
"start": "lerna run watch --stream --parallel",
"start-allow-unused": "lerna run watch-allow-unused --stream --parallel",
"watch": "yarn start",
"eas": "packages/eas-cli/bin/run",
"lint": "eslint .",
"lint-changelog": "./scripts/bin/run lint-changelog",
"release": "./scripts/bin/run release",
"release": "lerna run rebuild && ./scripts/bin/run release",
"test": "jest",
"clean": "lerna run clean && rm -rf node_modules coverage"
},
"devDependencies": {
"@types/jest": "29.2.5",
"@types/node": "18.11.18",
"eslint": "8.31.0",
"eslint-config-universe": "11.1.1",
"eslint-plugin-async-protect": "3.0.0",
"@types/jest": "29.5.11",
"@types/node": "20.11.0",
"eslint": "8.56.0",
"eslint-config-universe": "12.0.0",
"eslint-plugin-async-protect": "3.1.0",
"eslint-plugin-node": "11.1.0",
"jest": "29.3.1",
"jest-watch-typeahead": "2.2.1",
"lerna": "6.4.1",
"oclif": "3.4.3",
"prettier": "2.8.2",
"ts-jest": "29.0.3",
"typescript": "4.9.4"
"jest": "29.7.0",
"jest-watch-typeahead": "2.2.2",
"lerna": "7.4.2",
"oclif": "^3.9.0",
"prettier": "3.1.1",
"ts-jest": "29.1.1",
"typescript": "5.3.3"
},
"volta": {
"node": "18.6.0",
"yarn": "1.22.19"
"node": "20.11.0",
"yarn": "1.22.21"
}
}
+395 -130
View File
File diff suppressed because it is too large. Load diff
+18008 -2167
View File
File diff suppressed because it is too large. Load diff
-7
View File
@@ -1,7 +0,0 @@
module.exports = {
...require('../../jest/jest.config.js'),
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
setupFilesAfterEnv: ['./jest/setup-after-env.ts'],
};
+14
View File
@@ -0,0 +1,14 @@
import type { Config } from 'jest';
// eslint-disable-next-line import/no-relative-packages
import baseConfig from '../../jest/jest.config';
const config: Config = {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
setupFilesAfterEnv: ['./jest/setup-after-env.ts'],
};
export default config;
+1
View File
@@ -2,4 +2,5 @@ module.exports = async () => {
process.env.TZ = 'UTC';
process.env.EAS_NO_VCS = '1';
process.env.EAS_PROJECT_ROOT = '/app';
process.env.FORCE_COLOR = '1';
};
+58 -51
View File
@@ -1,62 +1,65 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "3.4.1",
"version": "10.2.4",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.34",
"@expo/apple-utils": "1.7.1",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "7.0.3",
"@expo/config-plugins": "5.0.4",
"@expo/config-types": "47.0.0",
"@expo/eas-build-job": "0.2.102",
"@expo/eas-json": "3.3.2",
"@expo/config": "8.5.4",
"@expo/config-plugins": "7.8.4",
"@expo/config-types": "50.0.0",
"@expo/eas-build-job": "1.0.130",
"@expo/eas-json": "10.2.4",
"@expo/json-file": "8.2.37",
"@expo/logger": "1.0.117",
"@expo/multipart-body-parser": "1.1.0",
"@expo/osascript": "2.0.33",
"@expo/package-manager": "0.0.57",
"@expo/package-manager": "1.1.2",
"@expo/pkcs12": "0.0.8",
"@expo/plist": "0.0.20",
"@expo/plugin-help": "5.1.22",
"@expo/plugin-warn-if-update-available": "2.5.0",
"@expo/prebuild-config": "5.0.7",
"@expo/plugin-help": "5.1.23",
"@expo/plugin-warn-if-update-available": "2.5.1",
"@expo/prebuild-config": "6.7.3",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.0",
"@expo/steps": "1.0.130",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "1.23.2",
"@oclif/plugin-autocomplete": "1.3.10",
"@oclif/core": "^1.26.2",
"@oclif/plugin-autocomplete": "^2.3.10",
"@segment/ajv-human-errors": "^2.1.2",
"@urql/core": "3.1.1",
"@urql/exchange-retry": "1.0.0",
"@urql/core": "4.0.11",
"@urql/exchange-retry": "1.2.0",
"ajv": "8.11.0",
"ajv-formats": "2.1.1",
"better-opn": "3.0.2",
"chalk": "4.1.2",
"cli-progress": "3.11.2",
"cli-progress": "3.12.0",
"dateformat": "4.6.3",
"dotenv": "16.0.3",
"dotenv": "16.3.1",
"env-paths": "2.2.0",
"envinfo": "7.8.1",
"envinfo": "7.11.0",
"fast-deep-equal": "3.1.3",
"fast-glob": "3.2.12",
"fast-glob": "3.3.2",
"figures": "3.2.0",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"fs-extra": "11.2.0",
"getenv": "1.0.0",
"gradle-to-js": "2.0.1",
"graphql": "16.6.0",
"graphql": "16.8.1",
"graphql-tag": "2.12.6",
"https-proxy-agent": "5.0.1",
"ignore": "5.2.4",
"ignore": "5.3.0",
"indent-string": "4.0.0",
"jks-js": "1.1.0",
"joi": "17.7.0",
"keychain": "1.3.0",
"joi": "17.11.0",
"jsonwebtoken": "9.0.0",
"keychain": "1.5.0",
"log-symbols": "4.1.0",
"mime": "3.0.0",
"minimatch": "5.1.2",
@@ -66,58 +69,59 @@
"nullthrows": "1.1.1",
"ora": "5.1.0",
"pkg-dir": "4.2.0",
"pngjs": "6.0.0",
"pngjs": "7.0.0",
"promise-limit": "2.7.0",
"promise-retry": "2.0.1",
"prompts": "2.4.2",
"qrcode-terminal": "0.12.0",
"resolve-from": "5.0.0",
"semver": "7.3.8",
"semver": "7.5.4",
"slash": "3.0.0",
"tar": "6.1.13",
"tar": "6.2.1",
"terminal-link": "2.1.1",
"tslib": "2.4.1",
"turndown": "7.1.1",
"tslib": "2.6.2",
"turndown": "7.1.2",
"untildify": "4.0.0",
"uuid": "9.0.0",
"uuid": "9.0.1",
"wrap-ansi": "7.0.0"
},
"devDependencies": {
"@graphql-codegen/cli": "2.16.3",
"@graphql-codegen/introspection": "2.2.3",
"@graphql-codegen/typescript": "2.8.7",
"@graphql-codegen/typescript-operations": "2.5.12",
"@types/cli-progress": "3.11.0",
"@graphql-codegen/cli": "5.0.0",
"@graphql-codegen/introspection": "4.0.0",
"@graphql-codegen/typescript": "4.0.1",
"@graphql-codegen/typescript-operations": "4.0.1",
"@types/cli-progress": "3.11.5",
"@types/dateformat": "3.0.1",
"@types/envinfo": "7.8.1",
"@types/envinfo": "7.8.3",
"@types/express": "4.17.15",
"@types/fs-extra": "9.0.13",
"@types/fs-extra": "11.0.4",
"@types/getenv": "^1.0.0",
"@types/jsonwebtoken": "8.5.1",
"@types/mime": "3.0.1",
"@types/node-fetch": "2.6.2",
"@types/node-forge": "1.3.1",
"@types/pngjs": "6.0.1",
"@types/pngjs": "6.0.4",
"@types/promise-retry": "1.1.3",
"@types/prompts": "2.4.2",
"@types/semver": "7.3.13",
"@types/tar": "6.1.3",
"@types/semver": "7.5.6",
"@types/tar": "6.1.10",
"@types/tough-cookie": "4.0.2",
"@types/uuid": "9.0.0",
"@types/uuid": "9.0.7",
"@types/wrap-ansi": "3.0.0",
"axios": "0.27.2",
"axios": "0.28.1",
"eslint-plugin-graphql": "4.0.0",
"express": "4.18.2",
"express": "4.19.2",
"memfs": "3.4.13",
"mockdate": "3.0.5",
"nock": "13.2.9",
"nock": "13.4.0",
"rimraf": "3.0.2",
"ts-deepmerge": "4.0.0",
"ts-deepmerge": "6.2.0",
"ts-mockito": "2.6.1",
"ts-node": "10.9.1",
"typescript": "4.9.4"
"ts-node": "10.9.2",
"typescript": "5.3.3"
},
"engines": {
"node": ">=14.0.0"
"node": ">=18.0.0"
},
"files": [
"/bin",
@@ -202,10 +206,13 @@
"repository": "expo/eas-cli",
"scripts": {
"postpack": "rimraf oclif.manifest.json",
"prepack": "rimraf build && yarn build && node ./scripts/prepack.js",
"prepack": "yarn rebuild && node ./scripts/prepack.js",
"rebuild": "rimraf build && yarn build",
"pretarball-ci": "./scripts/pretarball-ci.sh",
"build": "tsc --project tsconfig.build.json",
"build-allow-unused": "tsc --project tsconfig.allowUnused.json",
"watch": "yarn build --watch --preserveWatchOutput",
"watch-allow-unused": "yarn build-allow-unused --watch --preserveWatchOutput",
"typecheck": "tsc",
"test": "jest",
"version": "yarn oclif readme && node scripts/patch-readme && git add README.md",
@@ -213,7 +220,7 @@
"clean": "rimraf dist build tmp node_modules yarn-error.log"
},
"volta": {
"node": "18.6.0",
"yarn": "1.22.19"
"node": "20.11.0",
"yarn": "1.22.21"
}
}
+2 -2
View File
@@ -437,8 +437,8 @@
"$ref": "#/definitions/apple/AppleKidsAge"
}
],
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer.apple.com/news/?id=091202019a",
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer.apple.com/news/?id=091202019a)"
"description": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n@see https://developer-mdn.apple.com/news/?id=091202019a",
"markdownDescription": "When parents visit the Kids category on the App Store, they expect the apps they find will protect their children’s data, provide only age-appropriate content, and require a parental gate in order to link out of the app, request permissions, or present purchasing opportunities. It’s critical that no personally identifiable information or device information be transmitted to third parties, and that advertisements are human-reviewed for age appropriateness in order to be displayed.\n\n[Learn more](https://developer-mdn.apple.com/news/?id=091202019a)"
},
"seventeenPlus": {
"type": "boolean",
+1 -2
View File
@@ -3,8 +3,7 @@ const fs = require('fs/promises');
(async () => {
// Patch `oclif readme` path and link generation
let readmeContent = await fs.readFile('README.md', 'utf8');
readmeContent = readmeContent.replace(/\[build\//g, '[src/');
readmeContent = readmeContent.replace(/build\/commands/g, 'packages/eas-cli/src/commands');
readmeContent = readmeContent.replace(/src\/commands/g, 'packages/eas-cli/src/commands');
await fs.writeFile('README.md', readmeContent);
// eslint-disable-next-line no-console
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BuildVersionGetView reading version when appVersionSource is set to local 1`] = `"This project is not configured for using remote version source. Add {"cli": { "appVersionSource": "remote" }} in eas.json or re-run this command without "--non-interactive" flag."`;
@@ -0,0 +1,161 @@
import { AppVersionSource, EasJson } from '@expo/eas-json';
import chalk from 'chalk';
import { getMockEasJson, mockCommandContext, mockProjectId, mockTestCommand } from './utils';
import BuildVersionGetView from '../../commands/build/version/get';
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
import Log from '../../log';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
jest.mock('../../project/applicationIdentifier');
jest.mock('fs');
jest.mock('../../log');
jest.mock('../../utils/json');
jest.mock('../../graphql/queries/AppVersionQuery');
function withRemoteVersionSource(easJson: EasJson): EasJson {
return {
...easJson,
cli: {
...easJson.cli,
appVersionSource: AppVersionSource.REMOTE,
},
};
}
describe(BuildVersionGetView, () => {
afterEach(() => {
jest.clearAllMocks();
});
test('reading version for platform android', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '100',
storeVersion: '1.0.0',
}));
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(Log.log).toHaveBeenCalledWith(`Android versionCode - ${chalk.bold('100')}`);
expect(enableJsonOutput).not.toHaveBeenCalled();
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
});
test('reading version for platform android when no remote version is set', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(Log.log).toHaveBeenCalledWith(`No remote versions are configured for this project.`);
expect(enableJsonOutput).not.toHaveBeenCalled();
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
});
test('reading version with --json flag for platform android', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '100',
storeVersion: '1.0.0',
}));
const cmd = mockTestCommand(
BuildVersionGetView,
['--non-interactive', '--json', '--platform=android'],
ctx
);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(enableJsonOutput).toHaveBeenCalled();
expect(printJsonOnlyOutput).toHaveBeenCalledWith({
versionCode: '100',
});
});
test('reading version with --json flag for platform ios', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '100',
storeVersion: '1.0.0',
}));
const cmd = mockTestCommand(
BuildVersionGetView,
['--non-interactive', '--json', '--platform=ios'],
ctx
);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'IOS',
'eas.test.com'
);
expect(enableJsonOutput).toHaveBeenCalled();
expect(printJsonOnlyOutput).toHaveBeenCalledWith({
buildNumber: '100',
});
});
test('reading version with --json flag when no remote version is set', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
const cmd = mockTestCommand(
BuildVersionGetView,
['--non-interactive', '--json', '--platform=android'],
ctx
);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(enableJsonOutput).toHaveBeenCalled();
expect(printJsonOnlyOutput).toHaveBeenCalledWith({});
});
test('reading version when appVersionSource is set to local ', async () => {
const ctx = mockCommandContext(BuildVersionGetView, {});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '100',
storeVersion: '1.0.0',
}));
const cmd = mockTestCommand(
BuildVersionGetView,
['--non-interactive', '--json', '--platform=android'],
ctx
);
await expect(cmd.run()).rejects.toThrowErrorMatchingSnapshot();
});
});
@@ -0,0 +1,140 @@
import { AppVersionSource, EasJson } from '@expo/eas-json';
import fs from 'fs-extra';
import path from 'path';
import {
getMockAppFragment,
getMockEasJson,
mockCommandContext,
mockProjectId,
mockTestCommand,
} from './utils';
import BuildVersionSetView from '../../commands/build/version/set';
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
import { AppQuery } from '../../graphql/queries/AppQuery';
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
import Log from '../../log';
import * as prompts from '../../prompts';
jest.mock('../../project/applicationIdentifier');
jest.mock('../../graphql/queries/AppVersionQuery');
jest.mock('../../graphql/queries/AppQuery');
jest.mock('../../graphql/mutations/AppVersionMutation');
jest.mock('fs');
jest.mock('../../log');
jest.mock('../../prompts');
jest.mock('../../utils/json');
function withRemoteVersionSource(easJson: EasJson): EasJson {
return {
...easJson,
cli: {
...easJson.cli,
appVersionSource: AppVersionSource.REMOTE,
},
};
}
describe(BuildVersionSetView, () => {
afterEach(() => {
jest.clearAllMocks();
});
test('setting version for platform android', async () => {
const ctx = mockCommandContext(BuildVersionSetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(AppVersionMutation.createAppVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
expect.objectContaining({
buildVersion: '1000',
storeVersion: '1.0.0',
})
);
});
test('printing current remote version before prompting for a new one', async () => {
const ctx = mockCommandContext(BuildVersionSetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '100',
storeVersion: '1.0.0',
}));
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
await cmd.run();
expect(Log.log).toHaveBeenCalledWith(expect.stringMatching('configured with versionCode 100'));
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
});
test('printing info that there is no remote version configured', async () => {
const ctx = mockCommandContext(BuildVersionSetView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
await cmd.run();
expect(Log.log).toHaveBeenCalledWith(
expect.stringMatching('does not have any versionCode configured')
);
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
});
test('setting version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
const ctx = mockCommandContext(BuildVersionSetView, {});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
await expect(cmd.run()).rejects.toThrowError('Aborting...');
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
});
test('setting version when appVersionSource is set to local and user allows auto configuration', async () => {
const ctx = mockCommandContext(BuildVersionSetView, {});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
await cmd.run();
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
});
});
@@ -0,0 +1,204 @@
import { Workflow } from '@expo/eas-build-job';
import { AppVersionSource, EasJson } from '@expo/eas-json';
import fs from 'fs-extra';
import path from 'path';
import {
getMockAppFragment,
getMockEasJson,
mockCommandContext,
mockProjectId,
mockTestCommand,
} from './utils';
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../build/android/version';
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../build/ios/version';
import BuildVersionSyncView from '../../commands/build/version/sync';
import { Target } from '../../credentials/ios/types';
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
import { AppQuery } from '../../graphql/queries/AppQuery';
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
import { getAppBuildGradleAsync } from '../../project/android/gradleUtils';
import { resolveTargetsAsync } from '../../project/ios/target';
import { resolveWorkflowAsync } from '../../project/workflow';
import * as prompts from '../../prompts';
jest.mock('../../build/android/version');
jest.mock('../../build/ios/version');
jest.mock('../../project/applicationIdentifier');
jest.mock('../../graphql/queries/AppVersionQuery');
jest.mock('../../graphql/queries/AppQuery');
jest.mock('../../graphql/mutations/AppVersionMutation');
jest.mock('../../project/workflow');
jest.mock('../../project/android/gradleUtils');
jest.mock('../../project/ios/target');
jest.mock('../../project/ios/scheme');
jest.mock('fs');
jest.mock('../../log');
jest.mock('../../prompts');
jest.mock('../../utils/json');
function withRemoteVersionSource(easJson: EasJson): EasJson {
return {
...easJson,
cli: {
...easJson.cli,
appVersionSource: AppVersionSource.REMOTE,
},
};
}
describe(BuildVersionSyncView, () => {
afterEach(() => {
jest.clearAllMocks();
});
test('syncing version for managed project on platform android', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '1000',
storeVersion: '0.0.1',
}));
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.MANAGED);
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
const syncAndroidAsync = jest.spyOn(cmd, 'syncAndroidAsync' as any);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(syncAndroidAsync).not.toHaveBeenCalled();
});
test('syncing version for bare project on platform android', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '1000',
storeVersion: '0.0.1',
}));
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.GENERIC);
jest.mocked(getAppBuildGradleAsync).mockImplementation(async () => ({
android: {},
}));
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
const syncAndroidAsync = jest.spyOn(cmd, 'syncAndroidAsync' as any);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'ANDROID',
'eas.test.com'
);
expect(syncAndroidAsync).toHaveBeenCalled();
expect(updateAndroidNativeVersionsAsync).toHaveBeenCalledWith({
projectDir: ctx.projectDir,
versionCode: 1000,
});
});
test('syncing version for managed project on platform ios', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '1000',
storeVersion: '0.0.1',
}));
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.MANAGED);
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=ios'], ctx);
const syncIosAsync = jest.spyOn(cmd, 'syncIosAsync' as any);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'IOS',
'eas.test.com'
);
expect(syncIosAsync).not.toHaveBeenCalled();
});
test('syncing version for bare project on platform ios', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {
easJson: withRemoteVersionSource(getMockEasJson()),
});
const fakeTarget: Target = {
targetName: 'testapp',
bundleIdentifier: 'eas.test.com',
entitlements: {},
};
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
buildVersion: '1000',
storeVersion: '0.0.1',
}));
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
version: '1000',
}));
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.GENERIC);
jest.mocked(resolveTargetsAsync).mockImplementation(async () => [fakeTarget]);
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=ios'], ctx);
const syncIosAsync = jest.spyOn(cmd, 'syncIosAsync' as any);
await cmd.run();
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
ctx.loggedIn.graphqlClient,
mockProjectId,
'IOS',
'eas.test.com'
);
expect(syncIosAsync).toHaveBeenCalled();
expect(updateIosNativeVersionsAsync).toHaveBeenCalledWith({
buildNumber: '1000',
projectDir: ctx.projectDir,
targets: [fakeTarget],
});
});
test('syncing version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
await expect(cmd.run()).rejects.toThrowError('Aborting...');
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
});
test('syncing version when appVersionSource is set to local and user allows auto configuration', async () => {
const ctx = mockCommandContext(BuildVersionSyncView, {});
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
await cmd.run();
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
});
});
@@ -0,0 +1,150 @@
import { ExpoConfig } from '@expo/config-types';
import { EasJson } from '@expo/eas-json';
import { Command, Config } from '@oclif/core';
import { vol } from 'memfs';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { ContextInput, ContextOutput } from '../../commandUtils/EasCommand';
import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjectConfigContextField';
import { AppFragment, Role } from '../../graphql/generated';
export function getMockEasJson(): EasJson {
return {
cli: {},
build: {
production: {},
},
};
}
export const mockProjectId = 'fake-project-id';
export function getMockAppFragment(): AppFragment {
return {
id: mockProjectId,
slug: 'testapp',
name: 'testapp',
fullName: '@testuser/testpp',
ownerAccount: {
id: 'test-account-id',
name: 'testuser',
users: [
{
role: Role.Owner,
actor: {
id: 'test-user-id',
},
},
],
},
};
}
export function getMockExpoConfig(): ExpoConfig {
return {
name: 'testapp',
slug: 'tetsapp',
extra: {
projectId: mockProjectId,
},
};
}
export interface LocalProjectContext {
projectDir: string;
exp?: ExpoConfig;
appId?: string;
}
export function mockCommandContext<
C extends {
[name: string]: any;
} = object,
>(
commandClass: { contextDefinition: ContextInput<C> },
overrides: {
easJson?: EasJson;
exp?: ExpoConfig;
projectId?: string;
getDynamicPrivateProjectConfigAsync?: DynamicConfigContextFn;
getDynamicPublicProjectConfigAsync?: DynamicConfigContextFn;
}
): ContextOutput<C> {
const projectDir = path.join('/test', uuidv4());
vol.reset();
vol.fromJSON({ 'eas.json': JSON.stringify(overrides.easJson ?? getMockEasJson()) }, projectDir);
const contextDefinition = commandClass.contextDefinition;
const result: any = {};
for (const [contextKey] of Object.entries(contextDefinition)) {
if (contextKey === 'loggedIn') {
result.loggedIn = {};
}
if (contextKey === 'projectDir') {
result.projectDir = projectDir;
}
if (contextKey === 'maybeLoggedIn') {
result.maybeLoggedIn = {};
}
if (contextKey === 'sessionManager') {
result.sessionManager = {};
}
if (contextKey === 'projectConfig') {
result.projectConfig = {
exp: overrides.exp ?? getMockExpoConfig(),
projectId: overrides.projectId ?? mockProjectId,
projectDir,
};
}
if (contextKey === 'getDynamicPrivateProjectConfigAsync') {
result.getDynamicPrivateProjectConfigAsync = () => ({
exp: overrides.exp ?? getMockExpoConfig(),
projectId: overrides.projectId ?? mockProjectId,
projectDir,
});
}
if (contextKey === 'getDynamicPublicProjectConfigAsync') {
result.getDynamicPublicProjectConfigAsync = () => ({
exp: overrides.exp ?? getMockExpoConfig(),
projectId: overrides.projectId ?? mockProjectId,
projectDir,
});
}
}
return result;
}
export function mockTestCommand<T extends Command>(
commandConstructor: new (argv: string[], config: Config) => T,
argv: string[],
ctx: any
): T {
const cmd = new commandConstructor(argv, new Config({ root: __dirname }));
(cmd as any).getContextAsync = jest.fn(() => ctx);
return cmd;
}
export class NoErrorThrownError extends Error {}
export const getErrorAsync = async <TError = any>(
call: () => unknown
): Promise<TError | NoErrorThrownError> => {
try {
await call();
throw new NoErrorThrownError();
} catch (error: unknown) {
return error as TError;
}
};
export const getError = <TError = any>(call: () => unknown): TError | NoErrorThrownError => {
try {
call();
throw new NoErrorThrownError();
} catch (error: unknown) {
return error as TError;
}
};
@@ -133,7 +133,9 @@ export async function createAnalyticsAsync(): Promise<AnalyticsWithOrchestration
await UserSettings.setAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, false);
}
const analyticsEnabled = await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true);
const analyticsEnabled =
!process.env.https_proxy && // disable analytics if running behind proxy
(await UserSettings.getAsync(USER_SETTINGS_KEY_ANALYTICS_ENABLED, true));
if (!analyticsEnabled) {
return new NoOpAnalytics();
}
@@ -0,0 +1,12 @@
import { UpdateBranchBasicInfoFragment } from '../../graphql/generated';
export const testBasicBranchInfo: UpdateBranchBasicInfoFragment = {
id: 'c4fec95d-7ab1-4d35-bfe6-fe7ff05beda9',
name: 'development',
__typename: 'UpdateBranch',
};
export const testBasicBranchInfo2: UpdateBranchBasicInfoFragment = {
id: '43ccb8a4-8aaa-47f0-9790-dcc80933441b',
name: 'preview',
__typename: 'UpdateBranch',
};
@@ -0,0 +1,107 @@
import { EASUpdateAction, EASUpdateContext, NonInteractiveError } from '../../eas-update/utils';
import { UpdateBranchBasicInfoFragment } from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import {
Connection,
Edge,
FilterPagination,
QueryParams,
selectPaginatedAsync,
} from '../../utils/relay';
/**
* Select a branch for the project.
*
* @constructor
* @param {function} options.filterPredicate - A predicate to filter the branches that are shown to the user. It takes a branchInfo object as a parameter and returns a boolean.
* @param {string} options.printedType - The type of branch printed to the user. Defaults to 'branch'.
* @param {number} options.pageSize - The number of branches to show per page. Defaults to 100.
* @param {function} options.beforeEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called before each query.
* @args externalQueryParams The query params for the pagination.
* @args totalNodesFetched The total number of nodes fetched so far.
* @args dataset The dataset so far.
* @param {function} options.afterEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called after each query.
* @args externalQueryParams The query params for the pagination.
* @args totalNodesFetched The total number of nodes fetched so far.
* @args dataset The dataset so far.
* @args willFetchAgain If the query will fetch again to get a complete page.
*/
export class SelectBranch implements EASUpdateAction<UpdateBranchBasicInfoFragment | null> {
constructor(
private options: {
filterPredicate?: (branchInfo: UpdateBranchBasicInfoFragment) => boolean;
printedType?: string;
pageSize?: number;
beforeEachFilterQuery?: (
externalQueryParams: QueryParams,
totalNodesFetched: number,
dataset: Edge<UpdateBranchBasicInfoFragment>[]
) => void;
afterEachFilterQuery?: (
externalQueryParams: QueryParams,
totalNodesFetched: number,
dataset: Edge<UpdateBranchBasicInfoFragment>[],
willFetchAgain: boolean
) => void;
} = {}
) {}
async queryAsync(
ctx: EASUpdateContext,
queryParams: QueryParams
): Promise<Connection<UpdateBranchBasicInfoFragment>> {
const { graphqlClient, app } = ctx;
const { projectId } = app;
return await BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync(graphqlClient, {
appId: projectId,
...queryParams,
});
}
async filterQueryAsync(
ctx: EASUpdateContext,
queryParams: QueryParams,
filterPredicate: (branchInfo: UpdateBranchBasicInfoFragment) => boolean
): Promise<Connection<UpdateBranchBasicInfoFragment>> {
const queryAsync = async (
queryParams: QueryParams
): Promise<Connection<UpdateBranchBasicInfoFragment>> =>
await this.queryAsync(ctx, queryParams);
return await FilterPagination.getPageAsync({
queryParams,
queryAsync,
filterPredicate,
beforeEachQuery: this.options.beforeEachFilterQuery,
afterEachQuery: this.options.afterEachFilterQuery,
});
}
public async runAsync(ctx: EASUpdateContext): Promise<UpdateBranchBasicInfoFragment | null> {
const { nonInteractive } = ctx;
const { filterPredicate } = this.options;
const printedType = this.options.printedType ?? 'branch';
const pageSize = this.options.pageSize ?? 100;
if (nonInteractive) {
throw new NonInteractiveError(
`${printedType} selection cannot be run in non-interactive mode.`
);
}
const queryAsync = async (
queryParams: QueryParams
): Promise<Connection<UpdateBranchBasicInfoFragment>> =>
filterPredicate
? this.filterQueryAsync(ctx, queryParams, filterPredicate)
: this.queryAsync(ctx, queryParams);
const getTitleAsync = async (branchInfo: UpdateBranchBasicInfoFragment): Promise<string> =>
branchInfo.name;
return await selectPaginatedAsync({
queryAsync,
getTitleAsync,
printedType,
pageSize,
});
}
}
@@ -0,0 +1,92 @@
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
import { NonInteractiveError } from '../../../eas-update/utils';
import { UpdateBranchBasicInfoFragment } from '../../../graphql/generated';
import { BranchQuery } from '../../../graphql/queries/BranchQuery';
import { promptAsync } from '../../../prompts';
import { testBasicBranchInfo, testBasicBranchInfo2 } from '../../__tests__/fixtures';
import { SelectBranch } from '../SelectBranch';
jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/BranchQuery');
describe(SelectBranch, () => {
afterEach(() => {
jest.clearAllMocks();
});
it('returns null if project has no branchs', async () => {
const ctx = createCtxMock();
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
const selectBranch = new SelectBranch();
const branch = await selectBranch.runAsync(ctx);
expect(branch).toBeNull();
});
it('still prompts if project has only one branch', async () => {
const ctx = createCtxMock();
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [{ node: testBasicBranchInfo, cursor: 'cursor' }],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
item: testBasicBranchInfo,
}));
const selectBranch = new SelectBranch();
const branch = await selectBranch.runAsync(ctx);
expect(branch).toBe(testBasicBranchInfo);
expect(promptAsync).toHaveBeenCalledTimes(1);
});
it('returns the selected branch', async () => {
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [
{ node: testBasicBranchInfo, cursor: 'cursor' },
{ node: testBasicBranchInfo2, cursor: 'cursor' },
],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
item: testBasicBranchInfo,
}));
const ctx = createCtxMock();
const selectBranch = new SelectBranch();
const branch = await selectBranch.runAsync(ctx);
expect(branch).toBe(testBasicBranchInfo);
});
it('returns the only available branch with a filter', async () => {
jest.mocked(BranchQuery.listBranchesBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [
{ node: testBasicBranchInfo, cursor: 'cursor' },
{ node: testBasicBranchInfo2, cursor: 'cursor' },
],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
const ctx = createCtxMock();
const selectBranch = new SelectBranch({
filterPredicate: (branchInfo: UpdateBranchBasicInfoFragment) =>
branchInfo === testBasicBranchInfo,
});
const branch = await selectBranch.runAsync(ctx);
expect(branch).toBe(testBasicBranchInfo);
});
it('throws an error in Non-Interactive Mode', async () => {
const ctx = createCtxMock({
nonInteractive: true,
});
const selectBranch = new SelectBranch();
// dont fail if users are running in non-interactive mode
await expect(selectBranch.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
});
});
+1 -1
View File
@@ -1,6 +1,7 @@
import chalk from 'chalk';
import gql from 'graphql-tag';
import { BranchNotFoundError } from './utils';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { withErrorHandlingAsync } from '../graphql/client';
@@ -19,7 +20,6 @@ import {
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
import { BranchNotFoundError } from './utils';
export const BRANCHES_LIMIT = 50;
+3 -4
View File
@@ -1,9 +1,8 @@
import { getVcsClient } from '../vcs';
import { Client } from '../vcs/vcs';
export async function getDefaultBranchNameAsync(): Promise<string> {
export async function getDefaultBranchNameAsync(vcsClient: Client): Promise<string> {
return (
(await getVcsClient().getBranchNameAsync()) ||
`branch-${Math.random().toString(36).substring(2, 4)}`
(await vcsClient.getBranchNameAsync()) || `branch-${Math.random().toString(36).substring(2, 4)}`
);
}
@@ -3,13 +3,7 @@ import { v4 as uuid } from 'uuid';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
import {
AppPlatform,
BuildFragment,
BuildPriority,
BuildResourceClass,
BuildStatus,
} from '../../graphql/generated';
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { confirmAsync, selectAsync } from '../../prompts';
@@ -91,6 +85,6 @@ function createMockBuildFragment({
},
},
status: BuildStatus.InQueue,
resourceClass: BuildResourceClass.AndroidMedium,
isForIosSimulator: false,
};
}
@@ -0,0 +1,90 @@
import { instance, mock } from 'ts-mockito';
import { v4 as uuid } from 'uuid';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { selectBuildToDeleteAsync } from '../../commands/build/delete';
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { confirmAsync, selectAsync } from '../../prompts';
jest.mock('../../ora', () => ({
ora: jest.fn().mockImplementation(() => ({
start: jest.fn().mockImplementation(() => ({
stop: jest.fn(),
fail: jest.fn(),
})),
})),
}));
jest.mock('../../prompts', () => {
return {
selectAsync: jest.fn(),
confirmAsync: jest.fn(),
};
});
jest.mock('../../graphql/queries/BuildQuery', () => {
const actual = jest.requireActual('../../graphql/queries/BuildQuery');
return {
BuildQuery: {
...actual.BuildQuery,
viewBuildsOnAppAsync: jest.fn(),
},
};
});
describe(selectBuildToDeleteAsync.name, () => {
const selectedBuildId = uuid();
const projectId = uuid();
beforeEach(() => {
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockImplementation(async () => [
createMockBuildFragment({ projectId, buildId: selectedBuildId }),
]);
});
it('does not return build id when confirmation is rejected', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(confirmAsync).mockResolvedValueOnce(false);
expect(selectBuildToDeleteAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(null);
});
it('returns build id when confirmation is confirmed', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
jest.mocked(selectAsync).mockResolvedValueOnce(selectedBuildId);
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
expect(selectBuildToDeleteAsync(graphqlClient, projectId, 'blah')).resolves.toEqual(
selectedBuildId
);
});
});
function createMockBuildFragment({
projectId,
buildId,
}: {
projectId: string;
buildId?: string;
}): BuildFragment {
return {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
platform: AppPlatform.Android,
id: buildId ?? uuid(),
priority: BuildPriority.Normal,
project: {
__typename: 'App',
slug: 'test-project',
id: projectId,
name: 'test-project',
ownerAccount: {
__typename: 'Account',
id: uuid(),
name: 'test-account',
},
},
status: BuildStatus.InQueue,
isForIosSimulator: false,
};
}
@@ -0,0 +1,597 @@
import { Platform } from '@expo/eas-build-job';
import { CombinedError } from '@urql/core';
import { GraphQLError } from 'graphql/error';
import { v4 as uuidv4 } from 'uuid';
import { getError } from '../../__tests__/commands/utils';
import { EasCommandError } from '../../commandUtils/errors';
import Build from '../../commands/build';
import Log, { link } from '../../log';
import { handleBuildRequestError } from '../build';
import {
EasBuildDownForMaintenanceError,
EasBuildFreeTierDisabledAndroidError,
EasBuildFreeTierDisabledError,
EasBuildFreeTierDisabledIOSError,
EasBuildFreeTierIosLimitExceededError,
EasBuildFreeTierLimitExceededError,
EasBuildResourceClassNotAvailableInFreeTierError,
EasBuildTooManyPendingBuildsError,
RequestValidationError,
TurtleDeprecatedJobFormatError,
} from '../errors';
beforeEach(() => {
jest.resetAllMocks();
});
const EXPECTED_STRINGIFIED_GRAPHQL_ERROR_JSON = `[
{
"name": "GraphQLError",
"extensions": {},
"message": "Error 1"
},
{
"name": "GraphQLError",
"extensions": {},
"message": "Error 2"
},
{
"name": "GraphQLError",
"extensions": {},
"message": "Error 3"
}
]`;
const EXPECTED_EAS_BUILD_DOWN_MESSAGE = `EAS Build is down for maintenance. Try again later. Check ${link(
'https://status.expo.dev/'
)} for updates.`;
const EXPECTED_MAX_BUILD_COUNT_MESSAGE_ANDROID = `You have already reached the maximum number of pending Android builds for your account. Try again later.`;
const EXPECTED_MAX_BUILD_COUNT_MESSAGE_IOS = `You have already reached the maximum number of pending iOS builds for your account. Try again later.`;
const EXPECTED_GENERIC_MESSAGE =
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.';
const mockRequestId = uuidv4();
const getGraphQLError = (message: string, errorCode: string): GraphQLError => {
return new GraphQLError(message, null, null, null, null, null, {
errorCode,
requestId: mockRequestId,
});
};
const assertReThrownError = (
caughtError: CombinedError | Error,
expectedType: typeof EasCommandError,
expectedMessage: string = 'Error 1'
): void => {
expect(caughtError).toBeInstanceOf(expectedType);
expect((caughtError as Error).message).toEqual(expectedMessage);
};
describe(Build.name, () => {
describe('handle build request errors', () => {
describe('logs graphQL errors to debug if present', () => {
it('does it with Android', async () => {
const platform = Platform.ANDROID;
const logDebugSpy = jest.spyOn(Log, 'debug');
const graphQLErrors = ['Error 1', 'Error 2', 'Error 3'];
const error = new CombinedError({ graphQLErrors });
try {
handleBuildRequestError(error, platform);
} catch {}
expect(logDebugSpy).toBeCalledWith(EXPECTED_STRINGIFIED_GRAPHQL_ERROR_JSON);
});
it('does it with iOS', async () => {
const platform = Platform.IOS;
const logDebugSpy = jest.spyOn(Log, 'debug');
const graphQLErrors = ['Error 1', 'Error 2', 'Error 3'];
const error = new CombinedError({ graphQLErrors });
try {
handleBuildRequestError(error, platform);
} catch {}
expect(logDebugSpy).toBeCalledWith(EXPECTED_STRINGIFIED_GRAPHQL_ERROR_JSON);
});
});
describe('logs nothing to debug if graphQLErrors not present', () => {
it('does it with Android', async () => {
const platform = Platform.ANDROID;
const logDebugSpy = jest.spyOn(Log, 'debug');
const error = new Error('Generic error');
try {
handleBuildRequestError(error, platform);
} catch {}
expect(logDebugSpy).toBeCalledWith(undefined);
});
it('does it with iOS', async () => {
const platform = Platform.IOS;
const logDebugSpy = jest.spyOn(Log, 'debug');
const error = new Error('Generic error');
try {
handleBuildRequestError(error, platform);
} catch {}
expect(logDebugSpy).toBeCalledWith(undefined);
});
});
describe('error is server-side defined error', () => {
describe('for TURTLE_DEPRECATED_JOB_FORMAT', () => {
describe('with Android', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<TurtleDeprecatedJobFormatError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
TurtleDeprecatedJobFormatError,
'Error 1'
);
});
it('throws correct EasBuildResourceClassNotAvailableInFreeTierError', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError(
'Error 1',
'EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER'
);
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError =
getError<EasBuildResourceClassNotAvailableInFreeTierError>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildResourceClassNotAvailableInFreeTierError,
'Error 1'
);
});
});
describe('with iOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<TurtleDeprecatedJobFormatError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
TurtleDeprecatedJobFormatError,
'Error 1'
);
});
it('throws correct EasBuildResourceClassNotAvailableInFreeTierError', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError(
'Error 1',
'EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER'
);
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError =
getError<EasBuildResourceClassNotAvailableInFreeTierError>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildResourceClassNotAvailableInFreeTierError,
'Error 1'
);
});
});
});
describe('for EAS_BUILD_FREE_TIER_DISABLED', () => {
describe('with Android', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildFreeTierDisabledError,
'Error 1'
);
});
});
describe('with iOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildFreeTierDisabledError,
'Error 1'
);
});
});
});
describe('for EAS_BUILD_FREE_TIER_DISABLED_IOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_IOS');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
try {
handleBuildRequestError(error, platform);
} catch (caughtError) {
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledIOSError, 'Error 1');
}
});
});
describe('for EAS_BUILD_FREE_TIER_DISABLED_ANDROID', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_ANDROID');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledAndroidError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildFreeTierDisabledAndroidError,
'Error 1'
);
});
});
describe('for EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierLimitExceededError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildFreeTierLimitExceededError,
'Error 1'
);
});
});
describe('for EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError =
getError<EasBuildFreeTierIosLimitExceededError>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildFreeTierIosLimitExceededError,
'Error 1'
);
});
});
describe('for VALIDATION_ERROR', () => {
describe('with Android', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<RequestValidationError>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(
handleBuildRequestErrorThrownError,
RequestValidationError,
'Error 1'
);
});
});
describe('with iOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<RequestValidationError>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(
handleBuildRequestErrorThrownError,
RequestValidationError,
'Error 1'
);
});
});
});
});
describe('error is other expected graphQL-related error', () => {
describe('for EAS_BUILD_DOWN_FOR_MAINTENANCE', () => {
describe('with Android', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildDownForMaintenanceError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildDownForMaintenanceError,
EXPECTED_EAS_BUILD_DOWN_MESSAGE
);
});
});
describe('with iOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildDownForMaintenanceError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildDownForMaintenanceError,
EXPECTED_EAS_BUILD_DOWN_MESSAGE
);
});
});
});
describe('for EAS_BUILD_TOO_MANY_PENDING_BUILDS', () => {
describe('with Android', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildTooManyPendingBuildsError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildTooManyPendingBuildsError,
EXPECTED_MAX_BUILD_COUNT_MESSAGE_ANDROID
);
});
});
describe('with iOS', () => {
it('throws correct EasCommandError subclass', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const handleBuildRequestErrorThrownError = getError<EasBuildTooManyPendingBuildsError>(
() => {
handleBuildRequestError(error, platform);
}
);
assertReThrownError(
handleBuildRequestErrorThrownError,
EasBuildTooManyPendingBuildsError,
EXPECTED_MAX_BUILD_COUNT_MESSAGE_IOS
);
});
});
});
});
describe('error is unexpected graphQL-related error', () => {
describe('with Android', () => {
it('throws base Error class with custom message', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nRequest ID: ${mockRequestId}\nError message: Error 1`;
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
it('throws base Error class with message including all error messages if combined error has multiple GraphQL errors', async () => {
const platform = Platform.ANDROID;
const graphQLErrors = [
getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR'),
getGraphQLError('Error 2', 'OTHER_GRAPHQL_ERROR'),
getGraphQLError('Error 3', 'YET_ANOTHER_GRAPHQL_ERROR'),
];
const error = new CombinedError({ graphQLErrors });
const expectedMessage =
`${EXPECTED_GENERIC_MESSAGE}\n` +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 1\n' +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 2\n' +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 3';
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
describe('without request ID', () => {
it('throws base Error class with custom message without request ID line', async () => {
const platform = Platform.ANDROID;
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
delete graphQLError.extensions.requestId;
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nError message: Error 1`;
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
});
});
describe('with iOS', () => {
it('throws base Error class with custom message', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nRequest ID: ${mockRequestId}\nError message: Error 1`;
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
it('throws base Error class with message including all error messages if combined error has multiple GraphQL errors', async () => {
const platform = Platform.IOS;
const graphQLErrors = [
getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR'),
getGraphQLError('Error 2', 'OTHER_GRAPHQL_ERROR'),
getGraphQLError('Error 3', 'YET_ANOTHER_GRAPHQL_ERROR'),
];
const error = new CombinedError({ graphQLErrors });
const expectedMessage =
`${EXPECTED_GENERIC_MESSAGE}\n` +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 1\n' +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 2\n' +
`Request ID: ${mockRequestId}\n` +
'Error message: Error 3';
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
describe('without request ID', () => {
it('throws base Error class with custom message without request ID line', async () => {
const platform = Platform.IOS;
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
delete graphQLError.extensions.requestId;
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nError message: Error 1`;
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
});
});
});
});
describe('error is unexpected non-graphQL-related error', () => {
describe('with Android', () => {
it('throws base Error class with propagated message', async () => {
const platform = Platform.ANDROID;
const error = new Error('Non-graphQL-related error');
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
expect(handleBuildRequestErrorThrownError).toStrictEqual(error);
});
});
describe('with iOS', () => {
it('throws base Error class with propagated message', async () => {
const platform = Platform.IOS;
const error = new Error('Non-graphQL-related error');
const handleBuildRequestErrorThrownError = getError<Error>(() => {
handleBuildRequestError(error, platform);
});
expect(handleBuildRequestErrorThrownError).toStrictEqual(error);
});
});
});
});
});
@@ -8,6 +8,7 @@ import os from 'os';
import path from 'path';
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
import { resolveVcsClient } from '../../../vcs';
import {
BumpStrategy,
bumpVersionAsync,
@@ -18,6 +19,8 @@ import {
const fsReal = jest.requireActual('fs').promises as typeof fs;
jest.mock('fs');
const vcsClient = resolveVcsClient();
afterAll(async () => {
// do not remove the following line
// this fixes a weird error with tempy in @expo/image-utils
@@ -191,7 +194,8 @@ describe(maybeResolveVersionsAsync, () => {
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/app',
exp,
{} as BuildProfile<Platform.ANDROID>
{} as BuildProfile<Platform.ANDROID>,
vcsClient
);
expect(appVersion).toBe('3.0.0');
expect(appBuildVersion).toBe('123');
@@ -203,7 +207,8 @@ describe(maybeResolveVersionsAsync, () => {
const { appVersion, appBuildVersion } = await maybeResolveVersionsAsync(
'/app',
exp,
{} as BuildProfile<Platform.ANDROID>
{} as BuildProfile<Platform.ANDROID>,
vcsClient
);
expect(appVersion).toBe('5.0.0');
expect(appBuildVersion).toBe('126');
+23 -11
View File
@@ -1,8 +1,12 @@
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { Android, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { AppVersionSource } from '@expo/eas-json';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteVersionCodeAsync } from './version';
import AndroidCredentialsProvider, {
AndroidCredentials,
} from '../../credentials/android/AndroidCredentialsProvider';
@@ -29,10 +33,6 @@ import {
checkNodeEnvVariable,
validatePNGsForManagedProjectAsync,
} from '../validate';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteVersionCodeAsync } from './version';
export async function createAndroidContextAsync(
ctx: CommonContext<Platform.ANDROID>
@@ -59,13 +59,22 @@ This means that it will most likely produce an AAB and you will not be able to i
await checkGoogleServicesFileAsync(ctx);
await validatePNGsForManagedProjectAsync(ctx);
const gradleContext = await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
const gradleContext = await resolveGradleBuildContextAsync(
ctx.projectDir,
buildProfile,
ctx.vcsClient
);
if (ctx.workflow === Workflow.MANAGED) {
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureApplicationIdIsDefinedForManagedProjectAsync(ctx);
}
const applicationId = await getApplicationIdAsync(ctx.projectDir, ctx.exp, gradleContext);
const applicationId = await getApplicationIdAsync(
ctx.projectDir,
ctx.exp,
ctx.vcsClient,
gradleContext
);
const versionCodeOverride =
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? await resolveRemoteVersionCodeAsync(ctx.graphqlClient, {
@@ -74,6 +83,7 @@ This means that it will most likely produce an AAB and you will not be able to i
exp: ctx.exp,
applicationId,
buildProfile,
vcsClient: ctx.vcsClient,
})
: undefined;
@@ -89,20 +99,21 @@ export async function prepareAndroidBuildAsync(
return await ensureAndroidCredentialsAsync(ctx);
},
syncProjectConfigurationAsync: async () => {
await syncProjectConfigurationAsync(ctx.graphqlClient, {
await syncProjectConfigurationAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
localAutoIncrement:
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
vcsClient: ctx.vcsClient,
env: ctx.buildProfile.env,
});
},
prepareJobAsync: async (
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData<AndroidCredentials>
): Promise<Job> => {
): Promise<Android.Job> => {
return await prepareJobAsync(ctx, jobData);
},
sendBuildRequestAsync: async (
@@ -136,6 +147,7 @@ async function ensureAndroidCredentialsAsync(
const androidApplicationIdentifier = await getApplicationIdAsync(
ctx.projectDir,
ctx.exp,
ctx.vcsClient,
ctx.android.gradleContext
);
const provider = new AndroidCredentialsProvider(ctx.credentialsCtx, {
+13 -2
View File
@@ -1,14 +1,20 @@
import { Android } from '@expo/eas-build-job';
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
import {
loggerLevelToGraphQLWorkerLoggerLevel,
transformBuildMode,
transformBuildTrigger,
transformProjectArchive,
transformWorkflow,
} from '../graphql';
export function transformJob(job: Android.Job): AndroidJobInput {
return {
type: transformWorkflow(job.type),
triggeredBy: transformBuildTrigger(job.triggeredBy),
projectArchive: transformProjectArchive(job.projectArchive),
projectRootDirectory: job.projectRootDirectory,
releaseChannel: job.releaseChannel,
updates: job.updates,
secrets: job.secrets,
builderEnvironment: job.builderEnvironment,
@@ -21,6 +27,11 @@ export function transformJob(job: Android.Job): AndroidJobInput {
buildType: job.buildType && transformBuildType(job.buildType),
developmentClient: job.developmentClient,
experimental: job.experimental,
mode: transformBuildMode(job.mode),
customBuildConfig: job.customBuildConfig,
loggerLevel: job.loggerLevel
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
: undefined,
};
}
@@ -3,17 +3,16 @@ import {
ArchiveSource,
BuildMode,
BuildTrigger,
Job,
Platform,
sanitizeJob,
sanitizeBuildJob,
} from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import path from 'path';
import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
@@ -23,18 +22,17 @@ interface JobData {
const cacheDefaults = {
disabled: false,
customPaths: [],
cacheDefaultPaths: true,
paths: [],
};
export async function prepareJobAsync(
ctx: BuildContext<Platform.ANDROID>,
jobData: JobData
): Promise<Job> {
): Promise<Android.Job> {
const username = getUsername(ctx.exp, ctx.user);
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
const { credentials } = jobData;
const buildCredentials = credentials
? {
@@ -49,11 +47,24 @@ export async function prepareJobAsync(
}
: {};
let buildType = ctx.buildProfile.buildType;
if (!buildType && !buildProfile.gradleCommand && ctx.buildProfile.distribution === 'internal') {
let buildType = buildProfile.buildType;
if (!buildType && !buildProfile.gradleCommand && buildProfile.distribution === 'internal') {
buildType = Android.BuildType.APK;
}
const maybeCustomBuildConfigPath = buildProfile.config
? getCustomBuildConfigPathForJob(buildProfile.config)
: undefined;
let buildMode;
if (ctx.repack) {
buildMode = BuildMode.REPACK;
} else if (buildProfile.config) {
buildMode = BuildMode.CUSTOM;
} else {
buildMode = BuildMode.BUILD;
}
const job: Android.Job = {
type: ctx.workflow,
platform: Platform.ANDROID,
@@ -62,9 +73,10 @@ export async function prepareJobAsync(
builderEnvironment: {
image: buildProfile.image,
node: buildProfile.node,
pnpm: buildProfile.pnpm,
bun: buildProfile.bun,
yarn: buildProfile.yarn,
ndk: buildProfile.ndk,
expoCli: buildProfile.expoCli,
env: buildProfile.env,
},
cache: {
@@ -75,8 +87,7 @@ export async function prepareJobAsync(
secrets: {
...buildCredentials,
},
releaseChannel: ctx.buildProfile.releaseChannel,
updates: { channel: ctx.buildProfile.channel },
updates: { channel: buildProfile.channel },
developmentClient: buildProfile.developmentClient,
gradleCommand: buildProfile.gradleCommand,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
@@ -89,11 +100,22 @@ export async function prepareJobAsync(
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
prebuildCommand: buildProfile.prebuildCommand,
},
mode: BuildMode.BUILD,
mode: buildMode,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
...(ctx.repack && {
customBuildConfig: {
path: '__eas/repack.yml',
},
}),
loggerLevel: ctx.loggerLevel,
};
return sanitizeJob(job);
return sanitizeBuildJob(job) as Android.Job;
}
@@ -1,39 +1,38 @@
import { ExpoConfig } from '@expo/config';
import { AndroidConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { Env, Platform, Workflow } from '@expo/eas-build-job';
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import Log from '../../log';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import { Client } from '../../vcs/vcs';
export async function syncProjectConfigurationAsync(
graphqlClient: ExpoGraphqlClient,
{
projectDir,
exp,
localAutoIncrement,
projectId,
}: {
projectDir: string;
exp: ExpoConfig;
localAutoIncrement?: AndroidVersionAutoIncrement;
projectId: string;
}
): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
export async function syncProjectConfigurationAsync({
projectDir,
exp,
localAutoIncrement,
vcsClient,
env,
}: {
projectDir: string;
exp: ExpoConfig;
localAutoIncrement?: AndroidVersionAutoIncrement;
vcsClient: Client;
env: Env | undefined;
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
if (isExpoUpdatesInstalled(projectDir)) {
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
await syncUpdatesConfigurationAsync({ projectDir, exp, workflow, env });
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
} else {
+13 -5
View File
@@ -18,6 +18,7 @@ import {
} from '../../project/android/gradleUtils';
import { getNextVersionCode } from '../../project/android/versions';
import { resolveWorkflowAsync } from '../../project/workflow';
import { Client } from '../../vcs/vcs';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
@@ -96,9 +97,10 @@ export async function bumpVersionInAppJsonAsync({
export async function maybeResolveVersionsAsync(
projectDir: string,
exp: ExpoConfig,
buildProfile: BuildProfile<Platform.ANDROID>
buildProfile: BuildProfile<Platform.ANDROID>,
vcsClient: Client
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.GENERIC) {
const buildGradle = await getAppBuildGradleAsync(projectDir);
try {
@@ -183,12 +185,14 @@ export async function resolveRemoteVersionCodeAsync(
exp,
applicationId,
buildProfile,
vcsClient,
}: {
projectDir: string;
projectId: string;
exp: ExpoConfig;
applicationId: string;
buildProfile: BuildProfile<Platform.ANDROID>;
vcsClient: Client;
}
): Promise<string> {
const remoteVersions = await AppVersionQuery.latestVersionAsync(
@@ -198,7 +202,7 @@ export async function resolveRemoteVersionCodeAsync(
applicationId
);
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile);
const localVersions = await maybeResolveVersionsAsync(projectDir, exp, buildProfile, vcsClient);
let currentBuildVersion: string;
if (remoteVersions?.buildVersion) {
currentBuildVersion = remoteVersions.buildVersion;
@@ -228,7 +232,9 @@ export async function resolveRemoteVersionCodeAsync(
applicationIdentifier: applicationId,
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
buildVersion: currentBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
runtimeVersion:
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
undefined,
});
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
} catch (err) {
@@ -250,7 +256,9 @@ export async function resolveRemoteVersionCodeAsync(
applicationIdentifier: applicationId,
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
buildVersion: String(nextBuildVersion),
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
runtimeVersion:
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
undefined,
});
spinner.succeed(
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
+250 -138
View File
@@ -1,25 +1,60 @@
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
import { CredentialsSource, EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
import {
ArchiveSource,
ArchiveSourceType,
BuildJob,
FingerprintSourceType,
Metadata,
Platform,
} from '@expo/eas-build-job';
import { CredentialsSource } from '@expo/eas-json';
import assert from 'assert';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
import fs from 'fs-extra';
import { GraphQLError } from 'graphql/error';
import nullthrows from 'nullthrows';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { BuildContext } from './context';
import {
EasBuildDownForMaintenanceError,
EasBuildFreeTierDisabledAndroidError,
EasBuildFreeTierDisabledError,
EasBuildFreeTierDisabledIOSError,
EasBuildFreeTierIosLimitExceededError,
EasBuildFreeTierLimitExceededError,
EasBuildLegacyResourceClassNotAvailableError,
EasBuildProjectArchiveUploadError,
EasBuildResourceClassNotAvailableInFreeTierError,
EasBuildTooManyPendingBuildsError,
RequestValidationError,
TurtleDeprecatedJobFormatError,
} from './errors';
import { transformMetadata } from './graphql';
import { LocalBuildMode, runLocalBuildAsync } from './local';
import { collectMetadataAsync } from './metadata';
import { printDeprecationWarnings } from './utils/printBuildInfo';
import {
LocalFile,
makeProjectMetadataFileAsync,
makeProjectTarballAsync,
reviewAndCommitChangesAsync,
} from './utils/repository';
import { BuildEvent } from '../analytics/AnalyticsManager';
import { withAnalyticsAsync } from '../analytics/common';
import { getExpoWebsiteBaseUrl } from '../api';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EasCommandError } from '../commandUtils/errors';
import {
AppPlatform,
BuildFragment,
BuildParamsInput,
BuildPriority,
BuildResourceClass,
BuildStatus,
UploadSessionType,
} from '../graphql/generated';
import { BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildMutation, BuildResult } from '../graphql/mutations/BuildMutation';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import Log, { learnMore, link } from '../log';
import { Ora, ora } from '../ora';
@@ -28,18 +63,13 @@ import {
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform';
import { confirmAsync } from '../prompts';
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
import { uploadFileAtPathToGCSAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { printJsonOnlyOutput } from '../utils/json';
import { getTmpDirectory } from '../utils/paths';
import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise';
import { getVcsClient } from '../vcs';
import { BuildContext } from './context';
import { LocalBuildMode, runLocalBuildAsync } from './local';
import { collectMetadataAsync } from './metadata';
import { printDeprecationWarnings } from './utils/printBuildInfo';
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
export interface CredentialsResult<Credentials> {
source: CredentialsSource.LOCAL | CredentialsSource.REMOTE;
@@ -51,14 +81,14 @@ export interface JobData<Credentials> {
projectArchive: ArchiveSource;
}
interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
interface Builder<TPlatform extends Platform, Credentials, TJob extends BuildJob> {
ctx: BuildContext<TPlatform>;
ensureCredentialsAsync(
ctx: BuildContext<TPlatform>
): Promise<CredentialsResult<Credentials> | undefined>;
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<BuildJob>;
sendBuildRequestAsync(
appId: string,
job: TJob,
@@ -69,16 +99,21 @@ interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
export type BuildRequestSender = () => Promise<BuildFragment | undefined>;
function resolveBuildParamsInput<T extends Platform>(ctx: BuildContext<T>): BuildParamsInput {
function resolveBuildParamsInput<T extends Platform>(
ctx: BuildContext<T>,
metadata: Metadata
): BuildParamsInput {
return {
resourceClass: ctx.resourceClass,
sdkVersion: metadata.sdkVersion,
reactNativeVersion: metadata.reactNativeVersion,
};
}
export async function prepareBuildRequestForPlatformAsync<
TPlatform extends Platform,
Credentials,
TJob extends Job
TJob extends BuildJob,
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
const { ctx } = builder;
const credentialsResult = await withAnalyticsAsync(
@@ -103,9 +138,10 @@ export async function prepareBuildRequestForPlatformAsync<
}
);
if (await getVcsClient().isCommitRequiredAsync()) {
if (await ctx.vcsClient.isCommitRequiredAsync()) {
Log.addNewLineIfNone();
await reviewAndCommitChangesAsync(
ctx.vcsClient,
`[EAS Build] Run EAS Build for ${requestedPlatformDisplayNames[ctx.platform as Platform]}`,
{ nonInteractive: ctx.nonInteractive }
);
@@ -113,9 +149,10 @@ export async function prepareBuildRequestForPlatformAsync<
let projectArchive: ArchiveSource | undefined;
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
const projectPath = (await makeProjectTarballAsync(ctx.vcsClient)).path;
projectArchive = {
type: ArchiveSourceType.PATH,
path: (await makeProjectTarballAsync()).path,
path: projectPath,
};
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
projectArchive = {
@@ -125,13 +162,14 @@ export async function prepareBuildRequestForPlatformAsync<
} else if (!ctx.localBuildOptions.localBuildMode) {
projectArchive = {
type: ArchiveSourceType.GCS,
bucketKey: await uploadProjectAsync(ctx),
...(await uploadProjectAsync(ctx)),
};
}
assert(projectArchive);
const metadata = await collectMetadataAsync(ctx);
const buildParams = resolveBuildParamsInput(ctx);
const runtimeMetadata = await createAndMaybeUploadFingerprintAsync(ctx);
const metadata = await collectMetadataAsync(ctx, runtimeMetadata);
const buildParams = resolveBuildParamsInput(ctx, metadata);
const job = await builder.prepareJobAsync(ctx, {
projectArchive,
credentials: credentialsResult?.credentials,
@@ -142,6 +180,10 @@ export async function prepareBuildRequestForPlatformAsync<
await runLocalBuildAsync(job, metadata, ctx.localBuildOptions);
return undefined;
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
await BuildMutation.updateBuildMetadataAsync(ctx.graphqlClient, {
buildId: nullthrows(process.env.EAS_BUILD_ID),
metadata: transformMetadata(metadata),
});
printJsonOnlyOutput({ job, metadata });
return undefined;
} else if (!ctx.localBuildOptions.localBuildMode) {
@@ -156,38 +198,50 @@ export async function prepareBuildRequestForPlatformAsync<
};
}
const SERVER_SIDE_DEFINED_ERRORS = [
'TURTLE_DEPRECATED_JOB_FORMAT',
'EAS_BUILD_FREE_TIER_DISABLED',
'EAS_BUILD_FREE_TIER_DISABLED_IOS',
'EAS_BUILD_FREE_TIER_DISABLED_ANDROID',
];
const SERVER_SIDE_DEFINED_ERRORS: Record<string, typeof EasCommandError> = {
TURTLE_DEPRECATED_JOB_FORMAT: TurtleDeprecatedJobFormatError,
EAS_BUILD_FREE_TIER_DISABLED: EasBuildFreeTierDisabledError,
EAS_BUILD_FREE_TIER_DISABLED_IOS: EasBuildFreeTierDisabledIOSError,
EAS_BUILD_FREE_TIER_DISABLED_ANDROID: EasBuildFreeTierDisabledAndroidError,
EAS_BUILD_FREE_TIER_LIMIT_EXCEEDED: EasBuildFreeTierLimitExceededError,
EAS_BUILD_FREE_TIER_IOS_LIMIT_EXCEEDED: EasBuildFreeTierIosLimitExceededError,
EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER:
EasBuildResourceClassNotAvailableInFreeTierError,
EAS_BUILD_LEGACY_RESOURCE_CLASS_NOT_AVAILABLE: EasBuildLegacyResourceClassNotAvailableError,
VALIDATION_ERROR: RequestValidationError,
};
function handleBuildRequestError(error: any, platform: Platform): never {
export function handleBuildRequestError(error: any, platform: Platform): never {
Log.debug(JSON.stringify(error.graphQLErrors, null, 2));
if (SERVER_SIDE_DEFINED_ERRORS.includes(error?.graphQLErrors?.[0]?.extensions?.errorCode)) {
Log.error(error?.graphQLErrors?.[0]?.message);
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE'
) {
Log.error(
const graphQLErrorCode: string = error?.graphQLErrors?.[0]?.extensions?.errorCode;
if (graphQLErrorCode in SERVER_SIDE_DEFINED_ERRORS) {
const ErrorClass: typeof EasCommandError = SERVER_SIDE_DEFINED_ERRORS[graphQLErrorCode];
throw new ErrorClass(error?.graphQLErrors?.[0]?.message);
} else if (graphQLErrorCode === 'EAS_BUILD_DOWN_FOR_MAINTENANCE') {
throw new EasBuildDownForMaintenanceError(
`EAS Build is down for maintenance. Try again later. Check ${link(
'https://status.expo.dev/'
)} for updates.`
);
throw new Error('Build request failed.');
} else if (
error?.graphQLErrors?.[0]?.extensions?.errorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS'
) {
Log.error(
} else if (graphQLErrorCode === 'EAS_BUILD_TOO_MANY_PENDING_BUILDS') {
throw new EasBuildTooManyPendingBuildsError(
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
);
throw new Error('Build request failed.');
} else if (error?.graphQLErrors) {
Log.error(
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.'
const errorMessage = error.graphQLErrors
.map((graphQLError: GraphQLError) => {
const requestIdLine = graphQLError?.extensions?.requestId
? `\nRequest ID: ${graphQLError.extensions.requestId}`
: '';
const errorMessageLine = graphQLError?.message
? `\nError message: ${graphQLError.message}`
: '';
return `${requestIdLine}${errorMessageLine}`;
})
.join('');
throw new Error(
`Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.${errorMessage}`
);
}
throw error;
@@ -195,7 +249,10 @@ function handleBuildRequestError(error: any, platform: Platform): never {
async function uploadProjectAsync<TPlatform extends Platform>(
ctx: BuildContext<TPlatform>
): Promise<string> {
): Promise<{
bucketKey: string;
metadataLocation?: string;
}> {
let projectTarballPath;
try {
return await withAnalyticsAsync(
@@ -207,7 +264,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
'https://expo.fyi/eas-build-archive'
)}`
);
const projectTarball = await makeProjectTarballAsync();
const projectTarball = await makeProjectTarballAsync(ctx.vcsClient);
if (projectTarball.size > 1024 * 1024 * 100) {
Log.warn(
@@ -219,22 +276,32 @@ async function uploadProjectAsync<TPlatform extends Platform>(
);
}
projectTarballPath = projectTarball.path;
if (projectTarball.size > 2 * 1024 * 1024 * 1024) {
throw new Error('Project archive is too big. Maximum allowed size is 2GB.');
}
const bucketKey = await uploadFileAtPathToGCSAsync(
ctx.graphqlClient,
UploadSessionType.EasBuildGcsProjectSources,
projectTarball.path,
createProgressTracker({
total: projectTarball.size,
message: ratio =>
`Uploading to EAS Build (${formatBytes(projectTarball.size * ratio)} / ${formatBytes(
projectTarball.size
)})`,
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
})
);
return bucketKey;
projectTarballPath = projectTarball.path;
const [bucketKey, { metadataLocation }] = await Promise.all([
uploadFileAtPathToGCSAsync(
ctx.graphqlClient,
UploadSessionType.EasBuildGcsProjectSources,
projectTarball.path,
createProgressTracker({
total: projectTarball.size,
message: ratio =>
`Uploading to EAS Build (${formatBytes(
projectTarball.size * ratio
)} / ${formatBytes(projectTarball.size)})`,
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
})
),
uploadMetadataFileAsync<TPlatform>(projectTarball, ctx),
]);
if (metadataLocation) {
return { bucketKey, metadataLocation };
}
return { bucketKey };
},
{
attemptEvent: BuildEvent.PROJECT_UPLOAD_ATTEMPT,
@@ -243,6 +310,14 @@ async function uploadProjectAsync<TPlatform extends Platform>(
properties: ctx.analyticsEventProperties,
}
);
} catch (err: any) {
let errMessage = 'Failed to upload the project tarball to EAS Build';
if (err.message) {
errMessage += `\n\nReason: ${err.message}`;
}
throw new EasBuildProjectArchiveUploadError(errMessage);
} finally {
if (projectTarballPath) {
await fs.remove(projectTarballPath);
@@ -250,7 +325,41 @@ async function uploadProjectAsync<TPlatform extends Platform>(
}
}
async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJob extends Job>(
async function uploadMetadataFileAsync<TPlatform extends Platform>(
projectTarball: LocalFile,
ctx: BuildContext<TPlatform>
): Promise<{ metadataLocation: string | null }> {
let projectMetadataFile: LocalFile | null = null;
try {
projectMetadataFile = await makeProjectMetadataFileAsync(projectTarball.path);
const metadataLocation = await uploadFileAtPathToGCSAsync(
ctx.graphqlClient,
UploadSessionType.EasBuildGcsProjectMetadata,
projectMetadataFile.path
);
return { metadataLocation };
} catch (err: any) {
let errMessage = 'Failed to upload metadata to EAS Build';
if (err.message) {
errMessage += `\n\nReason: ${err.message}`;
}
Log.warn(errMessage);
return { metadataLocation: null };
} finally {
if (projectMetadataFile) {
await fs.remove(projectMetadataFile.path);
}
}
}
async function sendBuildRequestAsync<
TPlatform extends Platform,
Credentials,
TJob extends BuildJob,
>(
builder: Builder<TPlatform, Credentials, TJob>,
job: TJob,
metadata: Metadata,
@@ -287,12 +396,7 @@ export type MaybeBuildFragment = BuildFragment | null;
export async function waitForBuildEndAsync(
graphqlClient: ExpoGraphqlClient,
{
buildIds,
accountName,
projectDir,
nonInteractive,
}: { buildIds: string[]; accountName: string; projectDir: string; nonInteractive: boolean },
{ buildIds, accountName }: { buildIds: string[]; accountName: string },
{ intervalSec = 10 } = {}
): Promise<MaybeBuildFragment[]> {
let spinner;
@@ -309,10 +413,7 @@ export async function waitForBuildEndAsync(
const builds = await getBuildsSafelyAsync(graphqlClient, buildIds);
const { refetch } =
builds.length === 1
? await handleSingleBuildProgressAsync(
{ build: builds[0], accountName, projectDir, nonInteractive },
{ spinner }
)
? await handleSingleBuildProgressAsync({ build: builds[0], accountName }, { spinner })
: await handleMultipleBuildsProgressAsync({ builds }, { spinner, originalSpinnerText });
if (!refetch) {
return builds;
@@ -350,13 +451,9 @@ async function handleSingleBuildProgressAsync(
{
build,
accountName,
projectDir,
nonInteractive,
}: {
build: MaybeBuildFragment;
accountName: string;
projectDir: string;
nonInteractive: boolean;
},
{ spinner }: { spinner: Ora }
): Promise<BuildProgressResult> {
@@ -410,54 +507,6 @@ async function handleSingleBuildProgressAsync(
);
}
if (
build.platform === AppPlatform.Ios &&
[BuildResourceClass.IosIntelLarge, BuildResourceClass.IosIntelMedium].includes(
build.resourceClass
)
) {
let askToSwitchToM1 = false;
if (
build.priority === BuildPriority.High &&
build.estimatedWaitTimeLeftSeconds >= /* 10 minutes */ 10 * 60
) {
Log.newLine();
Log.warn(
`Warning: Priority queue wait time for legacy iOS Intel workers is longer than usual (more than 10 minutes).`
);
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
Log.warn(
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
learnMoreMessage: 'Learn more on switching to M1 workers.',
})
);
askToSwitchToM1 = true;
} else if (
build.priority !== BuildPriority.High &&
build.estimatedWaitTimeLeftSeconds >= /* 120 minutes */ 120 * 60
) {
Log.newLine();
Log.warn(
`Warning: Free tier queue wait time for legacy iOS Intel workers is longer than usual.`
);
Log.warn(`We recommend switching to the new, faster M1 workers for iOS builds.`);
Log.warn(
learnMore('https://blog.expo.dev/m1-workers-on-eas-build-dcaa2c1333ad', {
learnMoreMessage: 'Learn more on switching to M1 workers.',
})
);
askToSwitchToM1 = true;
}
if (!nonInteractive && askToSwitchToM1) {
const shouldSwitchToM1 = await confirmAsync({
message: `Switch iOS builds to M1 workers (modifies build profiles in eas.json)?`,
});
if (shouldSwitchToM1) {
await updateIosBuildProfilesToUseM1WorkersAsync(projectDir);
}
}
}
Log.newLine();
Log.log(`Waiting in ${priorityToQueueDisplayName[build.priority]}`);
queueProgressBar.start(
@@ -472,6 +521,7 @@ async function handleSingleBuildProgressAsync(
}
break;
}
case BuildStatus.PendingCancel:
case BuildStatus.Canceled:
spinner.fail('Build canceled');
return { refetch: false };
@@ -502,6 +552,7 @@ const statusToDisplayName: Record<BuildStatus, string> = {
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
[BuildStatus.InQueue]: 'in queue',
[BuildStatus.InProgress]: 'in progress',
[BuildStatus.PendingCancel]: 'canceled',
[BuildStatus.Canceled]: 'canceled',
[BuildStatus.Finished]: 'finished',
[BuildStatus.Errored]: 'failed',
@@ -520,7 +571,12 @@ async function handleMultipleBuildsProgressAsync(
builds.filter(build => build.status === BuildStatus.Finished).length === buildCount;
const allSettled =
builds.filter(build =>
[BuildStatus.Finished, BuildStatus.Errored, BuildStatus.Canceled].includes(build.status)
[
BuildStatus.Finished,
BuildStatus.Errored,
BuildStatus.Canceled,
BuildStatus.PendingCancel,
].includes(build.status)
).length === buildCount;
if (allSettled) {
@@ -600,20 +656,76 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
).toString();
}
async function updateIosBuildProfilesToUseM1WorkersAsync(projectDir: string): Promise<void> {
const easJsonAccessor = new EasJsonAccessor(projectDir);
await easJsonAccessor.readRawJsonAsync();
const profileNames = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
easJsonAccessor.patch(easJsonRawObject => {
for (const profileName of profileNames) {
easJsonRawObject.build[profileName].ios = {
...easJsonRawObject.build[profileName].ios,
resourceClass: ResourceClass.M1_MEDIUM,
};
}
return easJsonRawObject;
async function createAndMaybeUploadFingerprintAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<{
runtimeVersion?: string;
fingerprintSource?: Metadata['fingerprintSource'];
}> {
const resolvedRuntimeVersion = await resolveRuntimeVersionAsync({
exp: ctx.exp,
platform: ctx.platform,
workflow: ctx.workflow,
projectDir: ctx.projectDir,
env: ctx.buildProfile.env,
cwd: ctx.projectDir,
});
await easJsonAccessor.writeAsync();
Log.withTick('Updated eas.json. Your next builds will run on M1 workers.');
/**
* It's ok for fingerprintSources to be empty
* fingerprintSources only exist if the project is using runtimeVersion.policy: fingerprint
*/
if (!resolvedRuntimeVersion?.fingerprintSources) {
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
};
}
await fs.mkdirp(getTmpDirectory());
const fingerprintLocation = path.join(getTmpDirectory(), `${uuidv4()}-runtime-fingerprint.json`);
await fs.writeJSON(fingerprintLocation, {
hash: resolvedRuntimeVersion.runtimeVersion,
sources: resolvedRuntimeVersion.fingerprintSources,
});
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
fingerprintSource: {
type: FingerprintSourceType.PATH,
path: fingerprintLocation,
},
};
}
let fingerprintGCSBucketKey = undefined;
try {
fingerprintGCSBucketKey = await uploadFileAtPathToGCSAsync(
ctx.graphqlClient,
UploadSessionType.EasUpdateFingerprint,
fingerprintLocation
);
} catch (err: any) {
let errMessage = 'Failed to upload fingerprint to EAS';
if (err.message) {
errMessage += `\n\nReason: ${err.message}`;
}
Log.warn(errMessage);
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
};
} finally {
await fs.remove(fingerprintLocation);
}
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
fingerprintSource: {
type: FingerprintSourceType.GCS,
bucketKey: fingerprintGCSBucketKey,
},
};
}
+29 -69
View File
@@ -1,17 +1,20 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { EasJson, EasJsonAccessor, ResourceClass } from '@expo/eas-json';
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
import chalk from 'chalk';
import fs from 'fs-extra';
import Log from '../log';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
import Log, { learnMore } from '../log';
import { easCliVersion } from '../utils/easCli';
import { Client } from '../vcs/vcs';
interface ConfigureParams {
projectDir: string;
nonInteractive: boolean;
vcsClient: Client;
}
export async function easJsonExistsAsync(projectDir: string): Promise<boolean> {
return await fs.pathExists(EasJsonAccessor.formatEasJsonPath(projectDir));
}
/**
@@ -24,7 +27,7 @@ interface ConfigureParams {
export async function ensureProjectConfiguredAsync(
configureParams: ConfigureParams
): Promise<boolean> {
if (await fs.pathExists(EasJsonAccessor.formatEasJsonPath(configureParams.projectDir))) {
if (await easJsonExistsAsync(configureParams.projectDir)) {
return false;
}
@@ -32,20 +35,24 @@ export async function ensureProjectConfiguredAsync(
return true;
}
async function configureAsync({ projectDir, nonInteractive }: ConfigureParams): Promise<void> {
await maybeBailOnRepoStatusAsync();
async function configureAsync({
projectDir,
nonInteractive,
vcsClient,
}: ConfigureParams): Promise<void> {
await maybeBailOnRepoStatusAsync(vcsClient, nonInteractive);
await createEasJsonAsync(projectDir);
await createEasJsonAsync(projectDir, vcsClient);
if (await getVcsClient().isCommitRequiredAsync()) {
if (await vcsClient.isCommitRequiredAsync()) {
Log.newLine();
await reviewAndCommitChangesAsync('Configure EAS Build', {
await reviewAndCommitChangesAsync(vcsClient, 'Configure EAS Build', {
nonInteractive,
});
}
}
const EAS_JSON_MANAGED_DEFAULT: EasJson = {
const EAS_JSON_DEFAULT: EasJson = {
cli: {
version: `>= ${easCliVersion}`,
},
@@ -53,72 +60,25 @@ const EAS_JSON_MANAGED_DEFAULT: EasJson = {
development: {
developmentClient: true,
distribution: 'internal',
ios: {
resourceClass: ResourceClass.M1_MEDIUM,
},
},
preview: {
distribution: 'internal',
ios: {
resourceClass: ResourceClass.M1_MEDIUM,
},
},
production: {
ios: {
resourceClass: ResourceClass.M1_MEDIUM,
},
},
production: {},
},
submit: {
production: {},
},
};
const EAS_JSON_BARE_DEFAULT: EasJson = {
cli: {
version: `>= ${easCliVersion}`,
},
build: {
development: {
distribution: 'internal',
android: {
gradleCommand: ':app:assembleDebug',
},
ios: {
buildConfiguration: 'Debug',
resourceClass: ResourceClass.M1_MEDIUM,
},
},
preview: {
distribution: 'internal',
ios: {
resourceClass: ResourceClass.M1_MEDIUM,
},
},
production: {
ios: {
resourceClass: ResourceClass.M1_MEDIUM,
},
},
},
submit: {
production: {},
},
};
async function createEasJsonAsync(projectDir: string): Promise<void> {
async function createEasJsonAsync(projectDir: string, vcsClient: Client): Promise<void> {
const easJsonPath = EasJsonAccessor.formatEasJsonPath(projectDir);
const hasAndroidNativeProject =
(await resolveWorkflowAsync(projectDir, Platform.ANDROID)) === Workflow.GENERIC;
const hasIosNativeProject =
(await resolveWorkflowAsync(projectDir, Platform.IOS)) === Workflow.GENERIC;
const easJson =
hasAndroidNativeProject || hasIosNativeProject
? EAS_JSON_BARE_DEFAULT
: EAS_JSON_MANAGED_DEFAULT;
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
await getVcsClient().trackFileAsync(easJsonPath);
Log.withTick(`Generated ${chalk.bold('eas.json')}`);
await fs.writeFile(easJsonPath, `${JSON.stringify(EAS_JSON_DEFAULT, null, 2)}\n`);
await vcsClient.trackFileAsync(easJsonPath);
Log.withTick(
`Generated ${chalk.bold('eas.json')}. ${learnMore(
'https://docs.expo.dev/build-reference/eas-json/'
)}`
);
}
+11 -1
View File
@@ -1,16 +1,20 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJson } from '@expo/eas-json';
import { LoggerLevel } from '@expo/logger';
import { NodePackageManager } from '@expo/package-manager';
import { LocalBuildOptions } from './local';
import { Analytics, AnalyticsEventProperties } from '../analytics/AnalyticsManager';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { CredentialsContext } from '../credentials/context';
import { Target } from '../credentials/ios/types';
import { BuildResourceClass } from '../graphql/generated';
import { GradleBuildContext } from '../project/android/gradle';
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { LocalBuildOptions } from './local';
import { Client } from '../vcs/vcs';
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
@@ -51,6 +55,12 @@ export interface BuildContext<T extends Platform> {
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
workflow: Workflow;
customBuildConfigMetadata?: CustomBuildConfigMetadata;
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
ios: T extends Platform.IOS ? IosBuildContext : undefined;
developmentClient: boolean;
requiredPackageManager: NodePackageManager['name'] | null;
vcsClient: Client;
loggerLevel?: LoggerLevel;
repack: boolean;
}
+49 -13
View File
@@ -1,22 +1,26 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJson } from '@expo/eas-json';
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
import JsonFile from '@expo/json-file';
import { LoggerLevel } from '@expo/logger';
import { resolvePackageManager } from '@expo/package-manager';
import getenv from 'getenv';
import resolveFrom from 'resolve-from';
import { v4 as uuidv4 } from 'uuid';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildMode, LocalBuildOptions } from './local';
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/AnalyticsManager';
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { CredentialsContext } from '../credentials/context';
import { BuildResourceClass } from '../graphql/generated';
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { Actor } from '../user/User';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { Client } from '../vcs/vcs';
export async function createBuildContextAsync<T extends Platform>({
buildProfileName,
@@ -28,12 +32,17 @@ export async function createBuildContextAsync<T extends Platform>({
noWait,
platform,
projectDir,
resourceClass,
resourceClassFlag,
message,
actor,
graphqlClient,
analytics,
getDynamicProjectConfigAsync,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
buildLoggerLevel,
freezeCredentials,
repack,
}: {
buildProfileName: string;
buildProfile: BuildProfile<T>;
@@ -44,20 +53,32 @@ export async function createBuildContextAsync<T extends Platform>({
noWait: boolean;
platform: T;
projectDir: string;
resourceClass: BuildResourceClass;
resourceClassFlag?: ResourceClass;
message?: string;
actor: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
getDynamicProjectConfigAsync: DynamicConfigContextFn;
vcsClient: Client;
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
customBuildConfigMetadata?: CustomBuildConfigMetadata;
buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean;
repack: boolean;
}): Promise<BuildContext<T>> {
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
const projectName = exp.slug;
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
const workflow = await resolveWorkflowAsync(projectDir, platform);
const workflow = await resolveWorkflowAsync(projectDir, platform, vcsClient);
const accountId = account.id;
const runFromCI = getenv.boolish('CI', false);
const developmentClient =
buildProfile.developmentClient ??
(platform === Platform.ANDROID
? (buildProfile as BuildProfile<Platform.ANDROID>)?.gradleCommand === ':app:assembleDebug'
: (buildProfile as BuildProfile<Platform.IOS>)?.buildConfiguration === 'Debug') ??
false;
const requiredPackageManager = resolvePackageManager(projectDir);
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
@@ -68,6 +89,8 @@ export async function createBuildContextAsync<T extends Platform>({
analytics,
env: buildProfile.env,
easJsonCliConfig,
vcsClient,
freezeCredentials,
});
const devClientProperties = getDevClientEventProperties({
@@ -85,9 +108,16 @@ export async function createBuildContextAsync<T extends Platform>({
...devClientProperties,
no_wait: noWait,
run_from_ci: runFromCI,
local: localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN,
};
analytics.logEvent(BuildEvent.BUILD_COMMAND, analyticsEventProperties);
const resourceClass = await resolveBuildResourceClassAsync(
buildProfile,
platform,
resourceClassFlag
);
const commonContext: CommonContext<T> = {
accountName: account.name,
buildProfile,
@@ -108,9 +138,15 @@ export async function createBuildContextAsync<T extends Platform>({
user: actor,
graphqlClient,
analytics,
vcsClient,
workflow,
message,
runFromCI,
customBuildConfigMetadata,
developmentClient,
requiredPackageManager,
loggerLevel: buildLoggerLevel,
repack,
};
if (platform === Platform.ANDROID) {
const common = commonContext as CommonContext<Platform.ANDROID>;
+25
View File
@@ -0,0 +1,25 @@
import { EasCommandError } from '../commandUtils/errors';
export class TurtleDeprecatedJobFormatError extends EasCommandError {}
export class EasBuildFreeTierDisabledError extends EasCommandError {}
export class EasBuildFreeTierDisabledIOSError extends EasCommandError {}
export class EasBuildFreeTierDisabledAndroidError extends EasCommandError {}
export class EasBuildFreeTierLimitExceededError extends EasCommandError {}
export class EasBuildFreeTierIosLimitExceededError extends EasCommandError {}
export class EasBuildResourceClassNotAvailableInFreeTierError extends EasCommandError {}
export class EasBuildLegacyResourceClassNotAvailableError extends EasCommandError {}
export class RequestValidationError extends EasCommandError {}
export class EasBuildDownForMaintenanceError extends EasCommandError {}
export class EasBuildTooManyPendingBuildsError extends EasCommandError {}
export class EasBuildProjectArchiveUploadError extends EasCommandError {}
+45 -15
View File
@@ -1,26 +1,33 @@
import { ArchiveSource, ArchiveSourceType, Metadata, Workflow } from '@expo/eas-build-job';
import {
ArchiveSource,
ArchiveSourceType,
BuildMode,
BuildTrigger,
Metadata,
Workflow,
} from '@expo/eas-build-job';
import { LoggerLevel } from '@expo/logger';
import assert from 'assert';
import {
BuildCredentialsSource,
BuildIosEnterpriseProvisioning,
BuildMetadataInput,
BuildMode,
BuildWorkflow,
DistributionType,
BuildMode as GraphQLBuildMode,
BuildTrigger as GraphQLBuildTrigger,
ProjectArchiveSourceInput,
ProjectArchiveSourceType,
WorkerLoggerLevel,
} from '../graphql/generated';
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
if (archiveSource.type === ArchiveSourceType.S3) {
return {
type: ProjectArchiveSourceType.S3,
bucketKey: archiveSource.bucketKey,
};
} else if (archiveSource.type === ArchiveSourceType.GCS) {
if (archiveSource.type === ArchiveSourceType.GCS) {
return {
type: ProjectArchiveSourceType.Gcs,
bucketKey: archiveSource.bucketKey,
metadataLocation: archiveSource.metadataLocation,
};
} else if (archiveSource.type === ArchiveSourceType.URL) {
return {
@@ -35,7 +42,7 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
return {
...metadata,
buildMode: metadata.buildMode && transformBuildMode(metadata.buildMode),
fingerprintSource: metadata.fingerprintSource as BuildMetadataInput['fingerprintSource'],
credentialsSource:
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
distribution: metadata.distribution && transformDistribution(metadata.distribution),
@@ -84,10 +91,33 @@ export function transformIosEnterpriseProvisioning(
}
}
export function transformBuildMode(buildMode: Metadata['buildMode']): BuildMode {
if (buildMode === 'build') {
return BuildMode.Build;
} else {
return BuildMode.Resign;
}
const buildModeToGraphQLBuildMode: Record<BuildMode, GraphQLBuildMode> = {
[BuildMode.BUILD]: GraphQLBuildMode.Build,
[BuildMode.CUSTOM]: GraphQLBuildMode.Custom,
[BuildMode.RESIGN]: GraphQLBuildMode.Resign,
[BuildMode.REPACK]: GraphQLBuildMode.Repack,
};
export function transformBuildMode(buildMode: BuildMode): GraphQLBuildMode {
const graphQLBuildMode = buildModeToGraphQLBuildMode[buildMode];
assert(graphQLBuildMode, `Unsupported build mode: ${buildMode}`);
return graphQLBuildMode;
}
export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildTrigger {
if (buildTrigger === BuildTrigger.EAS_CLI) {
return GraphQLBuildTrigger.EasCli;
} else if (buildTrigger === BuildTrigger.GIT_BASED_INTEGRATION) {
return GraphQLBuildTrigger.GitBasedIntegration;
}
throw new Error('Unknown build trigger');
}
export const loggerLevelToGraphQLWorkerLoggerLevel: Record<LoggerLevel, WorkerLoggerLevel> = {
[LoggerLevel.TRACE]: WorkerLoggerLevel.Trace,
[LoggerLevel.DEBUG]: WorkerLoggerLevel.Debug,
[LoggerLevel.INFO]: WorkerLoggerLevel.Info,
[LoggerLevel.WARN]: WorkerLoggerLevel.Warn,
[LoggerLevel.ERROR]: WorkerLoggerLevel.Error,
[LoggerLevel.FATAL]: WorkerLoggerLevel.Fatal,
};
@@ -6,6 +6,7 @@ import os from 'os';
import type { XCBuildConfiguration } from 'xcode';
import { readPlistAsync } from '../../../utils/plist';
import { resolveVcsClient } from '../../../vcs';
import {
BumpStrategy,
bumpVersionAsync,
@@ -18,6 +19,8 @@ import {
jest.mock('fs');
const vcsClient = resolveVcsClient();
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
IOSConfig.Target,
'getXCBuildConfigurationFromPbxproj'
@@ -59,7 +62,7 @@ describe(evaluateTemplateString, () => {
// bare workflow
describe(bumpVersionAsync, () => {
beforeEach(() => {
getPbxproj.mockImplementation(() => ({} as any));
getPbxproj.mockImplementation((): any => {});
});
afterEach(() => {
getXCBuildConfigurationFromPbxproj.mockReset();
@@ -73,11 +76,11 @@ describe(bumpVersionAsync, () => {
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
}) as XCBuildConfiguration
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.BUILD_NUMBER,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
targets: [
{
@@ -88,9 +91,9 @@ describe(bumpVersionAsync, () => {
],
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
'/app/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
@@ -109,11 +112,11 @@ describe(bumpVersionAsync, () => {
buildSettings: {
INFOPLIST_FILE: '$(SRCROOT)/myproject/Info2.plist',
},
} as XCBuildConfiguration)
}) as XCBuildConfiguration
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.BUILD_NUMBER,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
targets: [
{
@@ -124,9 +127,9 @@ describe(bumpVersionAsync, () => {
],
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info2.plist'
'/app/ios/myproject/Info2.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
@@ -144,11 +147,11 @@ describe(bumpVersionAsync, () => {
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
}) as XCBuildConfiguration
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.APP_VERSION,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
targets: [
{
@@ -159,9 +162,9 @@ describe(bumpVersionAsync, () => {
],
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
'/app/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.1');
expect(fakeExp.ios?.buildNumber).toBe('1');
@@ -179,11 +182,11 @@ describe(bumpVersionAsync, () => {
buildSettings: {
INFOPLIST_FILE: 'myproject/Info.plist',
},
} as XCBuildConfiguration)
}) as XCBuildConfiguration
);
await bumpVersionAsync({
bumpStrategy: BumpStrategy.NOOP,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
targets: [
{
@@ -194,9 +197,9 @@ describe(bumpVersionAsync, () => {
],
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
const infoPlist = (await readPlistAsync(
'/repo/ios/myproject/Info.plist'
'/app/ios/myproject/Info.plist'
)) as IOSConfig.InfoPlist;
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('1');
@@ -214,11 +217,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.BUILD_NUMBER,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('2');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -230,11 +233,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.APP_VERSION,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
expect(fakeExp.version).toBe('1.0.1');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.1');
@@ -246,11 +249,11 @@ describe(bumpVersionInAppJsonAsync, () => {
await bumpVersionInAppJsonAsync({
bumpStrategy: BumpStrategy.NOOP,
projectDir: '/repo',
projectDir: '/app',
exp: fakeExp,
});
const appJSON = await fs.readJSON('/repo/app.json');
const appJSON = await fs.readJSON('/app/app.json');
expect(fakeExp.version).toBe('1.0.0');
expect(fakeExp.ios?.buildNumber).toBe('1');
expect(appJSON.expo.version).toBe('1.0.0');
@@ -262,7 +265,7 @@ describe(readBuildNumberAsync, () => {
describe('bare project', () => {
it('reads the build number from native code', async () => {
const exp = initBareWorkflowProject();
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient);
expect(buildNumber).toBe('1');
});
});
@@ -270,7 +273,7 @@ describe(readBuildNumberAsync, () => {
describe('managed project', () => {
it('reads the build number from expo config', async () => {
const exp = initManagedProject();
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient);
expect(buildNumber).toBe('1');
});
});
@@ -280,24 +283,48 @@ describe(readShortVersionAsync, () => {
describe('bare project', () => {
it('reads the short version from native code', async () => {
const exp = initBareWorkflowProject();
const appVersion = await readShortVersionAsync('/repo', exp, {});
const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient);
expect(appVersion).toBe('1.0.0');
});
it('evaluates interpolated build number', async () => {
const exp = initBareWorkflowProject({
appVersion: '$(CURRENT_PROJECT_VERSION)',
});
const buildNumber = await readShortVersionAsync('/repo', exp, {
CURRENT_PROJECT_VERSION: '1.0.0',
});
const buildNumber = await readShortVersionAsync(
'/app',
exp,
{
CURRENT_PROJECT_VERSION: '1.0.0',
},
vcsClient
);
expect(buildNumber).toBe('1.0.0');
});
it('fails when build number is invalid', async () => {
const exp = initBareWorkflowProject({
appVersion: '$(CURRENT_PROJECT_VERSION)',
});
await expect(
readShortVersionAsync(
'/app',
exp,
{
CURRENT_PROJECT_VERSION: '0.0.7.1.028',
},
vcsClient
)
).rejects.toThrowError(
'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028'
);
});
});
describe('managed project', () => {
it('reads the version from app config', async () => {
const exp = initBareWorkflowProject();
const appVersion = await readShortVersionAsync('/repo', exp, {});
const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient);
expect(appVersion).toBe('1.0.0');
});
});
@@ -309,66 +336,75 @@ describe(getInfoPlistPath, () => {
{
'./ios/testapp/Info.plist': '',
},
'/repo'
'/app'
);
const plistPath = getInfoPlistPath('/repo', {});
expect(plistPath).toBe('/repo/ios/testapp/Info.plist');
const plistPath = getInfoPlistPath('/app', {});
expect(plistPath).toBe('/app/ios/testapp/Info.plist');
});
it('returns INFOPLIST_FILE if specified', () => {
vol.fromJSON(
{
'./ios/testapp/Info.plist': '',
},
'/repo'
'/app'
);
const plistPath = getInfoPlistPath('/repo', { INFOPLIST_FILE: './qwert/NotInfo.plist' });
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
const plistPath = getInfoPlistPath('/app', { INFOPLIST_FILE: './qwert/NotInfo.plist' });
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
});
it('evaluates SRCROOT in Info.plist', () => {
vol.fromJSON(
{
'./ios/testapp/Info.plist': '',
},
'/repo'
'/app'
);
const plistPath = getInfoPlistPath('/repo', {
const plistPath = getInfoPlistPath('/app', {
INFOPLIST_FILE: '$(SRCROOT)/qwert/NotInfo.plist',
});
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
});
it('evaluates BuildSettings in Info.plist', () => {
vol.fromJSON(
{
'./ios/testapp/Info.plist': '',
},
'/repo'
'/app'
);
const plistPath = getInfoPlistPath('/repo', {
const plistPath = getInfoPlistPath('/app', {
INFOPLIST_FILE: '$(SRCROOT)/qwert/$(TARGET_NAME).plist',
TARGET_NAME: 'NotInfo',
});
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
});
});
function initBareWorkflowProject({
appVersion = '1.0.0',
buildNumber = '1',
expoConfig = {},
infoPlistName = 'Info.plist',
}: { appVersion?: string; buildNumber?: string; infoPlistName?: string } = {}): ExpoConfig {
}: {
appVersion?: string;
buildNumber?: string;
infoPlistName?: string;
expoConfig?: { version?: string; ios?: object };
} = {}): ExpoConfig {
const fakeExp: ExpoConfig = {
name: 'myproject',
slug: 'myproject',
version: '1.0.0',
ios: {
buildNumber: '1',
...expoConfig.ios,
},
...expoConfig,
};
vol.fromJSON(
{
'./app.json': JSON.stringify({
expo: fakeExp,
}),
'./ios/myproject.xcodeproj/project.pbxproj': '',
[`./ios/myproject/${infoPlistName}`]: `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@@ -380,7 +416,7 @@ function initBareWorkflowProject({
</dict>
</plist>`,
},
'/repo'
'/app'
);
return fakeExp;
@@ -402,7 +438,7 @@ function initManagedProject(): ExpoConfig {
expo: fakeExp,
}),
},
'/repo'
'/app'
);
return fakeExp;
+14 -10
View File
@@ -1,6 +1,11 @@
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { Ios, Metadata, Platform, Workflow } from '@expo/eas-build-job';
import { AppVersionSource } from '@expo/eas-json';
import { ensureIosCredentialsAsync } from './credentials';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteBuildNumberAsync } from './version';
import { IosCredentials } from '../../credentials/ios/types';
import { BuildParamsInput } from '../../graphql/generated';
import { BuildMutation, BuildResult } from '../../graphql/mutations/BuildMutation';
@@ -15,11 +20,6 @@ import {
checkNodeEnvVariable,
validatePNGsForManagedProjectAsync,
} from '../validate';
import { ensureIosCredentialsAsync } from './credentials';
import { transformJob } from './graphql';
import { prepareJobAsync } from './prepareJob';
import { syncProjectConfigurationAsync } from './syncProjectConfiguration';
import { resolveRemoteBuildNumberAsync } from './version';
export async function createIosContextAsync(
ctx: CommonContext<Platform.IOS>
@@ -27,7 +27,7 @@ export async function createIosContextAsync(
const { buildProfile } = ctx;
if (ctx.workflow === Workflow.MANAGED) {
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx.projectDir, ctx.exp, ctx.user);
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx);
}
checkNodeEnvVariable(ctx);
@@ -39,6 +39,7 @@ export async function createIosContextAsync(
projectDir: ctx.projectDir,
nonInteractive: ctx.nonInteractive,
exp: ctx.exp,
vcsClient: ctx.vcsClient,
},
buildProfile
);
@@ -47,6 +48,7 @@ export async function createIosContextAsync(
exp: ctx.exp,
xcodeBuildContext,
env: buildProfile.env,
vcsClient: ctx.vcsClient,
});
const applicationTarget = findApplicationTarget(targets);
const buildNumberOverride =
@@ -57,6 +59,7 @@ export async function createIosContextAsync(
exp: ctx.exp,
applicationTarget,
buildProfile,
vcsClient: ctx.vcsClient,
})
: undefined;
return {
@@ -77,7 +80,7 @@ export async function prepareIosBuildAsync(
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
},
syncProjectConfigurationAsync: async () => {
await syncProjectConfigurationAsync(ctx.graphqlClient, {
await syncProjectConfigurationAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
targets: ctx.ios.targets,
@@ -85,13 +88,14 @@ export async function prepareIosBuildAsync(
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
vcsClient: ctx.vcsClient,
env: ctx.buildProfile.env,
});
},
prepareJobAsync: async (
ctx: BuildContext<Platform.IOS>,
jobData: JobData<IosCredentials>
): Promise<Job> => {
): Promise<Ios.Job> => {
return await prepareJobAsync(ctx, {
...jobData,
buildScheme: ctx.ios.xcodeBuildContext.buildScheme,
@@ -55,5 +55,5 @@ export async function ensureIosCredentialsForBuildResignAsync(
}
function shouldProvideCredentials(buildCtx: BuildContext<Platform.IOS>): boolean {
return !buildCtx.buildProfile.simulator;
return !buildCtx.buildProfile.simulator && !buildCtx.buildProfile.withoutCredentials;
}
+14 -3
View File
@@ -2,16 +2,22 @@ import { Ios } from '@expo/eas-build-job';
import nullthrows from 'nullthrows';
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
import { transformProjectArchive, transformWorkflow } from '../graphql';
import {
loggerLevelToGraphQLWorkerLoggerLevel,
transformBuildMode,
transformBuildTrigger,
transformProjectArchive,
transformWorkflow,
} from '../graphql';
export function transformJob(job: Ios.Job): IosJobInput {
return {
type: transformWorkflow(job.type),
triggeredBy: transformBuildTrigger(job.triggeredBy),
projectArchive: transformProjectArchive(job.projectArchive),
projectRootDirectory: nullthrows(job.projectRootDirectory),
releaseChannel: job.releaseChannel,
updates: job.updates,
secrets: transformIosSecrets(job.secrets),
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
builderEnvironment: job.builderEnvironment,
cache: job.cache,
version: job.version?.buildNumber ? { buildNumber: job.version.buildNumber } : undefined,
@@ -23,6 +29,11 @@ export function transformJob(job: Ios.Job): IosJobInput {
developmentClient: job.developmentClient,
simulator: job.simulator,
experimental: job.experimental,
mode: transformBuildMode(job.mode),
customBuildConfig: job.customBuildConfig,
loggerLevel: job.loggerLevel
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
: undefined,
};
}
+52 -29
View File
@@ -3,18 +3,18 @@ import {
BuildMode,
BuildTrigger,
Ios,
Job,
Platform,
sanitizeJob,
sanitizeBuildJob,
} from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import nullthrows from 'nullthrows';
import path from 'path';
import slash from 'slash';
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { IosJobSecretsInput } from '../../graphql/generated';
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
@@ -25,18 +25,18 @@ interface JobData {
const cacheDefaults = {
disabled: false,
customPaths: [],
cacheDefaultPaths: true,
paths: [],
};
export async function prepareJobAsync(
ctx: BuildContext<Platform.IOS>,
jobData: JobData
): Promise<Job> {
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
): Promise<Ios.Job> {
const username = getUsername(ctx.exp, ctx.user);
const buildCredentials: Ios.Job['secrets']['buildCredentials'] = {};
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
const projectRootDirectory =
slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
if (jobData.credentials) {
const targetNames = Object.keys(jobData.credentials);
for (const targetName of targetNames) {
@@ -44,38 +44,50 @@ export async function prepareJobAsync(
}
}
const maybeCustomBuildConfigPath = buildProfile.config
? getCustomBuildConfigPathForJob(buildProfile.config)
: undefined;
let buildMode;
if (ctx.repack) {
buildMode = BuildMode.REPACK;
} else if (buildProfile.config) {
buildMode = BuildMode.CUSTOM;
} else {
buildMode = BuildMode.BUILD;
}
const job: Ios.Job = {
type: ctx.workflow,
platform: Platform.IOS,
projectArchive: jobData.projectArchive,
projectRootDirectory,
builderEnvironment: {
image: ctx.buildProfile.image,
node: ctx.buildProfile.node,
yarn: ctx.buildProfile.yarn,
bundler: ctx.buildProfile.bundler,
cocoapods: ctx.buildProfile.cocoapods,
fastlane: ctx.buildProfile.fastlane,
expoCli: ctx.buildProfile.expoCli,
env: ctx.buildProfile.env,
image: buildProfile.image,
node: buildProfile.node,
pnpm: buildProfile.pnpm,
bun: buildProfile.bun,
yarn: buildProfile.yarn,
bundler: buildProfile.bundler,
cocoapods: buildProfile.cocoapods,
fastlane: buildProfile.fastlane,
env: buildProfile.env,
},
cache: {
...cacheDefaults,
...ctx.buildProfile.cache,
...buildProfile.cache,
clear: ctx.clearCache,
},
secrets: {
buildCredentials,
},
releaseChannel: ctx.buildProfile.releaseChannel,
updates: { channel: ctx.buildProfile.channel },
developmentClient: ctx.buildProfile.developmentClient,
simulator: ctx.buildProfile.simulator,
updates: { channel: buildProfile.channel },
developmentClient: buildProfile.developmentClient,
simulator: buildProfile.simulator,
scheme: jobData.buildScheme,
buildConfiguration: ctx.buildProfile.buildConfiguration,
applicationArchivePath:
ctx.buildProfile.applicationArchivePath ?? ctx.buildProfile.artifactPath,
buildArtifactPaths: ctx.buildProfile.buildArtifactPaths,
buildConfiguration: buildProfile.buildConfiguration,
applicationArchivePath: buildProfile.applicationArchivePath ?? buildProfile.artifactPath,
buildArtifactPaths: buildProfile.buildArtifactPaths,
username,
...(ctx.ios.buildNumberOverride && {
version: {
@@ -83,12 +95,23 @@ export async function prepareJobAsync(
},
}),
experimental: {
prebuildCommand: ctx.buildProfile.prebuildCommand,
prebuildCommand: buildProfile.prebuildCommand,
},
mode: BuildMode.BUILD,
mode: buildMode,
triggeredBy: BuildTrigger.EAS_CLI,
...(maybeCustomBuildConfigPath && {
customBuildConfig: {
path: maybeCustomBuildConfigPath,
},
}),
...(ctx.repack && {
customBuildConfig: {
path: '__eas/repack.yml',
},
}),
loggerLevel: ctx.loggerLevel,
};
return sanitizeJob(job);
return sanitizeBuildJob(job) as Ios.Job;
}
export function prepareCredentialsToResign(credentials: IosCredentials): IosJobSecretsInput {
@@ -1,36 +1,35 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { Env, Platform, Workflow } from '@expo/eas-build-job';
import { IosVersionAutoIncrement } from '@expo/eas-json';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import { Target } from '../../credentials/ios/types';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
import { Client } from '../../vcs/vcs';
export async function syncProjectConfigurationAsync(
graphqlClient: ExpoGraphqlClient,
{
projectDir,
exp,
targets,
localAutoIncrement,
projectId,
}: {
projectDir: string;
exp: ExpoConfig;
targets: Target[];
localAutoIncrement?: IosVersionAutoIncrement;
projectId: string;
}
): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
export async function syncProjectConfigurationAsync({
projectDir,
exp,
targets,
localAutoIncrement,
vcsClient,
env,
}: {
projectDir: string;
exp: ExpoConfig;
targets: Target[];
localAutoIncrement?: IosVersionAutoIncrement;
vcsClient: Client;
env: Env | undefined;
}): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
if (isExpoUpdatesInstalled(projectDir)) {
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
await syncUpdatesConfigurationAsync({ vcsClient, projectDir, exp, workflow, env });
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
} else {
+42 -14
View File
@@ -19,9 +19,12 @@ import { resolveWorkflowAsync } from '../../project/workflow';
import { promptAsync } from '../../prompts';
import uniqBy from '../../utils/expodash/uniqBy';
import { readPlistAsync, writePlistAsync } from '../../utils/plist';
import { Client } from '../../vcs/vcs';
import { updateAppJsonConfigAsync } from '../utils/appJson';
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
const SHORT_VERSION_REGEX = /^\d+.\d+.\d+$/;
export enum BumpStrategy {
APP_VERSION,
BUILD_NUMBER,
@@ -97,19 +100,32 @@ export async function bumpVersionInAppJsonAsync({
}
}
function validateShortVersion(shortVersion: string | undefined): void {
if (shortVersion && !SHORT_VERSION_REGEX.test(shortVersion)) {
throw new Error(
`CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: ${shortVersion}`
);
}
}
export async function readShortVersionAsync(
projectDir: string,
exp: ExpoConfig,
buildSettings: XCBuildConfiguration['buildSettings']
buildSettings: XCBuildConfiguration['buildSettings'],
vcsClient: Client
): Promise<string | undefined> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
if (workflow === Workflow.GENERIC) {
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
return (
const shortVersion =
infoPlist.CFBundleShortVersionString &&
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings)
);
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings);
validateShortVersion(shortVersion);
return shortVersion;
} else {
validateShortVersion(exp.version);
return exp.version;
}
}
@@ -117,9 +133,10 @@ export async function readShortVersionAsync(
export async function readBuildNumberAsync(
projectDir: string,
exp: ExpoConfig,
buildSettings: XCBuildConfiguration['buildSettings']
buildSettings: XCBuildConfiguration['buildSettings'],
vcsClient: Client
): Promise<string | undefined> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
if (workflow === Workflow.GENERIC) {
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
return (
@@ -133,7 +150,8 @@ export async function readBuildNumberAsync(
export async function maybeResolveVersionsAsync(
projectDir: string,
exp: ExpoConfig,
targets: Target[]
targets: Target[],
vcsClient: Client
): Promise<{ appVersion?: string; appBuildVersion?: string }> {
const applicationTarget = findApplicationTarget(targets);
try {
@@ -141,12 +159,14 @@ export async function maybeResolveVersionsAsync(
appBuildVersion: await readBuildNumberAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
applicationTarget.buildSettings ?? {},
vcsClient
),
appVersion: await readShortVersionAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
applicationTarget.buildSettings ?? {},
vcsClient
),
};
} catch (err: any) {
@@ -264,12 +284,14 @@ export async function resolveRemoteBuildNumberAsync(
exp,
applicationTarget,
buildProfile,
vcsClient,
}: {
projectDir: string;
projectId: string;
exp: ExpoConfig;
applicationTarget: Target;
buildProfile: BuildProfile<Platform.IOS>;
vcsClient: Client;
}
): Promise<string> {
const remoteVersions = await AppVersionQuery.latestVersionAsync(
@@ -282,12 +304,14 @@ export async function resolveRemoteBuildNumberAsync(
const localBuildNumber = await readBuildNumberAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
applicationTarget.buildSettings ?? {},
vcsClient
);
const localShortVersion = await readShortVersionAsync(
projectDir,
exp,
applicationTarget.buildSettings ?? {}
applicationTarget.buildSettings ?? {},
vcsClient
);
let currentBuildVersion: string;
if (remoteVersions?.buildVersion) {
@@ -318,7 +342,9 @@ export async function resolveRemoteBuildNumberAsync(
applicationIdentifier: applicationTarget.bundleIdentifier,
storeVersion: localShortVersion ?? '1.0.0',
buildVersion: currentBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
runtimeVersion:
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
undefined,
});
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
} catch (err) {
@@ -340,7 +366,9 @@ export async function resolveRemoteBuildNumberAsync(
applicationIdentifier: applicationTarget.bundleIdentifier,
storeVersion: localShortVersion ?? '1.0.0',
buildVersion: nextBuildVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
runtimeVersion:
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
undefined,
});
spinner.succeed(
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
+1 -1
View File
@@ -6,7 +6,7 @@ import semver from 'semver';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '0.0.128';
const PLUGIN_PACKAGE_VERSION = '1.0.131';
export enum LocalBuildMode {
/**
+41 -54
View File
@@ -1,34 +1,28 @@
import { Updates } from '@expo/config-plugins';
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { FingerprintSource, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
import Log from '../log';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
import {
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
} from '../update/android/UpdatesModule';
import {
readChannelSafelyAsync as readIosChannelSafelyAsync,
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
} from '../update/ios/UpdatesModule';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
import { BuildContext } from './context';
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
import { LocalBuildMode } from './local';
import { BuildDistributionType } from './types';
import Log from '../log';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
import { readChannelSafelyAsync as readAndroidChannelSafelyAsync } from '../update/android/UpdatesModule';
import { readChannelSafelyAsync as readIosChannelSafelyAsync } from '../update/ios/UpdatesModule';
import { easCliVersion } from '../utils/easCli';
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
ctx: BuildContext<T>,
runtimeMetadata: {
runtimeVersion?: string | undefined;
fingerprintSource?: FingerprintSource | undefined;
}
): Promise<Metadata> {
const vcsClient = getVcsClient();
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
? 'simulator'
: ctx.buildProfile.distribution) ?? 'store';
const channelObject = await resolveChannelAsync(ctx);
const distribution = ctx.buildProfile.distribution ?? BuildDistributionType.STORE;
const metadata: Metadata = {
trackingContext: ctx.analyticsEventProperties,
...(await maybeResolveVersionsAsync(ctx)),
@@ -36,18 +30,22 @@ export async function collectMetadataAsync<T extends Platform>(
workflow: ctx.workflow,
credentialsSource: ctx.buildProfile.credentialsSource,
sdkVersion: ctx.exp.sdkVersion,
runtimeVersion: Updates.getRuntimeVersionNullable(ctx.exp, ctx.platform) ?? undefined,
reactNativeVersion: await getReactNativeVersionAsync(ctx),
...channelOrReleaseChannel,
runtimeVersion: runtimeMetadata?.runtimeVersion,
fingerprintSource: runtimeMetadata?.fingerprintSource,
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
...channelObject,
distribution,
appName: ctx.exp.name,
appIdentifier: resolveAppIdentifier(ctx),
buildProfile: ctx.buildProfileName,
gitCommitHash: await vcsClient.getCommitHashAsync(),
gitCommitHash: await ctx.vcsClient.getCommitHashAsync(),
gitCommitMessage: truncateGitCommitMessage(
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
(await ctx.vcsClient.getLastCommitMessageAsync()) ?? undefined
),
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
isGitWorkingTreeDirty:
ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL
? false
: await ctx.vcsClient.hasUncommittedChangesAsync(),
username: getUsername(ctx.exp, ctx.user),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
@@ -57,6 +55,13 @@ export async function collectMetadataAsync<T extends Platform>(
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
developmentClient: ctx.developmentClient,
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
selectedImage: ctx.buildProfile.image,
customNodeVersion: ctx.buildProfile.node,
environment: ctx.buildProfile.environment,
simulator: 'simulator' in ctx.buildProfile && ctx.buildProfile.simulator,
};
return sanitizeMetadata(metadata);
}
@@ -69,7 +74,8 @@ async function maybeResolveVersionsAsync<T extends Platform>(
const resolvedVersion = await maybeResolveIosVersionsAsync(
ctx.projectDir,
ctx.exp,
iosContext.ios.targets
iosContext.ios.targets,
ctx.vcsClient
);
if (iosContext.ios.buildNumberOverride) {
return {
@@ -83,7 +89,8 @@ async function maybeResolveVersionsAsync<T extends Platform>(
const resolvedVersion = await maybeResolveAndroidVersionsAsync(
ctx.projectDir,
ctx.exp,
androidCtx.buildProfile
androidCtx.buildProfile,
ctx.vcsClient
);
if (androidCtx.android.versionCodeOverride) {
return {
@@ -105,39 +112,21 @@ function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string
}
}
async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
async function resolveChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<{ channel: string } | { releaseChannel: string } | null> {
): Promise<{ channel: string } | null> {
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
return null;
}
if (ctx.buildProfile.channel) {
return { channel: ctx.buildProfile.channel };
}
if (ctx.buildProfile.releaseChannel) {
return { releaseChannel: ctx.buildProfile.releaseChannel };
}
const channel = await getNativeChannelAsync(ctx);
if (channel) {
return { channel };
}
const releaseChannel = await getNativeReleaseChannelAsync(ctx);
return { releaseChannel };
}
async function getNativeReleaseChannelAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<string> {
switch (ctx.platform) {
case Platform.ANDROID: {
return (await readAndroidReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
}
case Platform.IOS: {
return (await readIosReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
}
default:
return 'default';
}
return null;
}
async function getNativeChannelAsync<T extends Platform>(
@@ -155,11 +144,9 @@ async function getNativeChannelAsync<T extends Platform>(
return undefined;
}
async function getReactNativeVersionAsync(
ctx: BuildContext<Platform>
): Promise<string | undefined> {
export async function getReactNativeVersionAsync(projectDir: string): Promise<string | undefined> {
try {
const reactNativePackageJsonPath = resolveFrom(ctx.projectDir, 'react-native/package.json');
const reactNativePackageJsonPath = resolveFrom(projectDir, 'react-native/package.json');
return (await fs.readJson(reactNativePackageJsonPath)).version;
} catch (err) {
Log.debug('Failed to resolve react-native version:');
+25 -13
View File
@@ -1,5 +1,6 @@
import chalk from 'chalk';
import { formatGraphQLBuild } from './utils/formatBuild';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { AppPlatform, BuildFilter, BuildFragment } from '../graphql/generated';
@@ -11,7 +12,6 @@ import {
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
import { formatGraphQLBuild } from './utils/formatBuild';
export const BUILDS_LIMIT = 50;
@@ -99,10 +99,6 @@ export async function listAndSelectBuildOnAppAsync(
}
}
function formatBuildChoiceValue(value: string | undefined | null): string {
return value ? chalk.bold(value) : 'Unknown';
}
function createBuildToPartialChoiceMaker(
selectPromptDisabledFunction?: (build: BuildFragment) => boolean
) {
@@ -119,19 +115,35 @@ function createBuildToPartialChoiceMaker(
? `${build.gitCommitHash.slice(0, 7)} "${chalk.bold(
splitCommitMessage[0] + (splitCommitMessage.length > 1 ? '…' : '')
)}"`
: 'Unknown';
: null;
const descriptionItems: { name: string; value: string | null }[] = [
{ name: 'Version', value: build.appVersion ? chalk.bold(build.appVersion) : null },
{ name: 'Commit', value: formattedCommitData },
{
name: 'Message',
value: build.message
? chalk.bold(
build.message.length > 200 ? `${build.message.slice(0, 200)}...` : build.message
)
: null,
},
{
name: build.platform === AppPlatform.Ios ? 'Build number' : 'Version code',
value: build.appBuildVersion ? chalk.bold(build.appBuildVersion) : null,
},
{ name: 'Profile', value: build.buildProfile ? chalk.bold(build.buildProfile) : null },
];
const filteredDescriptionArray: string[] = descriptionItems
.filter(item => item.value)
.map(item => `${chalk.bold(item.name)}: ${item.value}`);
return {
title: `${chalk.bold(`ID:`)} ${build.id} (${chalk.bold(
`${fromNow(new Date(build.completedAt))} ago`
)})`,
description: [
`\t${chalk.bold(`Version:`)} ${formatBuildChoiceValue(build.appVersion)}`,
`\t${chalk.bold(
build.platform === AppPlatform.Ios ? 'Build number:' : 'Version code:'
)} ${formatBuildChoiceValue(build.appBuildVersion)}`,
`\t${chalk.bold(`Commit:`)} ${formattedCommitData}`,
].join('\n'),
description: filteredDescriptionArray.length > 0 ? filteredDescriptionArray.join('\n') : '',
disabled: selectPromptDisabledFunction?.(build),
};
};
+223 -123
View File
@@ -1,4 +1,5 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { ExpoConfig } from '@expo/config-types';
import { Env, Platform, Workflow } from '@expo/eas-build-job';
import {
AppVersionSource,
BuildProfile,
@@ -8,33 +9,53 @@ import {
ResourceClass,
SubmitProfile,
} from '@expo/eas-json';
import { LoggerLevel } from '@expo/logger';
import assert from 'assert';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
import { prepareAndroidBuildAsync } from './android/build';
import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build';
import { ensureProjectConfiguredAsync } from './configure';
import { BuildContext } from './context';
import { createBuildContextAsync } from './createContext';
import { prepareIosBuildAsync } from './ios/build';
import { LocalBuildMode, LocalBuildOptions } from './local';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
import { ensureRepoIsCleanAsync } from './utils/repository';
import { Analytics } from '../analytics/AnalyticsManager';
import { createAndLinkChannelAsync, doesChannelExistAsync } from '../channel/queries';
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import {
AppPlatform,
BuildFragment,
BuildResourceClass,
BuildStatus,
BuildWithSubmissionsFragment,
SubmissionFragment,
} from '../graphql/generated';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import { toAppPlatform, toPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import Log, { learnMore } from '../log';
import {
RequestedPlatform,
appPlatformDisplayNames,
appPlatformEmojis,
toPlatforms,
} from '../platform';
import {
CustomBuildConfigMetadata,
validateCustomBuildConfigAsync,
} from '../project/customBuildConfig';
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
import {
isExpoUpdatesInstalledAsDevDependency,
isExpoUpdatesInstalledOrAvailable,
isUsingEASUpdate,
validateAppVersionRuntimePolicySupportAsync,
} from '../project/projectUtils';
import {
validateAppConfigForRemoteVersionSource,
validateBuildProfileVersionSettings,
@@ -49,23 +70,13 @@ import {
waitToCompleteAsync as waitForSubmissionsToCompleteAsync,
} from '../submit/submit';
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
import { validateBuildProfileConfigMatchesProjectConfigAsync } from '../update/utils';
import { ensureEASUpdateIsConfiguredAsync } from '../update/configure';
import { Actor } from '../user/User';
import { downloadAndMaybeExtractAppAsync } from '../utils/download';
import { truthy } from '../utils/expodash/filter';
import { printJsonOnlyOutput } from '../utils/json';
import { ProfileData, getProfilesAsync } from '../utils/profiles';
import { getVcsClient } from '../vcs';
import { prepareAndroidBuildAsync } from './android/build';
import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build';
import { ensureProjectConfiguredAsync } from './configure';
import { BuildContext } from './context';
import { createBuildContextAsync } from './createContext';
import { prepareIosBuildAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
import { ensureRepoIsCleanAsync } from './utils/repository';
import { Client } from '../vcs/vcs';
let metroConfigValidated = false;
let sdkVersionChecked = false;
@@ -82,99 +93,31 @@ export interface BuildFlags {
localBuildOptions: LocalBuildOptions;
resourceClass?: ResourceClass;
message?: string;
}
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
[ResourceClass.M1_EXPERIMENTAL]: BuildResourceClass.IosM1Large,
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosM1Medium,
[ResourceClass.M1_LARGE]: BuildResourceClass.IosM1Large,
[ResourceClass.INTEL_MEDIUM]: BuildResourceClass.IosIntelMedium,
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
};
const androidResourceClassToBuildResourceClassMapping: Record<
Exclude<
ResourceClass,
| ResourceClass.M1_EXPERIMENTAL
| ResourceClass.M1_MEDIUM
| ResourceClass.M1_LARGE
| ResourceClass.INTEL_MEDIUM
>,
BuildResourceClass
> = {
[ResourceClass.DEFAULT]: BuildResourceClass.AndroidDefault,
[ResourceClass.LARGE]: BuildResourceClass.AndroidLarge,
[ResourceClass.MEDIUM]: BuildResourceClass.AndroidMedium,
};
function resolveBuildResourceClass(
profile: ProfileData<'build'>,
resourceClassFlag?: ResourceClass
): BuildResourceClass {
if (
profile.platform !== Platform.IOS &&
resourceClassFlag &&
[
ResourceClass.M1_EXPERIMENTAL,
ResourceClass.M1_MEDIUM,
ResourceClass.M1_LARGE,
ResourceClass.INTEL_MEDIUM,
].includes(resourceClassFlag)
) {
throw new Error(`Resource class ${resourceClassFlag} is only available for iOS builds`);
}
const profileResourceClass = profile.profile.resourceClass;
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
Log.warn(
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing the "${resourceClassFlag}" as the override.`
);
}
const resourceClass = resourceClassFlag ?? profileResourceClass ?? ResourceClass.DEFAULT;
if (profile.platform === Platform.IOS && resourceClass === ResourceClass.M1_EXPERIMENTAL) {
Log.warn(`Resource class ${chalk.bold('m1-experimental')} is deprecated.`);
}
if (
profile.platform === Platform.IOS &&
[ResourceClass.LARGE, ResourceClass.M1_LARGE].includes(resourceClass)
) {
Log.warn(
`Large resource classes are not available for iOS builds yet. Your build will use the medium resource class.`
);
}
return profile.platform === Platform.ANDROID
? androidResourceClassToBuildResourceClassMapping[
resourceClass as Exclude<
ResourceClass,
| ResourceClass.M1_EXPERIMENTAL
| ResourceClass.M1_MEDIUM
| ResourceClass.M1_LARGE
| ResourceClass.INTEL_MEDIUM
>
]
: iosResourceClassToBuildResourceClassMapping[resourceClass];
buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean;
repack: boolean;
}
export async function runBuildAndSubmitAsync(
graphqlClient: ExpoGraphqlClient,
analytics: Analytics,
vcsClient: Client,
projectDir: string,
flags: BuildFlags,
actor: Actor,
getDynamicProjectConfigAsync: DynamicConfigContextFn
): Promise<void> {
await getVcsClient().ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(flags.nonInteractive);
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
): Promise<{
buildIds: string[];
}> {
await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
await ensureProjectConfiguredAsync({
projectDir,
nonInteractive: flags.nonInteractive,
vcsClient,
});
const easJsonAccessor = new EasJsonAccessor(projectDir);
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
const easJsonCliConfig: EasJson['cli'] =
(await EasJsonUtils.getCliConfigAsync(easJsonAccessor)) ?? {};
@@ -184,16 +127,61 @@ export async function runBuildAndSubmitAsync(
easJsonAccessor,
platforms,
profileName: flags.profile ?? undefined,
projectDir,
});
Log.log(
`Loaded "env" configuration for the "${buildProfiles[0].profileName}" profile: ${
buildProfiles[0].profile.env
? Object.keys(buildProfiles[0].profile.env).join(', ')
: 'no environment variables specified'
}. ${learnMore('https://docs.expo.dev/build-reference/variables/')}`
);
for (const buildProfile of buildProfiles) {
if (buildProfile.profile.image && ['default', 'stable'].includes(buildProfile.profile.image)) {
Log.warn(
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${buildProfile.profile.image}". This tag is deprecated and will be removed in the future. Use other images or tags listed here: https://docs.expo.dev/build-reference/infrastructure/`
);
} else if (
buildProfile.profile.image &&
[
'ubuntu-20.04-jdk-11-ndk-r19c',
'ubuntu-20.04-jdk-8-ndk-r19c',
'ubuntu-20.04-jdk-11-ndk-r21e',
'ubuntu-20.04-jdk-8-ndk-r21e',
'ubuntu-22.04-jdk-8-ndk-r21e',
'ubuntu-20.04-jdk-11-ndk-r23b',
].includes(buildProfile.profile.image)
) {
Log.warn(
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${
buildProfile.profile.image
}". This image is deprecated and will be removed on September 1st, 2024. ${learnMore(
'https://expo.dev/changelog/2024/07-12-eas-build-upcoming-android-images-updates'
)}`
);
}
}
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
projectDir,
nonInteractive: flags.nonInteractive,
buildProfiles,
vcsClient,
});
const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } =
{};
for (const buildProfile of buildProfiles) {
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
const maybeMetadata = await validateCustomBuildConfigAsync({
projectDir,
profile: buildProfile.profile,
vcsClient,
});
if (maybeMetadata) {
customBuildConfigMetadataByPlatform[toAppPlatform(buildProfile.platform)] = maybeMetadata;
}
}
const startedBuilds: {
@@ -203,31 +191,45 @@ export async function runBuildAndSubmitAsync(
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
for (const buildProfile of buildProfiles) {
const platform = toAppPlatform(buildProfile.platform);
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
projectDir,
flags,
moreBuilds: platforms.length > 1,
buildProfile,
resourceClass: resolveBuildResourceClass(buildProfile, flags.resourceClass),
easJsonCliConfig,
actor,
graphqlClient,
analytics,
getDynamicProjectConfigAsync,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
});
if (maybeBuild) {
startedBuilds.push({ build: maybeBuild, buildProfile });
}
buildCtxByPlatform[toAppPlatform(buildProfile.platform)] = buildCtx;
buildCtxByPlatform[platform] = buildCtx;
}
if (flags.localBuildOptions.localBuildMode) {
return;
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
Log.newLine();
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
Log.newLine();
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
const startedBuild = await BuildQuery.byIdAsync(
graphqlClient,
nullthrows(process.env.EAS_BUILD_ID, 'EAS_BUILD_ID is not defined')
);
startedBuilds.push({ build: startedBuild, buildProfile: buildProfiles[0] });
}
if (!flags.localBuildOptions.localBuildMode) {
Log.newLine();
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
Log.newLine();
}
const submissions: SubmissionFragment[] = [];
if (flags.autoSubmit) {
@@ -236,6 +238,7 @@ export async function runBuildAndSubmitAsync(
platforms,
profileName: flags.submitProfile,
type: 'submit',
projectDir,
});
for (const startedBuild of startedBuilds) {
const submitProfile = nullthrows(
@@ -251,6 +254,7 @@ export async function runBuildAndSubmitAsync(
buildProfile: startedBuild.buildProfile.profile,
submitProfile,
nonInteractive: flags.nonInteractive,
selectedSubmitProfileName: flags.submitProfile,
});
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(
graphqlClient,
@@ -259,31 +263,41 @@ export async function runBuildAndSubmitAsync(
submissions.push(submission);
}
Log.newLine();
printSubmissionDetailsUrls(submissions);
Log.newLine();
if (!flags.localBuildOptions.localBuildMode) {
Log.newLine();
printSubmissionDetailsUrls(submissions);
Log.newLine();
}
}
if (flags.localBuildOptions.localBuildMode) {
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
if (!flags.wait) {
if (flags.json) {
printJsonOnlyOutput(startedBuilds.map(buildInfo => buildInfo.build));
}
return;
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
const { accountName } = Object.values(buildCtxByPlatform)[0];
const builds = await waitForBuildEndAsync(graphqlClient, {
buildIds: startedBuilds.map(({ build }) => build.id),
accountName,
projectDir,
nonInteractive: flags.nonInteractive,
});
if (!flags.json) {
printBuildResults(builds);
}
const haveAllBuildsFailedOrCanceled = builds.every(
build => build?.status && [BuildStatus.Errored, BuildStatus.Canceled].includes(build?.status)
build =>
build?.status &&
[BuildStatus.Errored, BuildStatus.Canceled, BuildStatus.PendingCancel].includes(build?.status)
);
await maybeDownloadAndRunSimulatorBuildsAsync(builds, flags);
@@ -309,6 +323,9 @@ export async function runBuildAndSubmitAsync(
}
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
}
return {
buildIds: startedBuilds.map(({ build }) => build.id),
};
}
async function prepareAndStartBuildAsync({
@@ -316,27 +333,29 @@ async function prepareAndStartBuildAsync({
flags,
moreBuilds,
buildProfile,
resourceClass,
easJsonCliConfig,
actor,
graphqlClient,
analytics,
getDynamicProjectConfigAsync,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
}: {
projectDir: string;
flags: BuildFlags;
moreBuilds: boolean;
buildProfile: ProfileData<'build'>;
resourceClass: BuildResourceClass;
easJsonCliConfig: EasJson['cli'];
actor: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
getDynamicProjectConfigAsync: DynamicConfigContextFn;
vcsClient: Client;
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
customBuildConfigMetadata?: CustomBuildConfigMetadata;
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
const buildCtx = await createBuildContextAsync({
buildProfileName: buildProfile.profileName,
resourceClass,
resourceClassFlag: flags.resourceClass,
clearCache: flags.clearCache,
buildProfile: buildProfile.profile,
nonInteractive: flags.nonInteractive,
@@ -349,7 +368,12 @@ async function prepareAndStartBuildAsync({
actor,
graphqlClient,
analytics,
getDynamicProjectConfigAsync,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
buildLoggerLevel: flags.buildLoggerLevel,
freezeCredentials: flags.freezeCredentials,
repack: flags.repack,
});
if (moreBuilds) {
@@ -362,12 +386,30 @@ async function prepareAndStartBuildAsync({
);
}
await validateBuildProfileConfigMatchesProjectConfigAsync(
buildCtx.exp,
buildProfile,
buildCtx.projectId,
flags.nonInteractive
);
if (buildProfile.profile.channel) {
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
exp: buildCtx.exp,
projectId: buildCtx.projectId,
projectDir,
vcsClient: buildCtx.vcsClient,
sdkVersion: buildCtx.exp.sdkVersion,
nonInteractive: flags.nonInteractive,
buildProfile,
env: buildProfile.profile.env,
});
if (isUsingEASUpdate(buildCtx.exp, buildCtx.projectId)) {
const doesChannelExist = await doesChannelExistAsync(graphqlClient, {
appId: buildCtx.projectId,
channelName: buildProfile.profile.channel,
});
if (!doesChannelExist) {
await createAndLinkChannelAsync(graphqlClient, {
appId: buildCtx.projectId,
channelName: buildProfile.profile.channel,
});
}
}
}
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
@@ -408,6 +450,7 @@ async function prepareAndStartSubmissionAsync({
projectDir,
buildProfile,
submitProfile,
selectedSubmitProfileName,
nonInteractive,
}: {
build: BuildFragment;
@@ -416,6 +459,7 @@ async function prepareAndStartSubmissionAsync({
projectDir: string;
buildProfile: BuildProfile;
submitProfile: SubmitProfile;
selectedSubmitProfileName?: string;
nonInteractive: boolean;
}): Promise<SubmissionFragment> {
const platform = toPlatform(build.platform);
@@ -433,6 +477,9 @@ async function prepareAndStartSubmissionAsync({
analytics: buildCtx.analytics,
projectId: buildCtx.projectId,
exp: buildCtx.exp,
vcsClient: buildCtx.vcsClient,
isVerboseFastlaneEnabled: false,
specifiedProfile: selectedSubmitProfileName,
});
if (moreBuilds) {
@@ -487,3 +534,56 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
}
}
}
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
exp,
projectId,
projectDir,
vcsClient,
buildProfile,
nonInteractive,
sdkVersion,
env,
}: {
exp: ExpoConfig;
projectId: string;
projectDir: string;
vcsClient: Client;
buildProfile: ProfileData<'build'>;
nonInteractive: boolean;
sdkVersion?: string;
env: Env | undefined;
}): Promise<void> {
if (isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion)) {
return;
}
if (isExpoUpdatesInstalledAsDevDependency(projectDir)) {
Log.warn(
`The build profile "${buildProfile.profileName}" uses the channel "${buildProfile.profile.channel}", but you've added "expo-updates" as a dev dependency. To make channels work for your builds, move "expo-updates" from dev dependencies to the main dependencies in your project.`
);
} else if (nonInteractive) {
Log.warn(
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates" followed by "eas update:configure".`
);
} else {
Log.warn(
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package and run "eas update:configure".`
);
const installExpoUpdates = await confirmAsync({
message: `Would you like to install the "expo-updates" package and configure EAS Update now?`,
});
if (installExpoUpdates) {
await ensureEASUpdateIsConfiguredAsync({
exp,
projectId,
projectDir,
platform: RequestedPlatform.All,
vcsClient,
env,
});
Log.withTick('Installed expo-updates and configured EAS Update.');
throw new Error('Command must be re-run to pick up new updates configuration.');
}
}
}
+2
View File
@@ -2,6 +2,7 @@ export enum BuildStatus {
NEW = 'new',
IN_QUEUE = 'in-queue',
IN_PROGRESS = 'in-progress',
PENDING_CANCEL = 'pending-cancel',
ERRORED = 'errored',
FINISHED = 'finished',
CANCELED = 'canceled',
@@ -10,5 +11,6 @@ export enum BuildStatus {
export enum BuildDistributionType {
STORE = 'store',
INTERNAL = 'internal',
/** @deprecated Use simulator flag instead */
SIMULATOR = 'simulator',
}
+22 -6
View File
@@ -3,6 +3,7 @@ import { Errors } from '@oclif/core';
import chalk from 'chalk';
import resolveFrom from 'resolve-from';
import { reviewAndCommitChangesAsync } from './repository';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log, { learnMore } from '../../log';
import { appPlatformDisplayNames } from '../../platform';
@@ -10,15 +11,16 @@ import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCli';
import { ProfileData } from '../../utils/profiles';
import { getVcsClient } from '../../vcs';
import { reviewAndCommitChangesAsync } from './repository';
import { Client } from '../../vcs/vcs';
export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
projectDir,
vcsClient,
nonInteractive = false,
buildProfiles = [],
}: {
projectDir: string;
vcsClient: Client;
nonInteractive?: boolean;
buildProfiles?: ProfileData<'build'>[];
}): Promise<void> {
@@ -41,7 +43,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
);
const workflowPerPlatformList = await Promise.all(
platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform))
platformsToCheck.map(platform => resolveWorkflowAsync(projectDir, platform, vcsClient))
);
Log.newLine();
@@ -55,6 +57,19 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
'expo-dev-client'
)} installed for your project.`
);
if (nonInteractive) {
Log.error(`You'll need to install ${chalk.bold('expo-dev-client')} manually.`);
Log.error(
learnMore('https://docs.expo.dev/clients/installation/', {
learnMoreMessage: 'See installation instructions on how to do it.',
dim: false,
})
);
Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and try again later.`, {
exit: 1,
});
}
const areAllManaged = workflowPerPlatformList.every(i => i === Workflow.MANAGED);
if (areAllManaged) {
const install = await confirmAsync({
@@ -62,7 +77,7 @@ export async function ensureExpoDevClientInstalledForDevClientBuildsAsync({
instructions: 'The command will abort unless you agree.',
});
if (install) {
await installExpoDevClientAsync(projectDir, { nonInteractive });
await installExpoDevClientAsync(projectDir, vcsClient, { nonInteractive });
} else {
Errors.error(`Install ${chalk.bold('expo-dev-client')} manually and come back later.`, {
exit: 1,
@@ -100,6 +115,7 @@ async function isExpoDevClientInstalledAsync(projectDir: string): Promise<boolea
async function installExpoDevClientAsync(
projectDir: string,
vcsClient: Client,
{ nonInteractive }: { nonInteractive: boolean }
): Promise<void> {
Log.newLine();
@@ -107,8 +123,8 @@ async function installExpoDevClientAsync(
Log.newLine();
await expoCommandAsync(projectDir, ['install', 'expo-dev-client']);
Log.newLine();
if (await getVcsClient().isCommitRequiredAsync()) {
await reviewAndCommitChangesAsync('Install expo-dev-client', {
if (await vcsClient.isCommitRequiredAsync()) {
await reviewAndCommitChangesAsync(vcsClient, 'Install expo-dev-client', {
nonInteractive,
});
}
@@ -1,5 +1,6 @@
import chalk from 'chalk';
import { getBuildLogsUrl } from './url';
import {
AppPlatform,
BuildFragment,
@@ -7,7 +8,6 @@ import {
} from '../../graphql/generated';
import { appPlatformDisplayNames } from '../../platform';
import formatFields from '../../utils/formatFields';
import { getBuildLogsUrl } from './url';
export function formatGraphQLBuild(build: BuildFragment): string {
const actor = getActorName(build);
@@ -27,6 +27,7 @@ export function formatGraphQLBuild(build: BuildFragment): string {
return chalk.blue('in queue');
case GraphQLBuildStatus.InProgress:
return chalk.blue('in progress');
case GraphQLBuildStatus.PendingCancel:
case GraphQLBuildStatus.Canceled:
return chalk.gray('canceled');
case GraphQLBuildStatus.Finished:
@@ -38,6 +39,14 @@ export function formatGraphQLBuild(build: BuildFragment): string {
}
},
},
{
label: 'Profile',
value: build.buildProfile,
},
{
label: 'Message',
value: build.message,
},
{
label: 'Distribution',
value: build.distribution?.toLowerCase(),
@@ -46,10 +55,6 @@ export function formatGraphQLBuild(build: BuildFragment): string {
label: 'Enterprise Provisioning',
value: build.iosEnterpriseProvisioning?.toLowerCase(),
},
{
label: 'Release Channel',
value: build.releaseChannel,
},
{
label: 'Channel',
value: build.channel,
@@ -86,6 +91,7 @@ export function formatGraphQLBuild(build: BuildFragment): string {
case GraphQLBuildStatus.InQueue:
case GraphQLBuildStatus.InProgress:
return '<in progress>';
case GraphQLBuildStatus.PendingCancel:
case GraphQLBuildStatus.Canceled:
case GraphQLBuildStatus.Errored:
return null;
@@ -2,6 +2,7 @@ import assert from 'assert';
import indentString from 'indent-string';
import qrcodeTerminal from 'qrcode-terminal';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
import {
AppPlatform,
BuildError,
@@ -13,7 +14,32 @@ import {
} from '../../graphql/generated';
import Log, { learnMore, link } from '../../log';
import { appPlatformDisplayNames, appPlatformEmojis } from '../../platform';
import { getBuildLogsUrl, getInternalDistributionInstallUrl } from './url';
function terminalLinkFallback(url: string, text: string): string {
return `${text} (${url})`;
}
const errorCodeToErrorMessageOverride: Record<string, (build: BuildFragment) => string> = {
EAS_BUILD_UNKNOWN_FASTLANE_ERROR: build =>
`The ${link(getBuildLogsUrl(build, 'run-fastlane'), {
text: '"Run fastlane"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-fastlane'), '"Run fastlane"'),
})} step failed with an unknown error. Refer to the ${link(
getBuildLogsUrl(build, 'xcode-logs'),
{
text: '"Xcode logs"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'xcode-logs'), '"Xcode logs"'),
}
)} phase for additional, more detailed logs`,
EAS_BUILD_UNKNOWN_GRADLE_ERROR: build =>
`Gradle build failed with unknown error. See logs for the ${link(
getBuildLogsUrl(build, 'run-gradlew'),
{
text: '"Run gradlew"',
fallback: terminalLinkFallback(getBuildLogsUrl(build, 'run-gradlew'), '"Run gradlew"'),
}
)} phase for more information.`,
};
export function printLogsUrls(builds: BuildFragment[]): void {
if (builds.length === 1) {
@@ -31,7 +57,6 @@ export function printLogsUrls(builds: BuildFragment[]): void {
}
export function printBuildResults(builds: (BuildFragment | null)[]): void {
Log.newLine();
if (builds.length === 1) {
const [build] = builds;
assert(build, 'Build should be defined');
@@ -42,8 +67,8 @@ export function printBuildResults(builds: (BuildFragment | null)[]): void {
}
function printBuildResult(build: BuildFragment): void {
Log.addNewLineIfNone();
if (build.status === BuildStatus.Errored) {
Log.addNewLineIfNone();
const userError = build.error;
Log.error(
`${appPlatformEmojis[build.platform]} ${
@@ -51,11 +76,12 @@ function printBuildResult(build: BuildFragment): void {
} build failed${userError ? ':' : ''}`
);
if (userError) {
printUserError(userError);
printUserError(userError, build);
}
return;
}
if (build.status === BuildStatus.Canceled) {
if ([BuildStatus.Canceled, BuildStatus.PendingCancel].includes(build.status)) {
Log.addNewLineIfNone();
Log.error(
`${appPlatformEmojis[build.platform]} ${
appPlatformDisplayNames[build.platform]
@@ -65,6 +91,7 @@ function printBuildResult(build: BuildFragment): void {
}
if (build.distribution === DistributionType.Internal) {
Log.addNewLineIfNone();
const logsUrl = getBuildLogsUrl(build);
// It's tricky to install the .apk file directly on Android so let's fallback
// to the build details page and let people press the button to download there
@@ -82,9 +109,14 @@ function printBuildResult(build: BuildFragment): void {
} else {
// TODO: it looks like buildUrl could possibly be undefined, based on the code below.
// we should account for this case better if it is possible
const url = build.artifacts?.buildUrl ?? '';
Log.log(`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`);
Log.log(`${link(url)}`);
const url = build.artifacts?.buildUrl;
if (url) {
Log.addNewLineIfNone();
Log.log(
`${appPlatformEmojis[build.platform]} ${appPlatformDisplayNames[build.platform]} app:`
);
Log.log(link(url));
}
}
}
@@ -105,9 +137,22 @@ export function printDeprecationWarnings(deprecationInfo?: EasBuildDeprecationIn
}
}
export function printUserError(error: BuildError): void {
Log.error(error.message);
if (error.docsUrl) {
Log.error(learnMore(error.docsUrl, { dim: false }));
export function printUserError(error: BuildError, build: BuildFragment): void {
const maybeErrorMessageOverride = maybeGetErrorMessageOverride(error, build);
if (maybeErrorMessageOverride) {
Log.error(maybeErrorMessageOverride);
} else {
Log.error(error.message);
if (error.docsUrl) {
Log.error(learnMore(error.docsUrl, { dim: false }));
}
}
}
function maybeGetErrorMessageOverride(error: BuildError, build: BuildFragment): string | null {
if (!(error.errorCode in errorCodeToErrorMessageOverride)) {
return null;
}
return errorCodeToErrorMessageOverride[error.errorCode](build);
}
+64 -20
View File
@@ -10,10 +10,13 @@ import { confirmAsync, promptAsync } from '../../prompts';
import { formatBytes } from '../../utils/files';
import { getTmpDirectory } from '../../utils/paths';
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
import { getVcsClient } from '../../vcs';
import { Client } from '../../vcs/vcs';
export async function maybeBailOnRepoStatusAsync(): Promise<void> {
if (!(await getVcsClient().isCommitRequiredAsync())) {
export async function maybeBailOnRepoStatusAsync(
vcsClient: Client,
nonInteractive: boolean
): Promise<void> {
if (!(await vcsClient.isCommitRequiredAsync())) {
return;
}
Log.addNewLineIfNone();
@@ -28,12 +31,20 @@ export async function maybeBailOnRepoStatusAsync(): Promise<void> {
});
if (!answer) {
if (nonInteractive) {
Log.log('The following files need to be committed:');
await vcsClient.showChangedFilesAsync();
}
throw new Error('Commit all changes. Aborting...');
}
}
export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<void> {
if (!(await getVcsClient().isCommitRequiredAsync())) {
export async function ensureRepoIsCleanAsync(
vcsClient: Client,
nonInteractive = false
): Promise<void> {
if (!(await vcsClient.isCommitRequiredAsync())) {
return;
}
Log.addNewLineIfNone();
@@ -44,25 +55,31 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<vo
)}.`
);
if (nonInteractive) {
Log.log('The following files need to be committed:');
await vcsClient.showChangedFilesAsync();
throw new Error('Commit all changes. Aborting...');
}
const answer = await confirmAsync({
message: `Commit changes to git?`,
});
if (answer) {
await commitPromptAsync({ commitAllFiles: true });
await commitPromptAsync(vcsClient, { commitAllFiles: true });
} else {
throw new Error('Commit all changes. Aborting...');
}
}
export async function commitPromptAsync({
initialCommitMessage,
commitAllFiles,
}: {
initialCommitMessage?: string;
commitAllFiles?: boolean;
} = {}): Promise<void> {
export async function commitPromptAsync(
vcsClient: Client,
{
initialCommitMessage,
commitAllFiles,
}: {
initialCommitMessage?: string;
commitAllFiles?: boolean;
} = {}
): Promise<void> {
const { message } = await promptAsync({
type: 'text',
name: 'message',
@@ -70,14 +87,40 @@ export async function commitPromptAsync({
initial: initialCommitMessage,
validate: (input: string) => input !== '',
});
await getVcsClient().commitAsync({
await vcsClient.commitAsync({
commitAllFiles,
commitMessage: message,
nonInteractive: false,
});
}
export async function makeProjectTarballAsync(): Promise<{ path: string; size: number }> {
export type LocalFile = {
path: string;
size: number;
};
export async function makeProjectMetadataFileAsync(archivePath: string): Promise<LocalFile> {
await fs.mkdirp(getTmpDirectory());
const metadataLocation = path.join(getTmpDirectory(), `${uuidv4()}-eas-build-metadata.json`);
const archiveContent: string[] = [];
await tar.list({
file: archivePath,
onentry: (entry: tar.ReadEntry) => {
if (entry.type === 'File' && !entry.path.includes('.git/')) {
archiveContent.push(entry.path);
}
},
});
await fs.writeJSON(metadataLocation, {
archiveContent,
});
return { path: metadataLocation, size: await fs.stat(metadataLocation).then(stat => stat.size) };
}
export async function makeProjectTarballAsync(vcsClient: Client): Promise<LocalFile> {
const spinner = ora('Compressing project files');
await fs.mkdirp(getTmpDirectory());
@@ -99,7 +142,7 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n
startTimer(compressTimerLabel);
try {
await getVcsClient().makeShallowCopyAsync(shallowClonePath);
await vcsClient.makeShallowCopyAsync(shallowClonePath);
await tar.create({ cwd: shallowClonePath, file: tarPath, prefix: 'project', gzip: true }, [
'.',
]);
@@ -133,11 +176,12 @@ enum ShouldCommitChanges {
}
export async function reviewAndCommitChangesAsync(
vcsClient: Client,
initialCommitMessage: string,
{ nonInteractive, askedFirstTime = true }: { nonInteractive: boolean; askedFirstTime?: boolean }
): Promise<void> {
if (process.env.EAS_BUILD_AUTOCOMMIT) {
await getVcsClient().commitAsync({
await vcsClient.commitAsync({
commitMessage: initialCommitMessage,
commitAllFiles: false,
nonInteractive,
@@ -171,11 +215,11 @@ export async function reviewAndCommitChangesAsync(
"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 });
await commitPromptAsync(vcsClient, { initialCommitMessage });
Log.withTick('Committed changes.');
} else if (selected === ShouldCommitChanges.ShowDiffFirst) {
await getVcsClient().showDiffAsync();
await reviewAndCommitChangesAsync(initialCommitMessage, {
await vcsClient.showDiffAsync();
await reviewAndCommitChangesAsync(vcsClient, initialCommitMessage, {
nonInteractive,
askedFirstTime: false,
});
@@ -0,0 +1,92 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, ResourceClass } from '@expo/eas-json';
import chalk from 'chalk';
import { BuildResourceClass } from '../../graphql/generated';
import Log, { link } from '../../log';
type AndroidResourceClass = Exclude<
ResourceClass,
ResourceClass.M1_MEDIUM | ResourceClass.M_MEDIUM | ResourceClass.M_LARGE
>;
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosMMedium,
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
[ResourceClass.M_MEDIUM]: BuildResourceClass.IosMMedium,
[ResourceClass.M_LARGE]: BuildResourceClass.IosMLarge,
};
const androidResourceClassToBuildResourceClassMapping: Record<
AndroidResourceClass,
BuildResourceClass
> = {
[ResourceClass.DEFAULT]: BuildResourceClass.AndroidDefault,
[ResourceClass.LARGE]: BuildResourceClass.AndroidLarge,
[ResourceClass.MEDIUM]: BuildResourceClass.AndroidMedium,
};
export async function resolveBuildResourceClassAsync<T extends Platform>(
profile: BuildProfile<T>,
platform: Platform,
resourceClassFlag?: ResourceClass
): Promise<BuildResourceClass> {
const profileResourceClass = profile.resourceClass;
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
Log.warn(
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing "${resourceClassFlag}" as the override.`
);
}
const selectedResourceClass = resourceClassFlag ?? profileResourceClass;
return platform === Platform.IOS
? resolveIosResourceClass(resourceClassFlag, profileResourceClass)
: resolveAndroidResourceClass(selectedResourceClass);
}
function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): BuildResourceClass {
if (selectedResourceClass && ResourceClass.M1_MEDIUM === selectedResourceClass) {
throw new Error(`Resource class ${selectedResourceClass} is only available for iOS builds`);
}
const resourceClass = selectedResourceClass ?? ResourceClass.DEFAULT;
return androidResourceClassToBuildResourceClassMapping[resourceClass as AndroidResourceClass];
}
function resolveIosResourceClass(
resourceClassFlag?: ResourceClass,
profileResourceClass?: ResourceClass
): BuildResourceClass {
const resourceClass = resourceClassFlag ?? profileResourceClass ?? ResourceClass.DEFAULT;
if (resourceClassFlag === ResourceClass.LARGE) {
throw new Error(
`Experimental "large" resource class for Intel iOS workers is no longer available. Remove the specified resource class to use the default, or learn more about all available resource classes: ${link(
'https://docs.expo.dev/build-reference/eas-json/'
)}`
);
}
if (ResourceClass.M1_MEDIUM === resourceClass) {
Log.warn(
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
'm-medium'
)} instead.`
);
}
if (ResourceClass.M_LARGE === resourceClass) {
Log.warn(
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
'large'
)} instead.`
);
}
return iosResourceClassToBuildResourceClassMapping[resourceClass];
}
+4 -2
View File
@@ -10,12 +10,14 @@ export function getProjectDashboardUrl(accountName: string, projectName: string)
).toString();
}
export function getBuildLogsUrl(build: BuildFragment): string {
export function getBuildLogsUrl(build: BuildFragment, hash?: string): string {
const { project } = build;
const url =
project.__typename !== 'App'
? `/builds/${build.id}`
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}`;
: `/accounts/${project.ownerAccount.name}/projects/${project.slug}/builds/${build.id}${
hash ? `#${hash}` : ''
}`;
return new URL(url, getExpoWebsiteBaseUrl()).toString();
}
+1 -1
View File
@@ -3,9 +3,9 @@ import chalk from 'chalk';
import nullthrows from 'nullthrows';
import semver from 'semver';
import { updateAppJsonConfigAsync } from './appJson';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { updateAppJsonConfigAsync } from './appJson';
export function ensureStaticConfigExists(projectDir: string): void {
const paths = getConfigFilePaths(projectDir);
+3 -4
View File
@@ -4,10 +4,9 @@ import fs from 'fs-extra';
import path from 'path';
import semver from 'semver';
import { CommonContext } from './context';
import Log, { learnMore } from '../log';
import { isPNGAsync } from '../utils/image';
import { getVcsClient } from '../vcs';
import { CommonContext } from './context';
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
@@ -28,12 +27,12 @@ export async function checkGoogleServicesFileAsync<T extends Platform>(
if (!googleServicesFilePath) {
return;
}
const rootDir = path.normalize(await getVcsClient().getRootPathAsync());
const rootDir = path.normalize(await ctx.vcsClient.getRootPathAsync());
const absGoogleServicesFilePath = path.resolve(ctx.projectDir, googleServicesFilePath);
if (
(await fs.pathExists(absGoogleServicesFilePath)) &&
(!isInsideDirectory(absGoogleServicesFilePath, rootDir) ||
(await getVcsClient().isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath))))
(await ctx.vcsClient.isFileIgnoredAsync(path.relative(rootDir, absGoogleServicesFilePath))))
) {
Log.warn(
`File specified via "${ctx.platform}.googleServicesFile" field in your app.json is not checked in to your repository and won't be uploaded to the builder.`
@@ -0,0 +1,22 @@
import { BranchBasicInfo, ChannelBasicInfo, UpdateChannelInfoWithBranches } from '../utils';
export const testChannelBasicInfo: ChannelBasicInfo = {
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
name: 'production',
branchMapping:
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
};
export const testBasicBranchInfo1: BranchBasicInfo = {
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
name: 'wrong-channel',
};
export const testBasicBranchInfo2: BranchBasicInfo = {
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
name: 'production',
};
export const channelInfoWithBranches: UpdateChannelInfoWithBranches<BranchBasicInfo> = {
...testChannelBasicInfo,
updateBranches: [testBasicBranchInfo1, testBasicBranchInfo2],
};
@@ -0,0 +1,70 @@
import { testChannelBasicInfo } from './branch-mapping-fixtures';
import {
emptyBranchMapping,
rolloutBranchMapping,
standardBranchMapping,
} from '../../rollout/__tests__/fixtures';
import {
BranchMappingValidationError,
assertVersion,
getAlwaysTrueBranchMapping,
getBranchMapping,
getEmptyBranchMapping,
getStandardBranchId,
isAlwaysTrueBranchMapping,
isEmptyBranchMapping,
} from '../branch-mapping';
describe(assertVersion, () => {
it('throws if the branch mapping is not the correct version', () => {
expect(() => assertVersion(testChannelBasicInfo, 5)).toThrowError(BranchMappingValidationError);
});
it('asserts the correct version', () => {
assertVersion(testChannelBasicInfo, 0);
expect(getBranchMapping(testChannelBasicInfo.branchMapping).version).toBe(0);
});
});
describe(getEmptyBranchMapping, () => {
it('gets an empty branch mapping', () => {
expect(isEmptyBranchMapping(getEmptyBranchMapping())).toBe(true);
});
});
describe(isEmptyBranchMapping, () => {
it('detects empty branch mappings', () => {
expect(isEmptyBranchMapping(emptyBranchMapping)).toBe(true);
expect(isEmptyBranchMapping(standardBranchMapping)).toBe(false);
expect(isEmptyBranchMapping(rolloutBranchMapping)).toBe(false);
});
});
describe(isAlwaysTrueBranchMapping, () => {
it('detects always true branch mappings', () => {
expect(isAlwaysTrueBranchMapping(standardBranchMapping)).toBe(true);
expect(isAlwaysTrueBranchMapping(rolloutBranchMapping)).toBe(false);
expect(isAlwaysTrueBranchMapping(emptyBranchMapping)).toBe(false);
});
});
describe(getAlwaysTrueBranchMapping, () => {
it('gets an always true branch mapping', () => {
const alwaysTrueBranchMapping = getAlwaysTrueBranchMapping('test-id');
expect(isAlwaysTrueBranchMapping(alwaysTrueBranchMapping)).toBe(true);
});
});
describe(getStandardBranchId, () => {
it('throws if the branch mapping is not a standard mapping', () => {
expect(() => getStandardBranchId(testChannelBasicInfo)).toThrowError(
BranchMappingValidationError
);
});
it('gets a standard branch id', () => {
const channelObjectWithStandardMapping = { ...testChannelBasicInfo };
channelObjectWithStandardMapping.branchMapping = JSON.stringify(standardBranchMapping);
expect(getStandardBranchId(channelObjectWithStandardMapping)).toBe(
standardBranchMapping.data[0].branchId
);
});
});
@@ -0,0 +1,148 @@
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
import { UpdateBranchObject, UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
export const testUpdateBranch1: UpdateBranchObject = {
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
name: 'wrong-channel',
updateGroups: [
[
{
id: 'bbe3e63a-f620-4603-8c4d-258f3e51590d',
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
message: 'fix bug',
createdAt: '2023-07-17T22:48:59.278Z',
runtimeVersion: 'exposdk:48.0.0',
platform: 'ios',
manifestFragment:
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"7d40544b395c5949f4646f5e150fe020","fileSHA256":"LN_rjlzN55dvcBL7jM5zryIpAp7iHU8VCf7RFIccbNg","storageKey":"Pi65ns2w9xLI5G_m9aGrJ5wK5FpWcGcInSSFwnA_0aA","contentType":"image/png","fileExtension":".png"},{"bundleKey":"cdd04e13d4ec83ff0cd13ec8dabdc341","fileSHA256":"irHji01LPI9giDYxtJo9bTPHaEHzuUcCMTlb4xn90Ks","storageKey":"oJZ9JX1m8IKNDgnIqcKOo5KUFOoa_NCaxruigZu65E0","contentType":"image/png","fileExtension":".png"},{"bundleKey":"a132ecc4ba5c1517ff83c0fb321bc7fc","fileSHA256":"K3RDqaWOksoRpXW9wRWYYVpX3eMFzYznDWAmBrMCzZg","storageKey":"X3OAkUu5P_LqyKFx1bNkA_qDQFLlSjCYNAlVEaOamHE","contentType":"image/png","fileExtension":".png"},{"bundleKey":"0ea69b5077e7c4696db85dbcba75b0e1","fileSHA256":"Pd0Hc-0n4j0leJ8wFzGzrEVa2L5aonoKG4OPicbSciU","storageKey":"FwJkAbrRNahBGX1KXnTAgiiwyzENu3D7DeEzCwmInyo","contentType":"image/png","fileExtension":".png"},{"bundleKey":"f5b790e2ac193b3d41015edb3551f9b8","fileSHA256":"vCfIss7SLYKt1AAi0hxjsgOMEDceLJB6ZWjQf6gTU4k","storageKey":"GQtYm7r0G9N3WJyrymjZ26p8eNMIb7rzqwSVgGco5EU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"3d0ac172fe41ab941a8c68cd7e3a17ee","fileSHA256":"jjnar-ZPrb_g0Bx5tcFfjJrP3v8HdeAlXMk__xN7I1U","storageKey":"bimf-UXq5IFH5nNHCIHdcQ2MyJwTnwDVBb-P8hr2CZw","contentType":"application/javascript","fileExtension":".bundle"}}',
isRollBackToEmbedded: false,
manifestPermalink: 'https://u.expo.dev/update/bbe3e63a-f620-4603-8c4d-258f3e51590d',
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
actor: {
__typename: 'User',
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
username: 'quintest113',
},
branch: {
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
name: 'wrong-channel',
__typename: 'UpdateBranch',
},
codeSigningInfo: null,
__typename: 'Update',
},
{
id: 'db22401b-d321-4629-a068-21d6d1f19c02',
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
message: 'fix bug',
createdAt: '2023-07-17T22:48:59.278Z',
runtimeVersion: 'exposdk:48.0.0',
platform: 'android',
manifestFragment:
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"778ffc9fe8773a878e9c30a6304784de","fileSHA256":"i2Gkx-9w3JJ1PwSUl2SC9m_UFQ7CPfx3KrZeEDc6-lU","storageKey":"idnX8z03q4vGLzhYMPaQGBADjhqvWe67_afPqJ1vGzM","contentType":"image/png","fileExtension":".png"},{"bundleKey":"376d6a4c7f622917c39feb23671ef71d","fileSHA256":"QJsG0VpGM-5viirUgoKq0M4zQ_TYyPfLhAhN5mrM54I","storageKey":"46DQ95-UdSKiRkQgt5FyXkE1FrKOT9tBZP3nS7auKlg","contentType":"image/png","fileExtension":".png"},{"bundleKey":"c79c3606a1cf168006ad3979763c7e0c","fileSHA256":"kGZm3WiTRq2iMs42ia3vkHtCV_obWT8DY40rlJf2SIQ","storageKey":"k-3xHp3vP8mR36WdccUZVPTRJKpP-zlyboWdkXCIQpQ","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02bc1fa7c0313217bde2d65ccbff40c9","fileSHA256":"_6fuRbdkBbpzkhSVAI99aMneY5X0tpQdsGNGX244IyA","storageKey":"lfqeUjiWFXwowhxVDBEzxsbTOEzYxMLebljy5Bh1mpU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"35ba0eaec5a4f5ed12ca16fabeae451d","fileSHA256":"hM9es7ICUPaeDkczvrTaX0F_BoKJKlrRteYlcHU8IbE","storageKey":"fMoMrsUeB5xWw_Ugn8FqGJf-M2pld2hkXKR704z0MM8","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"4cc2ee49988e438ac066827b809721de","fileSHA256":"ctfK72f8EdS1IQmDvJLya38RMSma16beJcMoQToZIe0","storageKey":"QdEoKuCQPvVPA71gY52wqJ9vmtV4ObnCcPE2FP4SIdo","contentType":"application/javascript","fileExtension":".bundle"}}',
isRollBackToEmbedded: false,
manifestPermalink: 'https://u.expo.dev/update/db22401b-d321-4629-a068-21d6d1f19c02',
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
actor: {
__typename: 'User',
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
username: 'quintest113',
},
branch: {
id: '754bf17f-efc0-46ab-8a59-a03f20e53e9b',
name: 'wrong-channel',
__typename: 'UpdateBranch',
},
codeSigningInfo: null,
__typename: 'Update',
},
],
],
__typename: 'UpdateBranch',
};
export const testUpdateBranch2: UpdateBranchObject = {
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
name: 'production',
updateGroups: [
[
{
id: 'f04aafc1-0bf5-4548-8c35-726b467e7c40',
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
message: 'fix bug',
createdAt: '2023-06-23T23:37:10.004Z',
runtimeVersion: 'exposdk:48.0.0',
platform: 'ios',
manifestFragment:
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"7d40544b395c5949f4646f5e150fe020","fileSHA256":"LN_rjlzN55dvcBL7jM5zryIpAp7iHU8VCf7RFIccbNg","storageKey":"Pi65ns2w9xLI5G_m9aGrJ5wK5FpWcGcInSSFwnA_0aA","contentType":"image/png","fileExtension":".png"},{"bundleKey":"cdd04e13d4ec83ff0cd13ec8dabdc341","fileSHA256":"irHji01LPI9giDYxtJo9bTPHaEHzuUcCMTlb4xn90Ks","storageKey":"oJZ9JX1m8IKNDgnIqcKOo5KUFOoa_NCaxruigZu65E0","contentType":"image/png","fileExtension":".png"},{"bundleKey":"a132ecc4ba5c1517ff83c0fb321bc7fc","fileSHA256":"K3RDqaWOksoRpXW9wRWYYVpX3eMFzYznDWAmBrMCzZg","storageKey":"X3OAkUu5P_LqyKFx1bNkA_qDQFLlSjCYNAlVEaOamHE","contentType":"image/png","fileExtension":".png"},{"bundleKey":"0ea69b5077e7c4696db85dbcba75b0e1","fileSHA256":"Pd0Hc-0n4j0leJ8wFzGzrEVa2L5aonoKG4OPicbSciU","storageKey":"FwJkAbrRNahBGX1KXnTAgiiwyzENu3D7DeEzCwmInyo","contentType":"image/png","fileExtension":".png"},{"bundleKey":"f5b790e2ac193b3d41015edb3551f9b8","fileSHA256":"vCfIss7SLYKt1AAi0hxjsgOMEDceLJB6ZWjQf6gTU4k","storageKey":"GQtYm7r0G9N3WJyrymjZ26p8eNMIb7rzqwSVgGco5EU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"76ec49faddaef30ba78fe50edc5ab3d6","fileSHA256":"OLUdgP-fZI2shwGS5qlIk7nWnybL_13RJl5xR85CDBU","storageKey":"OCvI0-34zApbdo3eSGfIaQUbWdZeLeKH-nJSeedKTFs","contentType":"application/javascript","fileExtension":".bundle"}}',
isRollBackToEmbedded: false,
manifestPermalink: 'https://u.expo.dev/update/f04aafc1-0bf5-4548-8c35-726b467e7c40',
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
actor: {
__typename: 'User',
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
username: 'quintest113',
},
branch: {
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
name: 'production',
__typename: 'UpdateBranch',
},
codeSigningInfo: null,
__typename: 'Update',
},
{
id: 'd8d15786-2727-407f-bf3e-f4bda7e397d9',
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
message: 'fix bug',
createdAt: '2023-06-23T23:37:10.004Z',
runtimeVersion: 'exposdk:48.0.0',
platform: 'android',
manifestFragment:
'{"extra":{"expoClient":{"ios":{"supportsTablet":true,"bundleIdentifier":"com.quintest113.updatestest4"},"web":{"favicon":"./assets/favicon.png"},"icon":"./assets/icon.png","name":"updates-test-4","slug":"updates-test-4","extra":{"eas":{"projectId":"789a8784-0186-4f41-8bd2-7f219291a2b8"}},"splash":{"image":"./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},"android":{"package":"com.quintest113.updatestest4","adaptiveIcon":{"backgroundColor":"#ffffff","foregroundImage":"./assets/adaptive-icon.png"}},"updates":{"url":"https://u.expo.dev/789a8784-0186-4f41-8bd2-7f219291a2b8"},"version":"1.0.0","platforms":["ios","android"],"sdkVersion":"48.0.0","orientation":"portrait","runtimeVersion":{"policy":"sdkVersion"},"currentFullName":"@quintest113/updates-test-4","originalFullName":"@quintest113/updates-test-4","userInterfaceStyle":"light","assetBundlePatterns":["**/*"]}},"assets":[{"bundleKey":"778ffc9fe8773a878e9c30a6304784de","fileSHA256":"i2Gkx-9w3JJ1PwSUl2SC9m_UFQ7CPfx3KrZeEDc6-lU","storageKey":"idnX8z03q4vGLzhYMPaQGBADjhqvWe67_afPqJ1vGzM","contentType":"image/png","fileExtension":".png"},{"bundleKey":"376d6a4c7f622917c39feb23671ef71d","fileSHA256":"QJsG0VpGM-5viirUgoKq0M4zQ_TYyPfLhAhN5mrM54I","storageKey":"46DQ95-UdSKiRkQgt5FyXkE1FrKOT9tBZP3nS7auKlg","contentType":"image/png","fileExtension":".png"},{"bundleKey":"c79c3606a1cf168006ad3979763c7e0c","fileSHA256":"kGZm3WiTRq2iMs42ia3vkHtCV_obWT8DY40rlJf2SIQ","storageKey":"k-3xHp3vP8mR36WdccUZVPTRJKpP-zlyboWdkXCIQpQ","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02bc1fa7c0313217bde2d65ccbff40c9","fileSHA256":"_6fuRbdkBbpzkhSVAI99aMneY5X0tpQdsGNGX244IyA","storageKey":"lfqeUjiWFXwowhxVDBEzxsbTOEzYxMLebljy5Bh1mpU","contentType":"image/png","fileExtension":".png"},{"bundleKey":"35ba0eaec5a4f5ed12ca16fabeae451d","fileSHA256":"hM9es7ICUPaeDkczvrTaX0F_BoKJKlrRteYlcHU8IbE","storageKey":"fMoMrsUeB5xWw_Ugn8FqGJf-M2pld2hkXKR704z0MM8","contentType":"image/png","fileExtension":".png"},{"bundleKey":"5223c8d9b0d08b82a5670fb5f71faf78","fileSHA256":"LJXowmdeeZM1_QBGuwfb2gbLfs_1HtN5IsYwnWGcxNM","storageKey":"jeqOrdQBNY4hgl04B3wtGs8QNWMDmjvbjIMUXPtOBpw","contentType":"image/png","fileExtension":".png"},{"bundleKey":"02d5848f62ade9617cf96894727d32e7","fileSHA256":"u1-8zHOj9oxS8maH-7JbShJIq1-KEV7FXnrGpEUcR-4","storageKey":"LMUvUZZpdGFb7KqaB_l9N4TXTkFn1Pw92ws9fWDNZPw","contentType":"model/gltf-binary","fileExtension":".glb"},{"bundleKey":"6013c27e421ab23c826713af3ebec891","fileSHA256":"dutQNnvzTCS4NgwvbeMbvhro5QnW0GJUeY_4rrvlcLY","storageKey":"85KB9Jr7eqz1Jwt1mYQGRzTy6ZXyO0j7ZcaLImCbzkU","contentType":"image/jpeg","fileExtension":".jpg"}],"launchAsset":{"bundleKey":"f4c6981626dbabcf51078d07a1611f4e","fileSHA256":"q-k1hSWl49aXpSg4jOSaVVnYFSFL6xf7HVl_TQE-ObM","storageKey":"FwK1PaZqtpL0QZIgaHsxU1AV93JhMdu9K1wZizXROAc","contentType":"application/javascript","fileExtension":".bundle"}}',
isRollBackToEmbedded: false,
manifestPermalink: 'https://u.expo.dev/update/d8d15786-2727-407f-bf3e-f4bda7e397d9',
gitCommitHash: 'e91c5bb733eff675dd8a3722ed5d4f885a4b10ce',
actor: {
__typename: 'User',
id: '026ace2b-c1f8-4a02-818d-6b52c100bdf2',
username: 'quintest113',
},
branch: {
id: '6941a8dd-5c0a-48bc-8876-f49c88ed419f',
name: 'production',
__typename: 'UpdateBranch',
},
codeSigningInfo: null,
__typename: 'Update',
},
],
],
__typename: 'UpdateBranch',
};
export const testChannelObject: UpdateChannelObject = {
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
name: 'production',
createdAt: '2023-05-16T21:26:16.946Z',
updatedAt: '2023-05-16T21:26:16.946Z',
branchMapping:
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
updateBranches: [testUpdateBranch1, testUpdateBranch2],
__typename: 'UpdateChannel',
};
export const testBasicChannelInfo: UpdateChannelBasicInfoFragment = {
id: '9309afc2-9752-40db-8ef7-4abc10744c61',
name: 'production',
branchMapping:
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
__typename: 'UpdateChannel',
};
export const testBasicChannelInfo2: UpdateChannelBasicInfoFragment = {
id: '9d5c7bf0-d52d-474c-9140-c1bad7f0de9d',
name: 'staging',
branchMapping:
'{"data":[{"branchId":"d7d68e32-d9c9-4a8d-8d1b-21e53100a5e8","branchMappingLogic":{"operand":0.1,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"f9f708c2-0c91-4360-b2a4-0b61834aef4a","branchMappingLogic":"true"}],"version":0}',
__typename: 'UpdateChannel',
};
@@ -0,0 +1,57 @@
import { format } from '@expo/timeago.js';
import { testChannelObject, testUpdateBranch1, testUpdateBranch2 } from './fixtures';
import { getAlwaysTrueBranchMapping, getEmptyBranchMapping } from '../branch-mapping';
import { getDescriptionByBranchId } from '../print-utils';
describe(getDescriptionByBranchId, () => {
it('should get descriptions for multiple branches with multiple update groups', () => {
const descriptionByBranchId = getDescriptionByBranchId(testChannelObject);
expect(Object.values(descriptionByBranchId)).toHaveLength(2);
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({
branch: 'wrong-channel',
branchRolloutPercentage: 15,
update: {
codeSigningKey: undefined,
group: '16ca6dba-e63b-48b0-baa3-15a894ee9434',
isRollBackToEmbedded: false,
message: `"fix bug" (${format('2023-07-17T22:48:59.278Z', 'en_US')} by quintest113)`,
platforms: 'android, ios',
runtimeVersion: 'exposdk:48.0.0',
},
});
expect(descriptionByBranchId[testUpdateBranch2.id]).toEqual({
branch: 'production',
branchRolloutPercentage: 85,
update: {
codeSigningKey: undefined,
group: 'e40ad156-e9af-4cc2-8e9d-c7b5c328db48',
isRollBackToEmbedded: false,
message: `"fix bug" (${format('2023-06-23T23:37:10.004Z', 'en_US')} by quintest113)`,
platforms: 'android, ios',
runtimeVersion: 'exposdk:48.0.0',
},
});
});
it('should get descriptions for branches with no updates', () => {
const noUpdatesBranch = { ...testUpdateBranch1, updateGroups: [] };
const oneBranchChannel = {
...testChannelObject,
updateBranches: [noUpdatesBranch],
branchMapping: JSON.stringify(getAlwaysTrueBranchMapping(noUpdatesBranch.id)),
};
const descriptionByBranchId = getDescriptionByBranchId(oneBranchChannel);
expect(Object.values(descriptionByBranchId)).toHaveLength(1);
expect(descriptionByBranchId[testUpdateBranch1.id]).toEqual({ branch: 'wrong-channel' });
});
it('should get descriptions for no branches', () => {
const noBranchChannel = {
...testChannelObject,
updateBranches: [],
branchMapping: JSON.stringify(getEmptyBranchMapping()),
};
const descriptionByBranchId = getDescriptionByBranchId(noBranchChannel);
expect(Object.values(descriptionByBranchId)).toHaveLength(0);
});
});
@@ -0,0 +1,11 @@
import { channelInfoWithBranches, testBasicBranchInfo1 } from './branch-mapping-fixtures';
import { getUpdateBranch } from '../utils';
describe(getUpdateBranch, () => {
it('gets the update branch', () => {
expect(getUpdateBranch(channelInfoWithBranches, '754bf17f-efc0-46ab-8a59-a03f20e53e9b')).toBe(
testBasicBranchInfo1
);
expect(() => getUpdateBranch(channelInfoWithBranches, 'not-a-branch-ID')).toThrow();
});
});
@@ -0,0 +1,107 @@
import { EASUpdateAction, EASUpdateContext, NonInteractiveError } from '../../eas-update/utils';
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import {
Connection,
Edge,
FilterPagination,
QueryParams,
selectPaginatedAsync,
} from '../../utils/relay';
/**
* Select a channel for the project.
*
* @constructor
* @param {function} options.filterPredicate - A predicate to filter the channels that are shown to the user. It takes a channelInfo object as a parameter and returns a boolean.
* @param {string} options.printedType - The type of channel printed to the user. Defaults to 'channel'.
* @param {number} options.pageSize - The number of channels to show per page. Defaults to 100.
* @param {function} options.beforeEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called before each query.
* @args externalQueryParams The query params for the pagination.
* @args totalNodesFetched The total number of nodes fetched so far.
* @args dataset The dataset so far.
* @param {function} options.afterEachFilterQuery Optional. If a filter predicate was specified, this callback function will be called after each query.
* @args externalQueryParams The query params for the pagination.
* @args totalNodesFetched The total number of nodes fetched so far.
* @args dataset The dataset so far.
* @args willFetchAgain If the query will fetch again to get a complete page.
*/
export class SelectChannel implements EASUpdateAction<UpdateChannelBasicInfoFragment | null> {
constructor(
private options: {
filterPredicate?: (channelInfo: UpdateChannelBasicInfoFragment) => boolean;
printedType?: string;
pageSize?: number;
beforeEachFilterQuery?: (
externalQueryParams: QueryParams,
totalNodesFetched: number,
dataset: Edge<UpdateChannelBasicInfoFragment>[]
) => void;
afterEachFilterQuery?: (
externalQueryParams: QueryParams,
totalNodesFetched: number,
dataset: Edge<UpdateChannelBasicInfoFragment>[],
willFetchAgain: boolean
) => void;
} = {}
) {}
async queryAsync(
ctx: EASUpdateContext,
queryParams: QueryParams
): Promise<Connection<UpdateChannelBasicInfoFragment>> {
const { graphqlClient, app } = ctx;
const { projectId } = app;
return await ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync(graphqlClient, {
appId: projectId,
...queryParams,
});
}
async filterQueryAsync(
ctx: EASUpdateContext,
queryParams: QueryParams,
filterPredicate: (channelInfo: UpdateChannelBasicInfoFragment) => boolean
): Promise<Connection<UpdateChannelBasicInfoFragment>> {
const queryAsync = async (
queryParams: QueryParams
): Promise<Connection<UpdateChannelBasicInfoFragment>> =>
await this.queryAsync(ctx, queryParams);
return await FilterPagination.getPageAsync({
queryParams,
queryAsync,
filterPredicate,
beforeEachQuery: this.options.beforeEachFilterQuery,
afterEachQuery: this.options.afterEachFilterQuery,
});
}
public async runAsync(ctx: EASUpdateContext): Promise<UpdateChannelBasicInfoFragment | null> {
const { nonInteractive } = ctx;
const { filterPredicate } = this.options;
const printedType = this.options.printedType ?? 'channel';
const pageSize = this.options.pageSize ?? 100;
if (nonInteractive) {
throw new NonInteractiveError(
`${printedType} selection cannot be run in non-interactive mode.`
);
}
const queryAsync = async (
queryParams: QueryParams
): Promise<Connection<UpdateChannelBasicInfoFragment>> =>
filterPredicate
? this.filterQueryAsync(ctx, queryParams, filterPredicate)
: this.queryAsync(ctx, queryParams);
const getTitleAsync = async (channelInfo: UpdateChannelBasicInfoFragment): Promise<string> =>
channelInfo.name;
return await selectPaginatedAsync({
queryAsync,
getTitleAsync,
printedType,
pageSize,
});
}
}
@@ -0,0 +1,92 @@
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
import { NonInteractiveError } from '../../../eas-update/utils';
import { UpdateChannelBasicInfoFragment } from '../../../graphql/generated';
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
import { promptAsync } from '../../../prompts';
import { testBasicChannelInfo, testBasicChannelInfo2 } from '../../__tests__/fixtures';
import { SelectChannel } from '../SelectChannel';
jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/ChannelQuery');
describe(SelectChannel, () => {
afterEach(() => {
jest.clearAllMocks();
});
it('returns null if project has no channels', async () => {
const ctx = createCtxMock();
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
const selectChannel = new SelectChannel();
const channel = await selectChannel.runAsync(ctx);
expect(channel).toBeNull();
});
it('still prompts if project has only one channel', async () => {
const ctx = createCtxMock();
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [{ node: testBasicChannelInfo, cursor: 'cursor' }],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
item: testBasicChannelInfo,
}));
const selectChannel = new SelectChannel();
const channel = await selectChannel.runAsync(ctx);
expect(channel).toBe(testBasicChannelInfo);
expect(promptAsync).toHaveBeenCalledTimes(1);
});
it('returns the selected channel', async () => {
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [
{ node: testBasicChannelInfo, cursor: 'cursor' },
{ node: testBasicChannelInfo2, cursor: 'cursor' },
],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
item: testBasicChannelInfo,
}));
const ctx = createCtxMock();
const selectChannel = new SelectChannel();
const channel = await selectChannel.runAsync(ctx);
expect(channel).toBe(testBasicChannelInfo);
});
it('returns the only available channel with a filter', async () => {
jest.mocked(ChannelQuery.viewUpdateChannelsBasicInfoPaginatedOnAppAsync).mockResolvedValue({
edges: [
{ node: testBasicChannelInfo, cursor: 'cursor' },
{ node: testBasicChannelInfo2, cursor: 'cursor' },
],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
});
const ctx = createCtxMock();
const selectChannel = new SelectChannel({
filterPredicate: (channelInfo: UpdateChannelBasicInfoFragment) =>
channelInfo === testBasicChannelInfo,
});
const channel = await selectChannel.runAsync(ctx);
expect(channel).toBe(testBasicChannelInfo);
});
it('throws an error in Non-Interactive Mode', async () => {
const ctx = createCtxMock({
nonInteractive: true,
});
const selectChannel = new SelectChannel();
// dont fail if users are running in non-interactive mode
await expect(selectChannel.runAsync(ctx)).rejects.toThrowError(NonInteractiveError);
});
});
@@ -0,0 +1,214 @@
import { ChannelBasicInfo } from './utils';
// TODO(quin): move this into a common package with www
export type BranchMappingOperator =
| '=='
| '!='
| '<'
| '>'
| '<='
| '>='
| 'in'
| 'regex'
| 'hash_lt'
| 'hash_lte'
| 'hash_gt'
| 'hash_gte';
export type BranchMappingObject = {
clientKey: string;
branchMappingOperator: BranchMappingOperator;
operand: number | string | string[];
};
export type BranchMappingAlwaysTrue = 'true';
export type BranchMappingStatement =
| ['and' | 'or', ...BranchMappingNode[]]
| ['not', BranchMappingNode];
export type BranchMappingNode =
| BranchMappingAlwaysTrue
| BranchMappingObject
| BranchMappingStatement;
export type BranchMapping = {
version: number;
data: {
branchId: string;
branchMappingLogic: BranchMappingNode;
}[];
};
export type AlwaysTrueBranchMapping = {
version: number;
data: [
{
branchId: string;
branchMappingLogic: BranchMappingAlwaysTrue;
},
];
};
export type EmptyBranchMapping = {
version: number;
data: [];
};
export function getEmptyBranchMapping(): EmptyBranchMapping {
return {
version: 0,
data: [],
};
}
export function getAlwaysTrueBranchMapping(branchId: string): AlwaysTrueBranchMapping {
return {
version: 0,
data: [
{
branchId,
branchMappingLogic: 'true',
},
],
};
}
export function hasEmptyBranchMap(channelInfo: ChannelBasicInfo): boolean {
const branchMapping = getBranchMapping(channelInfo.branchMapping);
return isEmptyBranchMapping(branchMapping);
}
export function hasStandardBranchMap(channelInfo: ChannelBasicInfo): boolean {
const branchMapping = getBranchMapping(channelInfo.branchMapping);
return isAlwaysTrueBranchMapping(branchMapping);
}
export function getStandardBranchId(channelInfo: ChannelBasicInfo): string {
const branchMapping = getBranchMapping(channelInfo.branchMapping);
assertAlwaysTrueBranchMapping(branchMapping);
return getBranchIdFromStandardMapping(branchMapping);
}
export function isEmptyBranchMapping(
branchMapping: BranchMapping
): branchMapping is EmptyBranchMapping {
return branchMapping.data.length === 0;
}
export function isAlwaysTrueBranchMapping(
branchMapping: BranchMapping
): branchMapping is AlwaysTrueBranchMapping {
const numBranches = branchMapping.data.length;
if (numBranches !== 1) {
return false;
}
const branchMappingLogic = branchMapping.data[0].branchMappingLogic;
return isAlwaysTrue(branchMappingLogic);
}
function getBranchIdFromStandardMapping(branchMapping: AlwaysTrueBranchMapping): string {
return branchMapping.data[0].branchId;
}
export function getBranchIds(branchMapping: BranchMapping): string[] {
return branchMapping.data.map(data => data.branchId);
}
export function getBranchMapping(branchMappingString: string): BranchMapping {
try {
return JSON.parse(branchMappingString);
} catch {
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
}
}
export function getNodesFromStatement(statement: BranchMappingStatement): BranchMappingNode[] {
return statement.slice(1) as BranchMappingNode[];
}
export function isAndStatement(
statement: BranchMappingStatement
): statement is BranchMappingStatement {
return statement[0] === 'and';
}
export function isStatement(node: BranchMappingNode): node is BranchMappingStatement {
return Array.isArray(node);
}
export function isNodeObject(node: BranchMappingNode): node is BranchMappingObject {
return typeof node === 'object' && !isStatement(node);
}
export function andStatement(nodes: BranchMappingNode[]): ['and', ...BranchMappingNode[]] {
return ['and', ...nodes];
}
export function isAlwaysTrue(node: BranchMappingNode): boolean {
return node === 'true';
}
export function alwaysTrue(): BranchMappingAlwaysTrue {
return 'true';
}
export function equalsOperator(): BranchMappingOperator {
return '==';
}
export function hashLtOperator(): BranchMappingOperator {
return 'hash_lt';
}
function isVersion(branchMapping: BranchMapping, version: number): boolean {
return branchMapping.version === version;
}
export function assertVersion(channelInfo: ChannelBasicInfo, version: number): void {
const branchMapping = getBranchMapping(channelInfo.branchMapping);
if (!isVersion(branchMapping, version)) {
throw new BranchMappingValidationError(
`Expected branch mapping version ${version}. Received: ${JSON.stringify(branchMapping)}`
);
}
}
export function assertStatement(node: BranchMappingNode): asserts node is BranchMappingStatement {
if (!isStatement(node)) {
throw new BranchMappingValidationError(
'Branch mapping node must be a statement. Received: ' + JSON.stringify(node)
);
}
}
export function assertNodeObject(node: BranchMappingNode): asserts node is BranchMappingObject {
if (!isNodeObject(node)) {
throw new BranchMappingValidationError(
'Branch mapping node must be an object. Received: ' + JSON.stringify(node)
);
}
}
export function assertNumber(operand: string | number | string[]): asserts operand is number {
if (typeof operand !== 'number') {
throw new BranchMappingValidationError(
'Expected a number. Received: ' + JSON.stringify(operand)
);
}
}
export function assertString(operand: string | number | string[]): asserts operand is string {
if (typeof operand !== 'string') {
throw new BranchMappingValidationError(
'Expected a string. Received: ' + JSON.stringify(operand)
);
}
}
function assertAlwaysTrueBranchMapping(
branchMapping: BranchMapping
): asserts branchMapping is AlwaysTrueBranchMapping {
if (!isAlwaysTrueBranchMapping(branchMapping)) {
throw new BranchMappingValidationError(
'Expected standard branch mapping. Received: ' + JSON.stringify(branchMapping)
);
}
}
export class BranchMappingValidationError extends Error {}
@@ -0,0 +1,71 @@
import chalk from 'chalk';
import { assertVersion, hasEmptyBranchMap, hasStandardBranchMap } from './branch-mapping';
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
import Log from '../log';
import { getRollout, isRollout } from '../rollout/branch-mapping';
import {
FormattedBranchDescription,
formatBranch,
getUpdateGroupDescriptionsWithBranch,
} from '../update/utils';
/**
* Log all the branches associated with the channel and their most recent update group
*/
export function logChannelDetails(channel: UpdateChannelObject): void {
assertVersion(channel, 0);
const doesSupportMapping =
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel);
if (!doesSupportMapping) {
Log.log(chalk.dim('Custom branch mapping detected.'));
return;
}
const branchDescriptionByBranchId = getDescriptionByBranchId(channel);
const entries = Object.entries(branchDescriptionByBranchId);
if (entries.length === 0) {
Log.log(chalk.dim('No branches are pointed to this channel.'));
} else {
Log.log(
entries
.map(([_branchId, description]) => formatBranch(description))
.join(`\n\n${chalk.dim('———')}\n\n`)
);
}
}
export function getDescriptionByBranchId(
channel: UpdateChannelObject
): Record<string, FormattedBranchDescription> {
return channel.updateBranches.reduce(
(acc, branch) => {
const maybeRollout = isRollout(channel) ? getRollout(channel) : null;
let maybePercentOnBranch: number | undefined = undefined;
if (maybeRollout) {
maybePercentOnBranch =
maybeRollout.rolledOutBranchId === branch.id
? maybeRollout.percentRolledOut
: 100 - maybeRollout.percentRolledOut;
}
if (branch.updateGroups.length === 0) {
acc[branch.id] = { branch: branch.name, branchRolloutPercentage: maybePercentOnBranch };
return acc;
}
const updateGroupsWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
branch.updateGroups
);
// display the most recent update group
const updateGroupWithBranchDescription = updateGroupsWithBranchDescriptions[0];
const { branch: branchName, ...updateGroup } = updateGroupWithBranchDescription;
acc[branch.id] = {
branch: branchName,
branchRolloutPercentage: maybePercentOnBranch,
update: updateGroup,
};
return acc;
},
{} as Record<string, FormattedBranchDescription>
);
}
+108 -3
View File
@@ -1,6 +1,12 @@
import chalk from 'chalk';
import { print } from 'graphql';
import gql from 'graphql-tag';
import { ChannelNotFoundError } from './errors';
import { logChannelDetails } from './print-utils';
import { ChannelBasicInfo } from './utils';
import { createUpdateBranchOnAppAsync } from '../branch/queries';
import { BranchNotFoundError } from '../branch/utils';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { withErrorHandlingAsync } from '../graphql/client';
@@ -12,14 +18,15 @@ import {
} from '../graphql/generated';
import { BranchQuery, UpdateBranchOnChannelObject } from '../graphql/queries/BranchQuery';
import { ChannelQuery, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
import { UpdateChannelBasicInfoFragmentNode } from '../graphql/types/UpdateChannelBasicInfo';
import Log from '../log';
import { getDisplayNameForProjectIdAsync } from '../project/projectUtils';
import formatFields from '../utils/formatFields';
import { printJsonOnlyOutput } from '../utils/json';
import {
paginatedQueryWithConfirmPromptAsync,
paginatedQueryWithSelectPromptAsync,
} from '../utils/queries';
import { logChannelDetails } from './utils';
export const CHANNELS_LIMIT = 25;
@@ -227,11 +234,11 @@ export async function createChannelOnAppAsync(
updateChannel {
createUpdateChannelForApp(appId: $appId, name: $name, branchMapping: $branchMapping) {
id
name
branchMapping
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{
appId,
@@ -262,3 +269,101 @@ export async function ensureChannelExistsAsync(
}
}
}
export async function doesChannelExistAsync(
graphqlClient: ExpoGraphqlClient,
{ appId, channelName }: { appId: string; channelName: string }
): Promise<boolean> {
try {
await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId,
channelName,
});
} catch (err) {
if (err instanceof ChannelNotFoundError) {
return false;
}
throw err;
}
return true;
}
/**
*
* Creates a channel and links it to a branch with the same name.
*
* @param appId the app ID, also known as the project ID
* @param channelName the name of the channel to create
* @param shouldPrintJson print only the JSON output
*/
export async function createAndLinkChannelAsync(
graphqlClient: ExpoGraphqlClient,
{
appId,
channelName,
shouldPrintJson,
}: { appId: string; channelName: string; shouldPrintJson?: boolean }
): Promise<ChannelBasicInfo> {
let branchId: string;
let branchMessage: string;
try {
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
appId,
name: channelName,
});
branchId = branch.id;
branchMessage = `We found a branch with the same name`;
} catch (error) {
if (error instanceof BranchNotFoundError) {
const newBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
appId,
name: channelName,
});
branchId = newBranch.id;
branchMessage = `We also went ahead and made a branch with the same name`;
} else {
throw error;
}
}
const {
updateChannel: { createUpdateChannelForApp: newChannel },
} = await createChannelOnAppAsync(graphqlClient, {
appId,
channelName,
branchId,
});
if (!newChannel) {
throw new Error(
`Could not create channel with name ${channelName} on project with id ${appId}`
);
}
if (shouldPrintJson) {
printJsonOnlyOutput(newChannel);
} else {
Log.addNewLineIfNone();
Log.withTick(
`Created a new channel on project ${chalk.bold(
await getDisplayNameForProjectIdAsync(graphqlClient, appId)
)}`
);
Log.log(
formatFields([
{ label: 'Name', value: newChannel.name },
{ label: 'ID', value: newChannel.id },
])
);
Log.addNewLineIfNone();
Log.withTick(`${branchMessage} and have pointed the channel at it.`);
Log.log(
formatFields([
{ label: 'Name', value: newChannel.name },
{ label: 'ID', value: branchId },
])
);
}
return newChannel;
}
+27 -109
View File
@@ -1,117 +1,35 @@
import assert from 'assert';
import chalk from 'chalk';
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
import Log from '../log';
import {
FormattedBranchDescription,
formatBranch,
getUpdateGroupDescriptionsWithBranch,
} from '../update/utils';
export type BranchMapping = {
version: number;
data: {
branchId: string;
branchMappingLogic: {
operand: number;
clientKey: string;
branchMappingOperator: string;
} & string;
}[];
export type ChannelBasicInfo = {
id: string;
name: string;
branchMapping: string;
};
export type BranchBasicInfo = {
id: string;
name: string;
};
/**
* Get the branch mapping and determine whether it is a rollout.
* Ensure that the branch mapping is properly formatted.
*/
export function getBranchMapping(branchMappingString?: string): {
branchMapping: BranchMapping;
isRollout: boolean;
rolloutPercent?: number;
} {
if (!branchMappingString) {
throw new Error('Missing branch mapping.');
}
let branchMapping: BranchMapping;
try {
branchMapping = JSON.parse(branchMappingString);
} catch {
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
}
assert(branchMapping, 'Branch Mapping must be defined.');
export type UpdateChannelInfoWithBranches<Branch extends BranchBasicInfo> = ChannelBasicInfo & {
updateBranches: Branch[];
};
if (branchMapping.version !== 0) {
throw new Error('Branch mapping must be version 0.');
}
const isRollout = branchMapping.data.length === 2;
const rolloutPercent = branchMapping.data[0].branchMappingLogic.operand;
switch (branchMapping.data.length) {
case 0:
break;
case 1:
if (branchMapping.data[0].branchMappingLogic !== 'true') {
throw new Error('Branch mapping logic for a single branch must be "true"');
}
break;
case 2:
if (branchMapping.data[0].branchMappingLogic.clientKey !== 'rolloutToken') {
throw new Error('Client key of initial branch mapping must be "rolloutToken"');
}
if (branchMapping.data[0].branchMappingLogic.branchMappingOperator !== 'hash_lt') {
throw new Error('Branch mapping operator of initial branch mapping must be "hash_lt"');
}
if (rolloutPercent == null) {
throw new Error('Branch mapping is missing a "rolloutPercent"');
}
if (branchMapping.data[1].branchMappingLogic !== 'true') {
throw new Error('Branch mapping logic for a the second branch of a rollout must be "true"');
}
break;
default:
throw new Error('Branch mapping data must have length less than or equal to 2.');
}
return { branchMapping, isRollout, rolloutPercent };
function getUpdateBranchNullable<Branch extends BranchBasicInfo>(
channel: UpdateChannelInfoWithBranches<Branch>,
branchId: string
): Branch | null {
const updateBranches = channel.updateBranches;
const updateBranch = updateBranches.find(branch => branch.id === branchId);
return updateBranch ?? null;
}
export function logChannelDetails(channel: UpdateChannelObject): void {
const { branchMapping, isRollout, rolloutPercent } = getBranchMapping(channel.branchMapping);
if (branchMapping.data.length > 2) {
throw new Error('Branch Mapping data must have length less than or equal to 2.');
}
const rolloutBranchIds = branchMapping.data.map(data => data.branchId);
const branchDescription = channel.updateBranches.flatMap(branch => {
const updateGroupWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
branch.updateGroups
);
const isRolloutBranch = isRollout && rolloutBranchIds.includes(branch.id);
const isBaseBranch = rolloutBranchIds.length > 0 && rolloutBranchIds[0] === branch.id;
let rolloutPercentNumber: number | undefined = undefined;
if (isRolloutBranch) {
rolloutPercentNumber = isBaseBranch ? rolloutPercent! * 100 : (1 - rolloutPercent!) * 100;
}
return updateGroupWithBranchDescriptions.map(
({ branch, ...updateGroup }): FormattedBranchDescription => ({
branch,
branchRolloutPercentage: rolloutPercentNumber,
update: updateGroup,
})
);
});
if (branchDescription.length === 0) {
Log.log(chalk.dim('No branches are pointed to this channel.'));
} else {
Log.log(
branchDescription
.map(description => formatBranch(description))
.join(`\n\n${chalk.dim('———')}\n\n`)
export function getUpdateBranch<Branch extends BranchBasicInfo>(
channel: UpdateChannelInfoWithBranches<Branch>,
branchId: string
): Branch {
const updateBranch = getUpdateBranchNullable(channel, branchId);
if (!updateBranch) {
throw new Error(
`Could not find branch with id "${branchId}" in branch-mapping of channel "${channel.name}"`
);
}
return updateBranch;
}
+89 -28
View File
@@ -1,38 +1,50 @@
import { Command } from '@oclif/core';
import { CombinedError } from '@urql/core';
import chalk from 'chalk';
import { GraphQLError } from 'graphql/error';
import nullthrows from 'nullthrows';
import AnalyticsContextField from './context/AnalyticsContextField';
import ContextField from './context/ContextField';
import {
DynamicPrivateProjectConfigContextField,
DynamicPublicProjectConfigContextField,
} from './context/DynamicProjectConfigContextField';
import LoggedInContextField from './context/LoggedInContextField';
import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
import { OptionalPrivateProjectConfigContextField } from './context/OptionalPrivateProjectConfigContextField';
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
import ProjectDirContextField from './context/ProjectDirContextField';
import SessionManagementContextField from './context/SessionManagementContextField';
import VcsClientContextField from './context/VcsClientContextField';
import { EasCommandError } from './errors';
import {
AnalyticsWithOrchestration,
CommandEvent,
createAnalyticsAsync,
} from '../analytics/AnalyticsManager';
import Log from '../log';
import SessionManager from '../user/SessionManager';
import AnalyticsContextField from './context/AnalyticsContextField';
import ContextField from './context/ContextField';
import { DynamicProjectConfigContextField } from './context/DynamicProjectConfigContextField';
import LoggedInContextField from './context/LoggedInContextField';
import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
import { OptionalProjectConfigContextField } from './context/OptionalProjectConfigContextField';
import ProjectConfigContextField from './context/ProjectConfigContextField';
import ProjectDirContextField from './context/ProjectDirContextField';
import SessionManagementContextField from './context/SessionManagementContextField';
import { Client } from '../vcs/vcs';
type ContextInput<
export type ContextInput<
T extends {
[name: string]: any;
} = object
} = object,
> = {
[P in keyof T]: ContextField<T[P]>;
};
type ContextOutput<
export type ContextOutput<
T extends {
[name: string]: any;
} = object
} = object,
> = {
[P in keyof T]: T[P];
};
const BASE_GRAPHQL_ERROR_MESSAGE: string = 'GraphQL request failed.';
export default abstract class EasCommand extends Command {
protected static readonly ContextOptions = {
/**
@@ -60,7 +72,7 @@ export default abstract class EasCommand extends Command {
* run within a project directory, null otherwise.
*/
OptionalProjectConfig: {
projectConfig: new OptionalProjectConfigContextField(),
privateProjectConfig: new OptionalPrivateProjectConfigContextField(),
},
/**
* Require this command to be run in a project directory. Return the project directory in the context.
@@ -73,7 +85,9 @@ export default abstract class EasCommand extends Command {
*/
DynamicProjectConfig: {
// eslint-disable-next-line async-protect/async-suffix
getDynamicProjectConfigAsync: new DynamicProjectConfigContextField(),
getDynamicPublicProjectConfigAsync: new DynamicPublicProjectConfigContextField(),
// eslint-disable-next-line async-protect/async-suffix
getDynamicPrivateProjectConfigAsync: new DynamicPrivateProjectConfigContextField(),
},
/**
* Require the project to be identified and registered on server. Returns the project config in the context.
@@ -82,7 +96,7 @@ export default abstract class EasCommand extends Command {
*/
ProjectConfig: {
loggedIn: new LoggedInContextField(),
projectConfig: new ProjectConfigContextField(),
privateProjectConfig: new PrivateProjectConfigContextField(),
},
/**
* Analytics manager. Returns the analytics manager in the context for use by the command.
@@ -90,6 +104,9 @@ export default abstract class EasCommand extends Command {
Analytics: {
analytics: new AnalyticsContextField(),
},
Vcs: {
vcsClient: new VcsClientContextField(),
},
};
/**
@@ -103,16 +120,28 @@ export default abstract class EasCommand extends Command {
*/
static contextDefinition: ContextInput = {};
/**
* The user session manager. Responsible for coordinating all user session related state.
* If needed in a subclass, use the SessionManager ContextOption.
*/
private sessionManagerInternal?: SessionManager;
/**
* The analytics manager. Used for logging analytics.
* It is set up here to ensure a consistent setup.
*/
private analyticsInternal?: AnalyticsWithOrchestration;
/**
* Execute the context in the contextDefinition to satisfy command prerequisites.
*/
protected async getContextAsync<
C extends {
[name: string]: any;
} = object
} = object,
>(
commandClass: { contextDefinition: ContextInput<C> },
{ nonInteractive }: { nonInteractive: boolean }
{ nonInteractive, vcsClientOverride }: { nonInteractive: boolean; vcsClientOverride?: Client }
): Promise<ContextOutput<C>> {
const contextDefinition = commandClass.contextDefinition;
@@ -125,6 +154,7 @@ export default abstract class EasCommand extends Command {
nonInteractive,
sessionManager: this.sessionManager,
analytics: this.analytics,
vcsClientOverride,
}),
]);
}
@@ -132,20 +162,10 @@ export default abstract class EasCommand extends Command {
return Object.fromEntries(contextValuePairs);
}
/**
* The user session manager. Responsible for coordinating all user session related state.
* If needed in a subclass, use the SessionManager ContextOption.
*/
private sessionManagerInternal?: SessionManager;
private get sessionManager(): SessionManager {
return nullthrows(this.sessionManagerInternal);
}
/**
* The analytics manager. Used for logging analytics.
* It is set up here to ensure a consistent set up.
*/
private analyticsInternal?: AnalyticsWithOrchestration;
private get analytics(): AnalyticsWithOrchestration {
return nullthrows(this.analyticsInternal);
}
@@ -175,4 +195,45 @@ export default abstract class EasCommand extends Command {
await this.analytics.flushAsync();
return super.finally(err);
}
protected override catch(err: Error): Promise<any> {
let baseMessage = `${this.id} command failed.`;
if (err instanceof EasCommandError) {
Log.error(err.message);
} else if (err instanceof CombinedError && err?.graphQLErrors) {
const cleanGQLErrorsMessage = err?.graphQLErrors
.map((graphQLError: GraphQLError) => {
const messageLine = graphQLError.message.replace('[GraphQL] ', '');
const requestIdLine = graphQLError.extensions?.requestId
? `\nRequest ID: ${err.graphQLErrors[0].extensions.requestId}`
: '';
const defaultMsg = `${messageLine}${requestIdLine}`;
if (graphQLError.extensions?.errorCode === 'UNAUTHORIZED_ERROR') {
return `${chalk.bold(
`You don't have the required permissions to perform this operation.`
)}\n\nThis can sometimes happen if you are logged in as incorrect user.\nRun ${chalk.bold(
'eas whoami'
)} to check the username you are logged in as.\nRun ${chalk.bold(
'eas login'
)} to change the account.\n\nOriginal error message: ${defaultMsg}`;
}
return defaultMsg;
})
.join('\n');
const cleanMessage = err.networkError
? `${cleanGQLErrorsMessage}\n${err.networkError.message}`
: cleanGQLErrorsMessage;
Log.error(cleanMessage);
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
} else {
Log.error(err.message);
}
Log.debug(err);
const sanitizedError = new Error(baseMessage);
sanitizedError.stack = err.stack;
throw sanitizedError;
}
}
@@ -1,4 +1,9 @@
import { CombinedError } from '@urql/core';
import { GraphQLError } from 'graphql/error';
import { v4 as uuidv4 } from 'uuid';
import { AnalyticsWithOrchestration, createAnalyticsAsync } from '../../analytics/AnalyticsManager';
import Log from '../../log';
import SessionManager from '../../user/SessionManager';
import EasCommand from '../EasCommand';
@@ -11,8 +16,10 @@ jest.mock('../../analytics/AnalyticsManager', () => {
createAnalyticsAsync: jest.fn(),
};
});
jest.mock('../../log');
let originalProcessArgv: string[];
const mockRequestId = uuidv4();
beforeAll(() => {
originalProcessArgv = process.argv;
@@ -40,7 +47,7 @@ const createTestEasCommand = (): typeof EasCommand => {
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
TestEasCommand.id = 'testEasCommand'; // normally oclif will assign ids, but b/c this is located outside the commands folder it will not
return TestEasCommand;
};
@@ -96,5 +103,169 @@ describe(EasCommand.name, () => {
expect(analytics.flushAsync).toHaveBeenCalled();
});
describe('catch', () => {
it('logs the message', async () => {
const TestEasCommand = createTestEasCommand();
const logErrorSpy = jest.spyOn(Log, 'error');
const logDebugSpy = jest.spyOn(Log, 'debug');
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
const error = new Error('Unexpected, internal error message');
runAsyncMock.mockImplementation(() => {
throw error;
});
try {
await TestEasCommand.run();
} catch {}
expect(logErrorSpy).toBeCalledWith('Unexpected, internal error message');
expect(logDebugSpy).toBeCalledWith(error);
});
it('logs the cleaned message if needed', async () => {
const TestEasCommand = createTestEasCommand();
const logErrorSpy = jest.spyOn(Log, 'error');
const logDebugSpy = jest.spyOn(Log, 'debug');
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
const graphQLErrors = ['Unexpected GraphQL error message'];
const error = new CombinedError({ graphQLErrors });
runAsyncMock.mockImplementation(() => {
throw error;
});
try {
await TestEasCommand.run();
} catch {}
expect(logErrorSpy).toBeCalledWith('Unexpected GraphQL error message');
expect(logDebugSpy).toBeCalledWith(error);
});
it('logs the cleaned message with request ID if present', async () => {
const TestEasCommand = createTestEasCommand();
const logErrorSpy = jest.spyOn(Log, 'error');
const logDebugSpy = jest.spyOn(Log, 'debug');
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
const graphQLError = new GraphQLError(
'Unexpected GraphQL error message',
null,
null,
null,
null,
null,
{
errorCode: 'UNKNOWN_GRAPHQL_ERROR',
requestId: mockRequestId,
}
);
const graphQLErrors = [graphQLError];
const error = new CombinedError({ graphQLErrors });
runAsyncMock.mockImplementation(() => {
throw error;
});
try {
await TestEasCommand.run();
} catch {}
expect(logErrorSpy).toBeCalledWith(
`Unexpected GraphQL error message\nRequest ID: ${mockRequestId}`
);
expect(logDebugSpy).toBeCalledWith(error);
});
it('logs the cleaned messages with request IDs if multiple GraphQL errors present', async () => {
const TestEasCommand = createTestEasCommand();
const logErrorSpy = jest.spyOn(Log, 'error');
const logDebugSpy = jest.spyOn(Log, 'debug');
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
const graphQLErrors = [
new GraphQLError('Unexpected GraphQL error message', null, null, null, null, null, {
errorCode: 'UNKNOWN_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
new GraphQLError('Other GraphQL error message', null, null, null, null, null, {
errorCode: 'OTHER_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
new GraphQLError('Yet another GraphQL error message', null, null, null, null, null, {
errorCode: 'YET_ANOTHER_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
];
const error = new CombinedError({ graphQLErrors });
runAsyncMock.mockImplementation(() => {
throw error;
});
try {
await TestEasCommand.run();
} catch {}
expect(logErrorSpy).toBeCalledWith(
'Unexpected GraphQL error message\n' +
`Request ID: ${mockRequestId}\n` +
'Other GraphQL error message\n' +
`Request ID: ${mockRequestId}\n` +
'Yet another GraphQL error message\n' +
`Request ID: ${mockRequestId}`
);
expect(logDebugSpy).toBeCalledWith(error);
});
it('re-throws the error with default base message', async () => {
const TestEasCommand = createTestEasCommand();
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
runAsyncMock.mockImplementation(() => {
throw new Error('Error message');
});
try {
await TestEasCommand.run();
} catch (caughtError) {
expect(caughtError).toBeInstanceOf(Error);
expect((caughtError as Error).message).toEqual('testEasCommand command failed.');
}
});
it('re-throws the error with a different default base message in case of CombinedError', async () => {
const TestEasCommand = createTestEasCommand();
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
runAsyncMock.mockImplementation(() => {
const graphQLErrors = ['Unexpected GraphQL error message'];
throw new CombinedError({ graphQLErrors });
});
try {
await TestEasCommand.run();
} catch (caughtError) {
expect(caughtError).toBeInstanceOf(Error);
expect((caughtError as Error).message).toEqual('GraphQL request failed.');
}
});
it('re-throws the error with a different default base message in case of CombinedError with multiple GraphQLErrors', async () => {
const TestEasCommand = createTestEasCommand();
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
runAsyncMock.mockImplementation(() => {
const graphQLErrors = [
new GraphQLError('Unexpected GraphQL error message', null, null, null, null, null, {
errorCode: 'UNEXPECTED_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
new GraphQLError('Other GraphQL error message', null, null, null, null, null, {
errorCode: 'OTHER_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
new GraphQLError('Yet another GraphQL error message', null, null, null, null, null, {
errorCode: 'YET_ANOTHER_GRAPHQL_ERROR',
requestId: mockRequestId,
}),
];
throw new CombinedError({ graphQLErrors });
});
try {
await TestEasCommand.run();
} catch (caughtError) {
expect(caughtError).toBeInstanceOf(Error);
expect((caughtError as Error).message).toEqual('GraphQL request failed.');
}
});
});
});
});
@@ -0,0 +1,99 @@
import chalk from 'chalk';
import { ExpoGraphqlClient } from './context/contextUtils/createGraphqlClient';
import {
AppPlatform,
Build,
BuildFilter,
BuildFragment,
BuildStatus,
InputMaybe,
} from '../graphql/generated';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import { RequestedPlatform, appPlatformEmojis } from '../platform';
const platformToAppPlatform: Record<
Exclude<RequestedPlatform, RequestedPlatform.All>,
AppPlatform
> = {
android: AppPlatform.Android,
ios: AppPlatform.Ios,
};
export async function ensureBuildExistsAsync(
graphqlClient: ExpoGraphqlClient,
buildId: string
): Promise<void> {
try {
await BuildQuery.byIdAsync(graphqlClient, buildId);
} catch {
throw new Error(`Couldn't find a build matching the id ${buildId}`);
}
}
export async function fetchBuildsAsync({
graphqlClient,
projectId,
filters,
}: {
graphqlClient: ExpoGraphqlClient;
projectId: string;
filters?: { statuses?: BuildStatus[]; platform?: RequestedPlatform; profile?: string };
}): Promise<BuildFragment[]> {
let builds: BuildFragment[];
const queryFilters: InputMaybe<BuildFilter> = {};
if (filters?.platform && filters.platform !== 'all') {
queryFilters['platform'] = platformToAppPlatform[filters.platform];
}
if (filters?.profile) {
queryFilters['buildProfile'] = filters.profile;
}
if (!filters?.statuses) {
builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId,
offset: 0,
limit: 10,
filter: queryFilters ? queryFilters : undefined,
});
} else {
builds = (
await Promise.all(
filters.statuses.map(status =>
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId,
offset: 0,
limit: 10,
filter: queryFilters ? { ...queryFilters, status } : { status },
})
)
)
).flat();
}
builds.sort((buildA, buildB) => (buildA.createdAt > buildB.createdAt ? -1 : 1));
return builds;
}
export function formatBuild(
build: Pick<Build, 'id' | 'platform' | 'status' | 'createdAt'>
): string {
const platform = appPlatformEmojis[build.platform];
const startTime = new Date(build.createdAt).toLocaleString();
let statusText: string;
if (build.status === BuildStatus.New) {
statusText = 'new';
} else if (build.status === BuildStatus.InQueue) {
statusText = 'in queue';
} else if (build.status === BuildStatus.InProgress) {
statusText = 'in progress';
} else if (build.status === BuildStatus.Finished) {
statusText = 'finished';
} else if (build.status === BuildStatus.Errored) {
statusText = 'errored';
} else if ([BuildStatus.PendingCancel, BuildStatus.Canceled].includes(build.status)) {
statusText = 'canceled';
} else {
statusText = 'unknown';
}
const status = chalk.blue(statusText);
return `${platform} Started at: ${startTime}, Status: ${status}, Id: ${build.id}`;
}
@@ -1,5 +1,5 @@
import { Analytics } from '../../analytics/AnalyticsManager';
import ContextField, { ContextOptions } from './ContextField';
import { Analytics } from '../../analytics/AnalyticsManager';
export default class AnalyticsContextField extends ContextField<Analytics> {
async getValueAsync({ analytics }: ContextOptions): Promise<Analytics> {
@@ -1,10 +1,12 @@
import { Analytics } from '../../analytics/AnalyticsManager';
import SessionManager from '../../user/SessionManager';
import { Client } from '../../vcs/vcs';
export interface ContextOptions {
sessionManager: SessionManager;
analytics: Analytics;
nonInteractive: boolean;
vcsClientOverride?: Client;
}
export default abstract class ContextField<T> {
@@ -1,9 +1,13 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfigOptions, getExpoConfig } from '../../project/expoConfig';
import ContextField, { ContextOptions } from './ContextField';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import {
ExpoConfigOptions,
getPrivateExpoConfig,
getPublicExpoConfig,
} from '../../project/expoConfig';
export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
projectId: string;
@@ -11,19 +15,41 @@ export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
projectDir: string;
}>;
export class DynamicProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
export class DynamicPublicProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
async getValueAsync({
nonInteractive,
sessionManager,
}: ContextOptions): Promise<DynamicConfigContextFn> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
return async (options?: ExpoConfigOptions) => {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
const expBefore = getExpoConfig(projectDir, options);
const expBefore = getPublicExpoConfig(projectDir, options);
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
nonInteractive,
env: options?.env,
});
const exp = getExpoConfig(projectDir, options);
const exp = getPublicExpoConfig(projectDir, options);
return {
exp,
projectDir,
projectId,
};
};
}
}
export class DynamicPrivateProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
async getValueAsync({
nonInteractive,
sessionManager,
}: ContextOptions): Promise<DynamicConfigContextFn> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
return async (options?: ExpoConfigOptions) => {
const expBefore = getPrivateExpoConfig(projectDir, options);
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
nonInteractive,
env: options?.env,
});
const exp = getPrivateExpoConfig(projectDir, options);
return {
exp,
projectDir,
@@ -1,8 +1,8 @@
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
import { Actor } from '../../user/User';
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
import FeatureGating from '../gating/FeatureGating';
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
export default class LoggedInContextField extends ContextField<{
actor: Actor;
@@ -1,8 +1,8 @@
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
import { Actor } from '../../user/User';
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
import FeatureGating from '../gating/FeatureGating';
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
export default class MaybeLoggedInContextField extends ContextField<{
actor: Actor | null;
Loaded 100 of 490 files, more files were not shown because too many files have changed in this diff. Show more