Compare commits

...
Author SHA1 Message Date
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
150 changed files with 6544 additions and 1606 deletions

No files matched your search

+2 -2
View File
@@ -14,9 +14,9 @@ jobs:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- name: Checkout Pull Request
run: hub pr checkout ${{ github.event.issue.number }}
run: gh pr checkout ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
+5 -1
View File
@@ -8,6 +8,10 @@ on:
- labeled
- unlabeled
concurrency:
group: changelog-${{ github.ref }}
cancel-in-progress: true
jobs:
pr-changelog-entry:
name: Check CHANGELOG.md updated
@@ -16,7 +20,7 @@ jobs:
- uses: actions/checkout@v2
- name: Check if CHANGELOG.md was updated
id: changelog-updated
uses: tj-actions/changed-files@v35
uses: tj-actions/changed-files@v41
with:
files: CHANGELOG.md
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
+10 -10
View File
@@ -27,9 +27,9 @@ 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-ci
- run: yarn build
- name: Build tarballs
working-directory: ${{ github.workspace }}/packages/eas-cli
run: |
@@ -68,9 +68,9 @@ 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-ci
- run: yarn build
- name: Build tarballs
working-directory: ${{ github.workspace }}/packages/eas-cli
run: |
@@ -96,10 +96,10 @@ 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-ci
- run: yarn build
- run: |
yarn pretarball-ci
yarn oclif pack:win
@@ -138,11 +138,11 @@ 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
run: yarn build-ci
run: yarn build
- name: Publish packages to npm
run: yarn lerna publish from-package --yes --no-verify-access
env:
@@ -165,7 +165,7 @@ jobs:
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
@@ -175,7 +175,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
+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
+6 -2
View File
@@ -6,14 +6,18 @@ on:
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: ['16', '18', '20']
include:
- node: '18'
- node: '20'
coverage: true
name: Test with Node ${{ matrix.node }}
steps:
+151
View File
@@ -6,12 +6,162 @@ This is the log of notable changes to EAS CLI and related packages.
### 🛠 Breaking changes
- Drop support for Node 14. ([#2175](https://github.com/expo/eas-cli/pull/2175) by [@szdziedzic](https://github.com/szdziedzic))
### 🎉 New features
- Allow undefined update message for EAS Update publishing when no VCS. ([#2148](https://github.com/expo/eas-cli/pull/2148) by [@wschurman](https://github.com/wschurman))
### 🐛 Bug fixes
### 🧹 Chores
- Upgrade `@expo/eas-build-job` to `1.0.56`, thus removing unused (since [#1524](https://github.com/expo/eas-cli/pull/1524)) support for S3 project archives. ([#2165](https://github.com/expo/eas-cli/pull/2165) by [@sjchmiela](https://github.com/sjchmiela))
## [5.9.3](https://github.com/expo/eas-cli/releases/tag/v5.9.3) - 2023-12-19
### 🧹 Chores
- Rename getUpdateGroupJsonInfo. ([#2157](https://github.com/expo/eas-cli/pull/2157) by [@quinlanj](https://github.com/quinlanj))
- Add new Xcode 15.1 image to `eas.schema.json`. ([#2155](https://github.com/expo/eas-cli/pull/2155) by [@szdziedzic](https://github.com/szdziedzic))
## [5.9.2](https://github.com/expo/eas-cli/releases/tag/v5.9.2) - 2023-12-15
### 🧹 Chores
- Throw error if custom build config is gitignored. ([#2123](https://github.com/expo/eas-cli/pull/2123) by [@szdziedzic](https://github.com/szdziedzic))
- Update `@expo/steps` library to `1.0.51`. ([#2130](https://github.com/expo/eas-cli/pull/2130) by [@szdziedzic](https://github.com/szdziedzic))
- Update `eas.schema.json` to include changes after some of our images were migrated from Ubuntu 18.04 to Ubuntu 20.04. ([#2137](https://github.com/expo/eas-cli/pull/2137) by [@szdziedzic](https://github.com/szdziedzic))
- Update oclif dependencies. ([#2008](https://github.com/expo/eas-cli/pull/2008) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
- Upgrade `eas-build-job` and unify how we're handling `buildMode`. ([#2138](https://github.com/expo/eas-cli/pull/2138) by [@sjchmiela](https://github.com/sjchmiela))
## [5.9.1](https://github.com/expo/eas-cli/releases/tag/v5.9.1) - 2023-11-20
### 🐛 Bug fixes
- Don't ask a user to install the dev client if running in non-interactive mode. ([#2124](https://github.com/expo/eas-cli/pull/2124) by [@szdziedzic](https://github.com/szdziedzic))
- Always refresh existing provisioning profile before use. ([#2125](https://github.com/expo/eas-cli/pull/2125) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
## [5.9.0](https://github.com/expo/eas-cli/releases/tag/v5.9.0) - 2023-11-15
### 🎉 New features
- Add `--profile` flag to `eas build:run` command. ([#2035](https://github.com/expo/eas-cli/pull/2035) by [@szdziedzic](https://github.com/szdziedzic))
## [5.8.0](https://github.com/expo/eas-cli/releases/tag/v5.8.0) - 2023-11-13
### 🎉 New features
- Move channel:rollout out of developer preview. ([#2114](https://github.com/expo/eas-cli/pull/2114) by [@quinlanj](https://github.com/quinlanj))
### 🐛 Bug fixes
- Fixed provisioning of new devices into an existing profile. ([#2119](https://github.com/expo/eas-cli/pull/2119) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
### 🧹 Chores
- Update `@expo/package-manager` to `1.1.2` to change package manager resolution order. ([#2118](https://github.com/expo/eas-cli/pull/2118) by [@szdziedzic](https://github.com/szdziedzic))
## [5.7.0](https://github.com/expo/eas-cli/releases/tag/v5.7.0) - 2023-11-08
### 🎉 New features
- Add `EXPO_APPLE_TEAM_ID` and `EXPO_APPLE_PROVIDER_ID` support. ([#2091](https://github.com/expo/eas-cli/pull/2091) by [@EvanBacon](https://github.com/EvanBacon))
### 🧹 Chores
- Add link to SDK upgrade page for SDK-gated command error. ([#2106](https://github.com/expo/eas-cli/pull/2106) by [@wschurman](https://github.com/wschurman))
- Add `selectedImage` and `customNodeVersion` information to build metadata. ([#2113](https://github.com/expo/eas-cli/pull/2113) by [@szdziedzic](https://github.com/szdziedzic))
## [5.6.0](https://github.com/expo/eas-cli/releases/tag/v5.6.0) - 2023-10-27
### 🎉 New features
- Use corresponding submit profile when selecting build from EAS. ([#2101](https://github.com/expo/eas-cli/pull/2101) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
### 🐛 Bug fixes
- Added `buildArtifactsUrl` to `eas-cli build:view --json` output. ([#2102](https://github.com/expo/eas-cli/pull/2102) by [@sjchmiela](https://github.com/sjchmiela))
## [5.5.0](https://github.com/expo/eas-cli/releases/tag/v5.5.0) - 2023-10-25
### 🎉 New features
- Add account type to the items in the prompt to select project owner. ([#2083](https://github.com/expo/eas-cli/pull/2083) by [@alanjhughes](https://github.com/alanjhughes))
- Gate roll back to embedded to expo-updates >= 0.19.0. ([#2094](https://github.com/expo/eas-cli/pull/2094) by [@wschurman](https://github.com/wschurman))
### 🐛 Bug fixes
- EAS Update: Increase asset upload timeout to 90s and reset on upload retry for slow connections. ([#2085](https://github.com/expo/eas-cli/pull/2085) by [@wschurman](https://github.com/wschurman))
### 🧹 Chores
- Add `requiredPackageManager` to metadata. ([#2067](https://github.com/expo/eas-cli/pull/2067) by [@kadikraman](https://github.com/kadikraman))
- Move `getVcsClient` into command context. ([#2086](https://github.com/expo/eas-cli/pull/2086) by [@Josh-McFarlin](https://github.com/Josh-McFarlin))
- Display Apple device creation date when listing devices. ([#2092](https://github.com/expo/eas-cli/pull/2092) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
- Clean up Intel resource classes code after their deletion. ([#2093](https://github.com/expo/eas-cli/pull/2093) by [@szdziedzic](https://github.com/szdziedzic))
- Update images descriptions in `eas.schema.json` and add info about the new JDK 17 image. ([#2099](https://github.com/expo/eas-cli/pull/2099) by [@szdziedzic](https://github.com/szdziedzic))
## [5.4.0](https://github.com/expo/eas-cli/releases/tag/v5.4.0) - 2023-09-28
### 🎉 New features
- Add support for the Tap to Pay on iPhone iOS entitlement. ([#2069](https://github.com/expo/eas-cli/pull/2069) by [@fobos531](https://github.com/fobos531))
## [5.3.1](https://github.com/expo/eas-cli/releases/tag/v5.3.1) - 2023-09-28
### 🧹 Chores
- Update EAS Build images description in our VSCode plugin. Add new `macos-ventura-13.6-xcode-15.0` image. ([#2068](https://github.com/expo/eas-cli/pull/2068) by [@szdziedzic](https://github.com/szdziedzic))
## [5.3.0](https://github.com/expo/eas-cli/releases/tag/v5.3.0) - 2023-09-25
### 🎉 New features
- Update: expose expo cli `--dev` flag as an argument. ([#2050](https://github.com/expo/eas-cli/pull/2050) by [@nderscore](https://github.com/nderscore))
- Support `bun` option in eas.json. ([#2055](https://github.com/expo/eas-cli/pull/2055) by [@kadikraman](https://github.com/kadikraman))
### 🐛 Bug fixes
- Support node aliases in .nvmrc. ([#2052](https://github.com/expo/eas-cli/pull/2052) by [@khamilowicz](https://github.com/khamilowicz))
### 🧹 Chores
- Rollouts: more robust printing function. ([#2047](https://github.com/expo/eas-cli/pull/2047) by [@quinlanj](https://github.com/quinlanj))
## [5.2.0](https://github.com/expo/eas-cli/releases/tag/v5.2.0) - 2023-09-05
### 🎉 New features
- Rollouts: json output for ci. ([#2037](https://github.com/expo/eas-cli/pull/2037) by [@quinlanj](https://github.com/quinlanj))
### 🐛 Bug fixes
- Update: only print channel-branch pairing if we created a channel. ([#2036](https://github.com/expo/eas-cli/pull/2036) by [@quinlanj](https://github.com/quinlanj))
### 🧹 Chores
- Added rollout tests. ([#2042](https://github.com/expo/eas-cli/pull/2042) by [@quinlanj](https://github.com/quinlanj))
- Remove unreachable codesigning option. ([#2041](https://github.com/expo/eas-cli/pull/2041) by [@quinlanj](https://github.com/quinlanj))
- Fix generated graphql tsc errors. ([#2039](https://github.com/expo/eas-cli/pull/2039) by [@quinlanj](https://github.com/quinlanj))
- Rollouts: view action for CI. ([#2040](https://github.com/expo/eas-cli/pull/2040) by [@quinlanj](https://github.com/quinlanj))
## [5.1.0](https://github.com/expo/eas-cli/releases/tag/v5.1.0) - 2023-09-01
### 🎉 New features
- Support `pnpm` option in eas.json. ([#1988](https://github.com/expo/eas-cli/pull/1988) by [@khamilowicz](https://github.com/khamilowicz))
### 🐛 Bug fixes
- Make app config error not repeat indefinitely. ([#2020](https://github.com/expo/eas-cli/pull/2020) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
## [5.0.2](https://github.com/expo/eas-cli/releases/tag/v5.0.2) - 2023-08-29
### 🐛 Bug fixes
- Add proper expo cli `--platform` flag handling when exporting updates. ([#1939](https://github.com/expo/eas-cli/pull/1939) by [@byCedric](https://github.com/byCedric))
## [5.0.1](https://github.com/expo/eas-cli/releases/tag/v5.0.1) - 2023-08-28
### 🐛 Bug fixes
@@ -35,6 +185,7 @@ This is the log of notable changes to EAS CLI and related packages.
- Support republishing roll back to embedded updates. ([#2006](https://github.com/expo/eas-cli/pull/2006) by [@wschurman](https://github.com/wschurman))
- Configure updates as well when somebody tries to run a build with channel set. ([#2016](https://github.com/expo/eas-cli/pull/2016) by [@wschurman](https://github.com/wschurman))
- Fix printing bug: branch with no update. ([#2023](https://github.com/expo/eas-cli/pull/2023) by [@quinlanj](https://github.com/quinlanj))
### 🧹 Chores
+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.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "5.0.1",
"version": "6.0.0",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+5 -5
View File
@@ -6,11 +6,11 @@
],
"scripts": {
"build": "lerna run build",
"build-ci": "lerna run build-ci",
"build:tarballs:linux": "yarn build-for-size-check",
"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 .",
@@ -21,7 +21,7 @@
},
"devDependencies": {
"@types/jest": "29.2.5",
"@types/node": "18.11.18",
"@types/node": "20.11.0",
"eslint": "8.31.0",
"eslint-config-universe": "11.1.1",
"eslint-plugin-async-protect": "3.0.0",
@@ -29,13 +29,13 @@
"jest": "29.3.1",
"jest-watch-typeahead": "2.2.1",
"lerna": "6.4.1",
"oclif": "3.4.3",
"oclif": "^3.9.0",
"prettier": "2.8.2",
"ts-jest": "29.0.3",
"typescript": "4.9.4"
},
"volta": {
"node": "18.6.0",
"yarn": "1.22.19"
"node": "20.11.0",
"yarn": "1.22.21"
}
}
+122 -69
View File
@@ -110,6 +110,8 @@ eas --help COMMAND
* [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
* [`eas update:list`](#eas-updatelist)
* [`eas update:republish`](#eas-updaterepublish)
* [`eas update:roll-back-to-embedded`](#eas-updateroll-back-to-embedded)
* [`eas update:rollback`](#eas-updaterollback)
* [`eas update:view GROUPID`](#eas-updateview-groupid)
* [`eas webhook:create`](#eas-webhookcreate)
* [`eas webhook:delete [ID]`](#eas-webhookdelete-id)
@@ -133,7 +135,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/login.ts)_
## `eas account:logout`
@@ -150,7 +152,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/logout.ts)_
## `eas account:view`
@@ -167,7 +169,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -181,7 +183,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -192,7 +194,7 @@ USAGE
$ eas autocomplete [SHELL] [-r]
ARGUMENTS
SHELL shell type
SHELL (zsh|bash|powershell) Shell type
FLAGS
-r, --refresh-cache Refresh cache (ignores displaying instructions)
@@ -207,10 +209,12 @@ EXAMPLES
$ eas autocomplete zsh
$ eas autocomplete powershell
$ eas autocomplete --refresh-cache
```
_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v1.3.10/src/commands/autocomplete/index.ts)_
_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v2.3.10/src/commands/autocomplete/index.ts)_
## `eas branch:create [NAME]`
@@ -231,7 +235,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -252,7 +256,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -272,7 +276,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -292,7 +296,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -315,7 +319,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/view.ts)_
## `eas build`
@@ -345,7 +349,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -362,7 +366,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -379,7 +383,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -414,7 +418,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -452,7 +456,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/list.ts)_
## `eas build:resign`
@@ -478,7 +482,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/resign.ts)_
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/resign.ts)_
## `eas build:run`
@@ -486,10 +490,12 @@ run simulator/emulator builds from eas-cli
```
USAGE
$ eas build:run [--latest | --id <value> | --path <value> | --url <value>] [-p android|ios] [--offset
<value>] [--limit <value>]
$ eas build:run [--latest | --id <value> | --path <value> | --url <value>] [-p android|ios] [-e <value>]
[--offset <value>] [--limit <value>]
FLAGS
-e, --profile=PROFILE_NAME Name of the build profile used to create the build to run. When specified, only builds
created with the specified build profile will be queried.
-p, --platform=(android|ios)
--id=<value> ID of the simulator/emulator build to run
--latest Run the latest simulator/emulator build for specified platform
@@ -502,7 +508,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/run.ts)_
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/run.ts)_
## `eas build:submit`
@@ -551,7 +557,7 @@ DESCRIPTION
get the latest version from EAS servers
```
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/version/get.ts)_
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -570,7 +576,7 @@ DESCRIPTION
update version of an app
```
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/version/set.ts)_
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -589,7 +595,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers
```
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -606,7 +612,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -627,7 +633,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -649,7 +655,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -669,7 +675,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/list.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -677,7 +683,7 @@ Roll a new branch out on a channel incrementally.
```
USAGE
$ eas channel:rollout [CHANNEL] [--action create|edit|end] [--percent <value>] [--outcome
$ eas channel:rollout [CHANNEL] [--action create|edit|end|view] [--percent <value>] [--outcome
republish-and-revert|revert] [--branch <value>] [--runtime-version <value>] [--private-key-path <value>] [--json
--non-interactive]
@@ -685,7 +691,7 @@ ARGUMENTS
CHANNEL channel on which the rollout should be done
FLAGS
--action=(create|edit|end) Rollout action to perform
--action=(create|edit|end|view) Rollout action to perform
--branch=<value> Branch to roll out. Use with --action=create
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
@@ -701,7 +707,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally.
```
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/channel/rollout.ts)_
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -724,7 +730,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/view.ts)_
## `eas config`
@@ -745,7 +751,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/config.ts)_
## `eas credentials`
@@ -762,7 +768,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/credentials.ts)_
## `eas device:create`
@@ -776,7 +782,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/create.ts)_
## `eas device:delete`
@@ -796,7 +802,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/delete.ts)_
## `eas device:list`
@@ -817,7 +823,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/list.ts)_
## `eas device:rename`
@@ -838,7 +844,7 @@ DESCRIPTION
rename a registered device
```
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/device/rename.ts)_
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -852,7 +858,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -866,11 +872,11 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
display help for eas-cli
Display help for eas.
```
USAGE
@@ -883,10 +889,10 @@ FLAGS
-n, --nested-commands Include all nested commands in the output.
DESCRIPTION
display help for eas-cli
Display help for eas.
```
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.1.22/src/commands/help.ts)_
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.1.23/src/commands/help.ts)_
## `eas init`
@@ -954,7 +960,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -971,7 +977,7 @@ DESCRIPTION
generate the local store configuration from the app stores
```
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -988,7 +994,7 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/push.ts)_
## `eas open`
@@ -1002,7 +1008,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/open.ts)_
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/open.ts)_
## `eas project:info`
@@ -1016,7 +1022,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/project/info.ts)_
## `eas project:init`
@@ -1038,7 +1044,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/project/init.ts)_
## `eas secret:create`
@@ -1061,7 +1067,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1079,7 +1085,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1093,7 +1099,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1113,7 +1119,7 @@ DESCRIPTION
read environment secrets from env file and store on the server
```
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/secret/push.ts)_
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/push.ts)_
## `eas submit`
@@ -1143,7 +1149,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/submit.ts)_
## `eas update`
@@ -1152,8 +1158,8 @@ publish an update group
```
USAGE
$ eas update [--branch <value>] [--channel <value>] [-m <value>] [--republish | --input-dir <value> |
--skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--auto] [--private-key-path <value>]
[--json --non-interactive]
--skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--dev] [--auto] [--private-key-path
<value>] [--json --non-interactive]
FLAGS
-m, --message=<value> A short message describing the update
@@ -1162,6 +1168,7 @@ FLAGS
--branch=<value> Branch to publish the update group on
--channel=<value> Channel that the published update should affect
--clear-cache Clear the bundler cache before publishing
--dev Publish a development bundle
--group=<value> Update group to republish (deprecated, see republish command)
--input-dir=<value> [default: dist] Location of the bundle
--json Enable JSON output, non-JSON messages will be printed to stderr.
@@ -1176,7 +1183,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1194,7 +1201,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1202,7 +1209,7 @@ delete all the updates in an update group
```
USAGE
$ eas update:delete [GROUPID] [--json --non-interactive]
$ eas update:delete GROUPID [--json --non-interactive]
ARGUMENTS
GROUPID The ID of an update group to delete.
@@ -1215,7 +1222,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1237,7 +1244,7 @@ DESCRIPTION
view the recent updates
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1264,7 +1271,53 @@ DESCRIPTION
roll back to an existing update
```
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded`
roll back to the embedded update
```
USAGE
$ eas update:roll-back-to-embedded [--branch <value>] [--channel <value>] [--message <value>] [-p android|ios|all] [--auto]
[--private-key-path <value>] [--json --non-interactive]
FLAGS
-p, --platform=(android|ios|all) [default: all]
--auto Use the current git branch and commit message for the EAS branch and update message
--branch=<value> Branch to publish the rollback to embedded update group on
--channel=<value> Channel that the published rollback to embedded update should affect
--json Enable JSON output, non-JSON messages will be printed to stderr.
--message=<value> A short message describing the rollback to embedded update
--non-interactive Run the command in non-interactive mode.
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
certificate's directory.
DESCRIPTION
roll back to the embedded update
```
_See code: [src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
roll back to an embedded update or an existing update
```
USAGE
$ eas update:rollback [--private-key-path <value>]
FLAGS
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
certificate's directory.
DESCRIPTION
roll back to an embedded update or an existing update
```
_See code: [src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -1272,7 +1325,7 @@ update group details
```
USAGE
$ eas update:view [GROUPID] [--json]
$ eas update:view GROUPID [--json]
ARGUMENTS
GROUPID The ID of an update group.
@@ -1284,7 +1337,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1305,7 +1358,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1325,7 +1378,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1343,7 +1396,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1365,7 +1418,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1373,7 +1426,7 @@ view a webhook
```
USAGE
$ eas webhook:view [ID]
$ eas webhook:view ID
ARGUMENTS
ID ID of the webhook to view
@@ -1382,7 +1435,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v5.0.1/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/view.ts)_
## `eas whoami`
+2469 -187
View File
File diff suppressed because it is too large. Load diff
+19 -18
View File
@@ -1,36 +1,36 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "5.0.1",
"version": "6.0.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "1.3.1",
"@expo/apple-utils": "1.3.2",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "8.1.2",
"@expo/config-plugins": "7.2.4",
"@expo/config-types": "49.0.0",
"@expo/eas-build-job": "1.0.39",
"@expo/eas-json": "5.0.0",
"@expo/eas-build-job": "1.0.56",
"@expo/eas-json": "6.0.0",
"@expo/json-file": "8.2.37",
"@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/plugin-help": "5.1.23",
"@expo/plugin-warn-if-update-available": "2.5.1",
"@expo/prebuild-config": "6.2.5",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.0",
"@expo/steps": "1.0.34",
"@expo/steps": "1.0.51",
"@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": "4.0.11",
"@urql/exchange-retry": "1.2.0",
@@ -50,7 +50,7 @@
"fs-extra": "10.1.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",
@@ -73,7 +73,7 @@
"prompts": "2.4.2",
"qrcode-terminal": "0.12.0",
"resolve-from": "5.0.0",
"semver": "7.3.8",
"semver": "7.5.2",
"slash": "3.0.0",
"tar": "6.1.13",
"terminal-link": "2.1.1",
@@ -118,7 +118,7 @@
"typescript": "4.9.4"
},
"engines": {
"node": ">=14.0.0"
"node": ">=16.0.0"
},
"files": [
"/bin",
@@ -204,19 +204,20 @@
"scripts": {
"postpack": "rimraf oclif.manifest.json",
"prepack": "yarn rebuild && node ./scripts/prepack.js",
"rebuild": "rimraf build && yarn build-ci",
"rebuild": "rimraf build && yarn build",
"pretarball-ci": "./scripts/pretarball-ci.sh",
"build": "tsc --project tsconfig.build.json",
"build-ci": "tsc --project tsconfig.build-ci.json",
"build-allow-unused": "tsc --project tsconfig.allowUnused.json",
"watch": "yarn build --watch --preserveWatchOutput",
"typecheck": "tsc --project tsconfig.build-ci.json",
"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",
"generate-graphql-code": "graphql-codegen --config graphql-codegen.yml",
"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"
}
}
+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)}`
);
}
@@ -8,6 +8,7 @@ import os from 'os';
import path from 'path';
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
import { getVcsClient } from '../../../vcs';
import {
BumpStrategy,
bumpVersionAsync,
@@ -18,6 +19,8 @@ import {
const fsReal = jest.requireActual('fs').promises as typeof fs;
jest.mock('fs');
const vcsClient = getVcsClient();
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');
+14 -2
View File
@@ -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);
}
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;
@@ -97,6 +107,7 @@ export async function prepareAndroidBuildAsync(
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
vcsClient: ctx.vcsClient,
});
},
prepareJobAsync: 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, {
@@ -14,7 +14,6 @@ import slash from 'slash';
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
@@ -34,7 +33,7 @@ export async function prepareJobAsync(
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
? {
@@ -66,6 +65,8 @@ 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,
@@ -11,6 +11,7 @@ import Log from '../../log';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
import { Client } from '../../vcs/vcs';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
export async function syncProjectConfigurationAsync(
@@ -20,14 +21,16 @@ export async function syncProjectConfigurationAsync(
exp,
localAutoIncrement,
projectId,
vcsClient,
}: {
projectDir: string;
exp: ExpoConfig;
localAutoIncrement?: AndroidVersionAutoIncrement;
projectId: string;
vcsClient: Client;
}
): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
if (workflow === Workflow.GENERIC) {
@@ -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;
+7 -87
View File
@@ -1,5 +1,5 @@
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
import { CredentialsSource, EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
import { CredentialsSource } from '@expo/eas-json';
import assert from 'assert';
import chalk from 'chalk';
import cliProgress from 'cli-progress';
@@ -17,7 +17,6 @@ import {
BuildFragment,
BuildParamsInput,
BuildPriority,
BuildResourceClass,
BuildStatus,
UploadSessionType,
} from '../graphql/generated';
@@ -30,13 +29,11 @@ import {
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform';
import { confirmAsync } from '../prompts';
import { uploadFileAtPathToGCSAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { printJsonOnlyOutput } from '../utils/json';
import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise';
import { getVcsClient } from '../vcs';
import { BuildContext } from './context';
import {
EasBuildDownForMaintenanceError,
@@ -125,9 +122,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 }
);
@@ -137,7 +135,7 @@ export async function prepareBuildRequestForPlatformAsync<
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
projectArchive = {
type: ArchiveSourceType.PATH,
path: (await makeProjectTarballAsync()).path,
path: (await makeProjectTarballAsync(ctx.vcsClient)).path,
};
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
projectArchive = {
@@ -245,7 +243,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(
@@ -335,12 +333,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;
@@ -357,10 +350,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;
@@ -398,13 +388,9 @@ async function handleSingleBuildProgressAsync(
{
build,
accountName,
projectDir,
nonInteractive,
}: {
build: MaybeBuildFragment;
accountName: string;
projectDir: string;
nonInteractive: boolean;
},
{ spinner }: { spinner: Ora }
): Promise<BuildProgressResult> {
@@ -458,54 +444,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(
@@ -654,21 +592,3 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
getExpoWebsiteBaseUrl()
).toString();
}
async function updateIosBuildProfilesToUseM1WorkersAsync(projectDir: string): Promise<void> {
const easJsonAccessor = EasJsonAccessor.fromProjectPath(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.M_MEDIUM,
};
}
return easJsonRawObject;
});
await easJsonAccessor.writeAsync();
Log.withTick('Updated eas.json. Your next builds will run on M1 workers.');
}
+15 -10
View File
@@ -6,12 +6,13 @@ import fs from 'fs-extra';
import Log, { learnMore } from '../log';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { getVcsClient } from '../vcs';
import { Client } from '../vcs/vcs';
import { maybeBailOnRepoStatusAsync, reviewAndCommitChangesAsync } from './utils/repository';
interface ConfigureParams {
projectDir: string;
nonInteractive: boolean;
vcsClient: Client;
}
export async function easJsonExistsAsync(projectDir: string): Promise<boolean> {
@@ -36,14 +37,18 @@ 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);
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,
});
}
@@ -92,20 +97,20 @@ const EAS_JSON_BARE_DEFAULT: EasJson = {
},
};
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;
(await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)) === Workflow.GENERIC;
const hasIosNativeProject =
(await resolveWorkflowAsync(projectDir, Platform.IOS)) === Workflow.GENERIC;
(await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)) === 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);
await vcsClient.trackFileAsync(easJsonPath);
Log.withTick(
`Generated ${chalk.bold('eas.json')}. ${learnMore(
'https://docs.expo.dev/build-reference/eas-json/'
+4
View File
@@ -1,6 +1,7 @@
import { ExpoConfig } from '@expo/config';
import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile, EasJson } from '@expo/eas-json';
import { NodePackageManager } from '@expo/package-manager';
import { Analytics, AnalyticsEventProperties } from '../analytics/AnalyticsManager';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
@@ -11,6 +12,7 @@ import { GradleBuildContext } from '../project/android/gradle';
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
import { XcodeBuildContext } from '../project/ios/scheme';
import { Actor } from '../user/User';
import { Client } from '../vcs/vcs';
import { LocalBuildOptions } from './local';
export type CommonContext<T extends Platform> = Omit<BuildContext<T>, 'android' | 'ios'>;
@@ -56,4 +58,6 @@ export interface BuildContext<T extends Platform> {
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
ios: T extends Platform.IOS ? IosBuildContext : undefined;
developmentClient: boolean;
requiredPackageManager: NodePackageManager['name'] | null;
vcsClient: Client;
}
+10 -1
View File
@@ -1,6 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
import JsonFile from '@expo/json-file';
import { resolvePackageManager } from '@expo/package-manager';
import getenv from 'getenv';
import resolveFrom from 'resolve-from';
import { v4 as uuidv4 } from 'uuid';
@@ -13,6 +14,7 @@ import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
import { resolveWorkflowAsync } from '../project/workflow';
import { Actor } from '../user/User';
import { Client } from '../vcs/vcs';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
@@ -34,6 +36,7 @@ export async function createBuildContextAsync<T extends Platform>({
actor,
graphqlClient,
analytics,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
}: {
@@ -51,13 +54,14 @@ export async function createBuildContextAsync<T extends Platform>({
actor: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
vcsClient: Client;
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
customBuildConfigMetadata?: CustomBuildConfigMetadata;
}): Promise<BuildContext<T>> {
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 =
@@ -67,6 +71,8 @@ export async function createBuildContextAsync<T extends Platform>({
: (buildProfile as BuildProfile<Platform.IOS>)?.buildConfiguration === 'Debug') ??
false;
const requiredPackageManager = resolvePackageManager(projectDir);
const credentialsCtx = new CredentialsContext({
projectInfo: { exp, projectId },
nonInteractive,
@@ -76,6 +82,7 @@ export async function createBuildContextAsync<T extends Platform>({
analytics,
env: buildProfile.env,
easJsonCliConfig,
vcsClient,
});
const devClientProperties = getDevClientEventProperties({
@@ -122,11 +129,13 @@ export async function createBuildContextAsync<T extends Platform>({
user: actor,
graphqlClient,
analytics,
vcsClient,
workflow,
message,
runFromCI,
customBuildConfigMetadata,
developmentClient,
requiredPackageManager,
};
if (platform === Platform.ANDROID) {
const common = commonContext as CommonContext<Platform.ANDROID>;
+14 -19
View File
@@ -1,30 +1,27 @@
import {
ArchiveSource,
ArchiveSourceType,
BuildMode,
BuildTrigger,
Metadata,
Workflow,
} from '@expo/eas-build-job';
import assert from 'assert';
import {
BuildCredentialsSource,
BuildIosEnterpriseProvisioning,
BuildMetadataInput,
BuildMode,
BuildWorkflow,
DistributionType,
BuildMode as GraphQLBuildMode,
BuildTrigger as GraphQLBuildTrigger,
ProjectArchiveSourceInput,
ProjectArchiveSourceType,
} 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,
@@ -42,7 +39,6 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
return {
...metadata,
buildMode: metadata.buildMode && transformBuildMode(metadata.buildMode),
credentialsSource:
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
distribution: metadata.distribution && transformDistribution(metadata.distribution),
@@ -91,17 +87,16 @@ export function transformIosEnterpriseProvisioning(
}
}
// TODO: check what in metadata
export function transformBuildMode(buildMode: string): BuildMode {
if (buildMode === 'build') {
return BuildMode.Build;
} else if (buildMode === 'resign') {
return BuildMode.Resign;
} else if (buildMode === 'custom') {
return BuildMode.Custom;
} else {
throw new Error(`Unsupported build mode: ${buildMode}`);
}
const buildModeToGraphQLBuildMode: Record<BuildMode, GraphQLBuildMode> = {
[BuildMode.BUILD]: GraphQLBuildMode.Build,
[BuildMode.CUSTOM]: GraphQLBuildMode.Custom,
[BuildMode.RESIGN]: GraphQLBuildMode.Resign,
};
export function transformBuildMode(buildMode: BuildMode): GraphQLBuildMode {
const graphQLBuildMode = buildModeToGraphQLBuildMode[buildMode];
assert(graphQLBuildMode, `Unsupported build mode: ${buildMode}`);
return graphQLBuildMode;
}
export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildTrigger {
@@ -6,6 +6,7 @@ import os from 'os';
import type { XCBuildConfiguration } from 'xcode';
import { readPlistAsync } from '../../../utils/plist';
import { getVcsClient } from '../../../vcs';
import {
BumpStrategy,
bumpVersionAsync,
@@ -18,6 +19,8 @@ import {
jest.mock('fs');
const vcsClient = getVcsClient();
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
IOSConfig.Target,
'getXCBuildConfigurationFromPbxproj'
@@ -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('/app', 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('/app', exp, {});
const buildNumber = await readBuildNumberAsync('/app', exp, {}, vcsClient);
expect(buildNumber).toBe('1');
});
});
@@ -280,16 +283,21 @@ describe(readShortVersionAsync, () => {
describe('bare project', () => {
it('reads the short version from native code', async () => {
const exp = initBareWorkflowProject();
const appVersion = await readShortVersionAsync('/app', 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('/app', 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');
});
@@ -299,9 +307,14 @@ describe(readShortVersionAsync, () => {
});
await expect(
readShortVersionAsync('/app', exp, {
CURRENT_PROJECT_VERSION: '0.0.7.1.028',
})
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'
);
@@ -311,7 +324,7 @@ describe(readShortVersionAsync, () => {
describe('managed project', () => {
it('reads the version from app config', async () => {
const exp = initBareWorkflowProject();
const appVersion = await readShortVersionAsync('/app', exp, {});
const appVersion = await readShortVersionAsync('/app', exp, {}, vcsClient);
expect(appVersion).toBe('1.0.0');
});
});
+4
View File
@@ -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 {
@@ -86,6 +89,7 @@ export async function prepareIosBuildAsync(
? false
: ctx.buildProfile.autoIncrement,
projectId: ctx.projectId,
vcsClient: ctx.vcsClient,
});
},
prepareJobAsync: async (
+3 -2
View File
@@ -16,7 +16,6 @@ import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
import { IosJobSecretsInput } from '../../graphql/generated';
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
import { getUsername } from '../../project/projectUtils';
import { getVcsClient } from '../../vcs';
import { BuildContext } from '../context';
interface JobData {
@@ -37,7 +36,7 @@ export async function prepareJobAsync(
const username = getUsername(ctx.exp, ctx.user);
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
const projectRootDirectory =
slash(path.relative(await getVcsClient().getRootPathAsync(), ctx.projectDir)) || '.';
slash(path.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
const buildCredentials: Ios.BuildSecrets['buildCredentials'] = {};
if (jobData.credentials) {
const targetNames = Object.keys(jobData.credentials);
@@ -58,6 +57,8 @@ export async function prepareJobAsync(
builderEnvironment: {
image: buildProfile.image,
node: buildProfile.node,
pnpm: buildProfile.pnpm,
bun: buildProfile.bun,
yarn: buildProfile.yarn,
bundler: buildProfile.bundler,
cocoapods: buildProfile.cocoapods,
@@ -7,6 +7,7 @@ import { Target } from '../../credentials/ios/types';
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
import { resolveWorkflowAsync } from '../../project/workflow';
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { Client } from '../../vcs/vcs';
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
export async function syncProjectConfigurationAsync(
@@ -17,20 +18,22 @@ export async function syncProjectConfigurationAsync(
targets,
localAutoIncrement,
projectId,
vcsClient,
}: {
projectDir: string;
exp: ExpoConfig;
targets: Target[];
localAutoIncrement?: IosVersionAutoIncrement;
projectId: string;
vcsClient: Client;
}
): Promise<void> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
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(graphqlClient, vcsClient, projectDir, exp, projectId);
}
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
} else {
+19 -9
View File
@@ -19,6 +19,7 @@ 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';
@@ -110,9 +111,10 @@ function validateShortVersion(shortVersion: string | undefined): void {
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);
@@ -131,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 (
@@ -147,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 {
@@ -155,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) {
@@ -278,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(
@@ -296,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) {
+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 = '1.0.39';
const PLUGIN_PACKAGE_VERSION = '1.0.56';
export enum LocalBuildMode {
/**
+11 -9
View File
@@ -1,5 +1,5 @@
import { Updates } from '@expo/config-plugins';
import { BuildMode, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
@@ -15,7 +15,6 @@ import {
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';
@@ -24,7 +23,6 @@ import { LocalBuildMode } from './local';
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<Metadata> {
const vcsClient = getVcsClient();
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
const distribution =
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
@@ -44,14 +42,14 @@ export async function collectMetadataAsync<T extends Platform>(
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:
ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL
? false
: await vcsClient.hasUncommittedChangesAsync(),
: await ctx.vcsClient.hasUncommittedChangesAsync(),
username: getUsername(ctx.exp, ctx.user),
message: ctx.message,
...(ctx.platform === Platform.IOS && {
@@ -61,9 +59,11 @@ export async function collectMetadataAsync<T extends Platform>(
}),
runWithNoWaitFlag: ctx.noWait,
runFromCI: ctx.runFromCI,
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
developmentClient: ctx.developmentClient,
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
selectedImage: ctx.buildProfile.image,
customNodeVersion: ctx.buildProfile.node,
};
return sanitizeMetadata(metadata);
}
@@ -76,7 +76,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 {
@@ -90,7 +91,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 {
@@ -66,7 +66,7 @@ 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 { Client } from '../vcs/vcs';
import { prepareAndroidBuildAsync } from './android/build';
import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './build';
import { ensureProjectConfiguredAsync } from './configure';
@@ -98,17 +98,19 @@ export interface BuildFlags {
export async function runBuildAndSubmitAsync(
graphqlClient: ExpoGraphqlClient,
analytics: Analytics,
vcsClient: Client,
projectDir: string,
flags: BuildFlags,
actor: Actor,
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
): Promise<void> {
await getVcsClient().ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(flags.nonInteractive);
await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
await ensureProjectConfiguredAsync({
projectDir,
nonInteractive: flags.nonInteractive,
vcsClient,
});
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
const easJsonCliConfig: EasJson['cli'] =
@@ -134,13 +136,18 @@ export async function runBuildAndSubmitAsync(
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, buildProfile.profile);
const maybeMetadata = await validateCustomBuildConfigAsync({
projectDir,
profile: buildProfile.profile,
vcsClient,
});
if (maybeMetadata) {
customBuildConfigMetadataByPlatform[toAppPlatform(buildProfile.platform)] = maybeMetadata;
}
@@ -163,6 +170,7 @@ export async function runBuildAndSubmitAsync(
actor,
graphqlClient,
analytics,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
});
@@ -227,8 +235,6 @@ export async function runBuildAndSubmitAsync(
const builds = await waitForBuildEndAsync(graphqlClient, {
buildIds: startedBuilds.map(({ build }) => build.id),
accountName,
projectDir,
nonInteractive: flags.nonInteractive,
});
if (!flags.json) {
printBuildResults(builds);
@@ -274,6 +280,7 @@ async function prepareAndStartBuildAsync({
actor,
graphqlClient,
analytics,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
}: {
@@ -285,6 +292,7 @@ async function prepareAndStartBuildAsync({
actor: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
vcsClient: Client;
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
customBuildConfigMetadata?: CustomBuildConfigMetadata;
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
@@ -303,6 +311,7 @@ async function prepareAndStartBuildAsync({
actor,
graphqlClient,
analytics,
vcsClient,
getDynamicPrivateProjectConfigAsync,
customBuildConfigMetadata,
});
@@ -329,6 +338,7 @@ async function prepareAndStartBuildAsync({
exp: buildCtx.exp,
projectId: buildCtx.projectId,
projectDir,
vcsClient: buildCtx.vcsClient,
sdkVersion: buildCtx.exp.sdkVersion,
nonInteractive: flags.nonInteractive,
buildProfile,
@@ -411,6 +421,7 @@ async function prepareAndStartSubmissionAsync({
analytics: buildCtx.analytics,
projectId: buildCtx.projectId,
exp: buildCtx.exp,
vcsClient: buildCtx.vcsClient,
});
if (moreBuilds) {
@@ -471,6 +482,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
graphqlClient,
projectId,
projectDir,
vcsClient,
buildProfile,
nonInteractive,
sdkVersion,
@@ -479,6 +491,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
exp: ExpoConfig;
projectId: string;
projectDir: string;
vcsClient: Client;
buildProfile: ProfileData<'build'>;
nonInteractive: boolean;
sdkVersion?: string;
@@ -508,6 +521,7 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
projectId,
projectDir,
platform: RequestedPlatform.All,
vcsClient,
});
Log.withTick('Installed expo-updates and configured EAS Update.');
throw new Error('Command must be re-run to pick up new updates configuration.');
+21 -5
View File
@@ -10,15 +10,17 @@ import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCli';
import { ProfileData } from '../../utils/profiles';
import { getVcsClient } from '../../vcs';
import { Client } from '../../vcs/vcs';
import { reviewAndCommitChangesAsync } from './repository';
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,
});
}
+29 -20
View File
@@ -10,10 +10,10 @@ 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): Promise<void> {
if (!(await vcsClient.isCommitRequiredAsync())) {
return;
}
Log.addNewLineIfNone();
@@ -32,8 +32,11 @@ export async function maybeBailOnRepoStatusAsync(): Promise<void> {
}
}
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();
@@ -50,19 +53,22 @@ export async function ensureRepoIsCleanAsync(nonInteractive = false): Promise<vo
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 +76,16 @@ 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 async function makeProjectTarballAsync(
vcsClient: Client
): Promise<{ path: string; size: number }> {
const spinner = ora('Compressing project files');
await fs.mkdirp(getTmpDirectory());
@@ -99,7 +107,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 +141,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 +180,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,
});
@@ -7,21 +7,13 @@ import Log, { link } from '../../log';
type AndroidResourceClass = Exclude<
ResourceClass,
| ResourceClass.M1_EXPERIMENTAL
| ResourceClass.M1_MEDIUM
| ResourceClass.M1_LARGE
| ResourceClass.INTEL_MEDIUM
| ResourceClass.M_MEDIUM
| ResourceClass.M_LARGE
ResourceClass.M1_MEDIUM | ResourceClass.M_MEDIUM | ResourceClass.M_LARGE
>;
const iosResourceClassToBuildResourceClassMapping: Record<ResourceClass, BuildResourceClass> = {
[ResourceClass.DEFAULT]: BuildResourceClass.IosDefault,
[ResourceClass.LARGE]: BuildResourceClass.IosLarge,
[ResourceClass.M1_EXPERIMENTAL]: BuildResourceClass.IosMMedium,
[ResourceClass.M1_MEDIUM]: BuildResourceClass.IosMMedium,
[ResourceClass.M1_LARGE]: BuildResourceClass.IosMLarge,
[ResourceClass.INTEL_MEDIUM]: BuildResourceClass.IosIntelMedium,
[ResourceClass.MEDIUM]: BuildResourceClass.IosMedium,
[ResourceClass.M_MEDIUM]: BuildResourceClass.IosMMedium,
[ResourceClass.M_LARGE]: BuildResourceClass.IosMLarge,
@@ -57,15 +49,7 @@ export async function resolveBuildResourceClassAsync<T extends Platform>(
}
function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): BuildResourceClass {
if (
selectedResourceClass &&
[
ResourceClass.M1_EXPERIMENTAL,
ResourceClass.M1_MEDIUM,
ResourceClass.M1_LARGE,
ResourceClass.INTEL_MEDIUM,
].includes(selectedResourceClass)
) {
if (selectedResourceClass && ResourceClass.M1_MEDIUM === selectedResourceClass) {
throw new Error(`Resource class ${selectedResourceClass} is only available for iOS builds`);
}
@@ -88,7 +72,7 @@ function resolveIosResourceClass(
);
}
if ([ResourceClass.M1_EXPERIMENTAL, ResourceClass.M1_MEDIUM].includes(resourceClass)) {
if (ResourceClass.M1_MEDIUM === resourceClass) {
Log.warn(
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
'm-medium'
@@ -96,7 +80,7 @@ function resolveIosResourceClass(
);
}
if ([ResourceClass.M_LARGE, ResourceClass.M1_LARGE].includes(resourceClass)) {
if (ResourceClass.M_LARGE === resourceClass) {
Log.warn(
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
'large'
+2 -3
View File
@@ -6,7 +6,6 @@ import semver from 'semver';
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 {
@@ -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.`
@@ -124,6 +124,7 @@ 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],
@@ -0,0 +1,57 @@
import { format } from '@expo/timeago.js';
import { getAlwaysTrueBranchMapping, getEmptyBranchMapping } from '../branch-mapping';
import { getDescriptionByBranchId } from '../print-utils';
import { testChannelObject, testUpdateBranch1, testUpdateBranch2 } from './fixtures';
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);
});
});
+41 -25
View File
@@ -1,4 +1,3 @@
import assert from 'assert';
import chalk from 'chalk';
import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
@@ -11,17 +10,35 @@ import {
} from '../update/utils';
import { assertVersion, hasEmptyBranchMap, hasStandardBranchMap } from './branch-mapping';
/**
* Log all the branches associated with the channel and their most recent update group
*/
export function logChannelDetails(channel: UpdateChannelObject): void {
assertVersion(channel, 0);
assert(
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel),
'Only standard branch mappings and rollouts are supported.'
);
const doesSupportMapping =
hasEmptyBranchMap(channel) || hasStandardBranchMap(channel) || isRollout(channel);
if (!doesSupportMapping) {
Log.log(chalk.dim('Custom branch mapping detected.'));
return;
}
const branchDescription = channel.updateBranches.flatMap(branch => {
const updateGroupWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
branch.updateGroups
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) {
@@ -31,22 +48,21 @@ export function logChannelDetails(channel: UpdateChannelObject): void {
: 100 - maybeRollout.percentRolledOut;
}
return updateGroupWithBranchDescriptions.map(
({ branch, ...updateGroup }): FormattedBranchDescription => ({
branch,
branchRolloutPercentage: maybePercentOnBranch,
update: updateGroup,
})
if (branch.updateGroups.length === 0) {
acc[branch.id] = { branch: branch.name, branchRolloutPercentage: maybePercentOnBranch };
return acc;
}
const updateGroupsWithBranchDescriptions = getUpdateGroupDescriptionsWithBranch(
branch.updateGroups
);
});
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`)
);
}
// 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>);
}
@@ -22,6 +22,7 @@ import { OptionalPrivateProjectConfigContextField } from './context/OptionalPriv
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
import ProjectDirContextField from './context/ProjectDirContextField';
import SessionManagementContextField from './context/SessionManagementContextField';
import VcsClientContextField from './context/VcsClientContextField';
import { EasCommandError } from './errors';
export type ContextInput<
@@ -101,6 +102,9 @@ export default abstract class EasCommand extends Command {
Analytics: {
analytics: new AnalyticsContextField(),
},
Vcs: {
vcsClient: new VcsClientContextField(),
},
};
/**
@@ -0,0 +1,9 @@
import { getVcsClient } from '../../vcs';
import { Client } from '../../vcs/vcs';
import ContextField from './ContextField';
export default class VcsClientContextField extends ContextField<Client> {
async getValueAsync(): Promise<Client> {
return getVcsClient();
}
}
@@ -27,6 +27,7 @@ export default class BranchCreate extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -37,6 +38,7 @@ export default class BranchCreate extends EasCommand {
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(BranchCreate, {
nonInteractive,
});
@@ -56,7 +58,7 @@ export default class BranchCreate extends EasCommand {
type: 'text',
name: 'name',
message: 'Provide a branch name:',
initial: await getDefaultBranchNameAsync(),
initial: await getDefaultBranchNameAsync(vcsClient),
validate: value => (value ? true : validationMessage),
}));
}
@@ -139,6 +139,7 @@ export default class BuildCancel extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -16,7 +16,6 @@ import {
ensureUseClassicUpdatesIsRemovedAsync,
} from '../../update/configure';
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
import { getVcsClient } from '../../vcs';
export default class BuildConfigure extends EasCommand {
static override description = 'configure the project to support EAS Build';
@@ -32,6 +31,7 @@ export default class BuildConfigure extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -39,6 +39,7 @@ export default class BuildConfigure extends EasCommand {
const {
privateProjectConfig: { exp, projectId, projectDir },
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(BuildConfigure, {
nonInteractive: false,
});
@@ -47,7 +48,9 @@ export default class BuildConfigure extends EasCommand {
'💡 The following process will configure your iOS and/or Android project to be compatible with EAS Build. These changes only apply to your local project files and you can safely revert them at any time.'
);
await getVcsClient().ensureRepoExistsAsync();
// BuildConfigure.ContextOptions.Vcs.client.getValueAsync()
await vcsClient.ensureRepoExistsAsync();
const expoUpdatesIsInstalled = isExpoUpdatesInstalled(projectDir);
@@ -64,6 +67,7 @@ export default class BuildConfigure extends EasCommand {
const didCreateEasJson = await ensureProjectConfiguredAsync({
projectDir,
nonInteractive: false,
vcsClient,
});
if (didCreateEasJson && isUsingEASUpdate(exp, projectId)) {
if (exp.updates?.useClassicUpdates) {
@@ -78,16 +82,22 @@ export default class BuildConfigure extends EasCommand {
// configure expo-updates
if (expoUpdatesIsInstalled) {
if ([RequestedPlatform.Android, RequestedPlatform.All].includes(platform)) {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.GENERIC) {
await syncAndroidUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
}
}
if ([RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)) {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
if (workflow === Workflow.GENERIC) {
await syncIosUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
await syncIosUpdatesConfigurationAsync(
graphqlClient,
vcsClient,
projectDir,
exp,
projectId
);
}
}
}
@@ -106,6 +106,7 @@ export default class Build extends EasCommand {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -121,6 +122,7 @@ export default class Build extends EasCommand {
getDynamicPrivateProjectConfigAsync,
projectDir,
analytics,
vcsClient,
} = await this.getContextAsync(Build, {
nonInteractive: flags.nonInteractive,
});
@@ -141,6 +143,7 @@ export default class Build extends EasCommand {
await runBuildAndSubmitAsync(
graphqlClient,
analytics,
vcsClient,
projectDir,
flagsWithPlatform,
actor,
@@ -11,7 +11,6 @@ import Log from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { getTmpDirectory } from '../../utils/paths';
import { getVcsClient } from '../../vcs';
enum InspectStage {
ARCHIVE = 'archive',
@@ -67,6 +66,7 @@ export default class BuildInspect extends EasCommand {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -76,6 +76,7 @@ export default class BuildInspect extends EasCommand {
getDynamicPrivateProjectConfigAsync,
projectDir,
analytics,
vcsClient,
} = await this.getContextAsync(BuildInspect, {
nonInteractive: false,
});
@@ -92,15 +93,15 @@ export default class BuildInspect extends EasCommand {
await this.prepareOutputDirAsync(outputDirectory, flags.force);
if (flags.stage === InspectStage.ARCHIVE) {
const vcs = getVcsClient();
await vcs.ensureRepoExistsAsync();
await vcs.makeShallowCopyAsync(tmpWorkingdir);
await vcsClient.ensureRepoExistsAsync();
await vcsClient.makeShallowCopyAsync(tmpWorkingdir);
await this.copyToOutputDirAsync(tmpWorkingdir, outputDirectory);
} else {
try {
await runBuildAndSubmitAsync(
graphqlClient,
analytics,
vcsClient,
projectDir,
{
nonInteractive: false,
@@ -37,6 +37,7 @@ export default class BuildInternal extends EasCommand {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -49,6 +50,7 @@ export default class BuildInternal extends EasCommand {
getDynamicPrivateProjectConfigAsync,
projectDir,
analytics,
vcsClient,
} = await this.getContextAsync(BuildInternal, {
nonInteractive: true,
});
@@ -60,6 +62,7 @@ export default class BuildInternal extends EasCommand {
await runBuildAndSubmitAsync(
graphqlClient,
analytics,
vcsClient,
projectDir,
{
requestedPlatform: flags.platform as RequestedPlatform,
@@ -56,6 +56,7 @@ export default class BuildList extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -88,6 +88,7 @@ export default class BuildResign extends EasCommand {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -104,6 +105,7 @@ export default class BuildResign extends EasCommand {
getDynamicPrivateProjectConfigAsync,
projectDir,
analytics,
vcsClient,
} = await this.getContextAsync(BuildResign, {
nonInteractive: flags.nonInteractive,
});
@@ -145,6 +147,7 @@ export default class BuildResign extends EasCommand {
analytics,
env: buildProfile.env,
easJsonCliConfig,
vcsClient,
});
if (buildProfile.credentialsSource !== CredentialsSource.LOCAL && !nonInteractive) {
await credentialsCtx.appStore.ensureAuthenticatedAsync();
@@ -154,6 +157,7 @@ export default class BuildResign extends EasCommand {
projectDir,
nonInteractive,
exp,
vcsClient,
},
buildProfile
);
@@ -162,6 +166,7 @@ export default class BuildResign extends EasCommand {
exp,
xcodeBuildContext,
env: buildProfile.env,
vcsClient,
});
const credentialsResult = await ensureIosCredentialsForBuildResignAsync(
credentialsCtx,
@@ -199,8 +204,6 @@ export default class BuildResign extends EasCommand {
const buildResult = await waitForBuildEndAsync(graphqlClient, {
buildIds: [newBuild.id],
accountName: account.name,
nonInteractive,
projectDir,
});
if (!flags.json) {
printBuildResults(buildResult);
+17 -1
View File
@@ -33,6 +33,7 @@ interface RawRunFlags {
platform?: string;
limit?: number;
offset?: number;
profile?: string;
}
interface RunCommandFlags {
@@ -40,6 +41,7 @@ interface RunCommandFlags {
runArchiveFlags: RunArchiveFlags;
limit?: number;
offset?: number;
profile?: string;
}
export default class Run extends EasCommand {
@@ -66,6 +68,12 @@ export default class Run extends EasCommand {
char: 'p',
options: ['android', 'ios'],
}),
profile: Flags.string({
char: 'e',
description:
'Name of the build profile used to create the build to run. When specified, only builds created with the specified build profile will be queried.',
helpValue: 'PROFILE_NAME',
}),
...EasPaginatedQueryFlags,
};
@@ -73,6 +81,7 @@ export default class Run extends EasCommand {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -97,7 +106,7 @@ export default class Run extends EasCommand {
}
private async sanitizeFlagsAsync(flags: RawRunFlags): Promise<RunCommandFlags> {
const { platform, limit, offset, ...runArchiveFlags } = flags;
const { platform, limit, offset, profile, ...runArchiveFlags } = flags;
const selectedPlatform = await resolvePlatformAsync(platform);
@@ -121,11 +130,16 @@ export default class Run extends EasCommand {
});
}
if (profile && (runArchiveFlags.id || runArchiveFlags.path || runArchiveFlags.url)) {
Log.warn('The --profile flag is ignored when using --id, --path, or --url flags.');
}
return {
selectedPlatform,
runArchiveFlags,
limit,
offset,
profile,
};
}
}
@@ -207,6 +221,7 @@ async function maybeGetBuildAsync(
platform: flags.selectedPlatform,
distribution: distributionType,
status: BuildStatus.Finished,
buildProfile: flags.profile,
},
paginatedQueryOptions,
selectPromptDisabledFunction: build => !isRunnableOnSimulatorOrEmulator(build),
@@ -222,6 +237,7 @@ async function maybeGetBuildAsync(
platform: flags.selectedPlatform,
distribution: distributionType,
status: BuildStatus.Finished,
buildProfile: flags.profile,
},
});
@@ -38,6 +38,7 @@ export default class BuildVersionGetView extends EasCommand {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
};
public async runAsync(): Promise<void> {
@@ -49,6 +50,7 @@ export default class BuildVersionGetView extends EasCommand {
loggedIn: { graphqlClient },
getDynamicPrivateProjectConfigAsync,
projectDir,
vcsClient,
} = await this.getContextAsync(BuildVersionGetView, {
nonInteractive: true,
});
@@ -85,6 +87,7 @@ export default class BuildVersionGetView extends EasCommand {
exp,
buildProfile: profile,
platform,
vcsClient,
});
const remoteVersions = await AppVersionQuery.latestVersionAsync(
graphqlClient,
@@ -41,6 +41,7 @@ export default class BuildVersionSetView extends EasCommand {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
};
public async runAsync(): Promise<void> {
@@ -49,6 +50,7 @@ export default class BuildVersionSetView extends EasCommand {
loggedIn: { graphqlClient },
getDynamicPrivateProjectConfigAsync,
projectDir,
vcsClient,
} = await this.getContextAsync(BuildVersionSetView, {
nonInteractive: false,
});
@@ -74,6 +76,7 @@ export default class BuildVersionSetView extends EasCommand {
exp,
buildProfile: profile,
platform,
vcsClient,
});
const remoteVersions = await AppVersionQuery.latestVersionAsync(
graphqlClient,
@@ -28,6 +28,7 @@ import {
} from '../../../project/remoteVersionSource';
import { resolveWorkflowAsync } from '../../../project/workflow';
import { getProfilesAsync } from '../../../utils/profiles';
import { Client } from '../../../vcs/vcs';
interface SyncContext<T extends Platform> {
projectDir: string;
@@ -35,6 +36,7 @@ interface SyncContext<T extends Platform> {
workflow: Workflow;
profile: BuildProfile<T>;
buildVersion: string;
vcsClient: Client;
}
export default class BuildVersionSyncView extends EasCommand {
@@ -58,6 +60,7 @@ export default class BuildVersionSyncView extends EasCommand {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
};
public async runAsync(): Promise<void> {
@@ -66,6 +69,7 @@ export default class BuildVersionSyncView extends EasCommand {
loggedIn: { graphqlClient },
getDynamicPrivateProjectConfigAsync,
projectDir,
vcsClient,
} = await this.getContextAsync(BuildVersionSyncView, {
nonInteractive: true,
});
@@ -97,6 +101,7 @@ export default class BuildVersionSyncView extends EasCommand {
exp,
buildProfile: profileInfo.profile,
platform: profileInfo.platform,
vcsClient,
});
const remoteVersions = await AppVersionQuery.latestVersionAsync(
graphqlClient,
@@ -104,7 +109,7 @@ export default class BuildVersionSyncView extends EasCommand {
toAppPlatform(profileInfo.platform),
applicationIdentifier
);
const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform);
const workflow = await resolveWorkflowAsync(projectDir, profileInfo.platform, vcsClient);
if (!remoteVersions?.buildVersion) {
Log.warn(
`Skipping versions sync for ${platformDisplayName}. There are no versions configured on Expo servers, use "eas build:version:set" or run a build to initialize it.`
@@ -128,6 +133,7 @@ export default class BuildVersionSyncView extends EasCommand {
profile: profileInfo.profile as BuildProfile<Platform.ANDROID>,
workflow,
buildVersion: remoteVersions.buildVersion,
vcsClient,
});
} else {
this.syncIosAsync({
@@ -136,6 +142,7 @@ export default class BuildVersionSyncView extends EasCommand {
profile: profileInfo.profile as BuildProfile<Platform.IOS>,
workflow,
buildVersion: remoteVersions.buildVersion,
vcsClient,
});
}
Log.withTick(
@@ -152,9 +159,10 @@ export default class BuildVersionSyncView extends EasCommand {
exp,
profile,
buildVersion,
vcsClient,
}: SyncContext<Platform.IOS>): Promise<void> {
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{ exp, projectDir, nonInteractive: false },
{ exp, projectDir, nonInteractive: false, vcsClient },
profile
);
const targets = await resolveTargetsAsync({
@@ -162,6 +170,7 @@ export default class BuildVersionSyncView extends EasCommand {
exp,
xcodeBuildContext,
env: profile.env,
vcsClient,
});
if (!isValidBuildNumber(buildVersion)) {
@@ -20,6 +20,7 @@ export default class BuildView extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -2,7 +2,6 @@ import { Flags } from '@oclif/core';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import Log from '../../log';
import { NonInteractiveOptions as CreateRolloutNonInteractiveOptions } from '../../rollout/actions/CreateRollout';
import { NonInteractiveOptions as EditRolloutNonInteractiveOptions } from '../../rollout/actions/EditRollout';
import {
@@ -17,11 +16,13 @@ import {
RolloutActions,
RolloutMainMenu,
} from '../../rollout/actions/RolloutMainMenu';
import { enableJsonOutput } from '../../utils/json';
enum ActionRawFlagValue {
CREATE = 'create',
EDIT = 'edit',
END = 'end',
VIEW = 'view',
}
type ChannelRolloutRawArgsAndFlags = {
channel?: string;
@@ -137,8 +138,7 @@ export default class ChannelRollout extends EasCommand {
nonInteractive: argsAndFlags.nonInteractive,
});
if (argsAndFlags.json) {
// TODO(quin): implement json output
throw new Error('Developer Preview doesnt support JSON output yet');
enableJsonOutput();
}
const app = { projectId, exp };
@@ -151,11 +151,6 @@ export default class ChannelRollout extends EasCommand {
if (argsAndFlags.nonInteractive) {
await new NonInteractiveRollout(argsAndFlags).runAsync(ctx);
} else {
Log.addNewLineIfNone();
Log.warn(
`✨ This command is in Developer Preview and has not been released to production yet. Website support is coming soon.`
);
Log.addNewLineIfNone();
await new RolloutMainMenu(argsAndFlags).runAsync(ctx);
}
}
@@ -168,6 +163,8 @@ export default class ChannelRollout extends EasCommand {
return ManageRolloutActions.EDIT;
case ActionRawFlagValue.END:
return ManageRolloutActions.END;
case ActionRawFlagValue.VIEW:
return ManageRolloutActions.VIEW;
}
}
@@ -15,6 +15,7 @@ export default class Credentials extends EasCommand {
...this.ContextOptions.OptionalProjectConfig,
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -24,6 +25,7 @@ export default class Credentials extends EasCommand {
privateProjectConfig,
getDynamicPrivateProjectConfigAsync,
analytics,
vcsClient,
} = await this.getContextAsync(Credentials, {
nonInteractive: false,
});
@@ -31,6 +33,7 @@ export default class Credentials extends EasCommand {
await new SelectPlatform(
actor,
graphqlClient,
vcsClient,
analytics,
privateProjectConfig ?? null,
getDynamicPrivateProjectConfigAsync,
+7 -5
View File
@@ -5,16 +5,18 @@ import EasCommand from '../commandUtils/EasCommand';
import Log from '../log';
import { resolveWorkflowAsync } from '../project/workflow';
import { easCliVersion } from '../utils/easCli';
import { Client } from '../vcs/vcs';
export default class Diagnostics extends EasCommand {
static override description = 'display environment info';
static override contextDefinition = {
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
const { projectDir } = await this.getContextAsync(Diagnostics, {
const { projectDir, vcsClient } = await this.getContextAsync(Diagnostics, {
nonInteractive: true,
});
@@ -43,13 +45,13 @@ export default class Diagnostics extends EasCommand {
);
Log.log(info.trimEnd());
await this.printWorkflowAsync(projectDir);
await this.printWorkflowAsync(projectDir, vcsClient);
Log.newLine();
}
private async printWorkflowAsync(projectDir: string): Promise<void> {
const androidWorkflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const iosWorkflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
private async printWorkflowAsync(projectDir: string, vcsClient: Client): Promise<void> {
const androidWorkflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
const iosWorkflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
if (androidWorkflow === iosWorkflow) {
Log.log(` Project workflow: ${androidWorkflow}`);
@@ -27,6 +27,7 @@ export default class MetadataPull extends EasCommand {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -37,12 +38,13 @@ export default class MetadataPull extends EasCommand {
loggedIn: { actor, graphqlClient },
privateProjectConfig: { exp, projectId, projectDir },
analytics,
vcsClient,
} = await this.getContextAsync(MetadataPull, {
nonInteractive: false,
});
// this command is interactive (all nonInteractive flags passed to utility functions are false)
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false });
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false, vcsClient });
const submitProfiles = await getProfilesAsync({
type: 'submit',
@@ -64,6 +66,7 @@ export default class MetadataPull extends EasCommand {
graphqlClient,
analytics,
nonInteractive: false,
vcsClient,
});
try {
@@ -25,6 +25,7 @@ export default class MetadataPush extends EasCommand {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -35,12 +36,13 @@ export default class MetadataPush extends EasCommand {
loggedIn: { actor, graphqlClient },
privateProjectConfig: { exp, projectId, projectDir },
analytics,
vcsClient,
} = await this.getContextAsync(MetadataPush, {
nonInteractive: false,
});
// this command is interactive (all nonInteractive flags passed to utility functions are false)
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false });
await ensureProjectConfiguredAsync({ projectDir, nonInteractive: false, vcsClient });
const submitProfiles = await getProfilesAsync({
type: 'submit',
@@ -62,6 +64,7 @@ export default class MetadataPush extends EasCommand {
graphqlClient,
analytics,
nonInteractive: false,
vcsClient,
});
try {
+70 -15
View File
@@ -16,7 +16,7 @@ import { ora } from '../../ora';
import { createOrModifyExpoConfigAsync, getPrivateExpoConfig } from '../../project/expoConfig';
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
import { toAppPrivacy } from '../../project/projectUtils';
import { confirmAsync, promptAsync } from '../../prompts';
import { Choice, confirmAsync, promptAsync } from '../../prompts';
import { Actor } from '../../user/User';
type InitializeMethodOptions = {
@@ -234,21 +234,11 @@ export default class ProjectInit extends EasCommand {
if (allAccounts.length === 1) {
accountName = allAccounts[0].name;
} else {
// if regular user, put primary account first
const sortedAccounts =
actor.__typename === 'Robot'
? allAccounts
: [...allAccounts].sort((a, _b) =>
actor.__typename === 'User' ? (a.name === actor.username ? -1 : 1) : 0
);
const choices = ProjectInit.getAccountChoices(
actor,
accountNamesWhereUserHasSufficientPermissionsToCreateApp
);
const choices = sortedAccounts.map(account => ({
title: account.name,
value: account,
description: !accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(account.name)
? '(Viewer Role)'
: undefined,
}));
accountName = (
await promptAsync({
type: 'select',
@@ -321,6 +311,71 @@ export default class ProjectInit extends EasCommand {
return createdProjectId;
}
private static getAccountChoices(
actor: Actor,
namesWithSufficientPermissions: Set<string>
): Choice[] {
const allAccounts = actor.accounts;
const sortedAccounts =
actor.__typename === 'Robot'
? allAccounts
: [...allAccounts].sort((a, _b) =>
actor.__typename === 'User' ? (a.name === actor.username ? -1 : 1) : 0
);
if (actor.__typename !== 'Robot') {
const personalAccount = allAccounts?.find(
account => account?.ownerUserActor?.id === actor.id
);
const personalAccountChoice = personalAccount
? {
title: personalAccount.name,
value: personalAccount,
description: !namesWithSufficientPermissions.has(personalAccount.name)
? '(Personal) (Viewer Role)'
: '(Personal)',
}
: undefined;
const userAccounts = allAccounts
?.filter(account => account.ownerUserActor && account.name !== actor.username)
.map(account => ({
title: account.name,
value: account,
description: !namesWithSufficientPermissions.has(account.name)
? '(Team) (Viewer Role)'
: '(Team)',
}));
const organizationAccounts = allAccounts
?.filter(account => account.name !== actor.username && !account.ownerUserActor)
.map(account => ({
title: account.name,
value: account,
description: !namesWithSufficientPermissions.has(account.name)
? '(Organization) (Viewer Role)'
: '(Organization)',
}));
let choices: Choice[] = [];
if (personalAccountChoice) {
choices = [personalAccountChoice];
}
return [...choices, ...userAccounts, ...organizationAccounts].sort((a, _b) =>
actor.__typename === 'User' ? (a.value.name === actor.username ? -1 : 1) : 0
);
}
return sortedAccounts.map(account => ({
title: account.name,
value: account,
description: !namesWithSufficientPermissions.has(account.name) ? '(Viewer Role)' : undefined,
}));
}
async runAsync(): Promise<void> {
const {
flags: { id: idArgument, force, 'non-interactive': nonInteractive },
+4
View File
@@ -94,6 +94,7 @@ export default class Submit extends EasCommand {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -102,6 +103,7 @@ export default class Submit extends EasCommand {
loggedIn: { actor, graphqlClient },
privateProjectConfig: { exp, projectId, projectDir },
analytics,
vcsClient,
} = await this.getContextAsync(Submit, {
nonInteractive: false,
});
@@ -135,6 +137,8 @@ export default class Submit extends EasCommand {
analytics,
exp,
projectId,
vcsClient,
specifiedProfile: flagsWithPlatform.profile,
});
if (submissionProfiles.length > 1) {
@@ -3,10 +3,12 @@ import { instance, mock } from 'ts-mockito';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { ensureEASUpdateIsConfiguredAsync } from '../../../update/configure';
import { Client } from '../../../vcs/vcs';
describe(ensureEASUpdateIsConfiguredAsync, () => {
it('errors with "useClassicUpdates" set and no app.json', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
const vcsClient = instance(mock<Client>({}));
const exp: ExpoConfig = {
name: 'test',
slug: 'test',
@@ -19,6 +21,7 @@ describe(ensureEASUpdateIsConfiguredAsync, () => {
projectId: 'test',
projectDir: '/tmp/test',
platform: null,
vcsClient,
});
}).rejects.toThrow(
`Your app config sets "updates.useClassicUpdates" but EAS Update does not support classic updates. Remove "useClassicUpdates" from your app config and run this command again.`
@@ -43,6 +43,10 @@ jest.mock('@expo/config');
jest.mock('@expo/config-plugins');
jest.mock('../../../branch/queries');
jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync');
jest.mock('../../../project/projectUtils', () => ({
...jest.requireActual('../../../project/projectUtils'),
enforceRollBackToEmbeddedUpdateSupportAsync: jest.fn(),
}));
jest.mock('../../../update/configure');
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
jest.mock('../../../graphql/mutations/PublishMutation');
@@ -10,7 +10,6 @@ import {
ensureEASUpdateIsConfiguredAsync,
ensureEASUpdateIsConfiguredInEasJsonAsync,
} from '../../update/configure';
import { getVcsClient } from '../../vcs';
export default class UpdateConfigure extends EasCommand {
static override description = 'configure the project to support EAS Update';
@@ -28,6 +27,7 @@ export default class UpdateConfigure extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -36,6 +36,7 @@ export default class UpdateConfigure extends EasCommand {
const {
privateProjectConfig: { projectId, exp, projectDir },
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(UpdateConfigure, {
nonInteractive: flags['non-interactive'],
});
@@ -44,13 +45,14 @@ export default class UpdateConfigure extends EasCommand {
'💡 The following process will configure your project to use EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
);
await getVcsClient().ensureRepoExistsAsync();
await vcsClient.ensureRepoExistsAsync();
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
exp,
projectId,
projectDir,
platform,
vcsClient,
});
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
+43 -18
View File
@@ -29,6 +29,7 @@ import {
collectAssetsAsync,
defaultPublishPlatforms,
filterExportedPlatformsByFlag,
generateEasMetadataAsync,
getBranchNameForCommandAsync,
getRequestedPlatform,
getRuntimeToPlatformMappingFromRuntimeVersions,
@@ -40,7 +41,7 @@ import {
uploadAssetsAsync,
} from '../../project/publish';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
import { getUpdateGroupJsonInfo } from '../../update/utils';
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
import {
checkManifestBodyAgainstUpdateInfoGroup,
getCodeSigningInfoAsync,
@@ -52,10 +53,10 @@ import uniqBy from '../../utils/expodash/uniqBy';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
import { getVcsClient } from '../../vcs';
type RawUpdateFlags = {
auto: boolean;
dev: boolean;
branch?: string;
channel?: string;
message?: string;
@@ -74,6 +75,7 @@ type RawUpdateFlags = {
type UpdateFlags = {
auto: boolean;
dev: boolean;
platform: ExpoCLIExportPlatformFlag;
branchName?: string;
channelName?: string;
@@ -134,6 +136,10 @@ export default class UpdatePublish extends EasCommand {
default: 'all',
required: false,
}),
dev: Flags.boolean({
description: 'Publish a development bundle',
default: false,
}),
auto: Flags.boolean({
description:
'Use the current git branch and commit message for the EAS branch and update message',
@@ -149,6 +155,7 @@ export default class UpdatePublish extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -158,6 +165,7 @@ export default class UpdatePublish extends EasCommand {
auto: autoFlag,
platform: platformFlag,
channelName: channelNameArg,
dev,
updateMessage: updateMessageArg,
inputDir,
skipBundler,
@@ -172,6 +180,7 @@ export default class UpdatePublish extends EasCommand {
getDynamicPublicProjectConfigAsync,
getDynamicPrivateProjectConfigAsync,
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(UpdatePublish, {
nonInteractive,
});
@@ -193,6 +202,7 @@ export default class UpdatePublish extends EasCommand {
platform: getRequestedPlatform(platformFlag),
projectDir,
projectId,
vcsClient,
});
const { exp } = await getDynamicPublicProjectConfigAsync();
@@ -201,6 +211,7 @@ export default class UpdatePublish extends EasCommand {
const branchName = await getBranchNameForCommandAsync({
graphqlClient,
vcsClient,
projectId,
channelNameArg,
branchNameArg,
@@ -209,7 +220,7 @@ export default class UpdatePublish extends EasCommand {
paginatedQueryOptions,
});
const updateMessage = await getUpdateMessageForCommandAsync({
const updateMessage = await getUpdateMessageForCommandAsync(vcsClient, {
updateMessageArg,
autoFlag,
nonInteractive,
@@ -220,7 +231,7 @@ export default class UpdatePublish extends EasCommand {
if (!skipBundler) {
const bundleSpinner = ora().start('Exporting...');
try {
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag, clearCache });
await buildBundlesAsync({ projectDir, inputDir, dev, exp, platformFlag, clearCache });
bundleSpinner.succeed('Exported bundle(s)');
} catch (e) {
bundleSpinner.fail('Export failed');
@@ -243,20 +254,22 @@ export default class UpdatePublish extends EasCommand {
realizedPlatforms = Object.keys(assets) as PublishPlatform[];
// Timeout mechanism:
// - Start with 60 second timeout. 60 seconds is chosen because the cloud function that processes
// uploaded assets has a timeout of 60 seconds.
// - Each time one or more assets reports as ready, reset the timeout to 60 seconds.
// - Start upload. Internally, uploadAssetsAsync uploads them all and then checks for successful
// - Start with NO_ACTIVITY_TIMEOUT. 90 seconds is chosen because the cloud function that processes
// uploaded assets has a timeout of 60 seconds and uploading can take some time on a slow connection.
// - Each time one or more assets reports as ready, reset the timeout.
// - Each time an asset upload begins, reset the timeout. This includes retries.
// - Start upload. Internally, uploadAssetsAsync uploads them all first and then checks for successful
// processing every (5 + n) seconds with a linear backoff of n + 1 second.
// - At the same time as upload is started, start timeout checker which checks every 1 second to see
// if timeout has been reached. When timeout expires, send a cancellation signal to currently running
// upload function call to instruct it to stop uploading or checking for successful processing.
const NO_ACTIVITY_TIMEOUT = 90 * 1000; // 90 seconds
let lastUploadedStorageKeys = new Set<string>();
let lastAssetUploadResults: {
asset: RawAsset & { storageKey: string };
finished: boolean;
}[] = [];
let timeAtWhichToTimeout = Date.now() + 60 * 1000; // sixty seconds from now
let timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT;
const cancelationToken = { isCanceledOrFinished: false };
const uploadResults = await Promise.race([
@@ -270,7 +283,7 @@ export default class UpdatePublish extends EasCommand {
assetUploadResults.filter(r => r.finished).map(r => r.asset.storageKey)
);
if (!areSetsEqual(currentUploadedStorageKeys, lastUploadedStorageKeys)) {
timeAtWhichToTimeout = Date.now() + 60 * 1000; // reset timeout to sixty seconds from now
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
lastUploadedStorageKeys = currentUploadedStorageKeys;
lastAssetUploadResults = assetUploadResults;
}
@@ -278,6 +291,10 @@ export default class UpdatePublish extends EasCommand {
const totalAssets = assetUploadResults.length;
const missingAssetCount = assetUploadResults.filter(a => !a.finished).length;
assetSpinner.text = `Uploading (${totalAssets - missingAssetCount}/${totalAssets})`;
},
() => {
// when an upload is retried, reset the timeout as we know this will now need more time
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
}
),
(async () => {
@@ -346,7 +363,12 @@ export default class UpdatePublish extends EasCommand {
throw e;
}
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir);
const runtimeVersions = await getRuntimeVersionObjectAsync(
exp,
realizedPlatforms,
projectDir,
vcsClient
);
const runtimeToPlatformMapping =
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
@@ -360,12 +382,11 @@ export default class UpdatePublish extends EasCommand {
branchId,
channelName: branchName,
});
Log.withTick(
`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`
);
}
Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`);
const vcsClient = getVcsClient();
const gitCommitHash = await vcsClient.getCommitHashAsync();
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
@@ -448,8 +469,11 @@ export default class UpdatePublish extends EasCommand {
throw e;
}
if (!skipBundler) {
await generateEasMetadataAsync(distRoot, getUpdateJsonInfosForUpdates(newUpdates));
}
if (jsonFlag) {
printJsonOnlyOutput(getUpdateGroupJsonInfo(newUpdates));
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
} else {
if (new Set(newUpdates.map(update => update.group)).size > 1) {
Log.addNewLineIfNone();
@@ -491,7 +515,7 @@ export default class UpdatePublish extends EasCommand {
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
: []),
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
{ label: 'Message', value: updateMessage },
{ label: 'Message', value: updateMessage ?? '' },
...(gitCommitHash
? [
{
@@ -523,7 +547,7 @@ export default class UpdatePublish extends EasCommand {
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
const nonInteractive = flags['non-interactive'] ?? false;
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
const { auto, branch: branchName, channel: channelName, dev, message: updateMessage } = flags;
if (nonInteractive && !auto && !(updateMessage && (branchName || channelName))) {
Errors.error(
'--branch and --message, or --channel and --message are required when updating in non-interactive mode unless --auto is specified',
@@ -552,6 +576,7 @@ export default class UpdatePublish extends EasCommand {
auto,
branchName,
channelName,
dev,
updateMessage,
inputDir: flags['input-dir'],
skipBundler: flags['skip-bundler'],
@@ -20,7 +20,10 @@ import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import Log, { link } from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import {
enforceRollBackToEmbeddedUpdateSupportAsync,
getOwnerAccountForProjectIdAsync,
} from '../../project/projectUtils';
import {
ExpoCLIExportPlatformFlag,
defaultPublishPlatforms,
@@ -31,7 +34,7 @@ import {
getUpdateMessageForCommandAsync,
} from '../../project/publish';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
import { getUpdateGroupJsonInfo } from '../../update/utils';
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
import {
CodeSigningInfo,
checkDirectiveBodyAgainstUpdateInfoGroup,
@@ -43,7 +46,6 @@ import uniqBy from '../../utils/expodash/uniqBy';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
import { getVcsClient } from '../../vcs';
type RawUpdateFlags = {
auto: boolean;
@@ -68,7 +70,6 @@ type UpdateFlags = {
};
export default class UpdateRollBackToEmbedded extends EasCommand {
static override hidden = true; // until we launch
static override description = 'roll back to the embedded update';
static override flags = {
@@ -109,6 +110,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
@@ -129,6 +131,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
getDynamicPublicProjectConfigAsync,
getDynamicPrivateProjectConfigAsync,
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(UpdateRollBackToEmbedded, {
nonInteractive,
});
@@ -150,14 +153,19 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
platform: getRequestedPlatform(platformFlag),
projectDir,
projectId,
vcsClient,
});
// check that the expo-updates package version supports roll back to embedded
await enforceRollBackToEmbeddedUpdateSupportAsync(projectDir);
const { exp } = await getDynamicPublicProjectConfigAsync();
const { exp: expPrivate } = await getDynamicPrivateProjectConfigAsync();
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
const branchName = await getBranchNameForCommandAsync({
graphqlClient,
vcsClient,
projectId,
channelNameArg,
branchNameArg,
@@ -166,7 +174,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
paginatedQueryOptions,
});
const updateMessage = await getUpdateMessageForCommandAsync({
const updateMessage = await getUpdateMessageForCommandAsync(vcsClient, {
updateMessageArg,
autoFlag,
nonInteractive,
@@ -190,12 +198,15 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`);
const vcsClient = getVcsClient();
const gitCommitHash = await vcsClient.getCommitHashAsync();
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir);
const runtimeVersions = await getRuntimeVersionObjectAsync(
exp,
realizedPlatforms,
projectDir,
vcsClient
);
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
const publishSpinner = ora('Publishing...').start();
@@ -217,7 +228,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
}
if (jsonFlag) {
printJsonOnlyOutput(getUpdateGroupJsonInfo(newUpdates));
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
} else {
if (new Set(newUpdates.map(update => update.group)).size > 1) {
Log.addNewLineIfNone();
@@ -259,7 +270,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
: []),
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
{ label: 'Message', value: updateMessage },
{ label: 'Message', value: updateMessage ?? '' },
...(gitCommitHash
? [
{
@@ -289,7 +300,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
graphqlClient: ExpoGraphqlClient;
isGitWorkingTreeDirty: boolean | undefined;
gitCommitHash: string | undefined;
updateMessage: string;
updateMessage: string | undefined;
branchId: string;
codeSigningInfo: CodeSigningInfo | undefined;
runtimeVersions: { platform: string; runtimeVersion: string }[];
@@ -8,8 +8,6 @@ import UpdateRollBackToEmbedded from './roll-back-to-embedded';
export default class UpdateRollback extends EasCommand {
static override description = 'roll back to an embedded update or an existing update';
static override hidden = true;
static override flags = {
'private-key-path': Flags.string({
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
+2 -2
View File
@@ -7,7 +7,7 @@ import Log from '../../log';
import {
formatUpdateGroup,
getUpdateGroupDescriptions,
getUpdateGroupJsonInfo,
getUpdateJsonInfosForUpdates,
} from '../../update/utils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -47,7 +47,7 @@ export default class UpdateView extends EasCommand {
const updatesByGroup = await UpdateQuery.viewUpdateGroupAsync(graphqlClient, { groupId });
if (jsonFlag) {
printJsonOnlyOutput(getUpdateGroupJsonInfo(updatesByGroup));
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(updatesByGroup));
} else {
const [updateGroupDescription] = getUpdateGroupDescriptions([updatesByGroup]);
@@ -11,6 +11,7 @@ import {
} from '../../../project/projectUtils';
import { promptAsync } from '../../../prompts';
import { CredentialsContext } from '../../context';
import { AndroidPackageNotDefinedError } from '../../errors';
import { AppLookupParams } from '../api/GraphqlClient';
/**
@@ -108,10 +109,11 @@ export async function getAppLookupParamsFromContextAsync(
const androidApplicationIdentifier = await getApplicationIdAsync(
ctx.projectDir,
ctx.exp,
ctx.vcsClient,
gradleContext
);
if (!androidApplicationIdentifier) {
throw new Error(
throw new AndroidPackageNotDefinedError(
`android.package needs to be defined in your ${getProjectConfigDescription(
ctx.projectDir
)} file`
@@ -260,7 +260,6 @@ export async function createKeystoreAsync(
keystorePassword: keystore.keystorePassword,
keyAlias: keystore.keyAlias,
keyPassword: keystore.keyPassword,
type: keystore.type,
},
account.id
);
@@ -9,6 +9,7 @@ import Log from '../log';
import { getPrivateExpoConfig } from '../project/expoConfig';
import { confirmAsync } from '../prompts';
import { Actor } from '../user/User';
import { Client } from '../vcs/vcs';
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
import * as IosGraphqlClient from './ios/api/GraphqlClient';
import AppStoreApi from './ios/appstore/AppStoreApi';
@@ -28,6 +29,7 @@ export class CredentialsContext {
public readonly user: Actor;
public readonly graphqlClient: ExpoGraphqlClient;
public readonly analytics: Analytics;
public readonly vcsClient: Client;
public readonly easJsonCliConfig?: EasJson['cli'];
private shouldAskAuthenticateAppStore: boolean = true;
@@ -44,6 +46,7 @@ export class CredentialsContext {
user: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
vcsClient: Client;
env?: Env;
}
) {
@@ -52,6 +55,7 @@ export class CredentialsContext {
this.user = options.user;
this.graphqlClient = options.graphqlClient;
this.analytics = options.analytics;
this.vcsClient = options.vcsClient;
this.nonInteractive = options.nonInteractive ?? false;
this.projectInfo = options.projectInfo;
}
@@ -6,8 +6,8 @@ import { AndroidAppBuildCredentialsFragment, IosDistributionType } from '../../g
import Log from '../../log';
import { findApplicationTarget, findTargetByName } from '../../project/ios/target';
import zipObject from '../../utils/expodash/zipObject';
import { getVcsClient } from '../../vcs';
import GitClient from '../../vcs/clients/git';
import { Client } from '../../vcs/vcs';
import { CredentialsContext } from '../context';
import { App, Target, TargetCredentials } from '../ios/types';
import { readRawAsync } from './read';
@@ -38,7 +38,7 @@ export async function updateAndroidCredentialsAsync(
rawCredentialsJson?.android?.keystore?.keystorePath ?? 'credentials/android/keystore.jks';
Log.log(`Writing Keystore to ${keystorePath}`);
await updateFileAsync(ctx.projectDir, keystorePath, keystore.keystore);
const shouldWarnKeystore = await isFileUntrackedAsync(keystorePath);
const shouldWarnKeystore = await isFileUntrackedAsync(keystorePath, ctx.vcsClient);
const androidCredentials: Partial<CredentialsJson['android']> = {
keystore: {
@@ -52,7 +52,7 @@ export async function updateAndroidCredentialsAsync(
await fs.writeJson(getCredentialsJsonPath(ctx.projectDir), rawCredentialsJson, {
spaces: 2,
});
const shouldWarnCredentialsJson = await isFileUntrackedAsync('credentials.json');
const shouldWarnCredentialsJson = await isFileUntrackedAsync('credentials.json', ctx.vcsClient);
const newFilePaths = [];
if (shouldWarnKeystore) {
@@ -137,14 +137,14 @@ export async function updateIosCredentialsAsync(
const newFilePaths = [];
for (const [, targetCredentials] of Object.entries(iosCredentials)) {
if (await isFileUntrackedAsync(targetCredentials.distributionCertificate.path)) {
if (await isFileUntrackedAsync(targetCredentials.distributionCertificate.path, ctx.vcsClient)) {
newFilePaths.push(targetCredentials.distributionCertificate.path);
}
if (await isFileUntrackedAsync(targetCredentials.provisioningProfilePath)) {
if (await isFileUntrackedAsync(targetCredentials.provisioningProfilePath, ctx.vcsClient)) {
newFilePaths.push(targetCredentials.provisioningProfilePath);
}
}
if (await isFileUntrackedAsync('credentials.json')) {
if (await isFileUntrackedAsync('credentials.json', ctx.vcsClient)) {
newFilePaths.push('credentials.json');
}
displayUntrackedFilesWarning(newFilePaths);
@@ -296,8 +296,7 @@ async function updateFileAsync(
}
}
async function isFileUntrackedAsync(path: string): Promise<boolean> {
const vcsClient = getVcsClient();
async function isFileUntrackedAsync(path: string, vcsClient: Client): Promise<boolean> {
if (vcsClient instanceof GitClient) {
return await vcsClient.isFileUntrackedAsync(path);
}
@@ -15,3 +15,9 @@ export class UnsupportedCredentialsChoiceError extends Error {
super(message);
}
}
export class AndroidPackageNotDefinedError extends Error {
constructor(message?: string) {
super(message ?? 'android.package needs to be defined in your app.json/app.config.js file');
}
}
@@ -31,7 +31,7 @@ export function formatDeviceLabel(device: AppleDeviceFragment): string {
const deviceDetails = formatDeviceDetails(device);
return `${device.identifier}${deviceDetails !== '' ? ` ${deviceDetails}` : ''}${
device.name ? ` (${device.name})` : ''
}`;
}${device.createdAt ? ` (created at: ${device.createdAt})` : ''}`;
}
function formatDeviceDetails(device: AppleDeviceFragment): string {
@@ -5,6 +5,7 @@ import nullthrows from 'nullthrows';
import DeviceCreateAction, { RegistrationMethod } from '../../../devices/actions/create/action';
import {
AppleAppIdentifierFragment,
AppleDeviceFragment,
AppleDistributionCertificateFragment,
AppleProvisioningProfileFragment,
@@ -19,6 +20,7 @@ import differenceBy from '../../../utils/expodash/differenceBy';
import { CredentialsContext } from '../../context';
import { MissingCredentialsNonInteractiveError } from '../../errors';
import { AppLookupParams } from '../api/graphql/types/AppLookupParams';
import { ProvisioningProfile } from '../appstore/Credentials.types';
import { ApplePlatform } from '../appstore/constants';
import { Target } from '../types';
import { validateProvisioningProfileAsync } from '../validators/validateProvisioningProfile';
@@ -144,27 +146,15 @@ export class SetUpAdhocProvisioningProfile {
app,
appleTeam
);
let appleProvisioningProfile: AppleProvisioningProfileFragment | null = null;
let appleProvisioningProfile: AppleProvisioningProfileFragment | null;
if (currentBuildCredentials?.provisioningProfile) {
if (
currentBuildCredentials.provisioningProfile.developerPortalIdentifier !==
provisioningProfileStoreInfo.provisioningProfileId
) {
await ctx.ios.deleteProvisioningProfilesAsync(ctx.graphqlClient, [
currentBuildCredentials.provisioningProfile.id,
]);
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
ctx.graphqlClient,
app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
developerPortalIdentifier: provisioningProfileStoreInfo.provisioningProfileId,
}
);
} else {
appleProvisioningProfile = currentBuildCredentials.provisioningProfile;
}
appleProvisioningProfile = await this.reuseCurrentProvisioningProfileAsync(
currentBuildCredentials.provisioningProfile,
provisioningProfileStoreInfo,
ctx,
app,
appleAppIdentifier
);
} else {
appleProvisioningProfile = await ctx.ios.createProvisioningProfileAsync(
ctx.graphqlClient,
@@ -183,7 +173,7 @@ export class SetUpAdhocProvisioningProfile {
appleProvisioningProfile.appleDevices,
'identifier'
);
if (diffList && diffList.length > 0) {
if (diffList.length > 0) {
Log.warn(`Failed to provision ${diffList.length} of the selected devices:`);
for (const missingDevice of diffList) {
Log.warn(`- ${formatDeviceLabel(missingDevice)}`);
@@ -205,6 +195,43 @@ export class SetUpAdhocProvisioningProfile {
);
}
private async reuseCurrentProvisioningProfileAsync(
currentProvisioningProfile: AppleProvisioningProfileFragment,
provisioningProfileStoreInfo: ProvisioningProfile,
ctx: CredentialsContext,
app: AppLookupParams,
appleAppIdentifier: AppleAppIdentifierFragment
): Promise<AppleProvisioningProfileFragment> {
if (
currentProvisioningProfile.developerPortalIdentifier !==
provisioningProfileStoreInfo.provisioningProfileId
) {
// If IDs don't match, the profile needs to be deleted and re-created
await ctx.ios.deleteProvisioningProfilesAsync(ctx.graphqlClient, [
currentProvisioningProfile.id,
]);
return await ctx.ios.createProvisioningProfileAsync(
ctx.graphqlClient,
app,
appleAppIdentifier,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
developerPortalIdentifier: provisioningProfileStoreInfo.provisioningProfileId,
}
);
} else {
// If not, the profile needs to be updated first
return await ctx.ios.updateProvisioningProfileAsync(
ctx.graphqlClient,
currentProvisioningProfile.id,
{
appleProvisioningProfile: provisioningProfileStoreInfo.provisioningProfile,
developerPortalIdentifier: provisioningProfileStoreInfo.provisioningProfileId,
}
);
}
}
private async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
const { app, target } = this.options;
const buildCredentials = await getBuildCredentialsAsync(ctx, app, IosDistributionType.AdHoc);
@@ -0,0 +1,25 @@
import { v4 as uuidv4 } from 'uuid';
import { AppleDeviceClass, AppleDeviceFragment } from '../../../../graphql/generated';
import { formatDeviceLabel } from '../DeviceUtils';
describe(formatDeviceLabel, () => {
it('returns createdAt clause', async () => {
const currentDate = new Date();
const appleDevice = {
__typename: 'AppleDevice',
id: uuidv4(),
identifier: 'test-apple-device-id',
name: 'test-apple-device-name',
model: '15',
deviceClass: AppleDeviceClass.Iphone,
createdAt: currentDate.toISOString(),
} as AppleDeviceFragment;
const result = formatDeviceLabel(appleDevice);
expect(result).toEqual(
`test-apple-device-id (iPhone 15) (test-apple-device-name) (created at: ${currentDate.toISOString()})`
);
});
});
@@ -15,6 +15,7 @@ import {
import Log from '../../../../log';
import { getApplePlatformFromTarget } from '../../../../project/ios/target';
import { Actor } from '../../../../user/User';
import { Client } from '../../../../vcs/vcs';
import { CredentialsContext, CredentialsContextProjectInfo } from '../../../context';
import { ProvisioningProfile } from '../../appstore/Credentials.types';
import { ApplePlatform } from '../../appstore/constants';
@@ -56,30 +57,63 @@ describe('runWithDistributionCertificateAsync', () => {
});
describe('compare chosen and provisioned devices', () => {
describe('not all devices provisioned', () => {
it('displays warning to the user and lists the missing devices', async () => {
const { ctx, distCert } = setUpTest();
jest.mocked(getBuildCredentialsAsync).mockResolvedValue({
provisioningProfile: {
describe('still not provisioned after an update', () => {
it('displays warning to the user and lists the missing devices', async () => {
const { ctx, distCert } = setUpTest();
jest.mocked(getBuildCredentialsAsync).mockResolvedValue({
provisioningProfile: {
appleTeam: {},
appleDevices: [{ identifier: 'id1' }],
developerPortalIdentifier: 'provisioningProfileId',
},
} as IosAppBuildCredentialsFragment);
ctx.ios.updateProvisioningProfileAsync = jest.fn().mockResolvedValue({
appleTeam: {},
appleDevices: [{ identifier: 'id1' }],
developerPortalIdentifier: 'provisioningProfileId',
},
} as IosAppBuildCredentialsFragment);
const LogWarnSpy = jest.spyOn(Log, 'warn');
const LogLogSpy = jest.spyOn(Log, 'log');
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
ctx,
distCert
);
expect(result).toEqual({} as IosAppBuildCredentialsFragment);
expect(LogWarnSpy).toHaveBeenCalledTimes(3);
expect(LogWarnSpy).toHaveBeenCalledWith('Failed to provision 2 of the selected devices:');
expect(LogWarnSpy).toHaveBeenCalledWith('- id2 (iPhone) (Device 2)');
expect(LogWarnSpy).toHaveBeenCalledWith('- id3 (Mac) (Device 3)');
expect(LogLogSpy).toHaveBeenCalledTimes(1);
expect(LogLogSpy).toHaveBeenCalledWith(
'Most commonly devices fail to to be provisioned while they are still being processed by Apple, which can take up to 24-72 hours. Check your Apple Developer Portal page at https://developer.apple.com/account/resources/devices/list, the devices in "Processing" status cannot be provisioned yet'
);
});
const LogWarnSpy = jest.spyOn(Log, 'warn');
const LogLogSpy = jest.spyOn(Log, 'log');
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
ctx,
distCert
);
expect(result).toEqual({} as IosAppBuildCredentialsFragment);
expect(LogWarnSpy).toHaveBeenCalledTimes(3);
expect(LogWarnSpy).toHaveBeenCalledWith('Failed to provision 2 of the selected devices:');
expect(LogWarnSpy).toHaveBeenCalledWith('- id2 (iPhone) (Device 2)');
expect(LogWarnSpy).toHaveBeenCalledWith('- id3 (Mac) (Device 3)');
expect(LogLogSpy).toHaveBeenCalledTimes(1);
expect(LogLogSpy).toHaveBeenCalledWith(
'Most commonly devices fail to to be provisioned while they are still being processed by Apple, which can take up to 24-72 hours. Check your Apple Developer Portal page at https://developer.apple.com/account/resources/devices/list, the devices in "Processing" status cannot be provisioned yet'
);
});
});
describe('all devices provisioned after an update', () => {
it('does not display warning', async () => {
const { ctx, distCert } = setUpTest();
jest.mocked(getBuildCredentialsAsync).mockResolvedValue({
provisioningProfile: {
appleTeam: {},
appleDevices: [{ identifier: 'id1' }],
developerPortalIdentifier: 'provisioningProfileId',
},
} as IosAppBuildCredentialsFragment);
ctx.ios.updateProvisioningProfileAsync = jest.fn().mockResolvedValue({
appleTeam: {},
appleDevices: [{ identifier: 'id1' }, { identifier: 'id2' }, { identifier: 'id3' }],
developerPortalIdentifier: 'provisioningProfileId',
});
const LogWarnSpy = jest.spyOn(Log, 'warn');
const LogLogSpy = jest.spyOn(Log, 'log');
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
ctx,
distCert
);
expect(result).toEqual({} as IosAppBuildCredentialsFragment);
expect(LogWarnSpy).not.toHaveBeenCalled();
expect(LogLogSpy).not.toHaveBeenCalled();
});
});
});
describe('all devices provisioned', () => {
@@ -92,6 +126,11 @@ describe('runWithDistributionCertificateAsync', () => {
developerPortalIdentifier: 'provisioningProfileId',
},
} as IosAppBuildCredentialsFragment);
ctx.ios.updateProvisioningProfileAsync = jest.fn().mockResolvedValue({
appleTeam: {},
appleDevices: [{ identifier: 'id1' }, { identifier: 'id2' }, { identifier: 'id3' }],
developerPortalIdentifier: 'provisioningProfileId',
});
const LogWarnSpy = jest.spyOn(Log, 'warn');
const LogLogSpy = jest.spyOn(Log, 'log');
const result = await setUpAdhocProvisioningProfile.runWithDistributionCertificateAsync(
@@ -118,6 +157,7 @@ function setUpTest(): { ctx: CredentialsContext; distCert: AppleDistributionCert
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
env?: Env;
vcsClient: Client;
}
)
);
@@ -8,6 +8,7 @@ import {
} from '@expo/apple-utils';
import assert from 'assert';
import chalk from 'chalk';
import { int } from 'getenv';
import Log from '../../../log';
import { toggleConfirmAsync } from '../../../prompts';
@@ -188,7 +189,10 @@ async function authenticateAsUserAsync(options: Options = {}): Promise<AuthCtx>
const authState = await loginAsync(
{
cookies: options.cookies,
teamId: options.teamId,
teamId: options.teamId ?? process.env.EXPO_APPLE_TEAM_ID,
providerId: process.env.EXPO_APPLE_PROVIDER_ID
? int(process.env.EXPO_APPLE_PROVIDER_ID)
: undefined,
},
{
// TODO: Provide a way to disable this for users who want to mix and match teams / providers.
@@ -733,6 +733,13 @@ export const CapabilityMapping: CapabilityClassifier[] = [
validateOptions: validateBooleanOptions,
getOptions: getBooleanOptions,
},
{
entitlement: 'com.apple.developer.proximity-reader.payment.acceptance',
name: 'Tap to Pay on iPhone',
capability: CapabilityType.TAP_TO_PAY_ON_IPHONE,
validateOptions: validateBooleanOptions,
getOptions: getBooleanOptions,
},
{
entitlement: 'com.apple.developer.matter.allow-setup-payload',
name: 'Matter Allow Setup Payload',
@@ -4,12 +4,14 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
import Log from '../../log';
import { pressAnyKeyToContinueAsync } from '../../prompts';
import { Actor } from '../../user/User';
import { Client } from '../../vcs/vcs';
import { CredentialsContext, CredentialsContextProjectInfo } from '../context';
export interface Action<T = void> {
actor: Actor;
graphqlClient: ExpoGraphqlClient;
analytics: Analytics;
vcsClient: Client;
projectInfo: CredentialsContextProjectInfo | null;
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
runAsync(ctx: CredentialsContext): Promise<T>;
@@ -27,6 +27,7 @@ import {
displayEmptyAndroidCredentials,
} from '../android/utils/printCredentials';
import { CredentialsContext, CredentialsContextProjectInfo } from '../context';
import { AndroidPackageNotDefinedError } from '../errors';
import { ActionInfo, AndroidActionType, Scope } from './Actions';
import {
buildCredentialsActions,
@@ -64,6 +65,7 @@ export class ManageAndroid {
analytics: this.callingAction.analytics,
env: buildProfile?.env,
nonInteractive: false,
vcsClient: this.callingAction.vcsClient,
});
let gradleContext;
@@ -131,6 +133,12 @@ export class ManageAndroid {
await this.runProjectSpecificActionAsync(ctx, chosenAction, gradleContext);
} catch (err) {
if (err instanceof AndroidPackageNotDefinedError) {
err.message += `\n${learnMore(
'https://docs.expo.dev/workflow/configuration/'
)} about configuration with app.json/app.config.js`;
throw err; // breaks out of the loop to avoid failing again after continuation
}
Log.error(err);
if (process.env.DEBUG) {
throw err; // breaks out of the loop so we can get stack trace
@@ -146,7 +154,7 @@ export class ManageAndroid {
buildProfile: BuildProfile<Platform.ANDROID>
): Promise<GradleBuildContext | undefined> {
assert(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.');
return await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile);
return await resolveGradleBuildContextAsync(ctx.projectDir, buildProfile, ctx.vcsClient);
}
private async runProjectSpecificActionAsync(
@@ -78,6 +78,7 @@ export class ManageIos {
analytics: this.callingAction.analytics,
env: buildProfile?.env,
nonInteractive: false,
vcsClient: this.callingAction.vcsClient,
});
const buildCredentialsActions = getBuildCredentialsActions(ctx);
const pushKeyActions = getPushKeyActions(ctx);
@@ -194,6 +195,7 @@ export class ManageIos {
projectDir: ctx.projectDir,
nonInteractive: ctx.nonInteractive,
exp: ctx.exp,
vcsClient: ctx.vcsClient,
},
buildProfile
);
@@ -202,6 +204,7 @@ export class ManageIos {
projectDir: ctx.projectDir,
xcodeBuildContext,
env: buildProfile.env,
vcsClient: ctx.vcsClient,
});
return {
app,
@@ -3,6 +3,7 @@ import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjec
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { selectPlatformAsync } from '../../platform';
import { Actor } from '../../user/User';
import { Client } from '../../vcs/vcs';
import { CredentialsContextProjectInfo } from '../context';
import { ManageAndroid } from './ManageAndroid';
import { ManageIos } from './ManageIos';
@@ -11,6 +12,7 @@ export class SelectPlatform {
constructor(
public readonly actor: Actor,
public readonly graphqlClient: ExpoGraphqlClient,
public readonly vcsClient: Client,
public readonly analytics: Analytics,
public readonly projectInfo: CredentialsContextProjectInfo | null,
public readonly getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn,
@@ -0,0 +1,71 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJsonUtils } from '@expo/eas-json';
import { Analytics } from '../../../analytics/AnalyticsManager';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import { learnMore } from '../../../log';
import { getProjectConfigDescription } from '../../../project/projectUtils';
import { pressAnyKeyToContinueAsync } from '../../../prompts';
import { Actor } from '../../../user/User';
import { Client } from '../../../vcs/vcs';
import { getAppLookupParamsFromContextAsync } from '../../android/actions/BuildCredentialsUtils';
import { CredentialsContextProjectInfo } from '../../context';
import { AndroidPackageNotDefinedError } from '../../errors';
import { Action } from '../HelperActions';
import { ManageAndroid } from '../ManageAndroid';
jest.mock('../../android/actions/BuildCredentialsUtils');
jest.mock('../../../prompts', () => {
return {
...jest.requireActual('../../../prompts'),
pressAnyKeyToContinueAsync: jest.fn(),
};
});
jest.mock('../../../project/projectUtils');
describe('runAsync', () => {
describe('"android.package" missing in app.json', () => {
const manageAndroid = new ManageAndroid(
{
projectInfo: {} as CredentialsContextProjectInfo,
actor: {} as Actor,
graphqlClient: {} as ExpoGraphqlClient,
analytics: {} as Analytics,
vcsClient: {} as Client,
getDynamicPrivateProjectConfigAsync: jest
.fn()
.mockResolvedValue({ exp: {}, projectId: '' }),
runAsync: jest.fn(),
} as Action,
''
);
it('does not repeat the error indefinitely and prints useful error', async () => {
jest.spyOn(EasJsonUtils, 'getBuildProfileNamesAsync').mockResolvedValue(['testProfile']);
jest
.spyOn(EasJsonUtils, 'getBuildProfileAsync')
.mockResolvedValue({} as BuildProfile<Platform.ANDROID>);
jest.mocked(getProjectConfigDescription).mockReturnValue('app.json');
jest.mocked(getAppLookupParamsFromContextAsync).mockImplementation(() => {
throw new AndroidPackageNotDefinedError(
'Specify "android.package" in app.json and run this command again.'
);
});
const pressAnyKeyToContinueAsyncMock = jest.mocked(pressAnyKeyToContinueAsync);
Array.from(Array(100)).map((_, _i) => {
// continue 101 times if error is rethrown indefinitely
pressAnyKeyToContinueAsyncMock.mockResolvedValueOnce();
});
pressAnyKeyToContinueAsyncMock.mockImplementationOnce(async () => {
// fail after 102nd time
fail('test should not reach this place - if it does, the error repeats indefinitely');
});
const reThrownError = new AndroidPackageNotDefinedError(
'Specify "android.package" in app.json and run this command again.\n' +
`${learnMore(
'https://docs.expo.dev/workflow/configuration/'
)} about configuration with app.json/app.config.js`
);
await expect(manageAndroid.runAsync()).rejects.toThrow(reThrownError);
});
});
});
File diff suppressed because it is too large. Load diff
@@ -60,6 +60,7 @@ export const ChannelQuery = {
updateChannelByName(name: $channelName) {
id
name
updatedAt
createdAt
branchMapping
updateBranches(offset: 0, limit: 5) {
@@ -104,6 +105,7 @@ export const ChannelQuery = {
updateChannels(offset: $offset, limit: $limit) {
id
name
updatedAt
createdAt
branchMapping
updateBranches(offset: 0, limit: 5) {
@@ -4,6 +4,10 @@ export const AccountFragmentNode = gql`
fragment AccountFragment on Account {
id
name
ownerUserActor {
id
username
}
users {
actor {
id
@@ -17,6 +17,7 @@ export const BuildFragmentNode = gql`
buildUrl
xcodeBuildLogsUrl
applicationArchiveUrl
buildArtifactsUrl
}
initiatingActor {
__typename
@@ -15,5 +15,6 @@ export const AppleDeviceFragmentNode = gql`
name
model
deviceClass
createdAt
}
`;
+11 -4
View File
@@ -6,6 +6,7 @@ import assert from 'assert';
import { CredentialsContext } from '../credentials/context';
import { getRequestContext } from '../credentials/ios/appstore/authenticate';
import { getBundleIdentifierAsync } from '../project/ios/bundleIdentifier';
import { Client } from '../vcs/vcs';
export type MetadataAppStoreAuthentication = {
/** The root entity of the App store */
@@ -21,13 +22,14 @@ export type MetadataAppStoreAuthentication = {
async function resolveAppStoreBundleIdentifierAsync(
projectDir: string,
profile: SubmitProfile,
exp: ExpoConfig
exp: ExpoConfig,
vcsClient: Client
): Promise<string> {
if ('bundleIdentifier' in profile) {
return profile.bundleIdentifier ?? (await getBundleIdentifierAsync(projectDir, exp));
return profile.bundleIdentifier ?? (await getBundleIdentifierAsync(projectDir, exp, vcsClient));
}
return await getBundleIdentifierAsync(projectDir, exp);
return await getBundleIdentifierAsync(projectDir, exp, vcsClient);
}
/**
@@ -45,7 +47,12 @@ export async function getAppStoreAuthAsync({
exp: ExpoConfig;
credentialsCtx: CredentialsContext;
}): Promise<MetadataAppStoreAuthentication> {
const bundleId = await resolveAppStoreBundleIdentifierAsync(projectDir, profile, exp);
const bundleId = await resolveAppStoreBundleIdentifierAsync(
projectDir,
profile,
exp,
credentialsCtx.vcsClient
);
const authCtx = await credentialsCtx.appStore.ensureAuthenticatedAsync();
assert(authCtx.authState, 'Failed to authenticate with App Store Connect');
@@ -0,0 +1,68 @@
import { AppJSONConfig, PackageJSONConfig } from '@expo/config';
import { vol } from 'memfs';
import path from 'path';
import resolveFrom from 'resolve-from';
import { enforceRollBackToEmbeddedUpdateSupportAsync } from '../projectUtils';
jest.mock('fs');
jest.mock('resolve-from');
const projectRoot = '/test-project';
describe(enforceRollBackToEmbeddedUpdateSupportAsync, () => {
it('does not throw when an appropriate version is installed', async () => {
mockTestProject('0.19.1');
await enforceRollBackToEmbeddedUpdateSupportAsync(projectRoot);
});
it('throws when an unappropriate version is installed', async () => {
mockTestProject('0.18.0');
await expect(enforceRollBackToEmbeddedUpdateSupportAsync(projectRoot)).rejects.toThrowError(
'The expo-updates package must have a version >= 0.19.0 to use roll back to embedded, which corresponds to Expo SDK 50 or greater.'
);
});
});
function mockTestProject(expoUpdatesPackageVersion: string): { appJson: AppJSONConfig } {
const packageJSON: PackageJSONConfig = {
name: 'testing123',
version: '0.1.0',
description: 'fake description',
main: 'index.js',
dependencies: {
'expo-updates': expoUpdatesPackageVersion,
},
};
const expoUpdatesPackageJSON: PackageJSONConfig = {
name: 'expo-updates',
version: expoUpdatesPackageVersion,
};
const appJSON: AppJSONConfig = {
expo: {
name: 'testing 123',
version: '0.1.0',
slug: 'testing-123',
sdkVersion: '33.0.0',
},
};
jest
.mocked(resolveFrom.silent)
.mockReturnValue(path.join(projectRoot, 'node_modules/expo-updates/package.json'));
vol.fromJSON(
{
'package.json': JSON.stringify(packageJSON),
'app.json': JSON.stringify(appJSON),
'node_modules/expo-updates/package.json': JSON.stringify(expoUpdatesPackageJSON),
},
projectRoot
);
return { appJson: appJSON };
}
@@ -576,6 +576,7 @@ describe(uploadAssetsAsync, () => {
assetsForUpdateInfoGroup,
testProjectId,
{ isCanceledOrFinished: false },
() => {},
() => {}
)
).resolves.toEqual({
@@ -622,6 +623,7 @@ describe(uploadAssetsAsync, () => {
{
isCanceledOrFinished: false,
},
() => {},
() => {}
)
).resolves.toEqual({
@@ -667,7 +669,8 @@ describe(uploadAssetsAsync, () => {
assetsForUpdateInfoGroup,
testProjectId,
{ isCanceledOrFinished: false },
onAssetUploadResultsChangedFn
onAssetUploadResultsChangedFn,
() => {}
);
expect(onAssetUploadResultsChangedFn).toHaveBeenCalledTimes(3);
});
@@ -1,11 +1,13 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { vol } from 'memfs';
import { getVcsClient } from '../../vcs';
import { resolveWorkflowAsync, resolveWorkflowPerPlatformAsync } from '../workflow';
jest.mock('fs');
const projectDir = '/app';
const vcsClient = getVcsClient();
describe(resolveWorkflowAsync, () => {
beforeEach(() => {
@@ -21,10 +23,12 @@ describe(resolveWorkflowAsync, () => {
projectDir
);
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe(
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe(
Workflow.GENERIC
);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe(
Workflow.GENERIC
);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.GENERIC);
});
test('bare workflow for single platform', async () => {
@@ -35,10 +39,12 @@ describe(resolveWorkflowAsync, () => {
projectDir
);
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe(
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe(
Workflow.MANAGED
);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.GENERIC);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe(
Workflow.GENERIC
);
});
test('android/ios directories are ignored', async () => {
@@ -51,10 +57,12 @@ describe(resolveWorkflowAsync, () => {
projectDir
);
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID)).resolves.toBe(
await expect(resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient)).resolves.toBe(
Workflow.MANAGED
);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient)).resolves.toBe(
Workflow.MANAGED
);
await expect(resolveWorkflowAsync(projectDir, Platform.IOS)).resolves.toBe(Workflow.MANAGED);
});
});
@@ -72,7 +80,7 @@ describe(resolveWorkflowPerPlatformAsync, () => {
projectDir
);
await expect(resolveWorkflowPerPlatformAsync(projectDir)).resolves.toEqual({
await expect(resolveWorkflowPerPlatformAsync(projectDir, vcsClient)).resolves.toEqual({
android: Workflow.GENERIC,
ios: Workflow.GENERIC,
});
@@ -7,6 +7,7 @@ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/cr
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { promptAsync } from '../../../prompts';
import { getVcsClient } from '../../../vcs';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
@@ -17,6 +18,8 @@ jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
const vcsClient = getVcsClient();
beforeEach(async () => {
vol.reset();
@@ -44,7 +47,9 @@ describe(getApplicationIdAsync, () => {
'/app'
);
const applicationId = await getApplicationIdAsync('/app', {} as any, { moduleName: 'app' });
const applicationId = await getApplicationIdAsync('/app', {} as any, vcsClient, {
moduleName: 'app',
});
expect(applicationId).toBe('com.expo.notdominik');
});
@@ -56,9 +61,9 @@ describe(getApplicationIdAsync, () => {
},
'/app'
);
await expect(getApplicationIdAsync('/app', {} as any, undefined)).rejects.toThrowError(
/Failed to find 'build.gradle' /
);
await expect(
getApplicationIdAsync('/app', {} as any, vcsClient, undefined)
).rejects.toThrowError(/Failed to find 'build.gradle' /);
});
it('throws an error if the project does not have applicationId defined in build.gradle', async () => {
@@ -71,7 +76,7 @@ describe(getApplicationIdAsync, () => {
);
await expect(
getApplicationIdAsync('/app', {} as any, { moduleName: 'app' })
getApplicationIdAsync('/app', {} as any, vcsClient, { moduleName: 'app' })
).rejects.toThrowError(/Could not read applicationId/);
});
});
@@ -83,6 +88,7 @@ describe(getApplicationIdAsync, () => {
{
android: { package: 'com.expo.notdominik' },
} as any,
vcsClient,
{ moduleName: 'app' }
);
expect(applicationId).toBe('com.expo.notdominik');
@@ -90,15 +96,20 @@ describe(getApplicationIdAsync, () => {
it('throws an error if Android package is not defined in app config', async () => {
await expect(
getApplicationIdAsync('/app', {} as any, { moduleName: 'app' })
getApplicationIdAsync('/app', {} as any, vcsClient, { moduleName: 'app' })
).rejects.toThrowError(/Specify "android.package"/);
});
it('throws an error if Android package in app config is invalid', async () => {
await expect(
getApplicationIdAsync('/app', { android: { package: '1com.expo.notdominik' } } as any, {
moduleName: 'app',
})
getApplicationIdAsync(
'/app',
{ android: { package: '1com.expo.notdominik' } } as any,
vcsClient,
{
moduleName: 'app',
}
)
).rejects.toThrowError(/Specify "android.package"/);
});
});
@@ -121,6 +132,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '',
exp: {} as any,
vcsClient,
})
).rejects.toThrowError(/we can't update this file programmatically/);
});
@@ -150,6 +162,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '',
exp: {} as any,
vcsClient,
})
).resolves.toBe('com.expo.notdominik');
expect(promptAsync).toHaveBeenCalled();
@@ -182,6 +195,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '',
exp: {} as any,
vcsClient,
})
).resolves.toBe('com.expo.notdominik');
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
@@ -4,12 +4,15 @@ import os from 'os';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import { getVcsClient } from '../../../vcs';
import { resolveGradleBuildContextAsync } from '../gradle';
jest.mock('fs');
jest.mock('../../../prompts');
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
const vcsClient = getVcsClient();
beforeEach(async () => {
vol.reset();
// do not remove the following line
@@ -36,7 +39,7 @@ describe(resolveGradleBuildContextAsync, () => {
'/app'
);
const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any);
const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any, vcsClient);
expect(gradleContext).toEqual({ moduleName: 'app' });
});
it('resolves to flavor', async () => {
@@ -59,9 +62,13 @@ describe(resolveGradleBuildContextAsync, () => {
'/app'
);
const gradleContext = await resolveGradleBuildContextAsync('/app', {
gradleCommand: ':app:buildAbcRelease',
} as any);
const gradleContext = await resolveGradleBuildContextAsync(
'/app',
{
gradleCommand: ':app:buildAbcRelease',
} as any,
vcsClient
);
expect(gradleContext).toEqual({ moduleName: 'app', flavor: 'abc' });
});
@@ -73,9 +80,13 @@ describe(resolveGradleBuildContextAsync, () => {
},
'/app'
);
const gradleContext = await resolveGradleBuildContextAsync('/app', {
gradleCommand: ':app:buildAbcRelease',
} as any);
const gradleContext = await resolveGradleBuildContextAsync(
'/app',
{
gradleCommand: ':app:buildAbcRelease',
} as any,
vcsClient
);
expect(gradleContext).toEqual(undefined);
});
it('returns undefined if flavor does not exist', async () => {
@@ -93,16 +104,20 @@ describe(resolveGradleBuildContextAsync, () => {
'/app'
);
const gradleContext = await resolveGradleBuildContextAsync('/app', {
gradleCommand: ':app:buildAbcRelease',
} as any);
const gradleContext = await resolveGradleBuildContextAsync(
'/app',
{
gradleCommand: ':app:buildAbcRelease',
} as any,
vcsClient
);
expect(gradleContext).toEqual(undefined);
});
});
describe('managed projects', () => {
it('resolves to { moduleName: app } for managed projects', async () => {
const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any);
const gradleContext = await resolveGradleBuildContextAsync('/app', {} as any, {} as any);
expect(gradleContext).toEqual({ moduleName: 'app' });
});
});
@@ -15,6 +15,7 @@ import {
getProjectConfigDescription,
} from '../../project/projectUtils';
import { promptAsync } from '../../prompts';
import { Client } from '../../vcs/vcs';
import { resolveWorkflowAsync } from '../workflow';
import { GradleBuildContext } from './gradle';
import * as gradleUtils from './gradleUtils';
@@ -26,17 +27,19 @@ export async function ensureApplicationIdIsDefinedForManagedProjectAsync({
projectDir,
projectId,
exp,
vcsClient,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
exp: ExpoConfig;
vcsClient: Client;
}): Promise<string> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
assert(workflow === Workflow.MANAGED, 'This function should be called only for managed projects');
try {
return await getApplicationIdAsync(projectDir, exp, {
return await getApplicationIdAsync(projectDir, exp, vcsClient, {
moduleName: gradleUtils.DEFAULT_MODULE_NAME,
});
} catch {
@@ -95,9 +98,10 @@ export async function getApplicationIdFromBareAsync(
export async function getApplicationIdAsync(
projectDir: string,
exp: ExpoConfig,
vcsClient: Client,
gradleContext?: GradleBuildContext
): Promise<string> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.GENERIC) {
warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(projectDir, exp);
@@ -3,6 +3,7 @@ import { BuildProfile } from '@expo/eas-json';
import Log from '../../log';
import { resolveWorkflowAsync } from '../../project/workflow';
import { Client } from '../../vcs/vcs';
import * as gradleUtils from './gradleUtils';
export interface GradleBuildContext {
@@ -12,9 +13,10 @@ export interface GradleBuildContext {
export async function resolveGradleBuildContextAsync(
projectDir: string,
buildProfile: BuildProfile<Platform.ANDROID>
buildProfile: BuildProfile<Platform.ANDROID>,
vcsClient: Client
): Promise<GradleBuildContext | undefined> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.GENERIC) {
try {
if (buildProfile.gradleCommand) {
@@ -3,6 +3,7 @@ import { Platform, Workflow } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { Client } from '../vcs/vcs';
import {
ensureApplicationIdIsDefinedForManagedProjectAsync,
getApplicationIdAsync,
@@ -23,6 +24,7 @@ export async function getApplicationIdentifierAsync({
exp,
buildProfile,
platform,
vcsClient,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
@@ -30,10 +32,11 @@ export async function getApplicationIdentifierAsync({
exp: ExpoConfig;
buildProfile: BuildProfile;
platform: Platform;
vcsClient: Client;
}): Promise<string> {
if (platform === Platform.ANDROID) {
const profile = buildProfile as BuildProfile<Platform.ANDROID>;
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID);
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.MANAGED) {
return await ensureApplicationIdIsDefinedForManagedProjectAsync({
@@ -41,13 +44,14 @@ export async function getApplicationIdentifierAsync({
projectDir,
projectId,
exp,
vcsClient,
});
}
const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile);
return await getApplicationIdAsync(projectDir, exp, gradleContext);
const gradleContext = await resolveGradleBuildContextAsync(projectDir, profile, vcsClient);
return await getApplicationIdAsync(projectDir, exp, vcsClient, gradleContext);
} else {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
const profile = buildProfile as BuildProfile<Platform.IOS>;
if (workflow === Workflow.MANAGED) {
return await ensureBundleIdentifierIsDefinedForManagedProjectAsync({
@@ -55,11 +59,12 @@ export async function getApplicationIdentifierAsync({
projectDir,
projectId,
exp,
vcsClient,
});
}
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{ exp, projectDir, nonInteractive: false },
{ exp, projectDir, nonInteractive: false, vcsClient },
profile
);
const targets = await resolveTargetsAsync({
@@ -67,9 +72,10 @@ export async function getApplicationIdentifierAsync({
exp,
xcodeBuildContext,
env: profile.env,
vcsClient,
});
const applicationTarget = findApplicationTarget(targets);
return await getBundleIdentifierAsync(projectDir, exp, {
return await getBundleIdentifierAsync(projectDir, exp, vcsClient, {
targetName: applicationTarget.targetName,
buildConfiguration: applicationTarget.buildConfiguration,
});
@@ -5,14 +5,21 @@ import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { Client } from '../vcs/vcs';
export interface CustomBuildConfigMetadata {
workflowName?: string;
}
export async function validateCustomBuildConfigAsync(
projectDir: string,
profile: BuildProfile<Platform>
): Promise<CustomBuildConfigMetadata | undefined> {
export async function validateCustomBuildConfigAsync({
profile,
projectDir,
vcsClient,
}: {
projectDir: string;
profile: BuildProfile<Platform>;
vcsClient: Client;
}): Promise<CustomBuildConfigMetadata | undefined> {
if (!profile.config) {
return undefined;
}
@@ -24,6 +31,13 @@ export async function validateCustomBuildConfigAsync(
`Custom build configuration file ${chalk.bold(relativeConfigPath)} does not exist.`
);
}
if (await vcsClient.isFileIgnoredAsync(relativeConfigPath)) {
throw new Error(
`Custom build configuration file ${chalk.bold(
relativeConfigPath
)} is ignored by your version control system. Remove it from the ignore list to successfully create custom build.`
);
}
try {
const config = await readAndValidateBuildConfigAsync(configPath, {
@@ -8,6 +8,7 @@ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/cr
import { jester, jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { promptAsync } from '../../../prompts';
import { getVcsClient } from '../../../vcs';
import {
ensureBundleIdentifierIsDefinedForManagedProjectAsync,
getBundleIdentifierAsync,
@@ -19,6 +20,8 @@ jest.mock('../../../prompts');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() => mockJester) }));
const vcsClient = getVcsClient();
beforeEach(async () => {
vol.reset();
@@ -44,7 +47,7 @@ describe(getBundleIdentifierAsync, () => {
'/app'
);
const bundleIdentifier = await getBundleIdentifierAsync('/app', {} as any);
const bundleIdentifier = await getBundleIdentifierAsync('/app', {} as any, vcsClient);
expect(bundleIdentifier).toBe('org.name.testproject');
});
@@ -59,7 +62,7 @@ describe(getBundleIdentifierAsync, () => {
'/app'
);
await expect(getBundleIdentifierAsync('/app', {} as any)).rejects.toThrowError(
await expect(getBundleIdentifierAsync('/app', {} as any, vcsClient)).rejects.toThrowError(
/Could not read bundle identifier/
);
});
@@ -67,21 +70,25 @@ describe(getBundleIdentifierAsync, () => {
describe('managed projects', () => {
it('reads bundleIdentifier from app config', async () => {
const applicationId = await getBundleIdentifierAsync('/app', {
ios: { bundleIdentifier: 'com.expo.notdominik' },
} as any);
const applicationId = await getBundleIdentifierAsync(
'/app',
{
ios: { bundleIdentifier: 'com.expo.notdominik' },
} as any,
vcsClient
);
expect(applicationId).toBe('com.expo.notdominik');
});
it('throws an error if bundleIdentifier is not defined in app config', async () => {
await expect(getBundleIdentifierAsync('/app', {} as any)).rejects.toThrowError(
await expect(getBundleIdentifierAsync('/app', {} as any, vcsClient)).rejects.toThrowError(
/Specify "ios.bundleIdentifier"/
);
});
it('throws an error if bundleIdentifier in app config is invalid', async () => {
await expect(
getBundleIdentifierAsync('/app', { ios: { bundleIdentifier: '' } } as any)
getBundleIdentifierAsync('/app', { ios: { bundleIdentifier: '' } } as any, vcsClient)
).rejects.toThrowError(/Specify "ios.bundleIdentifier"/);
});
});
@@ -103,6 +110,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '1234',
exp: {} as any,
vcsClient,
})
).rejects.toThrowError(/we can't update this file programmatically/);
});
@@ -132,6 +140,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '1234',
exp: {} as any,
vcsClient,
})
).resolves.toBe('com.expo.notdominik');
expect(promptAsync).toHaveBeenCalled();
@@ -162,6 +171,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
projectDir: '/app',
projectId: '1234',
exp: {} as any,
vcsClient,
})
).resolves.toBe('com.expo.notdominik');
const appJson = JSON.parse(await fs.readFile('/app/app.json', 'utf-8'));
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff. Show more