Compare commits

..
Author SHA1 Message Date
Will Schurman 0e1e29d119 [rfc][eas-cli] Add codepush commands 2024-08-12 13:51:27 -07:00
Piotr SzeremetaandSzymon Dziedzic 5cf526592c Add environment to Metadata (#2478)
* Add environment to Metadata

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

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

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

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

* bump yarn.lock

* bump eas-build-job

---------

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

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

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

* improve warn

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

* fix lint

---------

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

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

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

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

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

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

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

* apply suggested change

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

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

* add changelog

* fix changelog

---------

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

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

* Upload fingerprint to google cloud

* Rename fingerprintGCSLocation -> fingerprintGCSBucketKey

* Add fingerprint source in an object

* Update dependencies

* Add comment about fingerprintSources

* Add a changelog entry

* Add support for local build plugin

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

* add changelog entry
2024-06-26 11:18:30 +02:00
Expo Bot 559baf9150 Upgrade eas-build dependencies (#2444) 2024-06-25 14:45:57 +02:00
43 changed files with 7862 additions and 870 deletions

No files matched your search

+43
View File
@@ -12,6 +12,49 @@ This is the log of notable changes to EAS CLI and related packages.
### 🧹 Chores
- Update images list in `eas.schema.json` and warn users when using the deprecated Android images. ([#2450](https://github.com/expo/eas-cli/pull/2450) by [@szdziedzic](https://github.com/szdziedzic))
## [10.2.2](https://github.com/expo/eas-cli/releases/tag/v10.2.2) - 2024-07-31
### 🐛 Bug fixes
- Pass correct path to `vcsClient.isFileIgnoredAsync` check for monorepos to validate that custom build config file is not ignored. ([#2470](https://github.com/expo/eas-cli/pull/2470) by [@szdziedzic](https://github.com/szdziedzic))
### 🧹 Chores
- Bump asset upload timeout from 90 to 180 seconds. ([#2466](https://github.com/expo/eas-cli/pull/2466) by [@quinlanj](https://github.com/quinlanj))
## [10.2.1](https://github.com/expo/eas-cli/releases/tag/v10.2.1) - 2024-07-18
### 🧹 Chores
- Indicate if a user is logged in using `EXPO_TOKEN` when running `eas whoami` command. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
- Throw error when `eas login` command is run with `EXPO_TOKEN` environment variable set. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
- Check if user is already logged in when running `eas login` command. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
## [10.2.0](https://github.com/expo/eas-cli/releases/tag/v10.2.0) - 2024-07-15
### 🎉 New features
- Added flag `--emit-metadata` to emit `eas-update-metadata.json` in the bundle folder with detailed information about the generated updates ([#2451](https://github.com/expo/eas-cli/pull/2451) by [@rainst](https://github.com/rainst))
### 🐛 Bug fixes
- Bump `@expo/apple-utils` version to use the fallback Apple Developer Portal domain on every internal server error. ([#2459](https://github.com/expo/eas-cli/pull/2459) by [@szdziedzic](https://github.com/szdziedzic))
## [10.1.1](https://github.com/expo/eas-cli/releases/tag/v10.1.1) - 2024-07-04
## [10.1.0](https://github.com/expo/eas-cli/releases/tag/v10.1.0) - 2024-07-02
### 🎉 New features
- Save local fingeprint sources during build. ([#2422](https://github.com/expo/eas-cli/pull/2422) by [@kadikraman](https://github.com/kadikraman))
## [10.0.3](https://github.com/expo/eas-cli/releases/tag/v10.0.3) - 2024-06-26
### 🧹 Chores
- Track usage of `--local` build mode in analytics. ([#2445](https://github.com/expo/eas-cli/pull/2445) by [@szdziedzic](https://github.com/szdziedzic))
- Remove any mentions of deleted Xcode < 15 images. ([#2438](https://github.com/expo/eas-cli/pull/2438) by [@szdziedzic](https://github.com/szdziedzic))
## [10.0.2](https://github.com/expo/eas-cli/releases/tag/v10.0.2) - 2024-06-17
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "10.0.2",
"version": "10.2.2",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+65 -63
View File
@@ -39,7 +39,7 @@ An example of **eas.json** that enforces `eas-cli` in version `1.0.0` or newer:
}
```
Learn more: https://docs.expo.dev/build-reference/eas-json/
Learn more: https://docs.expo.dev/build/eas-json/
# Usage
@@ -143,7 +143,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/account/login.ts)_
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -160,7 +160,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -177,7 +177,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/account/view.ts)_
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -191,7 +191,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -243,7 +243,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -264,7 +264,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -284,7 +284,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -304,7 +304,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -327,7 +327,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -364,7 +364,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/index.ts)_
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -383,7 +383,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -400,7 +400,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -419,7 +419,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/delete.ts)_
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:inspect`
@@ -454,7 +454,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -504,7 +504,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/list.ts)_
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -533,7 +533,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/resign.ts)_
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -559,7 +559,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/run.ts)_
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -609,7 +609,7 @@ DESCRIPTION
get the latest version from EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/version/get.ts)_
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -628,7 +628,7 @@ DESCRIPTION
update version of an app
```
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/version/set.ts)_
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -647,7 +647,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -664,7 +664,7 @@ DESCRIPTION
view a build for your project
```
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/build/view.ts)_
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -685,7 +685,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -707,7 +707,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -727,7 +727,7 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -760,7 +760,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally.
```
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/channel/rollout.ts)_
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -783,7 +783,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -804,7 +804,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -821,7 +821,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/credentials/index.ts)_
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -839,7 +839,7 @@ DESCRIPTION
Set up credentials for building your project.
```
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas device:create`
@@ -853,7 +853,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/device/create.ts)_
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -873,7 +873,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -894,7 +894,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/device/list.ts)_
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -915,7 +915,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/device/rename.ts)_
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -929,7 +929,7 @@ DESCRIPTION
view a device for your project
```
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/device/view.ts)_
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -943,7 +943,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -1050,7 +1050,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1067,7 +1067,7 @@ DESCRIPTION
generate the local store configuration from the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1084,7 +1084,7 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1114,7 +1114,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1128,7 +1128,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/project/info.ts)_
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1150,7 +1150,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/project/init.ts)_
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1168,7 +1168,7 @@ ALIASES
$ eas onboarding
```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/project/onboarding.ts)_
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas secret:create`
@@ -1191,7 +1191,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1209,7 +1209,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1223,7 +1223,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1243,7 +1243,7 @@ DESCRIPTION
read environment secrets from env file and store on the server
```
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/secret/push.ts)_
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/push.ts)_
## `eas submit`
@@ -1274,7 +1274,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1283,8 +1283,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] [--emit-metadata] [-p android|ios|all] [--auto]
[--private-key-path <value>] [--json --non-interactive]
FLAGS
-m, --message=<value> A short message describing the update
@@ -1293,6 +1293,8 @@ 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
--emit-metadata Emit "eas-update-metadata.json" in the bundle folder with detailed information about
the generated updates
--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.
@@ -1308,7 +1310,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/index.ts)_
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1326,7 +1328,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1347,7 +1349,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1369,7 +1371,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/list.ts)_
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1397,7 +1399,7 @@ DESCRIPTION
roll back to an existing update
```
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded`
@@ -1425,7 +1427,7 @@ DESCRIPTION
roll back to the embedded update
```
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -1445,7 +1447,7 @@ DESCRIPTION
roll back to an embedded update or an existing update
```
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/rollback.ts)_
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -1465,7 +1467,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/update/view.ts)_
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1486,7 +1488,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1506,7 +1508,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1524,7 +1526,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1546,7 +1548,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1563,7 +1565,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v10.0.2/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+4951 -281
View File
File diff suppressed because it is too large. Load diff
+6 -6
View File
@@ -1,20 +1,20 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "10.0.2",
"version": "10.2.2",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "1.7.0",
"@expo/apple-utils": "1.7.1",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "8.5.4",
"@expo/config-plugins": "7.8.4",
"@expo/config-types": "50.0.0",
"@expo/eas-build-job": "1.0.117",
"@expo/eas-json": "10.0.0",
"@expo/eas-build-job": "1.0.130",
"@expo/eas-json": "10.2.2",
"@expo/json-file": "8.2.37",
"@expo/logger": "1.0.117",
"@expo/multipart-body-parser": "1.1.0",
@@ -28,7 +28,7 @@
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.0",
"@expo/steps": "1.0.118",
"@expo/steps": "1.0.129",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "^1.26.2",
"@oclif/plugin-autocomplete": "^2.3.10",
@@ -108,7 +108,7 @@
"@types/tough-cookie": "4.0.2",
"@types/uuid": "9.0.7",
"@types/wrap-ansi": "3.0.0",
"axios": "0.27.2",
"axios": "0.28.1",
"eslint-plugin-graphql": "4.0.0",
"express": "4.19.2",
"memfs": "3.4.13",
+81 -1
View File
@@ -2,6 +2,7 @@ import {
ArchiveSource,
ArchiveSourceType,
BuildJob,
FingerprintSourceType,
Metadata,
Platform,
} from '@expo/eas-build-job';
@@ -12,6 +13,8 @@ import cliProgress from 'cli-progress';
import fs from 'fs-extra';
import { GraphQLError } from 'graphql/error';
import nullthrows from 'nullthrows';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { BuildContext } from './context';
import {
@@ -60,9 +63,11 @@ import {
appPlatformEmojis,
requestedPlatformDisplayNames,
} from '../platform';
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
import { uploadFileAtPathToGCSAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { printJsonOnlyOutput } from '../utils/json';
import { getTmpDirectory } from '../utils/paths';
import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise';
@@ -162,7 +167,8 @@ export async function prepareBuildRequestForPlatformAsync<
}
assert(projectArchive);
const metadata = await collectMetadataAsync(ctx);
const runtimeMetadata = await createAndMaybeUploadFingerprintAsync(ctx);
const metadata = await collectMetadataAsync(ctx, runtimeMetadata);
const buildParams = resolveBuildParamsInput(ctx, metadata);
const job = await builder.prepareJobAsync(ctx, {
projectArchive,
@@ -649,3 +655,77 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
getExpoWebsiteBaseUrl()
).toString();
}
async function createAndMaybeUploadFingerprintAsync<T extends Platform>(
ctx: BuildContext<T>
): Promise<{
runtimeVersion?: string;
fingerprintSource?: Metadata['fingerprintSource'];
}> {
const resolvedRuntimeVersion = await resolveRuntimeVersionAsync({
exp: ctx.exp,
platform: ctx.platform,
workflow: ctx.workflow,
projectDir: ctx.projectDir,
env: ctx.buildProfile.env,
cwd: ctx.projectDir,
});
/**
* It's ok for fingerprintSources to be empty
* fingerprintSources only exist if the project is using runtimeVersion.policy: fingerprint
*/
if (!resolvedRuntimeVersion?.fingerprintSources) {
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
};
}
await fs.mkdirp(getTmpDirectory());
const fingerprintLocation = path.join(getTmpDirectory(), `${uuidv4()}-runtime-fingerprint.json`);
await fs.writeJSON(fingerprintLocation, {
hash: resolvedRuntimeVersion.runtimeVersion,
sources: resolvedRuntimeVersion.fingerprintSources,
});
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
fingerprintSource: {
type: FingerprintSourceType.PATH,
path: fingerprintLocation,
},
};
}
let fingerprintGCSBucketKey = undefined;
try {
fingerprintGCSBucketKey = await uploadFileAtPathToGCSAsync(
ctx.graphqlClient,
UploadSessionType.EasUpdateFingerprint,
fingerprintLocation
);
} catch (err: any) {
let errMessage = 'Failed to upload fingerprint to EAS';
if (err.message) {
errMessage += `\n\nReason: ${err.message}`;
}
Log.warn(errMessage);
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
};
} finally {
await fs.remove(fingerprintLocation);
}
return {
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
fingerprintSource: {
type: FingerprintSourceType.GCS,
bucketKey: fingerprintGCSBucketKey,
},
};
}
+2 -1
View File
@@ -10,7 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
import { createAndroidContextAsync } from './android/build';
import { BuildContext, CommonContext } from './context';
import { createIosContextAsync } from './ios/build';
import { LocalBuildOptions } from './local';
import { LocalBuildMode, LocalBuildOptions } from './local';
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/AnalyticsManager';
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
@@ -108,6 +108,7 @@ export async function createBuildContextAsync<T extends Platform>({
...devClientProperties,
no_wait: noWait,
run_from_ci: runFromCI,
local: localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN,
};
analytics.logEvent(BuildEvent.BUILD_COMMAND, analyticsEventProperties);
+1
View File
@@ -42,6 +42,7 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
return {
...metadata,
fingerprintSource: metadata.fingerprintSource as BuildMetadataInput['fingerprintSource'],
credentialsSource:
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
distribution: metadata.distribution && transformDistribution(metadata.distribution),
+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.118';
const PLUGIN_PACKAGE_VERSION = '1.0.129';
export enum LocalBuildMode {
/**
+9 -12
View File
@@ -1,4 +1,4 @@
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { FingerprintSource, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
import { IosEnterpriseProvisioning } from '@expo/eas-json';
import fs from 'fs-extra';
import resolveFrom from 'resolve-from';
@@ -10,13 +10,16 @@ import { LocalBuildMode } from './local';
import { BuildDistributionType } from './types';
import Log from '../log';
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
import { readChannelSafelyAsync as readAndroidChannelSafelyAsync } from '../update/android/UpdatesModule';
import { readChannelSafelyAsync as readIosChannelSafelyAsync } from '../update/ios/UpdatesModule';
import { easCliVersion } from '../utils/easCli';
export async function collectMetadataAsync<T extends Platform>(
ctx: BuildContext<T>
ctx: BuildContext<T>,
runtimeMetadata: {
runtimeVersion?: string | undefined;
fingerprintSource?: FingerprintSource | undefined;
}
): Promise<Metadata> {
const channelObject = await resolveChannelAsync(ctx);
const distribution = ctx.buildProfile.distribution ?? BuildDistributionType.STORE;
@@ -27,15 +30,8 @@ export async function collectMetadataAsync<T extends Platform>(
workflow: ctx.workflow,
credentialsSource: ctx.buildProfile.credentialsSource,
sdkVersion: ctx.exp.sdkVersion,
runtimeVersion:
(await resolveRuntimeVersionAsync({
exp: ctx.exp,
platform: ctx.platform,
workflow: ctx.workflow,
projectDir: ctx.projectDir,
env: ctx.buildProfile.env,
cwd: ctx.projectDir,
})) ?? undefined,
runtimeVersion: runtimeMetadata?.runtimeVersion,
fingerprintSource: runtimeMetadata?.fingerprintSource,
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
...channelObject,
distribution,
@@ -64,6 +60,7 @@ export async function collectMetadataAsync<T extends Platform>(
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
selectedImage: ctx.buildProfile.image,
customNodeVersion: ctx.buildProfile.node,
environment: ctx.buildProfile.environment,
simulator: 'simulator' in ctx.buildProfile && ctx.buildProfile.simulator,
};
return sanitizeMetadata(metadata);
@@ -142,6 +142,24 @@ export async function runBuildAndSubmitAsync(
Log.warn(
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${buildProfile.profile.image}". This tag is deprecated and will be removed in the future. Use other images or tags listed here: https://docs.expo.dev/build-reference/infrastructure/`
);
} else if (
buildProfile.profile.image &&
[
'ubuntu-20.04-jdk-11-ndk-r19c',
'ubuntu-20.04-jdk-8-ndk-r19c',
'ubuntu-20.04-jdk-11-ndk-r21e',
'ubuntu-20.04-jdk-8-ndk-r21e',
'ubuntu-22.04-jdk-8-ndk-r21e',
'ubuntu-20.04-jdk-11-ndk-r23b',
].includes(buildProfile.profile.image)
) {
Log.warn(
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${
buildProfile.profile.image
}". This image is deprecated and will be removed on September 1st, 2024. ${learnMore(
'https://expo.dev/changelog/2024/07-12-eas-build-upcoming-android-images-updates'
)}`
);
}
}
@@ -100,6 +100,7 @@ export type LocalFile = {
};
export async function makeProjectMetadataFileAsync(archivePath: string): Promise<LocalFile> {
await fs.mkdirp(getTmpDirectory());
const metadataLocation = path.join(getTmpDirectory(), `${uuidv4()}-eas-build-metadata.json`);
const archiveContent: string[] = [];
-28
View File
@@ -50,31 +50,3 @@ export function getUpdateGroupUrl(
getExpoWebsiteBaseUrl()
).toString();
}
export function getUpdatePreviewUrl({
message,
updateRuntimeVersion,
createdAt,
slug,
projectId,
group,
}: {
message?: string;
updateRuntimeVersion: string;
createdAt: string;
slug: string;
projectId: string;
group: string;
}): string {
const previewUrl = new URL(`/preview/update`, getExpoWebsiteBaseUrl());
if (message) {
//previewUrl.searchParams.append('message', message);
}
previewUrl.searchParams.append('updateRuntimeVersion', updateRuntimeVersion);
//previewUrl.searchParams.append('createdAt', createdAt);
previewUrl.searchParams.append('slug', slug);
previewUrl.searchParams.append('projectId', projectId);
previewUrl.searchParams.append('group', group);
return previewUrl.toString();
}
+98
View File
@@ -13,6 +13,14 @@ import { withErrorHandlingAsync } from '../graphql/client';
import {
CreateUpdateChannelOnAppMutation,
CreateUpdateChannelOnAppMutationVariables,
CreateUpdateChannelOnAppWithNoBranchMutation,
CreateUpdateChannelOnAppWithNoBranchMutationVariables,
DeleteUpdateChannelMutation,
DeleteUpdateChannelMutationVariables,
DeleteUpdateChannelResult,
UpdateChannelBasicInfoFragment,
UpdateChannelBranchMappingMutation,
UpdateChannelBranchMappingMutationVariables,
ViewBranchesOnUpdateChannelQueryVariables,
ViewUpdateChannelsOnAppQueryVariables,
} from '../graphql/generated';
@@ -209,6 +217,42 @@ function renderChannelHeaderContent({
Log.log(chalk`{bold Branches pointed at this channel and their most recent update group:}`);
}
export async function createChannelOnAppWithNoBranchAsync(
graphqlClient: ExpoGraphqlClient,
{
appId,
channelName,
}: {
appId: string;
channelName: string;
}
): Promise<CreateUpdateChannelOnAppWithNoBranchMutation> {
return await withErrorHandlingAsync(
graphqlClient
.mutation<
CreateUpdateChannelOnAppWithNoBranchMutation,
CreateUpdateChannelOnAppWithNoBranchMutationVariables
>(
gql`
mutation CreateUpdateChannelOnAppWithNoBranch($appId: ID!, $name: String!) {
updateChannel {
createUpdateChannelForApp(appId: $appId, name: $name) {
id
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{
appId,
name: channelName,
}
)
.toPromise()
);
}
export async function createChannelOnAppAsync(
graphqlClient: ExpoGraphqlClient,
{
@@ -367,3 +411,57 @@ export async function createAndLinkChannelAsync(
}
return newChannel;
}
export async function updateChannelBranchMappingAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId, branchMapping }: UpdateChannelBranchMappingMutationVariables
): Promise<UpdateChannelBasicInfoFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<UpdateChannelBranchMappingMutation, UpdateChannelBranchMappingMutationVariables>(
gql`
mutation UpdateChannelBranchMapping($channelId: ID!, $branchMapping: String!) {
updateChannel {
editUpdateChannel(channelId: $channelId, branchMapping: $branchMapping) {
id
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{ channelId, branchMapping }
)
.toPromise()
);
const channel = data.updateChannel.editUpdateChannel;
if (!channel) {
throw new Error(`Could not find a channel with id: ${channelId}`);
}
return channel;
}
export async function deleteChannelOnAppAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId }: DeleteUpdateChannelMutationVariables
): Promise<DeleteUpdateChannelResult> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteUpdateChannelMutation, DeleteUpdateChannelMutationVariables>(
gql`
mutation DeleteUpdateChannel($channelId: ID!) {
updateChannel {
deleteUpdateChannel(channelId: $channelId) {
id
}
}
}
`,
{
channelId,
}
)
.toPromise()
);
return data.updateChannel.deleteUpdateChannel;
}
+27 -2
View File
@@ -1,7 +1,10 @@
import { Flags } from '@oclif/core';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import Log from '../../log';
import { confirmAsync } from '../../prompts';
import { getActorDisplayName } from '../../user/User';
export default class AccountLogin extends EasCommand {
static override description = 'log in with your Expo account';
@@ -17,6 +20,7 @@ export default class AccountLogin extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.MaybeLoggedIn,
...this.ContextOptions.SessionManagment,
};
@@ -25,7 +29,28 @@ export default class AccountLogin extends EasCommand {
flags: { sso },
} = await this.parse(AccountLogin);
const { sessionManager } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
const {
sessionManager,
maybeLoggedIn: { actor },
} = await this.getContextAsync(AccountLogin, { nonInteractive: false });
if (sessionManager.getAccessToken()) {
throw new Error(
'EXPO_TOKEN is set in your environment, and is being used for all EAS authentication. To use username/password authentication, unset EXPO_TOKEN in your environment and re-run the command.'
);
}
if (actor) {
Log.warn(`You are already logged in as ${chalk.bold(getActorDisplayName(actor))}.`);
const shouldContinue = await confirmAsync({
message: 'Do you want to continue?',
});
if (!shouldContinue) {
Errors.error('Aborted', { exit: 1 });
}
}
await sessionManager.showLoginPromptAsync({ sso });
Log.log('Logged in');
}
@@ -12,14 +12,19 @@ export default class AccountView extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.MaybeLoggedIn,
...this.ContextOptions.SessionManagment,
};
async runAsync(): Promise<void> {
const {
maybeLoggedIn: { actor },
sessionManager,
} = await this.getContextAsync(AccountView, { nonInteractive: true });
if (actor) {
Log.log(chalk.green(getActorDisplayName(actor)));
const loggedInAs = sessionManager.getAccessToken()
? `${getActorDisplayName(actor)} (authenticated using EXPO_TOKEN)`
: getActorDisplayName(actor);
Log.log(chalk.green(loggedInAs));
// personal account is included, only show if more accounts that personal account
// but do show personal account in list if there are more
@@ -1,15 +1,6 @@
import gql from 'graphql-tag';
import { selectChannelOnAppAsync } from '../../channel/queries';
import { deleteChannelOnAppAsync, selectChannelOnAppAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { withErrorHandlingAsync } from '../../graphql/client';
import {
DeleteUpdateChannelMutation,
DeleteUpdateChannelMutationVariables,
DeleteUpdateChannelResult,
} from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import { toggleConfirmAsync } from '../../prompts';
@@ -95,32 +86,7 @@ export default class ChannelDelete extends EasCommand {
if (jsonFlag) {
printJsonOnlyOutput(deletionResult);
} else {
Log.withTick(`️Deleted channel "${channelName}".`);
Log.withTick(`Deleted channel "${channelName}".`);
}
}
}
async function deleteChannelOnAppAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId }: DeleteUpdateChannelMutationVariables
): Promise<DeleteUpdateChannelResult> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<DeleteUpdateChannelMutation, DeleteUpdateChannelMutationVariables>(
gql`
mutation DeleteUpdateChannel($channelId: ID!) {
updateChannel {
deleteUpdateChannel(channelId: $channelId) {
id
}
}
}
`,
{
channelId,
}
)
.toPromise()
);
return data.updateChannel.deleteUpdateChannel;
}
+1 -40
View File
@@ -1,7 +1,5 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { print } from 'graphql';
import gql from 'graphql-tag';
import { selectBranchOnAppAsync } from '../../branch/queries';
import {
@@ -9,52 +7,15 @@ import {
hasEmptyBranchMap,
hasStandardBranchMap,
} from '../../channel/branch-mapping';
import { selectChannelOnAppAsync } from '../../channel/queries';
import { selectChannelOnAppAsync, updateChannelBranchMappingAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { withErrorHandlingAsync } from '../../graphql/client';
import {
UpdateChannelBasicInfoFragment,
UpdateChannelBranchMappingMutation,
UpdateChannelBranchMappingMutationVariables,
} from '../../graphql/generated';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import { UpdateChannelBasicInfoFragmentNode } from '../../graphql/types/UpdateChannelBasicInfo';
import Log from '../../log';
import { isRollout } from '../../rollout/branch-mapping';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export async function updateChannelBranchMappingAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId, branchMapping }: UpdateChannelBranchMappingMutationVariables
): Promise<UpdateChannelBasicInfoFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<UpdateChannelBranchMappingMutation, UpdateChannelBranchMappingMutationVariables>(
gql`
mutation UpdateChannelBranchMapping($channelId: ID!, $branchMapping: String!) {
updateChannel {
editUpdateChannel(channelId: $channelId, branchMapping: $branchMapping) {
id
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{ channelId, branchMapping }
)
.toPromise()
);
const channel = data.updateChannel.editUpdateChannel;
if (!channel) {
throw new Error(`Could not find a channel with id: ${channelId}`);
}
return channel;
}
export default class ChannelEdit extends EasCommand {
static override description = 'point a channel at a new branch';
@@ -0,0 +1,40 @@
This set of commands does its best to map appcenter codepush commands to EAS update. Generally it works by mapping codepush deployments as EAS channels. Each release on a deployment corresponds to a branch with one update group. That release can be rolled out against the existing release on the deployment. Release history doesn't work too well, but that's ok I hope.
deployment add - channel create
deployment clear - channel edit, create empty branch, point to blank branch
deployment history - branch view of current branch associated with the channel
deployment list - channel list
deployment remove - channel delete
deployment rename - not possible, throw error
release-react: creates new branch, rolls the channel out to N % on that branch.
--use-hermes: not supported
--extra-hermes-flag: not supported
--extra-bundler-option: not supported
--target-binary-version -> not supported, inferred runtime version
--output-dir -> same as eas update output dir
--sourcemap-output-dir -> not supported
--sourcemap-output -> not supported
--xcode-target-name -> not supported, use target-binary-version
--build-configuration-name -> not supported, use target-binary-version
--plist-file-prefix -> not supported
--xcode-project-file -> not supported
--plist-file -> not supported
--pod-file -> not supported
--gradle-file -> not supported, use target-binary-version
--entry-file -> not supported, same as eas update
--development -> not supported, think this is same as eas update
--bundle-name -> not supported
--rollout -> set N % to N
--disable-duplicate-release-error -> no idea
--private-key-path -> same as eas update
--mandatory -> not supported for now
--disabled -> set N % to 0
--description -> message
--deployment-name -> channel to put the new branch on
promote: copies latest update on "latest branch" on channel to "latest branch" on other channel. it does this by creating a new branch with the update and pointing the channel to that new branch according to rollout or 100% if no rollout supplied.
rollback: sets the rollout percentage of branch on channel to 0 (i.e. points to prev branch) and republishes the latest update on the prev branch to itself
patch: only update the rollout percentage
@@ -0,0 +1,86 @@
/**
* $ appcenter codepush deployment add --help
Add a new deployment to an app
Usage: appcenter codepush deployment add [-a|--app <arg>] <new-deployment-name>
Options:
new-deployment-name New CodePush deployment name
*/
import chalk from 'chalk';
import { createChannelOnAppWithNoBranchAsync } from '../../../channel/queries';
import EasCommand from '../../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import Log from '../../../log';
import { getDisplayNameForProjectIdAsync } from '../../../project/projectUtils';
import formatFields from '../../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
export default class CodepushDeploymentAdd extends EasCommand {
static override hidden = true;
static override description = 'create a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the deployment to create',
},
];
static override flags = {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args: { name: deploymentName },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(CodepushDeploymentAdd);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushDeploymentAdd, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const createChannelResult = await createChannelOnAppWithNoBranchAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
const newChannel = createChannelResult.updateChannel.createUpdateChannelForApp;
if (jsonFlag) {
printJsonOnlyOutput(newChannel);
} else {
Log.addNewLineIfNone();
Log.withTick(
`Created a new deployment on project ${chalk.bold(
await getDisplayNameForProjectIdAsync(graphqlClient, projectId)
)}`
);
Log.log(
formatFields([
{ label: 'Name', value: newChannel.name },
{ label: 'ID', value: newChannel.id },
])
);
}
}
}
@@ -0,0 +1,93 @@
/**
* $ appcenter codepush deployment clear --help
Clear the release history associated with a deployment
Usage: appcenter codepush deployment clear [-a|--app <arg>] <deployment-name>
Options:
-a|--app <arg> Specify app in the <ownerName>/<appName> format
deployment-name Specifies CodePush deployment name to be cleared
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import chalk from 'chalk';
import { getEmptyBranchMapping } from '../../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../../channel/queries';
import EasCommand from '../../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
import Log from '../../../log';
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
export default class CodepushDeploymentClear extends EasCommand {
static override hidden = true;
static override description = 'clear a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the deployment to clear',
},
];
static override flags = {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args: { name: deploymentName },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(CodepushDeploymentClear);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushDeploymentClear, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const existingChannel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
const channel = await updateChannelBranchMappingAsync(graphqlClient, {
channelId: existingChannel.id,
branchMapping: JSON.stringify(getEmptyBranchMapping()),
});
if (jsonFlag) {
printJsonOnlyOutput(channel);
} else {
Log.withTick(chalk`Deployment {bold ${channel.name}} is now cleared.\n`);
Log.addNewLineIfNone();
Log.log(
chalk`Users with builds on deployment {bold ${channel.name}} will no longer have an available update.`
);
}
}
}
@@ -0,0 +1,86 @@
/**
* $ appcenter codepush deployment history --help
Display the release history for a CodePush deployment
Usage: appcenter codepush deployment history [-a|--app <arg>] <deployment-name>
Options:
-a|--app <arg> Specify app in the <ownerName>/<appName> format
deployment-name Specifies CodePush deployment name to view history
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import chalk from 'chalk';
import EasCommand from '../../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
import Log from '../../../log';
import { printRollout } from '../../../rollout/utils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
export default class CodepushDeploymentHistory extends EasCommand {
static override hidden = true;
static override description = 'get the release history for a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the deployment',
},
];
static override flags = {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args: { name: deploymentName },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(CodepushDeploymentHistory);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushDeploymentHistory, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const existingChannel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
if (jsonFlag) {
printJsonOnlyOutput(existingChannel);
} else {
Log.withTick(chalk`Deployment {bold ${existingChannel.name}} info:\n`);
Log.addNewLineIfNone();
printRollout(existingChannel);
Log.warn('For formatted output, add the --json flag to your command.');
}
}
}
@@ -0,0 +1,69 @@
/**
* $ appcenter codepush deployment list --help
List the deployments associated with an app
Usage: appcenter codepush deployment list [-s|--skipFetchingDeploymentMetrics] [-k|--displayKeys]
[-a|--app <arg>]
Options:
-s|--skipFetchingDeploymentMetrics Specifies whether to fetch deployment metrics
-k|--displayKeys Specifies whether to display the deployment keys
-a|--app <arg> Specify app in the <ownerName>/<appName> format
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import { CHANNELS_LIMIT, listAndRenderChannelsOnAppAsync } from '../../../channel/queries';
import EasCommand from '../../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import {
EasPaginatedQueryFlags,
getLimitFlagWithCustomValues,
getPaginatedQueryOptions,
} from '../../../commandUtils/pagination';
import { enableJsonOutput } from '../../../utils/json';
export default class CodepushDeploymentList extends EasCommand {
static override hidden = true;
static override description = 'list deployments';
static override flags = {
...EasPaginatedQueryFlags,
limit: getLimitFlagWithCustomValues({ defaultTo: 10, limit: CHANNELS_LIMIT }),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(CodepushDeploymentList);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushDeploymentList, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
await listAndRenderChannelsOnAppAsync(graphqlClient, {
projectId,
paginatedQueryOptions,
});
}
}
@@ -0,0 +1,87 @@
/**
* $ appcenter codepush deployment remove --help
Remove CodePush deployment
Usage: appcenter codepush deployment remove [-a|--app <arg>] <deployment-name>
Options:
-a|--app <arg> Specify app in the <ownerName>/<appName> format
deployment-name Specifies CodePush deployment name to be removed
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import { deleteChannelOnAppAsync } from '../../../channel/queries';
import EasCommand from '../../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
import Log from '../../../log';
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
export default class CodepushDeploymentRemove extends EasCommand {
static override hidden = true;
static override description = 'Delete a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the deployment to delete',
},
];
static override flags = {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args: { name: deploymentName },
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(CodepushDeploymentRemove);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushDeploymentRemove, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const { id: channelId, name: channelName } = await ChannelQuery.viewUpdateChannelAsync(
graphqlClient,
{
appId: projectId,
channelName: deploymentName,
}
);
const deletionResult = await deleteChannelOnAppAsync(graphqlClient, {
channelId,
});
if (jsonFlag) {
printJsonOnlyOutput(deletionResult);
} else {
Log.withTick(`Deleted deployment "${channelName}".`);
}
}
}
@@ -0,0 +1,155 @@
/**
* $ appcenter codepush patch --help
Update the metadata for an existing CodePush release
Usage: appcenter codepush patch [-r|--rollout <arg>] [-d|--description <arg>]
[-t|--target-binary-version <arg>] [-x|--disabled] [-m|--mandatory]
[-l|--existing-release-label <arg>] [-a|--app <arg>] <deployment-name>
Options:
-r|--rollout <arg> Specifies percentage of users this release should be
immediately available to. (The specified number must be
an integer between 1 and 100)
-d|--description <arg> Specifies description of the changes made to the app with
this release
-t|--target-binary-version <arg> Specifies binary app version(s) that specifies this
release is targeting for. (The value must be a semver
expression such as 1.1.0, ~1.2.3)
-x|--disabled Specifies whether this release should be immediately
downloadable. (Putting -x flag means disabled)
-m|--mandatory Specifies whether this release should be considered
mandatory. (Putting -m flag means mandatory)
-l|--existing-release-label <arg> Specifies label of one existing release to update.
(Defaults to the latest release within the specified
deployment)
-a|--app <arg> Specify app in the <ownerName>/<appName> format
deployment-name Specifies one existing deployment name.
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import { Flags } from '@oclif/core';
import {
BranchMapping,
getAlwaysTrueBranchMapping,
getBranchMapping,
isAlwaysTrueBranchMapping,
isEmptyBranchMapping,
} from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import {
editRolloutBranchMapping,
getRolloutInfoFromBranchMapping,
isRolloutBranchMapping,
} from '../../rollout/branch-mapping';
import { enableJsonOutput } from '../../utils/json';
export default class CodepushPatch extends EasCommand {
static override hidden = true;
static override description = 'update the metadata for the latest release on a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the codepush deployment to patch the latest release on',
},
];
static override flags = {
rollout: Flags.integer({
char: 'r',
description: 'Percentage of users this release should be available to',
required: false,
}),
disabled: Flags.boolean({
char: 'x',
description: 'Specifies whether this release should be immediately downloadable',
required: false,
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
flags: { rollout, disabled, json: jsonFlag, 'non-interactive': nonInteractive },
args: { name: deploymentName },
} = await this.parse(CodepushPatch);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushPatch, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
// set channel rollout
const rolloutPercent = disabled ? 0 : rollout ?? 100;
const existingBranchMappingString = channel.branchMapping;
let updatedBranchMapping: BranchMapping;
if (!existingBranchMappingString) {
throw new Error(
'No existing rollout on deployment. Publish an release with the `release-react` command.'
);
} else {
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
if (isEmptyBranchMapping(existingBranchMapping)) {
throw new Error(
'No existing rollout on deployment. Publish an release with the `release-react` command.'
);
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
throw new Error(
'No existing rollout on deployment. Publish an release with the `release-react` command.'
);
} else if (isRolloutBranchMapping(existingBranchMapping)) {
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
const existingRolloutRolloutBranchId = rolloutInfo.rolledOutBranchId;
updatedBranchMapping =
rolloutPercent === 100
? getAlwaysTrueBranchMapping(existingRolloutRolloutBranchId)
: editRolloutBranchMapping(existingBranchMapping, rolloutPercent);
} else {
throw new Error('Unrecognized custom deployment structure');
}
}
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
channelId: channel.id,
branchMapping: JSON.stringify(updatedBranchMapping),
});
Log.addNewLineIfNone();
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
}
}
@@ -0,0 +1,250 @@
/**
* $ appcenter codepush promote --help
Create a new release for the destination deployment, which includes the exact code and metadata from the latest release of the source deployment
Usage: appcenter codepush promote -s|--source-deployment-name <arg> -d|--destination-deployment-name <arg>
[-t|--target-binary-version <arg>] [-r|--rollout <arg>] [--disable-duplicate-release-error]
[-x|--disabled] [-m|--mandatory] [-l|--label <arg>] [--description <arg>] [-a|--app <arg>]
Options:
-s|--source-deployment-name <arg> Specifies source deployment name
-d|--destination-deployment-name <arg> Specifies destination deployment name
-t|--target-binary-version <arg> Specifies binary app version(s) that specifies this
release is targeting for. (The value must be a
semver expression such as 1.1.0, ~1.2.3)
-r|--rollout <arg> Specifies percentage of users this release should be
immediately available to. (The specified number must
be an integer between 1 and 100)
--disable-duplicate-release-error Specifies that if the update is identical to the
latest release on the deployment, the CLI should
generate a warning instead of an error
-x|--disabled Specifies whether this release should be immediately
downloadable. (Putting -x flag means disabled)
-m|--mandatory Specifies whether this release should be considered
mandatory. (Putting -m flag means mandatory)
-l|--label <arg> Allows you to pick the specified label from the
source deployment and promote it to the destination
deployment
--description <arg> Specifies description of the changes made to the app
with this release
-a|--app <arg> Specify app in the <ownerName>/<appName> format
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import { Flags } from '@oclif/core';
import nullthrows from 'nullthrows';
import { createUpdateBranchOnAppAsync } from '../../branch/queries';
import {
BranchMapping,
getAlwaysTrueBranchMapping,
getBranchMapping,
isAlwaysTrueBranchMapping,
isEmptyBranchMapping,
} from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import {
createRolloutBranchMapping,
getRolloutInfoFromBranchMapping,
isRolloutBranchMapping,
} from '../../rollout/branch-mapping';
import { republishAsync } from '../../update/republish';
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
import { enableJsonOutput } from '../../utils/json';
export default class CodepushPromote extends EasCommand {
static override hidden = true;
static override description =
'republish latest release from source deployment to destination deployment';
static override flags = {
sourceDeploymentName: Flags.string({
char: 's',
description: 'Source deployment',
required: true,
}),
destinationDeploymentName: Flags.string({
char: 'd',
description: 'Destination deployment',
required: true,
}),
rollout: Flags.integer({
char: 'r',
description: 'Percentage of users this release should be available to',
required: false,
}),
disabled: Flags.boolean({
char: 'x',
description: 'Specifies whether this release should be immediately downloadable',
required: false,
}),
'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. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
required: false,
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
flags: {
sourceDeploymentName,
destinationDeploymentName,
rollout,
disabled,
'private-key-path': privateKeyPath,
json: jsonFlag,
'non-interactive': nonInteractive,
},
} = await this.parse(CodepushPromote);
const {
privateProjectConfig: { projectId, exp },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushPromote, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!sourceDeploymentName) {
throw new Error('Source deployment name may not be empty.');
}
if (!destinationDeploymentName) {
throw new Error('Source deployment name may not be empty.');
}
const [sourceChannel, destinationChannel] = await Promise.all([
ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: sourceDeploymentName,
}),
ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: destinationDeploymentName,
}),
]);
const sourceBranchMappingString = sourceChannel.branchMapping;
let latestBranchIdOnSourceChannel: string;
if (!sourceBranchMappingString) {
throw new Error(
'No releases on source deployment. Publish an release with the `release-react` command.'
);
} else {
const existingBranchMapping = getBranchMapping(sourceBranchMappingString);
if (isEmptyBranchMapping(existingBranchMapping)) {
throw new Error(
'No releases on source deployment. Publish an update with the `release-react` command.'
);
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
latestBranchIdOnSourceChannel = existingBranchMapping.data[0].branchId;
} else if (isRolloutBranchMapping(existingBranchMapping)) {
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
latestBranchIdOnSourceChannel = rolloutInfo.rolledOutBranchId;
} else {
throw new Error('Unrecognized custom deployment structure');
}
}
const latestBranchOnSourceChannel = nullthrows(
sourceChannel.updateBranches.find(b => b.id === latestBranchIdOnSourceChannel)
);
// republish latest updates from source branch to new destination branch
const createdBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
appId: projectId,
name: latestBranchOnSourceChannel.name, // maybe change name
});
const updateGroupToRepublish = latestBranchOnSourceChannel.updateGroups[0];
const codeSigningInfo = await getCodeSigningInfoAsync(exp, privateKeyPath ?? undefined);
const arbitraryUpdate = updateGroupToRepublish[0];
const { message: oldUpdateMessage, group: oldGroupId } = arbitraryUpdate;
const newUpdateMessage = `Republish "${oldUpdateMessage!}" - group: ${oldGroupId}`;
await republishAsync({
graphqlClient,
app: {
projectId,
exp,
},
updatesToPublish: updateGroupToRepublish.map(update => ({
...update,
groupId: update.group,
branchId: update.branch.id,
branchName: update.branch.name,
})),
codeSigningInfo,
targetBranch: { branchId: createdBranch.id, branchName: createdBranch.name },
updateMessage: newUpdateMessage,
});
// set destination channel rollout
const destinationRolloutPercent = disabled ? 0 : rollout ?? 100;
const destinationBranchMappingString = destinationChannel.branchMapping;
let updatedBranchMapping: BranchMapping;
if (!destinationBranchMappingString) {
if (destinationRolloutPercent !== 100) {
throw new Error('Cannot roll out first release on deployment'); // TODO(wschurman): this needs to be possible
}
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else {
const existingBranchMapping = getBranchMapping(destinationBranchMappingString);
if (isEmptyBranchMapping(existingBranchMapping)) {
if (destinationRolloutPercent !== 100) {
throw new Error('Cannot roll out first release on deployment'); // TODO(wschurman): this needs to be possible
}
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
const existingDestinationBranchId = existingBranchMapping.data[0].branchId;
updatedBranchMapping =
destinationRolloutPercent === 100
? getAlwaysTrueBranchMapping(existingDestinationBranchId)
: createRolloutBranchMapping({
defaultBranchId: existingBranchMapping.data[0].branchId,
rolloutBranchId: createdBranch.id,
runtimeVersion: arbitraryUpdate.runtimeVersion,
percent: destinationRolloutPercent,
});
} else if (isRolloutBranchMapping(existingBranchMapping)) {
if (destinationRolloutPercent !== 100) {
throw new Error(
'Cannot roll out first release on deployment that already has a roll out in progress'
); // TODO(wschurman): handle runtime versions (only check for roll out for runtime version)
}
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else {
throw new Error('Unrecognized custom deployment structure');
}
}
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
channelId: destinationChannel.id,
branchMapping: JSON.stringify(updatedBranchMapping),
});
Log.addNewLineIfNone();
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
}
}
@@ -0,0 +1,369 @@
/**
* $ appcenter codepush release-react --help
Release a React Native update to an app deployment
Usage: appcenter codepush release-react [--use-hermes] [--extra-hermes-flag <arg>] [--extra-bundler-option <arg>]
[-t|--target-binary-version <arg>] [-o|--output-dir <arg>] [--sourcemap-output-dir <arg>]
[-s|--sourcemap-output <arg>] [-xt|--xcode-target-name <arg>] [-c|--build-configuration-name <arg>]
[--plist-file-prefix <arg>] [-xp|--xcode-project-file <arg>] [-p|--plist-file <arg>] [--pod-file <arg>]
[-g|--gradle-file <arg>] [-e|--entry-file <arg>] [--development] [-b|--bundle-name <arg>]
[-r|--rollout <arg>] [--disable-duplicate-release-error] [-k|--private-key-path <arg>] [-m|--mandatory]
[-x|--disabled] [--description <arg>] [-d|--deployment-name <arg>] [-a|--app <arg>]
Options:
--use-hermes Enable hermes and bypass automatic checks
--extra-hermes-flag <arg> Flag that gets passed to Hermes, JavaScript to bytecode compiler. Can
be specified multiple times
--extra-bundler-option <arg> Option that gets passed to react-native bundler. Can be specified
multiple times
-t|--target-binary-version <arg> Semver expression that specifies the binary app version(s) this
release is targeting (e.g. 1.1.0, ~1.2.3)
-o|--output-dir <arg> Path to where the bundle should be written. If omitted, the bundle
will not be saved on your machine
--sourcemap-output-dir <arg> Path to folder where the sourcemap for the resulting bundle should be
written. Name of sourcemap file will be generated automatically. This
argument will be ignored if "sourcemap-output" argument is provided.
If omitted, a sourcemap will not be generated
-s|--sourcemap-output <arg> Path to where the sourcemap for the resulting bundle should be
written. If omitted, a sourcemap will not be generated
-xt|--xcode-target-name <arg> Name of target (PBXNativeTarget) which specifies the binary version
you want to target this release at (iOS only)
-c|--build-configuration-name <arg> Name of build configuration which specifies the binary version you
want to target this release at. For example, "Debug" or "Release" (iOS
only)
--plist-file-prefix <arg> Prefix to append to the file name when attempting to find your app's
Info.plist file (iOS only)
-xp|--xcode-project-file <arg> Path to the Xcode project or project.pbxproj file
-p|--plist-file <arg> Path to the plist file which specifies the binary version you want to
target this release at (iOS only)
--pod-file <arg> Path to the cocopods config file (iOS only)
-g|--gradle-file <arg> Path to the gradle file which specifies the binary version you want to
target this release at (android only)
-e|--entry-file <arg> Path to the app's entry JavaScript file. If omitted,
"index.<platform>.js" and then "index.js" will be used (if they exist)
--development Specifies whether to generate a dev or release build
-b|--bundle-name <arg> Name of the generated JS bundle file. If unspecified, the standard
bundle name will be used, depending on the specified platform:
"main.jsbundle" (iOS), "index.android.bundle" (Android) or
"index.windows.bundle" (Windows)
-r|--rollout <arg> Percentage of users this release should be available to
--disable-duplicate-release-error When this flag is set, releasing a package that is identical to the
latest release will produce a warning instead of an error
-k|--private-key-path <arg> Specifies the location of a RSA private key to sign the release
with.NOTICE: use it for react native applications only, client SDK on
other platforms will be ignoring signature verification for now!
-m|--mandatory Specifies whether this release should be considered mandatory
-x|--disabled Specifies whether this release should be immediately downloadable
--description <arg> Description of the changes made to the app in this release
-d|--deployment-name <arg> Deployment to release the update to
-a|--app <arg> Specify app in the <ownerName>/<appName> format
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import { Workflow } from '@expo/eas-build-job';
import { Flags } from '@oclif/core';
import { createUpdateBranchOnAppAsync } from '../../branch/queries';
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
import { getUpdateGroupUrl } from '../../build/utils/url';
import {
BranchMapping,
getAlwaysTrueBranchMapping,
getBranchMapping,
isAlwaysTrueBranchMapping,
isEmptyBranchMapping,
} from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { StatuspageServiceName } from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log, { learnMore, link } from '../../log';
import { RequestedPlatform } from '../../platform';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import {
buildAndUploadAssetsAsync,
getRuntimeVersionObjectAsync,
isUploadedAssetCountAboveWarningThreshold,
publishUpdateGroupsAsync,
} from '../../project/publish';
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
import { createRolloutBranchMapping, isRolloutBranchMapping } from '../../rollout/branch-mapping';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
import uniqBy from '../../utils/expodash/uniqBy';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
export default class CodepushReleaseReact extends EasCommand {
static override hidden = true;
static override description = 'Release an update to a deployment';
static override flags = {
rollout: Flags.integer({
char: 'r',
description: 'Percentage of users this release should be available to',
required: false,
}),
description: Flags.string({
description: 'Description of the changes made to the app in this release',
required: true,
}),
deploymentName: Flags.string({
char: 'd',
description: 'Deployment to release the update to',
required: true,
}),
disabled: Flags.boolean({
char: 'x',
description: 'Specifies whether this release should be immediately downloadable',
required: false,
}),
'clear-cache': Flags.boolean({
description: `Clear the bundler cache before publishing`,
default: false,
}),
'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. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
required: false,
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
const {
flags: {
rollout,
description,
deploymentName,
disabled,
'clear-cache': clearCache,
'private-key-path': privateKeyPath,
json: jsonFlag,
'non-interactive': nonInteractive,
},
} = await this.parse(CodepushReleaseReact);
const {
getDynamicPublicProjectConfigAsync,
getDynamicPrivateProjectConfigAsync,
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(CodepushReleaseReact, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, nonInteractive);
const {
exp: expPossiblyWithoutEasUpdateConfigured,
projectId,
projectDir,
} = await getDynamicPublicProjectConfigAsync();
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
await ensureEASUpdateIsConfiguredAsync({
exp: expPossiblyWithoutEasUpdateConfigured,
platform: RequestedPlatform.All,
projectDir,
projectId,
vcsClient,
env: undefined,
});
const { exp } = await getDynamicPublicProjectConfigAsync();
const { exp: expPrivate } = await getDynamicPrivateProjectConfigAsync();
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
const createdBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
appId: projectId,
name: description, // TODO(wschurman): make this name something else maybe, maybe something with date or something
});
const {
realizedPlatforms,
unsortedUpdateInfoGroups,
uploadedAssetCount,
assetLimitPerUpdateGroup,
} = await buildAndUploadAssetsAsync({
graphqlClient,
projectId,
skipBundler: false,
projectDir,
inputDir: 'dist',
exp,
platformFlag: 'all',
clearCache,
});
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
const runtimeVersions = await getRuntimeVersionObjectAsync({
exp,
platforms: realizedPlatforms,
projectDir,
workflows: {
...workflows,
web: Workflow.UNKNOWN,
},
env: undefined,
});
const runtimeVersionsArr = Array.from(new Set(runtimeVersions.map(r => r.runtimeVersion)));
if (runtimeVersionsArr.length > 1) {
throw new Error(
'Using codepush requires the runtime version to be the same for all platforms'
);
}
const gitCommitHash = await vcsClient.getCommitHashAsync();
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
const { newUpdates } = await publishUpdateGroupsAsync({
graphqlClient,
branchId: createdBranch.id,
runtimeVersions,
gitCommitHash,
isGitWorkingTreeDirty,
unsortedUpdateInfoGroups,
updateMessage: description,
codeSigningInfo,
});
// set channel rollout
const rolloutPercent = disabled ? 0 : rollout ?? 100;
const existingBranchMappingString = channel.branchMapping;
let updatedBranchMapping: BranchMapping;
if (!existingBranchMappingString) {
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else {
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
if (isEmptyBranchMapping(existingBranchMapping)) {
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
updatedBranchMapping =
rolloutPercent === 100
? getAlwaysTrueBranchMapping(createdBranch.id)
: createRolloutBranchMapping({
defaultBranchId: existingBranchMapping.data[0].branchId,
rolloutBranchId: createdBranch.id,
runtimeVersion: runtimeVersionsArr[0],
percent: rolloutPercent,
});
} else if (isRolloutBranchMapping(existingBranchMapping)) {
if (rolloutPercent !== 100) {
throw new Error('Cannot start a rollout on a deployment when one is already in progress');
}
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
} else {
throw new Error('Unrecognized custom deployment structure');
}
}
await updateChannelBranchMappingAsync(graphqlClient, {
channelId: channel.id,
branchMapping: JSON.stringify(updatedBranchMapping),
});
if (jsonFlag) {
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
} else {
if (new Set(newUpdates.map(update => update.group)).size > 1) {
Log.addNewLineIfNone();
Log.log(
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
);
}
Log.addNewLineIfNone();
for (const runtime of uniqBy(runtimeVersions, version => version.runtimeVersion)) {
const newUpdatesForRuntimeVersion = newUpdates.filter(
update => update.runtimeVersion === runtime.runtimeVersion
);
if (newUpdatesForRuntimeVersion.length === 0) {
throw new Error(
`Publish response is missing updates with runtime ${runtime.runtimeVersion}.`
);
}
const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
const newAndroidUpdate = newUpdatesForRuntimeVersion.find(
update => update.platform === 'android'
);
const newIosUpdate = newUpdatesForRuntimeVersion.find(update => update.platform === 'ios');
const updateGroupId = newUpdatesForRuntimeVersion[0].group;
const projectName = exp.slug;
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
const updateGroupUrl = getUpdateGroupUrl(accountName, projectName, updateGroupId);
const updateGroupLink = link(updateGroupUrl, { dim: false });
Log.log(
formatFields([
{ label: 'Branch', value: createdBranch.name },
{ label: 'Runtime version', value: runtime.runtimeVersion },
{ label: 'Platform', value: platforms.join(', ') },
{ label: 'Update group ID', value: updateGroupId },
...(newAndroidUpdate
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
: []),
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
{ label: 'Message', value: description ?? '' },
...(gitCommitHash
? [
{
label: 'Commit',
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
},
]
: []),
{ label: 'EAS Dashboard', value: updateGroupLink },
])
);
Log.addNewLineIfNone();
if (
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
) {
Log.warn(
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
learnMoreMessage: 'Consider optimizing your usage of assets',
dim: false,
})}.`
);
Log.addNewLineIfNone();
}
}
}
}
}
@@ -0,0 +1,116 @@
/**
* $ appcenter codepush rollback --help
Rollback a deployment to a previous release
Usage: appcenter codepush rollback [--target-release <arg>] [-a|--app <arg>] <deployment-name>
Options:
--target-release <arg> Specifies the release label to be rolled back
-a|--app <arg> Specify app in the <ownerName>/<appName> format
deployment-name Specifies deployment name to be rolled back
Common Options (works on all commands):
--disable-telemetry Disable telemetry for this command
-v|--version Display appcenter version
--quiet Auto-confirm any prompts without waiting for input
-h|--help Display help for current command
--env <arg> Environment when using API token
--token <arg> API token
--output <arg> Output format: json
--debug Display extra output for debugging
*/
import {
BranchMapping,
getAlwaysTrueBranchMapping,
getBranchMapping,
isAlwaysTrueBranchMapping,
isEmptyBranchMapping,
} from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import Log from '../../log';
import {
getRolloutInfoFromBranchMapping,
isRolloutBranchMapping,
} from '../../rollout/branch-mapping';
import { enableJsonOutput } from '../../utils/json';
export default class CodepushRollback extends EasCommand {
static override hidden = true;
static override description = 'roll back a rollout on a deployment';
static override args = [
{
name: 'name',
required: true,
description: 'Name of the codepush deployment to roll back the current rollout on',
},
];
static override flags = {
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
args: { name: deploymentName },
} = await this.parse(CodepushRollback);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(CodepushRollback, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
if (!deploymentName) {
throw new Error('Deployment name may not be empty.');
}
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: deploymentName,
});
// set channel rollout
const existingBranchMappingString = channel.branchMapping;
let updatedBranchMapping: BranchMapping;
if (!existingBranchMappingString) {
throw new Error('No existing rollout on deployment.');
} else {
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
if (isEmptyBranchMapping(existingBranchMapping)) {
throw new Error('No existing rollout on deployment.');
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
throw new Error('No existing rollout on deployment.');
} else if (isRolloutBranchMapping(existingBranchMapping)) {
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
const existingRolloutDefaultBranchId = rolloutInfo.defaultBranchId;
updatedBranchMapping = getAlwaysTrueBranchMapping(existingRolloutDefaultBranchId);
} else {
throw new Error('Unrecognized custom deployment structure');
}
}
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
channelId: channel.id,
branchMapping: JSON.stringify(updatedBranchMapping),
});
Log.addNewLineIfNone();
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
}
}
+58 -261
View File
@@ -1,57 +1,33 @@
import { Platform as PublishPlatform } from '@expo/config';
import { Workflow } from '@expo/eas-build-job';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import indentString from 'indent-string';
import nullthrows from 'nullthrows';
import qrcodeTerminal from 'qrcode-terminal';
import { ensureBranchExistsAsync } from '../../branch/queries';
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
import { getUpdateGroupUrl, getUpdatePreviewUrl } from '../../build/utils/url';
import { getUpdateGroupUrl } from '../../build/utils/url';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import fetch from '../../fetch';
import {
PublishUpdateGroupInput,
StatuspageServiceName,
UpdateInfoGroup,
UpdatePublishMutation,
} from '../../graphql/generated';
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import { StatuspageServiceName } from '../../graphql/generated';
import Log, { learnMore, link } from '../../log';
import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import {
ExpoCLIExportPlatformFlag,
RawAsset,
buildBundlesAsync,
buildUnsortedUpdateInfoGroupAsync,
collectAssetsAsync,
buildAndUploadAssetsAsync,
defaultPublishPlatforms,
filterExportedPlatformsByFlag,
generateEasMetadataAsync,
getBranchNameForCommandAsync,
getRequestedPlatform,
getRuntimeToPlatformMappingFromRuntimeVersions,
getRuntimeVersionObjectAsync,
getUpdateMessageForCommandAsync,
isUploadedAssetCountAboveWarningThreshold,
platformDisplayNames,
resolveInputDirectoryAsync,
uploadAssetsAsync,
publishUpdateGroupsAsync,
} from '../../project/publish';
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
import {
checkManifestBodyAgainstUpdateInfoGroup,
getCodeSigningInfoAsync,
getManifestBodyAsync,
signBody,
} from '../../utils/code-signing';
import areSetsEqual from '../../utils/expodash/areSetsEqual';
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
import uniqBy from '../../utils/expodash/uniqBy';
import formatFields from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -68,6 +44,7 @@ type RawUpdateFlags = {
'clear-cache': boolean;
'private-key-path'?: string;
'non-interactive': boolean;
'emit-metadata': boolean;
json: boolean;
/** @deprecated see UpdateRepublish command */
group?: string;
@@ -87,6 +64,7 @@ type UpdateFlags = {
privateKeyPath?: string;
json: boolean;
nonInteractive: boolean;
emitMetadata: boolean;
};
export default class UpdatePublish extends EasCommand {
@@ -127,6 +105,10 @@ export default class UpdatePublish extends EasCommand {
description: `Clear the bundler cache before publishing`,
default: false,
}),
'emit-metadata': Flags.boolean({
description: `Emit "eas-update-metadata.json" in the bundle folder with detailed information about the generated updates`,
default: false,
}),
platform: Flags.enum({
char: 'p',
options: [
@@ -170,6 +152,7 @@ export default class UpdatePublish extends EasCommand {
json: jsonFlag,
nonInteractive,
branchName: branchNameArg,
emitMetadata,
} = this.sanitizeFlags(rawFlags);
const {
@@ -227,141 +210,27 @@ export default class UpdatePublish extends EasCommand {
jsonFlag,
});
// build bundle and upload assets for a new publish
if (!skipBundler) {
const bundleSpinner = ora().start('Exporting...');
try {
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag, clearCache });
bundleSpinner.succeed('Exported bundle(s)');
} catch (e) {
bundleSpinner.fail('Export failed');
throw e;
}
}
const {
distRoot,
realizedPlatforms,
unsortedUpdateInfoGroups,
uploadedAssetCount,
assetLimitPerUpdateGroup,
} = await buildAndUploadAssetsAsync({
graphqlClient,
projectId,
skipBundler,
projectDir,
inputDir,
exp,
platformFlag,
clearCache,
});
// After possibly bundling, assert that the input directory can be found.
const distRoot = await resolveInputDirectoryAsync(inputDir, { skipBundler });
const assetSpinner = ora().start('Uploading...');
let unsortedUpdateInfoGroups: UpdateInfoGroup = {};
let uploadedAssetCount = 0;
let assetLimitPerUpdateGroup = 0;
let realizedPlatforms: PublishPlatform[] = [];
try {
const collectedAssets = await collectAssetsAsync(distRoot);
const assets = filterExportedPlatformsByFlag(collectedAssets, platformFlag);
realizedPlatforms = Object.keys(assets) as PublishPlatform[];
// Timeout mechanism:
// - 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() + NO_ACTIVITY_TIMEOUT;
const cancelationToken = { isCanceledOrFinished: false };
const uploadResults = await Promise.race([
uploadAssetsAsync(
graphqlClient,
assets,
projectId,
cancelationToken,
assetUploadResults => {
const currentUploadedStorageKeys = new Set(
assetUploadResults.filter(r => r.finished).map(r => r.asset.storageKey)
);
if (!areSetsEqual(currentUploadedStorageKeys, lastUploadedStorageKeys)) {
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
lastUploadedStorageKeys = currentUploadedStorageKeys;
lastAssetUploadResults = assetUploadResults;
}
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 () => {
while (Date.now() < timeAtWhichToTimeout) {
if (cancelationToken.isCanceledOrFinished) {
break;
}
await new Promise(res => setTimeout(res, 1000)); // wait 1 second
}
cancelationToken.isCanceledOrFinished = true;
const timedOutAssets = lastAssetUploadResults
.filter(r => !r.finished)
.map(r => `\n- ${r.asset.originalPath ?? r.asset.path}`);
throw new Error(`Asset processing timed out for assets: ${timedOutAssets}`);
})(),
]);
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
// NOTE(cedric): we assume that bundles are always uploaded, and always are part of
// `uploadedAssetCount`, perferably we don't assume. For that, we need to refactor the
// `uploadAssetsAsync` and be able to determine asset type from the uploaded assets.
const uploadedBundleCount = uploadResults.launchAssetCount;
const uploadedNormalAssetCount = Math.max(0, uploadedAssetCount - uploadedBundleCount);
const reusedNormalAssetCount = uploadResults.uniqueAssetCount - uploadedNormalAssetCount;
assetSpinner.stop();
Log.withTick(
`Uploaded ${uploadedBundleCount} app ${uploadedBundleCount === 1 ? 'bundle' : 'bundles'}`
);
if (uploadedNormalAssetCount === 0) {
Log.withTick(`Uploading assets skipped - no new assets found`);
} else {
let message = `Uploaded ${uploadedNormalAssetCount} ${
uploadedNormalAssetCount === 1 ? 'asset' : 'assets'
}`;
if (reusedNormalAssetCount > 0) {
message += ` (reused ${reusedNormalAssetCount} ${
reusedNormalAssetCount === 1 ? 'asset' : 'assets'
})`;
}
Log.withTick(message);
}
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
}
const platformString = (Object.keys(assets) as PublishPlatform[])
.map(platform => {
const collectedAssetForPlatform = nullthrows(assets[platform]);
const totalAssetsForPlatform = collectedAssetForPlatform.assets.length + 1; // launch asset
const assetString = totalAssetsForPlatform === 1 ? 'asset' : 'assets';
return `${totalAssetsForPlatform} ${platformDisplayNames[platform]} ${assetString}`;
})
.join(', ');
Log.withInfo(
`${platformString} (maximum: ${assetLimitPerUpdateGroup} total per update). ${learnMore(
'https://expo.fyi/eas-update-asset-limits',
{ learnMoreMessage: 'Learn more about asset limits' }
)}`
);
} catch (e) {
assetSpinner.fail('Failed to upload');
throw e;
}
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
appId: projectId,
branchName,
});
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
const runtimeVersions = await getRuntimeVersionObjectAsync({
@@ -374,96 +243,25 @@ export default class UpdatePublish extends EasCommand {
},
env: undefined,
});
const runtimeToPlatformMapping =
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
appId: projectId,
branchName,
});
const gitCommitHash = await vcsClient.getCommitHashAsync();
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
// Sort the updates into different groups based on their platform specific runtime versions
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformMapping.map(
({ runtimeVersion, platforms }) => {
const localUpdateInfoGroup = Object.fromEntries(
platforms.map(platform => [
platform,
unsortedUpdateInfoGroups[platform as keyof UpdateInfoGroup],
])
);
const { newUpdates } = await publishUpdateGroupsAsync({
graphqlClient,
branchId,
runtimeVersions,
gitCommitHash,
isGitWorkingTreeDirty,
unsortedUpdateInfoGroups,
updateMessage,
codeSigningInfo,
});
return {
branchId,
updateInfoGroup: localUpdateInfoGroup,
runtimeVersion,
message: updateMessage,
gitCommitHash,
isGitWorkingTreeDirty,
awaitingCodeSigningInfo: !!codeSigningInfo,
};
}
);
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
const publishSpinner = ora('Publishing...').start();
try {
newUpdates = await PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
if (codeSigningInfo) {
Log.log('🔒 Signing updates');
const updatesTemp = [...newUpdates];
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
const newUpdates = updatesTemp.splice(
0,
Object.keys(nullthrows(updateGroup.updateInfoGroup)).length
);
return {
updateGroup,
newUpdates,
};
});
await Promise.all(
updateGroupsAndTheirUpdates.map(async ({ updateGroup, newUpdates }) => {
await Promise.all(
newUpdates.map(async newUpdate => {
const response = await fetch(newUpdate.manifestPermalink, {
method: 'GET',
headers: { accept: 'multipart/mixed' },
});
const manifestBody = nullthrows(await getManifestBodyAsync(response));
checkManifestBodyAgainstUpdateInfoGroup(
manifestBody,
nullthrows(
nullthrows(updateGroup.updateInfoGroup)[
newUpdate.platform as keyof UpdateInfoGroup
]
)
);
const manifestSignature = signBody(manifestBody, codeSigningInfo);
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
alg: codeSigningInfo.codeSigningMetadata.alg,
keyid: codeSigningInfo.codeSigningMetadata.keyid,
sig: manifestSignature,
});
})
);
})
);
}
publishSpinner.succeed('Published!');
} catch (e) {
publishSpinner.fail('Failed to publish updates');
throw e;
if (!skipBundler && emitMetadata) {
Log.log('Generating eas-update-metadata.json');
await generateEasMetadataAsync(distRoot, getUpdateJsonInfosForUpdates(newUpdates));
}
if (jsonFlag) {
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
} else {
@@ -496,15 +294,6 @@ export default class UpdatePublish extends EasCommand {
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
const updateGroupUrl = getUpdateGroupUrl(accountName, projectName, updateGroupId);
const updateGroupLink = link(updateGroupUrl, { dim: false });
const updateCreatedAt = (newAndroidUpdate ?? newIosUpdate)?.createdAt;
const updateQrCodeUrl = getUpdatePreviewUrl({
message: updateMessage,
updateRuntimeVersion: runtime.runtimeVersion,
createdAt: updateCreatedAt.toString(),
slug: projectName, // todo check this
projectId,
group: updateGroupId,
});
Log.log(
formatFields([
@@ -528,9 +317,6 @@ export default class UpdatePublish extends EasCommand {
{ label: 'EAS Dashboard', value: updateGroupLink },
])
);
qrcodeTerminal.generate(updateQrCodeUrl, { small: true }, code =>
Log.log(`${indentString(code, 2)}\n`)
);
Log.addNewLineIfNone();
if (
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
@@ -576,17 +362,28 @@ export default class UpdatePublish extends EasCommand {
Errors.error('--group and --republish flags are deprecated', { exit: 1 });
}
const skipBundler = flags['skip-bundler'] ?? false;
let emitMetadata = flags['emit-metadata'] ?? false;
if (skipBundler && emitMetadata) {
emitMetadata = false;
Log.warn(
'ignoring flag --emit-metadata as metadata cannot be generated when skipping bundle generation'
);
}
return {
auto,
branchName,
channelName,
updateMessage,
inputDir: flags['input-dir'],
skipBundler: flags['skip-bundler'],
skipBundler,
clearCache: flags['clear-cache'],
platform: flags.platform as RequestedPlatform,
privateKeyPath: flags['private-key-path'],
nonInteractive,
emitMetadata,
json: flags.json ?? false,
};
}
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
import { Platform } from '@expo/eas-build-job';
import { BuildProfile } from '@expo/eas-json';
import { errors, readAndValidateBuildConfigAsync } from '@expo/steps';
import { errors, readAndValidateBuildConfigFromPathAsync } from '@expo/steps';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
@@ -31,7 +31,9 @@ export async function validateCustomBuildConfigAsync({
`Custom build configuration file ${chalk.bold(relativeConfigPath)} does not exist.`
);
}
if (await vcsClient.isFileIgnoredAsync(relativeConfigPath)) {
const rootDir = path.normalize(await vcsClient.getRootPathAsync());
if (await vcsClient.isFileIgnoredAsync(path.relative(rootDir, configPath))) {
throw new Error(
`Custom build configuration file ${chalk.bold(
relativeConfigPath
@@ -40,7 +42,7 @@ export async function validateCustomBuildConfigAsync({
}
try {
const config = await readAndValidateBuildConfigAsync(configPath, {
const config = await readAndValidateBuildConfigFromPathAsync(configPath, {
skipNamespacedFunctionsOrFunctionGroupsCheck: true,
});
return {
+298 -2
View File
@@ -17,15 +17,32 @@ import { selectBranchOnAppAsync } from '../branch/queries';
import { getDefaultBranchNameAsync } from '../branch/utils';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { AssetMetadataStatus, PartialManifestAsset } from '../graphql/generated';
import fetch from '../fetch';
import {
AssetMetadataStatus,
PartialManifestAsset,
PublishUpdateGroupInput,
UpdatePublishMutation,
} from '../graphql/generated';
import { PublishMutation } from '../graphql/mutations/PublishMutation';
import { PublishQuery } from '../graphql/queries/PublishQuery';
import Log, { learnMore } from '../log';
import { ora } from '../ora';
import { RequestedPlatform, requestedPlatformDisplayNames } from '../platform';
import { promptAsync } from '../prompts';
import { getBranchNameFromChannelNameAsync } from '../update/getBranchNameFromChannelNameAsync';
import { formatUpdateMessage, truncateString as truncateUpdateMessage } from '../update/utils';
import {
UpdateJsonInfo,
formatUpdateMessage,
truncateString as truncateUpdateMessage,
} from '../update/utils';
import { PresignedPost, uploadWithPresignedPostWithRetryAsync } from '../uploads';
import {
CodeSigningInfo,
checkManifestBodyAgainstUpdateInfoGroup,
getManifestBodyAsync,
signBody,
} from '../utils/code-signing';
import {
expoCommandAsync,
shouldUseVersionedExpoCLI,
@@ -35,6 +52,7 @@ import {
ExpoUpdatesCLIModuleNotFoundError,
expoUpdatesCommandAsync,
} from '../utils/expoUpdatesCli';
import areSetsEqual from '../utils/expodash/areSetsEqual';
import chunk from '../utils/expodash/chunk';
import { truthy } from '../utils/expodash/filter';
import uniqBy from '../utils/expodash/uniqBy';
@@ -293,6 +311,14 @@ export function loadMetadata(distRoot: string): Metadata {
return metadata;
}
export async function generateEasMetadataAsync(
distRoot: string,
metadata: UpdateJsonInfo[]
): Promise<void> {
const easMetadataPath = path.join(distRoot, 'eas-update-metadata.json');
await JsonFile.writeAsync(easMetadataPath, { updates: metadata });
}
export function filterExportedPlatformsByFlag<T extends Partial<Record<Platform, any>>>(
record: T,
platformFlag: ExpoCLIExportPlatformFlag
@@ -799,3 +825,273 @@ export const platformDisplayNames: Record<Platform, string> = {
ios: 'iOS',
web: 'Web',
};
export async function buildAndUploadAssetsAsync({
graphqlClient,
projectId,
skipBundler,
projectDir,
inputDir,
exp,
platformFlag,
clearCache,
}: {
graphqlClient: ExpoGraphqlClient;
projectId: string;
skipBundler: boolean;
projectDir: string;
inputDir: string;
exp: ExpoConfig;
platformFlag: ExpoCLIExportPlatformFlag;
clearCache: boolean;
}): Promise<{
distRoot: string;
realizedPlatforms: Platform[];
unsortedUpdateInfoGroups: UpdateInfoGroup;
uploadedAssetCount: number;
assetLimitPerUpdateGroup: number;
}> {
// build bundle and upload assets for a new publish
if (!skipBundler) {
const bundleSpinner = ora().start('Exporting...');
try {
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag, clearCache });
bundleSpinner.succeed('Exported bundle(s)');
} catch (e) {
bundleSpinner.fail('Export failed');
throw e;
}
}
// After possibly bundling, assert that the input directory can be found.
const distRoot = await resolveInputDirectoryAsync(inputDir, { skipBundler });
const assetSpinner = ora().start('Uploading...');
try {
const collectedAssets = await collectAssetsAsync(distRoot);
const assets = filterExportedPlatformsByFlag(collectedAssets, platformFlag);
const realizedPlatforms = Object.keys(assets) as Platform[];
// Timeout mechanism:
// - Start with NO_ACTIVITY_TIMEOUT. 180 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 = 180 * 1000; // 180 seconds
let lastUploadedStorageKeys = new Set<string>();
let lastAssetUploadResults: {
asset: RawAsset & { storageKey: string };
finished: boolean;
}[] = [];
let timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT;
const cancelationToken = { isCanceledOrFinished: false };
const uploadResults = await Promise.race([
uploadAssetsAsync(
graphqlClient,
assets,
projectId,
cancelationToken,
assetUploadResults => {
const currentUploadedStorageKeys = new Set(
assetUploadResults.filter(r => r.finished).map(r => r.asset.storageKey)
);
if (!areSetsEqual(currentUploadedStorageKeys, lastUploadedStorageKeys)) {
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
lastUploadedStorageKeys = currentUploadedStorageKeys;
lastAssetUploadResults = assetUploadResults;
}
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 () => {
while (Date.now() < timeAtWhichToTimeout) {
if (cancelationToken.isCanceledOrFinished) {
break;
}
await new Promise(res => setTimeout(res, 1000)); // wait 1 second
}
cancelationToken.isCanceledOrFinished = true;
const timedOutAssets = lastAssetUploadResults
.filter(r => !r.finished)
.map(r => `\n- ${r.asset.originalPath ?? r.asset.path}`);
throw new Error(`Asset processing timed out for assets: ${timedOutAssets}`);
})(),
]);
const uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
const assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
const unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
// NOTE(cedric): we assume that bundles are always uploaded, and always are part of
// `uploadedAssetCount`, perferably we don't assume. For that, we need to refactor the
// `uploadAssetsAsync` and be able to determine asset type from the uploaded assets.
const uploadedBundleCount = uploadResults.launchAssetCount;
const uploadedNormalAssetCount = Math.max(0, uploadedAssetCount - uploadedBundleCount);
const reusedNormalAssetCount = uploadResults.uniqueAssetCount - uploadedNormalAssetCount;
assetSpinner.stop();
Log.withTick(
`Uploaded ${uploadedBundleCount} app ${uploadedBundleCount === 1 ? 'bundle' : 'bundles'}`
);
if (uploadedNormalAssetCount === 0) {
Log.withTick(`Uploading assets skipped - no new assets found`);
} else {
let message = `Uploaded ${uploadedNormalAssetCount} ${
uploadedNormalAssetCount === 1 ? 'asset' : 'assets'
}`;
if (reusedNormalAssetCount > 0) {
message += ` (reused ${reusedNormalAssetCount} ${
reusedNormalAssetCount === 1 ? 'asset' : 'assets'
})`;
}
Log.withTick(message);
}
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
}
const platformString = (Object.keys(assets) as Platform[])
.map(platform => {
const collectedAssetForPlatform = nullthrows(assets[platform]);
const totalAssetsForPlatform = collectedAssetForPlatform.assets.length + 1; // launch asset
const assetString = totalAssetsForPlatform === 1 ? 'asset' : 'assets';
return `${totalAssetsForPlatform} ${platformDisplayNames[platform]} ${assetString}`;
})
.join(', ');
Log.withInfo(
`${platformString} (maximum: ${assetLimitPerUpdateGroup} total per update). ${learnMore(
'https://expo.fyi/eas-update-asset-limits',
{ learnMoreMessage: 'Learn more about asset limits' }
)}`
);
return {
distRoot,
realizedPlatforms,
unsortedUpdateInfoGroups,
uploadedAssetCount,
assetLimitPerUpdateGroup,
};
} catch (e) {
assetSpinner.fail('Failed to upload');
throw e;
}
}
export async function publishUpdateGroupsAsync({
graphqlClient,
branchId,
runtimeVersions,
gitCommitHash,
isGitWorkingTreeDirty,
unsortedUpdateInfoGroups,
updateMessage,
codeSigningInfo,
}: {
graphqlClient: ExpoGraphqlClient;
branchId: string;
runtimeVersions: { platform: string; runtimeVersion: string }[];
gitCommitHash: string | undefined;
isGitWorkingTreeDirty: boolean | undefined;
unsortedUpdateInfoGroups: UpdateInfoGroup;
updateMessage: string | undefined;
codeSigningInfo: CodeSigningInfo | undefined;
}): Promise<{ newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'] }> {
const runtimeToPlatformMapping = getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
// Sort the updates into different groups based on their platform specific runtime versions
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformMapping.map(
({ runtimeVersion, platforms }) => {
const localUpdateInfoGroup = Object.fromEntries(
platforms.map(platform => [
platform,
unsortedUpdateInfoGroups[platform as keyof UpdateInfoGroup],
])
);
return {
branchId,
updateInfoGroup: localUpdateInfoGroup,
runtimeVersion,
message: updateMessage,
gitCommitHash,
isGitWorkingTreeDirty,
awaitingCodeSigningInfo: !!codeSigningInfo,
};
}
);
const publishSpinner = ora('Publishing...').start();
try {
const newUpdates = await PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
if (codeSigningInfo) {
Log.log('🔒 Signing updates');
const updatesTemp = [...newUpdates];
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
const newUpdates = updatesTemp.splice(
0,
Object.keys(nullthrows(updateGroup.updateInfoGroup)).length
);
return {
updateGroup,
newUpdates,
};
});
await Promise.all(
updateGroupsAndTheirUpdates.map(async ({ updateGroup, newUpdates }) => {
await Promise.all(
newUpdates.map(async newUpdate => {
const response = await fetch(newUpdate.manifestPermalink, {
method: 'GET',
headers: { accept: 'multipart/mixed' },
});
const manifestBody = nullthrows(await getManifestBodyAsync(response));
checkManifestBodyAgainstUpdateInfoGroup(
manifestBody,
nullthrows(
nullthrows(updateGroup.updateInfoGroup)[
newUpdate.platform as keyof UpdateInfoGroup
]
)
);
const manifestSignature = signBody(manifestBody, codeSigningInfo);
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
alg: codeSigningInfo.codeSigningMetadata.alg,
keyid: codeSigningInfo.codeSigningMetadata.keyid,
sig: manifestSignature,
});
})
);
})
);
}
publishSpinner.succeed('Published!');
return {
newUpdates,
};
} catch (e) {
publishSpinner.fail('Failed to publish updates');
throw e;
}
}
@@ -23,11 +23,17 @@ export async function resolveRuntimeVersionAsync({
projectDir: string;
env: Env | undefined;
cwd?: string;
}): Promise<string | null> {
}): Promise<{
runtimeVersion: string | null;
fingerprintSources: object[] | null;
} | null> {
if (!(await isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync(projectDir))) {
// fall back to the previous behavior (using the @expo/config-plugins eas-cli dependency rather
// than the versioned @expo/config-plugins dependency in the project)
return await Updates.getRuntimeVersionNullableAsync(projectDir, exp, platform);
return {
runtimeVersion: await Updates.getRuntimeVersionNullableAsync(projectDir, exp, platform),
fingerprintSources: null,
};
}
try {
@@ -45,7 +51,10 @@ export async function resolveRuntimeVersionAsync({
Log.debug('runtimeversion:resolve output:');
Log.debug(resolvedRuntimeVersionJSONResult);
return runtimeVersionResult.runtimeVersion ?? null;
return {
runtimeVersion: runtimeVersionResult.runtimeVersion ?? null,
fingerprintSources: runtimeVersionResult.fingerprintSources ?? null,
};
} catch (e: any) {
// if expo-updates is not installed, there's no need for a runtime version in the build
if (e instanceof ExpoUpdatesCLIModuleNotFoundError) {
@@ -7,8 +7,8 @@ import {
hasEmptyBranchMap,
hasStandardBranchMap,
} from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import { getUpdateBranch } from '../../channel/utils';
import { updateChannelBranchMappingAsync } from '../../commands/channel/edit';
import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
import {
UpdateBranchBasicInfoFragment,
@@ -286,7 +286,9 @@ export class CreateRollout implements EASUpdateAction<UpdateChannelBasicInfoFrag
})
)
)
).filter(truthy);
)
.map(runtime => runtime?.runtimeVersion)
.filter(truthy);
const dedupedRuntimes = [...new Set(runtimes)];
if (dedupedRuntimes.length === 0) {
@@ -1,7 +1,7 @@
import assert from 'assert';
import chalk from 'chalk';
import { updateChannelBranchMappingAsync } from '../../commands/channel/edit';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
import { ChannelQuery, UpdateChannelObject } from '../../graphql/queries/ChannelQuery';
@@ -2,7 +2,7 @@ import assert from 'assert';
import chalk from 'chalk';
import { getAlwaysTrueBranchMapping } from '../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../commands/channel/edit';
import { updateChannelBranchMappingAsync } from '../../channel/queries';
import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
import {
@@ -1,6 +1,6 @@
import { testBasicChannelInfo, testChannelObject } from '../../../channel/__tests__/fixtures';
import { getAlwaysTrueBranchMapping, getBranchMapping } from '../../../channel/branch-mapping';
import { updateChannelBranchMappingAsync } from '../../../commands/channel/edit';
import { updateChannelBranchMappingAsync } from '../../../channel/queries';
import { createCtxMock } from '../../../eas-update/__tests__/fixtures';
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
import { confirmAsync } from '../../../prompts';
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "10.0.0",
"version": "10.2.2",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@babel/code-frame": "7.23.5",
"@expo/eas-build-job": "1.0.117",
"@expo/eas-build-job": "1.0.129",
"chalk": "4.1.2",
"env-string": "1.0.1",
"fs-extra": "11.2.0",
+22 -11
View File
@@ -232,20 +232,15 @@
"sdk-51",
"sdk-50",
"sdk-49",
"ubuntu-22.04-jdk-17-ndk-r26b",
"ubuntu-22.04-jdk-17-ndk-r25b",
"ubuntu-22.04-jdk-11-ndk-r23b",
"ubuntu-20.04-jdk-11-ndk-r23b",
"ubuntu-22.04-jdk-17-ndk-r21e",
"ubuntu-22.04-jdk-11-ndk-r21e",
"ubuntu-20.04-jdk-11-ndk-r21e",
"ubuntu-22.04-jdk-8-ndk-r21e",
"ubuntu-20.04-jdk-8-ndk-r21e",
"ubuntu-20.04-jdk-11-ndk-r19c",
"ubuntu-20.04-jdk-8-ndk-r19c"
"ubuntu-22.04-jdk-11-ndk-r21e"
],
"markdownEnumDescriptions": [
"When using this option the build image is selected automatically based on the project configuration, detected Expo SDK and React Native versions.",
"The latest Android image currently available. It is resolved to `ubuntu-22.04-jdk-17-ndk-r25b`. The `latest` to image mapping will be updated as new images are released.",
"The latest Android image currently available. It is resolved to `ubuntu-22.04-jdk-17-ndk-r26b`. The `latest` to image mapping will be updated as new images are released.",
"The recommended image for SDK 51 builds",
"The recommended image for SDK 50 builds",
"The recommended image for SDK 49 builds"
@@ -253,10 +248,25 @@
},
{
"deprecated": true,
"enum": ["default", "stable"],
"enum": [
"default",
"stable",
"ubuntu-20.04-jdk-11-ndk-r23b",
"ubuntu-20.04-jdk-11-ndk-r19c",
"ubuntu-20.04-jdk-8-ndk-r19c",
"ubuntu-20.04-jdk-11-ndk-r21e",
"ubuntu-20.04-jdk-8-ndk-r21e",
"ubuntu-22.04-jdk-8-ndk-r21e"
],
"markdownEnumDescriptions": [
"This image tag is deprecated, please use other image tags or images directly instead.",
"This image tag is deprecated, please use other image tags or images directly instead."
"This image tag is deprecated, please use other image tags or images directly instead.",
"This image is deprecated",
"This image is deprecated",
"This image is deprecated",
"This image is deprecated",
"This image is deprecated",
"This image is deprecated"
]
}
]
@@ -368,6 +378,7 @@
"sdk-51",
"sdk-50",
"sdk-49",
"macos-sonoma-14.5-xcode-15.4",
"macos-sonoma-14.4-xcode-15.3",
"macos-ventura-13.6-xcode-15.2",
"macos-ventura-13.6-xcode-15.1",
@@ -375,7 +386,7 @@
],
"markdownEnumDescriptions": [
"When using this option the build image is selected automatically based on the project configuration, detected Expo SDK and React Native versions.",
"The latest iOS image currently available. It is resolved to `macos-sonoma-14.4-xcode-15.3`. The `latest` to image mapping will be updated as new images are released.",
"The latest iOS image currently available. It is resolved to `macos-sonoma-14.5-xcode-15.4`. The `latest` to image mapping will be updated as new images are released.",
"The recommended image for SDK 51 builds",
"The recommended image for SDK 50 builds",
"The recommended image for SDK 49 builds"
@@ -100,6 +100,35 @@ test('valid eas.json for development client builds', async () => {
});
});
test('valid eas.json with specified environment', async () => {
await fs.writeJson('/project/eas.json', {
build: {
development: {
environment: 'production',
},
},
});
const accessor = EasJsonAccessor.fromProjectPath('/project');
const iosProfile = await EasJsonUtils.getBuildProfileAsync(accessor, Platform.IOS, 'development');
const androidProfile = await EasJsonUtils.getBuildProfileAsync(
accessor,
Platform.ANDROID,
'development'
);
expect(androidProfile).toEqual({
credentialsSource: 'remote',
distribution: 'store',
environment: 'production',
});
expect(iosProfile).toEqual({
credentialsSource: 'remote',
distribution: 'store',
environment: 'production',
});
});
test('valid eas.json with top level withoutCredentials property', async () => {
await fs.writeJson('/project/eas.json', {
build: {
+2
View File
@@ -69,6 +69,8 @@ const CommonBuildProfileSchema = Joi.object({
// credentials
withoutCredentials: Joi.boolean(),
environment: Joi.string().valid('preview', 'production', 'development'),
});
const PlatformBuildProfileSchema = CommonBuildProfileSchema.concat(
+2
View File
@@ -69,6 +69,8 @@ export interface CommonBuildProfile {
// credentials
withoutCredentials?: boolean;
environment?: 'preview' | 'production' | 'development';
}
interface PlatformBuildProfile extends Omit<CommonBuildProfile, 'autoIncrement'> {
Generated
+33 -47
View File
@@ -1354,10 +1354,10 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
"@expo/[email protected].0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.7.0.tgz#b8ac565f37b077c3a1bb562b8db5c5864bd6aba7"
integrity sha512-RVzZTiOeuNT04fE5V4f536XmIyxbRFOJ3m/rE6kImbIZ65upOS7xdIQpihEdYOiHB5uZAcD3JClUEsMfFhTv4w==
"@expo/[email protected].1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.7.1.tgz#b072cc95ace4fed734d088740eff9945e8f4d22e"
integrity sha512-oKHKRMfpPsVIlMSwHsartqrFhxKR1PnwGzaM3i+d4/g9XE9A+bKMu5YIUT4IXX561E0YYoaeaRHuxiIzcmEmRw==
"@expo/bunyan@^4.0.0":
version "4.0.0"
@@ -1462,10 +1462,20 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/[email protected]17":
version "1.0.117"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.117.tgz#0a0a057bd1b0df5b2857a13ef4b54db173d837bf"
integrity sha512-l1EBpRH4lqRg0uPKl4/xn3V77dzWDrSAIU0r88aNq+Slznpu8U8HCkEZSh2YyF/BFhhs5lG7v8yQclVaflVBrQ==
"@expo/[email protected]29":
version "1.0.129"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.129.tgz#c5d7a8d87d0265d622222a3ae10b6ff91f0c287e"
integrity sha512-4afSML5IxhBNW1iLu5dQ8rfe4RlXFCAnjXaVhag/Y2Z4l+TmN30C27ff8qfxUDaa/5G+8D9ru3xl4L3JV906Tg==
dependencies:
"@expo/logger" "1.0.117"
joi "^17.13.1"
semver "^7.6.2"
zod "^3.23.8"
"@expo/[email protected]":
version "1.0.130"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.130.tgz#698de0139d14cd339f9d0f37ad40a86f1e9acbc8"
integrity sha512-3amSYB9QC24ZqMjNAl/s2EHbF4Y3btKnSAafp5sp5t2g8GvU1sBZ/+rXQPKH/Ga+PcbqNT+pduGIGg4uUC7gnw==
dependencies:
"@expo/logger" "1.0.117"
joi "^17.13.1"
@@ -1667,12 +1677,12 @@
dependencies:
cross-spawn "^7.0.3"
"@expo/[email protected]18":
version "1.0.118"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.118.tgz#67839259e67b52e7b24f0bfc5913c869c1ed1f2e"
integrity sha512-JzU4K5bmNG+RFj21ulPXNADwxdtynN8WAZnqRluafz7FcU9ibNMdowdXWR17zo0xpCd1f5awcsjhTebWDjd5hg==
"@expo/[email protected]29":
version "1.0.129"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.129.tgz#c91f8758d10b9f78e68dab988730980f3a4dcf6f"
integrity sha512-OIzKuOP6UZ/WmwwdVrNsgUTi41zdkAojCD+2cQufWZIsP62FiNEvtB+OqqDAQ/nznRPD4dj5UJzf0+gofmiQsg==
dependencies:
"@expo/eas-build-job" "1.0.117"
"@expo/eas-build-job" "1.0.129"
"@expo/logger" "1.0.117"
"@expo/spawn-async" "^1.7.2"
arg "^5.0.2"
@@ -4637,13 +4647,14 @@ aws-sdk@^2.1231.0:
uuid "8.0.0"
xml2js "0.4.19"
[email protected]7.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
[email protected]8.1:
version "0.28.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.28.1.tgz#2a7bcd34a3837b71ee1a5ca3762214b86b703e70"
integrity sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axios@^1.0.0:
version "1.6.5"
@@ -7084,7 +7095,7 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05"
integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==
follow-redirects@^1.14.9, follow-redirects@^1.15.4:
follow-redirects@^1.15.0, follow-redirects@^1.15.4:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
@@ -12690,16 +12701,7 @@ string-length@^5.0.1:
char-regex "^2.0.0"
strip-ansi "^7.0.1"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -12816,7 +12818,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -12830,13 +12832,6 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@@ -13998,16 +13993,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
[email protected], wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", [email protected], wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==