mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87b0cf41a8 | ||
|
|
7847b5297c | ||
|
|
539ef8f3ad | ||
|
|
819259ab1a | ||
|
|
47e1989c9c | ||
|
|
fa3262951b | ||
|
|
66ce64e0a3 | ||
|
|
5c553cee7b | ||
|
|
2fb079d4bb | ||
|
|
d15cd90c81 | ||
|
|
0fbd3e47f8 | ||
|
|
12efaf12cc | ||
|
|
a404bb383d | ||
|
|
feaa8cbf22 | ||
|
|
542ea7a791 | ||
|
|
7d80b0540f | ||
|
|
cc4e0ebda1 | ||
|
|
1621fb3a9b | ||
|
|
51b5946854 | ||
|
|
b8ed63cadc | ||
|
|
8fc0648419 | ||
|
|
4fbc6aa3ff | ||
|
|
83bf6de727 | ||
|
|
a2219beaba | ||
|
|
90a2612792 | ||
|
|
d94b57c1b7 | ||
|
|
d63fc1f01e | ||
|
|
57e7481a68 | ||
|
|
7bb18e2489 | ||
|
|
359464d55c | ||
|
|
e993e9a370 | ||
|
|
75046b0b35 | ||
|
|
3d55a4df40 | ||
|
|
b32ff2e733 | ||
|
|
6245fb418d | ||
|
|
50943f333a | ||
|
|
e930383a10 | ||
|
|
8c31825908 | ||
|
|
fbc39240a5 | ||
|
|
f31054c5a5 | ||
|
|
7af8b62a7b | ||
|
|
56d492cbac | ||
|
|
4f53ce353c | ||
|
|
736552b358 | ||
|
|
b4a3f5b526 | ||
|
|
28592f27fa |
No files matched your search
@@ -8,10 +8,71 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `worker:alias` command to assign aliases from the CLI. ([#2548](https://github.com/expo/eas-cli/pull/2548) by [@byCedric](https://github.com/byCedric))
|
||||
- Add `worker --prod` flag to deploy to production from the CLI. ([#2550](https://github.com/expo/eas-cli/pull/2550) by [@byCedric](https://github.com/byCedric))
|
||||
- Add `worker --alias` flag to assign custom aliases when deploying. ([#2551](https://github.com/expo/eas-cli/pull/2551) by [@byCedric](https://github.com/byCedric)))
|
||||
- Add `worker --id` flag to use a custom deployment identifier. ([#2552](https://github.com/expo/eas-cli/pull/2552) by [@byCedric](https://github.com/byCedric)))
|
||||
- Add `worker --environment` flag to deploy with EAS environment variables. ([#2557](https://github.com/expo/eas-cli/pull/2557) by [@kitten](https://github.com/kitten)))
|
||||
- Add `worker --export-dir` flag to select exported directory. ([#2560](https://github.com/expo/eas-cli/pull/2560) by [@byCedric](https://github.com/byCedric)))
|
||||
- Add `worker --json` flag to allow integrating with 3rd parties and custom tooling. ([#2561](https://github.com/expo/eas-cli/pull/2561) by [@byCedric](https://github.com/byCedric)))
|
||||
- Add `worker:alias --json` flag to allow integrating with 3rd parties and custom tooling. ([#2562](https://github.com/expo/eas-cli/pull/2562) by [@byCedric](https://github.com/byCedric)))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Avoid merging `expo.extra` plugin-generated data with `expo.extra.eas.projectId` in `eas init`. ([#2554](https://github.com/expo/eas-cli/pull/2554) by [@byCedric](https://github.com/byCedric)))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Make error message for invalid CFBundleShortVersionString more descriptive and actionable. Improve CFBundleShortVersionString validation regex. ([#2542](https://github.com/expo/eas-cli/pull/2542) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Add missing `--non-interactive` argument to `worker:deploy` command. ([#2544](https://github.com/expo/eas-cli/pull/2544) by [@kitten](https://github.com/kitten))
|
||||
- Source `@expo/env` dotenv files for worker deployments. ([#2545](https://github.com/expo/eas-cli/pull/2545) by [@kitten](https://github.com/kitten))
|
||||
- Support `worker --production` and clean up command output. ([#2555](https://github.com/expo/eas-cli/pull/2555) by [@byCedric](https://github.com/byCedric)))
|
||||
- Unify both `worker` and `worker:alias` command output. ([#2558](https://github.com/expo/eas-cli/pull/2558) by [@byCedric](https://github.com/byCedric)))
|
||||
- Share similar table/json output in both `worker` and `worker:alias` command outputs. ([#2563](https://github.com/expo/eas-cli/pull/2563) by [@byCedric](https://github.com/byCedric)))
|
||||
- Polish the project URL prompt when setting up new projects. ([#2564](https://github.com/expo/eas-cli/pull/2564) by [@byCedric](https://github.com/byCedric)))
|
||||
- Always assume `static` exports in `eas deploy` and add modified time. ([#2565](https://github.com/expo/eas-cli/pull/2565) by [@byCedric](https://github.com/byCedric)))
|
||||
|
||||
## [12.3.0](https://github.com/expo/eas-cli/releases/tag/v12.3.0) - 2024-09-09
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `--non-interactive` and `--force` support when `--id` is not passed to the `eas init` command. ([#1983](https://github.com/expo/eas-cli/pull/1983) by [@mymattcarroll](https://github.com/mymattcarroll))
|
||||
|
||||
## [12.1.1](https://github.com/expo/eas-cli/releases/tag/v12.1.1) - 2024-09-09
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fixed an issue where extensions retrieved the main app's entitlements instead of their own. ([#2532](https://github.com/expo/eas-cli/pull/2532) by [@HarrisHan](https://github.com/HarrisHan))
|
||||
|
||||
## [12.1.0](https://github.com/expo/eas-cli/releases/tag/v12.1.0) - 2024-09-06
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- **Internal/Experimental:** Add EAS Worker command ([#2447](https://github.com/expo/eas-cli/pull/2447) by [@kitten](https://github.com/kitten))
|
||||
- Upload fingeprint source as part of eas update command. ([#2533](https://github.com/expo/eas-cli/pull/2533) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
## [12.0.0](https://github.com/expo/eas-cli/releases/tag/v12.0.0) - 2024-09-04
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Prompt the users to set `appVersionSource`, while mentioning that `remote` is the default. ([#2411](https://github.com/expo/eas-cli/pull/2411) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add support for syncing Journaling Suggestions, Managed App Installation UI, and 5G Network Slicing capabilities. ([#2525](https://github.com/expo/eas-cli/pull/2525) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [11.0.3](https://github.com/expo/eas-cli/releases/tag/v11.0.3) - 2024-08-31
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Revert config-related packages to SDK 50 version from SDK 51 version to fix broken entitlements behavior for older SDKs. ([#2524](https://github.com/expo/eas-cli/pull/2524) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [11.0.2](https://github.com/expo/eas-cli/releases/tag/v11.0.2) - 2024-08-27
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Remove unncessary static project config context definition from `eas build` command context, to fix resolving dynamic projest ID and slug for dynamic app configs. ([#2521](https://github.com/expo/eas-cli/pull/2521) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [11.0.1](https://github.com/expo/eas-cli/releases/tag/v11.0.1) - 2024-08-26
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "11.0.1",
|
||||
"version": "12.4.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+66
-64
@@ -143,7 +143,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v12.4.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -971,10 +971,11 @@ create or link an EAS project
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas init [--force --id <value>] [--non-interactive ]
|
||||
$ eas init [--id <value>] [--force] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
--force Whether to overwrite any existing project ID
|
||||
--force Whether to create a new project/link an existing project without additional prompts or overwrite
|
||||
any existing project ID when running with --id flag
|
||||
--id=<value> ID of the EAS project to link
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
@@ -1050,7 +1051,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -1067,7 +1068,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -1084,7 +1085,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1114,7 +1115,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/v11.0.1/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v12.4.0/packages/eas-cli/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1128,7 +1129,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1136,10 +1137,11 @@ create or link an EAS project
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas project:init [--force --id <value>] [--non-interactive ]
|
||||
$ eas project:init [--id <value>] [--force] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
--force Whether to overwrite any existing project ID
|
||||
--force Whether to create a new project/link an existing project without additional prompts or overwrite
|
||||
any existing project ID when running with --id flag
|
||||
--id=<value> ID of the EAS project to link
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
@@ -1150,7 +1152,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1168,7 +1170,7 @@ ALIASES
|
||||
$ eas onboarding
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -1191,7 +1193,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -1209,7 +1211,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1223,7 +1225,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1243,7 +1245,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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1274,7 +1276,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v11.0.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v12.4.0/packages/eas-cli/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/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/v11.0.1/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/v12.4.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ documents:
|
||||
- 'src/commands/**/*.ts'
|
||||
- 'src/branch/**/*.ts'
|
||||
- 'src/channel/**/*.ts'
|
||||
- 'src/worker/**/*.ts'
|
||||
generates:
|
||||
src/graphql/generated.ts:
|
||||
plugins:
|
||||
|
||||
Generated
+4753
-550
File diff suppressed because it is too large.
Load diff
@@ -1,34 +1,35 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "11.0.1",
|
||||
"version": "12.4.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "1.7.1",
|
||||
"@expo/apple-utils": "1.8.0",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "9.0.3",
|
||||
"@expo/config-plugins": "8.0.8",
|
||||
"@expo/config-types": "51.0.2",
|
||||
"@expo/config": "8.5.4",
|
||||
"@expo/config-plugins": "7.8.4",
|
||||
"@expo/config-types": "50.0.0",
|
||||
"@expo/eas-build-job": "1.0.133",
|
||||
"@expo/eas-json": "11.0.0",
|
||||
"@expo/json-file": "8.3.3",
|
||||
"@expo/eas-json": "12.0.0",
|
||||
"@expo/env": "^0.3.0",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/logger": "1.0.117",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.1.3",
|
||||
"@expo/package-manager": "1.5.2",
|
||||
"@expo/pkcs12": "0.1.2",
|
||||
"@expo/plist": "0.1.3",
|
||||
"@expo/osascript": "2.0.33",
|
||||
"@expo/package-manager": "1.1.2",
|
||||
"@expo/pkcs12": "0.0.8",
|
||||
"@expo/plist": "0.0.20",
|
||||
"@expo/plugin-help": "5.1.23",
|
||||
"@expo/plugin-warn-if-update-available": "2.5.1",
|
||||
"@expo/prebuild-config": "7.0.8",
|
||||
"@expo/prebuild-config": "6.7.3",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "1.0.130",
|
||||
"@expo/spawn-async": "1.7.0",
|
||||
"@expo/steps": "1.0.134",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "^1.26.2",
|
||||
"@oclif/plugin-autocomplete": "^2.3.10",
|
||||
@@ -63,6 +64,7 @@
|
||||
"log-symbols": "4.1.0",
|
||||
"mime": "3.0.0",
|
||||
"minimatch": "5.1.2",
|
||||
"minizlib": "3.0.1",
|
||||
"nanoid": "3.3.4",
|
||||
"node-fetch": "2.6.7",
|
||||
"node-forge": "1.3.1",
|
||||
@@ -78,6 +80,7 @@
|
||||
"semver": "7.5.4",
|
||||
"slash": "3.0.0",
|
||||
"tar": "6.2.1",
|
||||
"tar-stream": "3.1.7",
|
||||
"terminal-link": "2.1.1",
|
||||
"tslib": "2.6.2",
|
||||
"turndown": "7.1.2",
|
||||
@@ -105,12 +108,13 @@
|
||||
"@types/prompts": "2.4.2",
|
||||
"@types/semver": "7.5.6",
|
||||
"@types/tar": "6.1.10",
|
||||
"@types/tar-stream": "3.1.3",
|
||||
"@types/tough-cookie": "4.0.2",
|
||||
"@types/uuid": "9.0.7",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"axios": "0.28.1",
|
||||
"eslint-plugin-graphql": "4.0.0",
|
||||
"express": "4.19.2",
|
||||
"express": "4.20.0",
|
||||
"memfs": "3.4.13",
|
||||
"mockdate": "3.0.5",
|
||||
"nock": "13.4.0",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`BuildVersionGetView reading version aborts when the appVersionSource is not specified and the user chooses to set it to LOCAL 1`] = `"Aborting..."`;
|
||||
|
||||
exports[`BuildVersionGetView reading version when appVersionSource is set to local 1`] = `"This project is not configured for using remote version source. Add [1m{"cli": { "appVersionSource": "remote" }}[22m in eas.json or re-run this command without "--non-interactive" flag."`;
|
||||
@@ -1,10 +1,18 @@
|
||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { getMockEasJson, mockCommandContext, mockProjectId, mockTestCommand } from './utils';
|
||||
import {
|
||||
getMockEasJson,
|
||||
mockCommandContext,
|
||||
mockProjectId,
|
||||
mockTestCommand,
|
||||
withLocalVersionSource,
|
||||
withRemoteVersionSource,
|
||||
} from './utils';
|
||||
import BuildVersionGetView from '../../commands/build/version/get';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import Log from '../../log';
|
||||
import { AppVersionSourceUpdateOption } from '../../project/remoteVersionSource';
|
||||
import * as prompts from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
jest.mock('../../project/applicationIdentifier');
|
||||
@@ -12,16 +20,7 @@ jest.mock('fs');
|
||||
jest.mock('../../log');
|
||||
jest.mock('../../utils/json');
|
||||
jest.mock('../../graphql/queries/AppVersionQuery');
|
||||
|
||||
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||
return {
|
||||
...easJson,
|
||||
cli: {
|
||||
...easJson.cli,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
};
|
||||
}
|
||||
jest.mock('../../prompts');
|
||||
|
||||
describe(BuildVersionGetView, () => {
|
||||
afterEach(() => {
|
||||
@@ -49,6 +48,29 @@ describe(BuildVersionGetView, () => {
|
||||
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('reading version for platform android when the appVersionSource is not specified and the user chooses to set it to REMOTE', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '100',
|
||||
storeVersion: '1.0.0',
|
||||
}));
|
||||
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_REMOTE);
|
||||
|
||||
await cmd.run();
|
||||
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||
ctx.loggedIn.graphqlClient,
|
||||
mockProjectId,
|
||||
'ANDROID',
|
||||
'eas.test.com'
|
||||
);
|
||||
expect(Log.log).toHaveBeenCalledWith(`Android versionCode - ${chalk.bold('100')}`);
|
||||
expect(enableJsonOutput).not.toHaveBeenCalled();
|
||||
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('reading version for platform android when no remote version is set', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||
@@ -145,7 +167,9 @@ describe(BuildVersionGetView, () => {
|
||||
});
|
||||
|
||||
test('reading version when appVersionSource is set to local ', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||
easJson: withLocalVersionSource(getMockEasJson()),
|
||||
});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '100',
|
||||
storeVersion: '1.0.0',
|
||||
@@ -158,4 +182,51 @@ describe(BuildVersionGetView, () => {
|
||||
);
|
||||
await expect(cmd.run()).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('reading version aborts when the appVersionSource is not specified and the user chooses to set it to LOCAL', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '100',
|
||||
storeVersion: '1.0.0',
|
||||
}));
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_LOCAL);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('reading version aborts when the appVersionSource is not specified and the user chooses to configure it manually', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '100',
|
||||
storeVersion: '1.0.0',
|
||||
}));
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.ABORT);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowError('Aborted.');
|
||||
});
|
||||
|
||||
test('reading version sets appVersionSource to LOCAL and aborts when the appVersionSource is not specified and is run in non-interactive mode', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '100',
|
||||
storeVersion: '1.0.0',
|
||||
}));
|
||||
|
||||
const cmd = mockTestCommand(
|
||||
BuildVersionGetView,
|
||||
['--non-interactive', '--json', '--platform=android'],
|
||||
ctx
|
||||
);
|
||||
await expect(cmd.run()).rejects.toThrowError(
|
||||
`This project is not configured for using remote version source. Add ${chalk.bold(
|
||||
'{"cli": { "appVersionSource": "remote" }}'
|
||||
)} in eas.json or re-run this command without "--non-interactive" flag.`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
@@ -8,12 +7,15 @@ import {
|
||||
mockCommandContext,
|
||||
mockProjectId,
|
||||
mockTestCommand,
|
||||
withLocalVersionSource,
|
||||
withRemoteVersionSource,
|
||||
} from './utils';
|
||||
import BuildVersionSetView from '../../commands/build/version/set';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import Log from '../../log';
|
||||
import { AppVersionSourceUpdateOption } from '../../project/remoteVersionSource';
|
||||
import * as prompts from '../../prompts';
|
||||
|
||||
jest.mock('../../project/applicationIdentifier');
|
||||
@@ -25,16 +27,6 @@ jest.mock('../../log');
|
||||
jest.mock('../../prompts');
|
||||
jest.mock('../../utils/json');
|
||||
|
||||
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||
return {
|
||||
...easJson,
|
||||
cli: {
|
||||
...easJson.cli,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe(BuildVersionSetView, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -67,6 +59,34 @@ describe(BuildVersionSetView, () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('setting version for platform android when the appVersionSource is not specified and the user chooses to set it to REMOTE', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(prompts.promptAsync).mockImplementationOnce(async () => ({
|
||||
version: '1000',
|
||||
}));
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_REMOTE);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||
await cmd.run();
|
||||
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||
ctx.loggedIn.graphqlClient,
|
||||
mockProjectId,
|
||||
'ANDROID',
|
||||
'eas.test.com'
|
||||
);
|
||||
expect(AppVersionMutation.createAppVersionAsync).toHaveBeenCalledWith(
|
||||
ctx.loggedIn.graphqlClient,
|
||||
expect.objectContaining({
|
||||
buildVersion: '1000',
|
||||
storeVersion: '1.0.0',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('printing current remote version before prompting for a new one', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||
@@ -115,7 +135,9 @@ describe(BuildVersionSetView, () => {
|
||||
});
|
||||
|
||||
test('setting version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||
easJson: withLocalVersionSource(getMockEasJson()),
|
||||
});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
|
||||
@@ -125,10 +147,26 @@ describe(BuildVersionSetView, () => {
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('setting version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||
test('setting version aborts when the appVersionSource is not specified and the user chooses to set it to LOCAL, and they refuse auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_LOCAL);
|
||||
jest.mocked(prompts.confirmAsync).mockImplementationOnce(async () => false);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowError('Aborting...');
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('setting version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||
easJson: withLocalVersionSource(getMockEasJson()),
|
||||
});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||
@@ -137,4 +175,33 @@ describe(BuildVersionSetView, () => {
|
||||
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||
});
|
||||
|
||||
test('setting version when the appVersionSource is not specified and the user chooses to set it to LOCAL, and they allow auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_LOCAL);
|
||||
jest.mocked(prompts.confirmAsync).mockImplementationOnce(async () => true);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||
await cmd.run();
|
||||
|
||||
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||
});
|
||||
|
||||
test('setting version aborts when the appVersionSource is not specified and the user chooses to configure manually', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.ABORT);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowError('Aborted.');
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
@@ -9,6 +8,8 @@ import {
|
||||
mockCommandContext,
|
||||
mockProjectId,
|
||||
mockTestCommand,
|
||||
withLocalVersionSource,
|
||||
withRemoteVersionSource,
|
||||
} from './utils';
|
||||
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../build/android/version';
|
||||
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../build/ios/version';
|
||||
@@ -19,6 +20,7 @@ import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import { getAppBuildGradleAsync } from '../../project/android/gradleUtils';
|
||||
import { resolveTargetsAsync } from '../../project/ios/target';
|
||||
import { AppVersionSourceUpdateOption } from '../../project/remoteVersionSource';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import * as prompts from '../../prompts';
|
||||
|
||||
@@ -37,16 +39,6 @@ jest.mock('../../log');
|
||||
jest.mock('../../prompts');
|
||||
jest.mock('../../utils/json');
|
||||
|
||||
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||
return {
|
||||
...easJson,
|
||||
cli: {
|
||||
...easJson.cli,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe(BuildVersionSyncView, () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -79,6 +71,34 @@ describe(BuildVersionSyncView, () => {
|
||||
expect(syncAndroidAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('syncing version for managed project on platform android when appVersionSource is not set and the user chooses to set it to REMOTE', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||
buildVersion: '1000',
|
||||
storeVersion: '0.0.1',
|
||||
}));
|
||||
jest.mocked(prompts.promptAsync).mockImplementationOnce(async () => ({
|
||||
version: '1000',
|
||||
}));
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_REMOTE);
|
||||
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.MANAGED);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||
const syncAndroidAsync = jest.spyOn(cmd, 'syncAndroidAsync' as any);
|
||||
|
||||
await cmd.run();
|
||||
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||
ctx.loggedIn.graphqlClient,
|
||||
mockProjectId,
|
||||
'ANDROID',
|
||||
'eas.test.com'
|
||||
);
|
||||
expect(syncAndroidAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('syncing version for bare project on platform android', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||
@@ -179,7 +199,9 @@ describe(BuildVersionSyncView, () => {
|
||||
});
|
||||
|
||||
test('syncing version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||
easJson: withLocalVersionSource(getMockEasJson()),
|
||||
});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
|
||||
@@ -189,10 +211,26 @@ describe(BuildVersionSyncView, () => {
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('syncing version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||
test('syncing version aborts when appVersionSource is not set and the user chooses to set it to LOCAL, and they refuse auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_LOCAL);
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowError('Aborting...');
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('syncing version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||
easJson: withLocalVersionSource(getMockEasJson()),
|
||||
});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||
@@ -201,4 +239,33 @@ describe(BuildVersionSyncView, () => {
|
||||
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||
});
|
||||
|
||||
test('syncing version when appVersionSource is not set and the user chooses to set it to LOCAL and they allow auto configuration', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.SET_TO_LOCAL);
|
||||
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||
await cmd.run();
|
||||
|
||||
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||
});
|
||||
|
||||
test('syncing version aborts when appVersionSource is not set and the user chooses to configure manually', async () => {
|
||||
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||
jest
|
||||
.mocked(prompts.selectAsync)
|
||||
.mockImplementationOnce(async () => AppVersionSourceUpdateOption.ABORT);
|
||||
|
||||
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||
await expect(cmd.run()).rejects.toThrowError('Aborted.');
|
||||
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { EasJson } from '@expo/eas-json';
|
||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||
import { Command, Config } from '@oclif/core';
|
||||
import { vol } from 'memfs';
|
||||
import path from 'path';
|
||||
@@ -148,3 +148,23 @@ export const getError = <TError = any>(call: () => unknown): TError | NoErrorThr
|
||||
return error as TError;
|
||||
}
|
||||
};
|
||||
|
||||
export function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||
return {
|
||||
...easJson,
|
||||
cli: {
|
||||
...easJson.cli,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function withLocalVersionSource(easJson: EasJson): EasJson {
|
||||
return {
|
||||
...easJson,
|
||||
cli: {
|
||||
...easJson.cli,
|
||||
appVersionSource: AppVersionSource.LOCAL,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { AppVersionSource, EasJsonAccessor } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import { vol } from 'memfs';
|
||||
|
||||
import { easCliVersion } from '../../utils/easCli';
|
||||
import GitClient from '../../vcs/clients/git';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
import { ensureProjectConfiguredAsync } from '../configure';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('../../vcs/vcs');
|
||||
jest.mock('../../vcs/clients/git');
|
||||
|
||||
beforeEach(async () => {
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
describe(ensureProjectConfiguredAsync, () => {
|
||||
it('returns false and does not configure if eas.json exists', async () => {
|
||||
vol.fromJSON({
|
||||
'./eas.json': JSON.stringify({
|
||||
cli: {
|
||||
version: `>= ${easCliVersion}`,
|
||||
},
|
||||
build: {
|
||||
development: {
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
},
|
||||
preview: {
|
||||
distribution: 'internal',
|
||||
},
|
||||
production: {},
|
||||
},
|
||||
submit: {
|
||||
production: {},
|
||||
},
|
||||
}),
|
||||
});
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeTruthy();
|
||||
const vcsClientMock = jest.mocked(new GitClient());
|
||||
vcsClientMock.showChangedFilesAsync.mockImplementation(async () => {});
|
||||
vcsClientMock.isCommitRequiredAsync.mockImplementation(async () => false);
|
||||
vcsClientMock.trackFileAsync.mockImplementation(async () => {});
|
||||
const result = await ensureProjectConfiguredAsync({
|
||||
projectDir: '.',
|
||||
nonInteractive: false,
|
||||
vcsClient: vcsClientMock as unknown as Client,
|
||||
});
|
||||
expect(result).toBeFalsy();
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeTruthy();
|
||||
});
|
||||
it('returns true and configures if eas.json does not exist', async () => {
|
||||
const writeFileMock = jest.spyOn(fs, 'writeFile');
|
||||
writeFileMock.mockImplementation(async (...args) => {
|
||||
const easJsonPath = args[0] as string;
|
||||
const easJsonData = args[1] as string;
|
||||
vol.fromJSON({
|
||||
[easJsonPath]: easJsonData,
|
||||
});
|
||||
});
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeFalsy();
|
||||
const vcsClientMock = jest.mocked(new GitClient());
|
||||
vcsClientMock.showChangedFilesAsync.mockImplementation(async () => {});
|
||||
vcsClientMock.isCommitRequiredAsync.mockImplementation(async () => false);
|
||||
vcsClientMock.trackFileAsync.mockImplementation(async () => {});
|
||||
const result = await ensureProjectConfiguredAsync({
|
||||
projectDir: '.',
|
||||
nonInteractive: false,
|
||||
vcsClient: vcsClientMock as unknown as Client,
|
||||
});
|
||||
expect(result).toBeTruthy();
|
||||
await expect(fs.pathExists(EasJsonAccessor.formatEasJsonPath('.'))).resolves.toBeTruthy();
|
||||
const easJson = await EasJsonAccessor.fromProjectPath('.').readAsync();
|
||||
expect(easJson.cli?.appVersionSource).toEqual(AppVersionSource.REMOTE);
|
||||
});
|
||||
});
|
||||
@@ -107,7 +107,7 @@ export async function prepareAndroidBuildAsync(
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
vcsClient: ctx.vcsClient,
|
||||
env: ctx.buildProfile.env,
|
||||
env: ctx.env,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
ArchiveSource,
|
||||
ArchiveSourceType,
|
||||
BuildJob,
|
||||
FingerprintSourceType,
|
||||
FingerprintSource,
|
||||
Metadata,
|
||||
Platform,
|
||||
} from '@expo/eas-build-job';
|
||||
@@ -13,8 +13,6 @@ 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 {
|
||||
@@ -64,11 +62,11 @@ import {
|
||||
appPlatformEmojis,
|
||||
requestedPlatformDisplayNames,
|
||||
} from '../platform';
|
||||
import { maybeUploadFingerprintAsync } from '../project/maybeUploadFingerprintAsync';
|
||||
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';
|
||||
|
||||
@@ -186,7 +184,7 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
|
||||
return async () => {
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
await runLocalBuildAsync(job, metadata, ctx.localBuildOptions);
|
||||
await runLocalBuildAsync(job, metadata, ctx.localBuildOptions, ctx.env);
|
||||
return undefined;
|
||||
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||
await BuildMutation.updateBuildMetadataAsync(ctx.graphqlClient, {
|
||||
@@ -669,14 +667,14 @@ async function createAndMaybeUploadFingerprintAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
): Promise<{
|
||||
runtimeVersion?: string;
|
||||
fingerprintSource?: Metadata['fingerprintSource'];
|
||||
fingerprintSource?: FingerprintSource;
|
||||
}> {
|
||||
const resolvedRuntimeVersion = await resolveRuntimeVersionAsync({
|
||||
exp: ctx.exp,
|
||||
platform: ctx.platform,
|
||||
workflow: ctx.workflow,
|
||||
projectDir: ctx.projectDir,
|
||||
env: ctx.buildProfile.env,
|
||||
env: ctx.env,
|
||||
cwd: ctx.projectDir,
|
||||
});
|
||||
|
||||
@@ -685,62 +683,19 @@ async function createAndMaybeUploadFingerprintAsync<T extends Platform>(
|
||||
}
|
||||
|
||||
/**
|
||||
* It's ok for fingerprintSources to be empty
|
||||
* It's ok for fingerprintSources or runtimeVersion to be empty
|
||||
* fingerprintSources only exist if the project is using runtimeVersion.policy: fingerprint
|
||||
*/
|
||||
if (!resolvedRuntimeVersion.fingerprint) {
|
||||
if (!resolvedRuntimeVersion.fingerprint || !resolvedRuntimeVersion.runtimeVersion) {
|
||||
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.fingerprint.fingerprintSources,
|
||||
return await maybeUploadFingerprintAsync({
|
||||
runtimeVersion: resolvedRuntimeVersion.runtimeVersion,
|
||||
fingerprint: resolvedRuntimeVersion.fingerprint,
|
||||
graphqlClient: ctx.graphqlClient,
|
||||
localBuildMode: ctx.localBuildOptions.localBuildMode,
|
||||
});
|
||||
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion.runtimeVersion ?? undefined,
|
||||
fingerprintSource: {
|
||||
type: FingerprintSourceType.PATH,
|
||||
path: fingerprintLocation,
|
||||
isDebugFingerprint: resolvedRuntimeVersion.fingerprint.isDebugFingerprintSource,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
isDebugFingerprint: resolvedRuntimeVersion.fingerprint.isDebugFingerprintSource,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
|
||||
import { AppVersionSource, EasJson, EasJsonAccessor } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
@@ -55,6 +55,7 @@ async function configureAsync({
|
||||
const EAS_JSON_DEFAULT: EasJson = {
|
||||
cli: {
|
||||
version: `>= ${easCliVersion}`,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
build: {
|
||||
development: {
|
||||
@@ -64,7 +65,9 @@ const EAS_JSON_DEFAULT: EasJson = {
|
||||
preview: {
|
||||
distribution: 'internal',
|
||||
},
|
||||
production: {},
|
||||
production: {
|
||||
autoIncrement: true,
|
||||
},
|
||||
},
|
||||
submit: {
|
||||
production: {},
|
||||
|
||||
@@ -63,4 +63,5 @@ export interface BuildContext<T extends Platform> {
|
||||
vcsClient: Client;
|
||||
loggerLevel?: LoggerLevel;
|
||||
repack: boolean;
|
||||
env: Record<string, string>;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
buildLoggerLevel,
|
||||
freezeCredentials,
|
||||
repack,
|
||||
env,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
@@ -64,8 +65,11 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
env: Record<string, string>;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
env,
|
||||
});
|
||||
const projectName = exp.slug;
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const workflow = await resolveWorkflowAsync(projectDir, platform, vcsClient);
|
||||
@@ -87,7 +91,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
analytics,
|
||||
env: buildProfile.env,
|
||||
env,
|
||||
easJsonCliConfig,
|
||||
vcsClient,
|
||||
freezeCredentials,
|
||||
@@ -147,6 +151,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
requiredPackageManager,
|
||||
loggerLevel: buildLoggerLevel,
|
||||
repack,
|
||||
env,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EnvironmentVariableEnvironment } from '../graphql/generated';
|
||||
import { EnvironmentVariablesQuery } from '../graphql/queries/EnvironmentVariablesQuery';
|
||||
import Log, { learnMore } from '../log';
|
||||
|
||||
function isEnvironment(env: string): env is EnvironmentVariableEnvironment {
|
||||
return Object.values(EnvironmentVariableEnvironment).includes(
|
||||
env as EnvironmentVariableEnvironment
|
||||
);
|
||||
}
|
||||
|
||||
export async function evaluateConfigWithEnvVarsAsync<Config extends { projectId: string }, Opts>({
|
||||
flags,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
graphqlClient,
|
||||
getProjectConfig,
|
||||
opts,
|
||||
}: {
|
||||
flags: { environment?: string };
|
||||
buildProfile: BuildProfile;
|
||||
buildProfileName: string;
|
||||
graphqlClient: ExpoGraphqlClient | null;
|
||||
opts: Opts;
|
||||
getProjectConfig(opts: Opts): Promise<Config>;
|
||||
}): Promise<Config & { env: Env }> {
|
||||
if (!graphqlClient) {
|
||||
Log.warn('An Expo user account is required to fetch environment variables.');
|
||||
const config = await getProjectConfig(opts);
|
||||
return { env: buildProfile.env ?? {}, ...config };
|
||||
}
|
||||
const { projectId } = await getProjectConfig({ env: buildProfile.env, ...opts });
|
||||
const env = await resolveEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
graphqlClient,
|
||||
projectId,
|
||||
});
|
||||
const config = await getProjectConfig({ ...opts, env });
|
||||
|
||||
return { env, ...config };
|
||||
}
|
||||
|
||||
async function resolveEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile,
|
||||
buildProfileName,
|
||||
graphqlClient,
|
||||
projectId,
|
||||
}: {
|
||||
flags: { environment?: string };
|
||||
buildProfile: BuildProfile;
|
||||
buildProfileName: string;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
}): Promise<Env> {
|
||||
const environment =
|
||||
flags.environment ??
|
||||
buildProfile.environment?.toUpperCase() ??
|
||||
process.env.EAS_CURRENT_ENVIRONMENT;
|
||||
|
||||
if (!environment || !isEnvironment(environment)) {
|
||||
Log.log(
|
||||
`Loaded "env" configuration for the "${buildProfileName}" profile: ${
|
||||
buildProfile.env && Object.keys(buildProfile.env).length > 0
|
||||
? Object.keys(buildProfile.env).join(', ')
|
||||
: 'no environment variables specified'
|
||||
}. ${learnMore('https://docs.expo.dev/build-reference/variables/')}`
|
||||
);
|
||||
return { ...buildProfile.env };
|
||||
}
|
||||
|
||||
try {
|
||||
const environmentVariables = await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
appId: projectId,
|
||||
environment,
|
||||
}
|
||||
);
|
||||
const serverEnvVars = Object.fromEntries(
|
||||
environmentVariables
|
||||
.filter(({ name, value }) => name && value)
|
||||
.map(({ name, value }) => [name, value])
|
||||
) as Record<string, string>;
|
||||
|
||||
const envVarsWithSource: Record<string, 'build profile' | 'EAS server'> = {
|
||||
...Object.fromEntries(Object.keys(serverEnvVars).map(key => [key, 'EAS server'])),
|
||||
...(buildProfile.env
|
||||
? Object.fromEntries(Object.keys(buildProfile.env).map(key => [key, 'build profile']))
|
||||
: null),
|
||||
};
|
||||
Log.log(
|
||||
`Loaded "env" configuration for the "${buildProfileName}" profile and "${environment.toLowerCase()}" environment: ${
|
||||
Object.keys(envVarsWithSource).length > 0
|
||||
? Object.keys(envVarsWithSource)
|
||||
.map(key => `${key} (source: ${envVarsWithSource[key]})`)
|
||||
.join('\n')
|
||||
: 'no environment variables specified'
|
||||
}\n${learnMore('https://docs.expo.dev/build-reference/variables/')}`
|
||||
);
|
||||
|
||||
return { ...serverEnvVars, ...buildProfile.env };
|
||||
} catch (e) {
|
||||
Log.error(`Failed to pull env variables for environment ${environment} from EAS servers`);
|
||||
Log.error(e);
|
||||
Log.error(
|
||||
'This can possibly be a bug in EAS/EAS CLI. Report it here: https://github.com/expo/eas-cli/issues'
|
||||
);
|
||||
return { ...buildProfile.env };
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ArchiveSourceType,
|
||||
BuildMode,
|
||||
BuildTrigger,
|
||||
FingerprintSourceType,
|
||||
Metadata,
|
||||
Workflow,
|
||||
} from '@expo/eas-build-job';
|
||||
@@ -15,8 +16,10 @@ import {
|
||||
BuildMetadataInput,
|
||||
BuildWorkflow,
|
||||
DistributionType,
|
||||
FingerprintSourceInput,
|
||||
BuildMode as GraphQLBuildMode,
|
||||
BuildTrigger as GraphQLBuildTrigger,
|
||||
FingerprintSourceType as GraphQLFingeprintSourceType,
|
||||
ProjectArchiveSourceInput,
|
||||
ProjectArchiveSourceType,
|
||||
WorkerLoggerLevel,
|
||||
@@ -42,7 +45,8 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
|
||||
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
||||
return {
|
||||
...metadata,
|
||||
fingerprintSource: metadata.fingerprintSource as BuildMetadataInput['fingerprintSource'],
|
||||
fingerprintSource:
|
||||
metadata.fingerprintSource && transformFingerprintSource(metadata.fingerprintSource),
|
||||
credentialsSource:
|
||||
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
|
||||
distribution: metadata.distribution && transformDistribution(metadata.distribution),
|
||||
@@ -53,6 +57,20 @@ export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
||||
};
|
||||
}
|
||||
|
||||
export function transformFingerprintSource(
|
||||
fingerprintSource: NonNullable<Metadata['fingerprintSource']>
|
||||
): FingerprintSourceInput | null {
|
||||
if (fingerprintSource.type !== FingerprintSourceType.GCS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: GraphQLFingeprintSourceType.Gcs,
|
||||
bucketKey: fingerprintSource.bucketKey,
|
||||
isDebugFingerprint: fingerprintSource.isDebugFingerprint,
|
||||
};
|
||||
}
|
||||
|
||||
function transformCredentialsSource(
|
||||
credentialsSource: Metadata['credentialsSource']
|
||||
): BuildCredentialsSource {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { vol } from 'memfs';
|
||||
import os from 'os';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { learnMore } from '../../../log';
|
||||
import { readPlistAsync } from '../../../utils/plist';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import {
|
||||
@@ -316,7 +317,9 @@ describe(readShortVersionAsync, () => {
|
||||
vcsClient
|
||||
)
|
||||
).rejects.toThrowError(
|
||||
'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028'
|
||||
`The required format for "CFBundleShortVersionString" in Info.plist is one to three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods. Current value: 0.0.7.1.028. Edit the "CFBundleShortVersionString" in your Info.plist to match the required format. ${learnMore(
|
||||
'https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
export async function createIosContextAsync(
|
||||
ctx: CommonContext<Platform.IOS>
|
||||
): Promise<IosBuildContext> {
|
||||
const { buildProfile } = ctx;
|
||||
const { buildProfile, env } = ctx;
|
||||
|
||||
if (ctx.workflow === Workflow.MANAGED) {
|
||||
await ensureBundleIdentifierIsDefinedForManagedProjectAsync(ctx);
|
||||
@@ -47,7 +47,7 @@ export async function createIosContextAsync(
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
xcodeBuildContext,
|
||||
env: buildProfile.env,
|
||||
env,
|
||||
vcsClient: ctx.vcsClient,
|
||||
});
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
@@ -89,7 +89,7 @@ export async function prepareIosBuildAsync(
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
vcsClient: ctx.vcsClient,
|
||||
env: ctx.buildProfile.env,
|
||||
env: ctx.env,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Target } from '../../credentials/ios/types';
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||
import Log from '../../log';
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { findApplicationTarget } from '../../project/ios/target';
|
||||
import { getNextBuildNumber, isValidBuildNumber } from '../../project/ios/versions';
|
||||
@@ -23,7 +23,7 @@ import { Client } from '../../vcs/vcs';
|
||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||
|
||||
const SHORT_VERSION_REGEX = /^\d+.\d+.\d+$/;
|
||||
const SHORT_VERSION_REGEX = /^\d+(\.\d+){0,2}$/;
|
||||
|
||||
export enum BumpStrategy {
|
||||
APP_VERSION,
|
||||
@@ -100,11 +100,27 @@ export async function bumpVersionInAppJsonAsync({
|
||||
}
|
||||
}
|
||||
|
||||
function validateShortVersion(shortVersion: string | undefined): void {
|
||||
function validateShortVersion({
|
||||
shortVersion,
|
||||
workflow,
|
||||
}: {
|
||||
shortVersion: string | undefined;
|
||||
workflow: Workflow;
|
||||
}): void {
|
||||
if (shortVersion && !SHORT_VERSION_REGEX.test(shortVersion)) {
|
||||
throw new Error(
|
||||
`CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: ${shortVersion}`
|
||||
);
|
||||
if (workflow === Workflow.MANAGED) {
|
||||
throw new Error(
|
||||
`The required format for "version" field from app.json/app.config.ts is one to three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods. Current value: ${shortVersion}. Edit the "version" field in your app.json/app.config.ts to match the required format. ${learnMore(
|
||||
'https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring'
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`The required format for "CFBundleShortVersionString" in Info.plist is one to three period-separated integers, such as 10.14.1. The string can only contain numeric characters (0-9) and periods. Current value: ${shortVersion}. Edit the "CFBundleShortVersionString" in your Info.plist to match the required format. ${learnMore(
|
||||
'https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +138,10 @@ export async function readShortVersionAsync(
|
||||
infoPlist.CFBundleShortVersionString &&
|
||||
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings);
|
||||
|
||||
validateShortVersion(shortVersion);
|
||||
validateShortVersion({ shortVersion, workflow });
|
||||
return shortVersion;
|
||||
} else {
|
||||
validateShortVersion(exp.version);
|
||||
validateShortVersion({ shortVersion: exp.version, workflow });
|
||||
return exp.version;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.131';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.135';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
@@ -39,7 +39,8 @@ export interface LocalBuildOptions {
|
||||
export async function runLocalBuildAsync(
|
||||
job: Job,
|
||||
metadata: Metadata,
|
||||
options: LocalBuildOptions
|
||||
options: LocalBuildOptions,
|
||||
env: Record<string, string>
|
||||
): Promise<void> {
|
||||
const { command, args } = await getCommandAndArgsAsync(job, metadata);
|
||||
let spinner;
|
||||
@@ -57,6 +58,7 @@ export async function runLocalBuildAsync(
|
||||
const spawnPromise = spawnAsync(command, args, {
|
||||
stdio: options.verbose ? 'inherit' : 'pipe',
|
||||
env: {
|
||||
...env,
|
||||
...process.env,
|
||||
EAS_LOCAL_BUILD_WORKINGDIR: options.workingdir ?? process.env.EAS_LOCAL_BUILD_WORKINGDIR,
|
||||
...(options.skipCleanup || options.skipNativeBuild
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ExpoConfig } from '@expo/config-types';
|
||||
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
AppVersionSource,
|
||||
BuildProfile,
|
||||
EasJson,
|
||||
EasJsonAccessor,
|
||||
EasJsonUtils,
|
||||
@@ -19,6 +18,7 @@ import { BuildRequestSender, MaybeBuildFragment, waitForBuildEndAsync } from './
|
||||
import { ensureProjectConfiguredAsync } from './configure';
|
||||
import { BuildContext } from './context';
|
||||
import { createBuildContextAsync } from './createContext';
|
||||
import { evaluateConfigWithEnvVarsAsync } from './evaluateConfigWithEnvVarsAsync';
|
||||
import { prepareIosBuildAsync } from './ios/build';
|
||||
import { LocalBuildMode, LocalBuildOptions } from './local';
|
||||
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
BuildFragment,
|
||||
BuildStatus,
|
||||
BuildWithSubmissionsFragment,
|
||||
EnvironmentVariableEnvironment,
|
||||
SubmissionFragment,
|
||||
} from '../graphql/generated';
|
||||
import { BuildQuery } from '../graphql/queries/BuildQuery';
|
||||
@@ -57,8 +58,9 @@ import {
|
||||
validateAppVersionRuntimePolicySupportAsync,
|
||||
} from '../project/projectUtils';
|
||||
import {
|
||||
ensureAppVersionSourceIsSetAsync,
|
||||
validateAppConfigForRemoteVersionSource,
|
||||
validateBuildProfileVersionSettings,
|
||||
validateBuildProfileVersionSettingsAsync,
|
||||
} from '../project/remoteVersionSource';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { runAsync } from '../run/run';
|
||||
@@ -96,6 +98,7 @@ export interface BuildFlags {
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync(
|
||||
@@ -129,13 +132,6 @@ export async function runBuildAndSubmitAsync(
|
||||
profileName: flags.profile ?? undefined,
|
||||
projectDir,
|
||||
});
|
||||
Log.log(
|
||||
`Loaded "env" configuration for the "${buildProfiles[0].profileName}" profile: ${
|
||||
buildProfiles[0].profile.env
|
||||
? Object.keys(buildProfiles[0].profile.env).join(', ')
|
||||
: 'no environment variables specified'
|
||||
}. ${learnMore('https://docs.expo.dev/build-reference/variables/')}`
|
||||
);
|
||||
|
||||
for (const buildProfile of buildProfiles) {
|
||||
if (buildProfile.profile.image && ['default', 'stable'].includes(buildProfile.profile.image)) {
|
||||
@@ -173,7 +169,12 @@ export async function runBuildAndSubmitAsync(
|
||||
const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } =
|
||||
{};
|
||||
for (const buildProfile of buildProfiles) {
|
||||
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
||||
await validateBuildProfileVersionSettingsAsync(
|
||||
buildProfile,
|
||||
easJsonCliConfig,
|
||||
projectDir,
|
||||
flags
|
||||
);
|
||||
const maybeMetadata = await validateCustomBuildConfigAsync({
|
||||
projectDir,
|
||||
profile: buildProfile.profile,
|
||||
@@ -192,6 +193,16 @@ export async function runBuildAndSubmitAsync(
|
||||
|
||||
for (const buildProfile of buildProfiles) {
|
||||
const platform = toAppPlatform(buildProfile.platform);
|
||||
|
||||
const { env } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile: buildProfile.profile,
|
||||
buildProfileName: buildProfile.profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: buildProfile.profile.env },
|
||||
});
|
||||
|
||||
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
|
||||
projectDir,
|
||||
flags,
|
||||
@@ -204,6 +215,7 @@ export async function runBuildAndSubmitAsync(
|
||||
vcsClient,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
|
||||
env,
|
||||
});
|
||||
if (maybeBuild) {
|
||||
startedBuilds.push({ build: maybeBuild, buildProfile });
|
||||
@@ -251,7 +263,6 @@ export async function runBuildAndSubmitAsync(
|
||||
buildCtx: nullthrows(buildCtxByPlatform[startedBuild.build.platform]),
|
||||
moreBuilds: startedBuilds.length > 1,
|
||||
projectDir,
|
||||
buildProfile: startedBuild.buildProfile.profile,
|
||||
submitProfile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
selectedSubmitProfileName: flags.submitProfile,
|
||||
@@ -340,6 +351,7 @@ async function prepareAndStartBuildAsync({
|
||||
vcsClient,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
customBuildConfigMetadata,
|
||||
env,
|
||||
}: {
|
||||
projectDir: string;
|
||||
flags: BuildFlags;
|
||||
@@ -352,6 +364,7 @@ async function prepareAndStartBuildAsync({
|
||||
vcsClient: Client;
|
||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||
env: Env;
|
||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||
const buildCtx = await createBuildContextAsync({
|
||||
buildProfileName: buildProfile.profileName,
|
||||
@@ -374,6 +387,7 @@ async function prepareAndStartBuildAsync({
|
||||
buildLoggerLevel: flags.buildLoggerLevel,
|
||||
freezeCredentials: flags.freezeCredentials,
|
||||
repack: flags.repack,
|
||||
env,
|
||||
});
|
||||
|
||||
if (moreBuilds) {
|
||||
@@ -412,6 +426,25 @@ async function prepareAndStartBuildAsync({
|
||||
}
|
||||
|
||||
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
||||
if (
|
||||
easJsonCliConfig?.appVersionSource === undefined &&
|
||||
buildProfile.profile.autoIncrement !== 'version'
|
||||
) {
|
||||
if (buildProfile.profile.autoIncrement !== true) {
|
||||
Log.warn(
|
||||
`The field "cli.appVersionSource" is not set, but it will be required in the future. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/'
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
easJsonCliConfig = await ensureAppVersionSourceIsSetAsync(
|
||||
easJsonAccessor,
|
||||
easJsonCliConfig,
|
||||
flags.nonInteractive
|
||||
);
|
||||
}
|
||||
}
|
||||
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
||||
validateAppConfigForRemoteVersionSource(buildCtx.exp, buildProfile.platform);
|
||||
}
|
||||
@@ -448,7 +481,6 @@ async function prepareAndStartSubmissionAsync({
|
||||
buildCtx,
|
||||
moreBuilds,
|
||||
projectDir,
|
||||
buildProfile,
|
||||
submitProfile,
|
||||
selectedSubmitProfileName,
|
||||
nonInteractive,
|
||||
@@ -457,7 +489,6 @@ async function prepareAndStartSubmissionAsync({
|
||||
buildCtx: BuildContext<Platform>;
|
||||
moreBuilds: boolean;
|
||||
projectDir: string;
|
||||
buildProfile: BuildProfile;
|
||||
submitProfile: SubmitProfile;
|
||||
selectedSubmitProfileName?: string;
|
||||
nonInteractive: boolean;
|
||||
@@ -469,7 +500,7 @@ async function prepareAndStartSubmissionAsync({
|
||||
profile: submitProfile,
|
||||
archiveFlags: { id: build.id },
|
||||
nonInteractive,
|
||||
env: buildProfile.env,
|
||||
env: buildCtx.env,
|
||||
credentialsCtx: buildCtx.credentialsCtx,
|
||||
applicationIdentifier: buildCtx.android?.applicationId ?? buildCtx.ios?.bundleIdentifier,
|
||||
actor: buildCtx.user,
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { ResourceClass } from '@expo/eas-json';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
|
||||
import { LocalBuildOptions } from './local';
|
||||
import { RequestedPlatform } from '../platform';
|
||||
|
||||
export enum BuildStatus {
|
||||
NEW = 'new',
|
||||
IN_QUEUE = 'in-queue',
|
||||
@@ -14,3 +20,20 @@ export enum BuildDistributionType {
|
||||
/** @deprecated Use simulator flag instead */
|
||||
SIMULATOR = 'simulator',
|
||||
}
|
||||
|
||||
export interface BuildFlags {
|
||||
requestedPlatform: RequestedPlatform;
|
||||
profile?: string;
|
||||
nonInteractive: boolean;
|
||||
wait: boolean;
|
||||
clearCache: boolean;
|
||||
json: boolean;
|
||||
autoSubmit: boolean;
|
||||
submitProfile?: string;
|
||||
localBuildOptions: LocalBuildOptions;
|
||||
resourceClass?: ResourceClass;
|
||||
message?: string;
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
}
|
||||
@@ -9,9 +9,9 @@ import Log, { learnMore } from '../log';
|
||||
import { isPNGAsync } from '../utils/image';
|
||||
|
||||
export function checkNodeEnvVariable(ctx: CommonContext<Platform>): void {
|
||||
if (ctx.buildProfile.env?.NODE_ENV === 'production') {
|
||||
if (ctx.env?.NODE_ENV === 'production') {
|
||||
Log.warn(
|
||||
'You set NODE_ENV=production in the build profile. Remember that it will be available during the entire build process. In particular, it will make yarn/npm install only production packages.'
|
||||
'You set NODE_ENV=production in the build profile or environment variables. Remember that it will be available during the entire build process. In particular, it will make yarn/npm install only production packages.'
|
||||
);
|
||||
Log.newLine();
|
||||
}
|
||||
|
||||
@@ -18,13 +18,15 @@ import { Actor, getActorUsername } from '../../../user/User';
|
||||
* Save an EAS project ID to the appropriate field in the app config.
|
||||
*
|
||||
* @deprecated Should not be used outside of context functions except in the init command.
|
||||
* @deprecated Starting from `@expo/config` from SDK 52, the `modifyConfigAsync` function is merging existing data. Once this is released, we can use that instead of manually merging.
|
||||
*/
|
||||
export async function saveProjectIdToAppConfigAsync(
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
options: { env?: Env } = {}
|
||||
): Promise<void> {
|
||||
const exp = getPrivateExpoConfig(projectDir, options);
|
||||
// NOTE(cedric): we disable plugins to avoid writing plugin-generated content to `expo.extra`
|
||||
const exp = getPrivateExpoConfig(projectDir, { skipPlugins: true, ...options });
|
||||
const result = await createOrModifyExpoConfigAsync(
|
||||
projectDir,
|
||||
{
|
||||
|
||||
@@ -38,6 +38,21 @@ export const EASEnvironmentFlag = {
|
||||
}),
|
||||
};
|
||||
|
||||
// NOTE: Used in build commands, should be replaced with EASEnvironmentFlag when
|
||||
// the feature is public
|
||||
export const EASEnvironmentFlagHidden = {
|
||||
environment: Flags.enum<EnvironmentVariableEnvironment>({
|
||||
description: "Environment variable's environment",
|
||||
parse: upperCaseAsync,
|
||||
hidden: true,
|
||||
options: mapToLowercase([
|
||||
EnvironmentVariableEnvironment.Development,
|
||||
EnvironmentVariableEnvironment.Preview,
|
||||
EnvironmentVariableEnvironment.Production,
|
||||
]),
|
||||
}),
|
||||
};
|
||||
|
||||
export const EASVariableFormatFlag = {
|
||||
format: Flags.enum({
|
||||
description: 'Output format',
|
||||
|
||||
@@ -10,8 +10,8 @@ import path from 'path';
|
||||
import { LocalBuildMode } from '../../build/local';
|
||||
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { StatuspageServiceName } from '../../graphql/generated';
|
||||
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { EnvironmentVariableEnvironment, StatuspageServiceName } from '../../graphql/generated';
|
||||
import Log, { link } from '../../log';
|
||||
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
|
||||
import { selectAsync } from '../../prompts';
|
||||
@@ -37,6 +37,7 @@ interface RawBuildFlags {
|
||||
'build-logger-level'?: LoggerLevel;
|
||||
'freeze-credentials': boolean;
|
||||
repack: boolean;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
}
|
||||
|
||||
export default class Build extends EasCommand {
|
||||
@@ -116,6 +117,7 @@ export default class Build extends EasCommand {
|
||||
description: 'Use the golden dev client build repack flow as it works for onboarding',
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
...EASEnvironmentFlagHidden,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -230,6 +232,7 @@ export default class Build extends EasCommand {
|
||||
buildLoggerLevel: flags['build-logger-level'],
|
||||
freezeCredentials: flags['freeze-credentials'],
|
||||
repack: flags.repack,
|
||||
environment: flags.environment,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@ import chalk from 'chalk';
|
||||
|
||||
import { handleDeprecatedEasJsonAsync } from '.';
|
||||
import { waitForBuildEndAsync } from '../../build/build';
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../../build/evaluateConfigWithEnvVarsAsync';
|
||||
import { ensureIosCredentialsForBuildResignAsync } from '../../build/ios/credentials';
|
||||
import { prepareCredentialsToResign } from '../../build/ios/prepareJob';
|
||||
import { listAndSelectBuildOnAppAsync } from '../../build/queries';
|
||||
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { EasPaginatedQueryFlags } from '../../commandUtils/pagination';
|
||||
import { CredentialsContext } from '../../credentials/context';
|
||||
import {
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
BuildStatus,
|
||||
BuildWorkflow,
|
||||
DistributionType,
|
||||
EnvironmentVariableEnvironment,
|
||||
IosJobOverridesInput,
|
||||
ProjectArchiveSourceType,
|
||||
StatuspageServiceName,
|
||||
@@ -46,6 +48,7 @@ interface BuildResignFlags {
|
||||
sourceProfile?: string;
|
||||
maybeBuildId?: string;
|
||||
wait: boolean;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
}
|
||||
interface RawBuildResignFlags {
|
||||
json: boolean;
|
||||
@@ -57,6 +60,7 @@ interface RawBuildResignFlags {
|
||||
'source-profile': string | undefined;
|
||||
wait: boolean;
|
||||
id: string | undefined;
|
||||
environment: EnvironmentVariableEnvironment | undefined;
|
||||
}
|
||||
|
||||
export default class BuildResign extends EasCommand {
|
||||
@@ -89,6 +93,7 @@ export default class BuildResign extends EasCommand {
|
||||
}),
|
||||
...EasPaginatedQueryFlags,
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
...EASEnvironmentFlagHidden,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -140,7 +145,16 @@ export default class BuildResign extends EasCommand {
|
||||
platform,
|
||||
flags.targetProfile ?? 'production'
|
||||
);
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||
|
||||
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile,
|
||||
buildProfileName: flags.targetProfile ?? 'production',
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: buildProfile.env },
|
||||
});
|
||||
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const build = await this.ensureBuildSelectedAsync(
|
||||
{
|
||||
@@ -161,7 +175,7 @@ export default class BuildResign extends EasCommand {
|
||||
user: actor,
|
||||
graphqlClient,
|
||||
analytics,
|
||||
env: buildProfile.env,
|
||||
env,
|
||||
easJsonCliConfig,
|
||||
vcsClient,
|
||||
});
|
||||
@@ -181,7 +195,7 @@ export default class BuildResign extends EasCommand {
|
||||
projectDir,
|
||||
exp,
|
||||
xcodeBuildContext,
|
||||
env: buildProfile.env,
|
||||
env,
|
||||
vcsClient,
|
||||
});
|
||||
const credentialsResult = await ensureIosCredentialsForBuildResignAsync(
|
||||
@@ -248,6 +262,7 @@ export default class BuildResign extends EasCommand {
|
||||
targetProfile: flags['target-profile'],
|
||||
maybeBuildId: flags.id,
|
||||
wait: flags.wait,
|
||||
environment: flags.environment,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,12 @@ import { EasJsonAccessor } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import {
|
||||
EASEnvironmentFlagHidden,
|
||||
EasNonInteractiveAndJsonFlags,
|
||||
} from '../../../commandUtils/flags';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||
import Log from '../../../log';
|
||||
@@ -31,6 +35,7 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
...EASEnvironmentFlagHidden,
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
@@ -74,8 +79,13 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
});
|
||||
const results: { [key in Platform]?: string } = {};
|
||||
for (const { profile, platform } of buildProfiles) {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
env: profile.env,
|
||||
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile: profile,
|
||||
buildProfileName: flags.profile ?? 'production',
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: profile.env },
|
||||
});
|
||||
|
||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||
@@ -89,6 +99,7 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
platform,
|
||||
vcsClient,
|
||||
nonInteractive: flags['non-interactive'],
|
||||
env,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -4,7 +4,9 @@ import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlagHidden } from '../../../commandUtils/flags';
|
||||
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||
@@ -35,6 +37,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
...EASEnvironmentFlagHidden,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -64,7 +67,14 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
flags.profile ?? undefined
|
||||
);
|
||||
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: profile.env });
|
||||
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile: profile,
|
||||
buildProfileName: flags.profile ?? 'production',
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: profile.env },
|
||||
});
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||
@@ -78,6 +88,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
platform,
|
||||
vcsClient,
|
||||
nonInteractive: false,
|
||||
env,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -5,8 +5,10 @@ import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../../build/android/version';
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
|
||||
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../../build/ios/version';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlagHidden } from '../../../commandUtils/flags';
|
||||
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||
import Log from '../../../log';
|
||||
@@ -54,6 +56,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
...EASEnvironmentFlagHidden,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -87,8 +90,13 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
projectDir,
|
||||
});
|
||||
for (const profileInfo of buildProfiles) {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
env: profileInfo.profile.env,
|
||||
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile: profileInfo.profile,
|
||||
buildProfileName: profileInfo.profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPrivateProjectConfigAsync,
|
||||
opts: { env: profileInfo.profile.env },
|
||||
});
|
||||
|
||||
validateAppConfigForRemoteVersionSource(exp, profileInfo.platform);
|
||||
@@ -103,6 +111,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
platform: profileInfo.platform,
|
||||
vcsClient,
|
||||
nonInteractive: false,
|
||||
env,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -4,8 +4,10 @@ import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { evaluateConfigWithEnvVarsAsync } from '../build/evaluateConfigWithEnvVarsAsync';
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
|
||||
import { createGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
|
||||
import { toAppPlatform } from '../graphql/types/AppPlatform';
|
||||
import Log from '../log';
|
||||
import { appPlatformEmojis } from '../platform';
|
||||
@@ -27,12 +29,14 @@ export default class Config extends EasCommand {
|
||||
'eas-json-only': Flags.boolean({
|
||||
hidden: true,
|
||||
}),
|
||||
...EASEnvironmentFlagHidden,
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.ProjectDir,
|
||||
...this.ContextOptions.SessionManagment,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -40,10 +44,15 @@ export default class Config extends EasCommand {
|
||||
if (flags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
const { platform: maybePlatform, profile: maybeProfile } = flags;
|
||||
const { getDynamicPublicProjectConfigAsync, projectDir } = await this.getContextAsync(Config, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
const {
|
||||
platform: maybePlatform,
|
||||
profile: maybeProfile,
|
||||
'non-interactive': nonInteractive,
|
||||
} = flags;
|
||||
const { getDynamicPublicProjectConfigAsync, projectDir, sessionManager } =
|
||||
await this.getContextAsync(Config, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
const accessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const profileName =
|
||||
@@ -80,8 +89,17 @@ export default class Config extends EasCommand {
|
||||
Log.log(JSON.stringify(profile, null, 2));
|
||||
}
|
||||
} else {
|
||||
const { exp: appConfig } = await getDynamicPublicProjectConfigAsync({
|
||||
env: profile.env,
|
||||
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
|
||||
nonInteractive,
|
||||
});
|
||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||
const { exp: appConfig } = await evaluateConfigWithEnvVarsAsync({
|
||||
flags,
|
||||
buildProfile: profile,
|
||||
buildProfileName: profileName,
|
||||
graphqlClient,
|
||||
getProjectConfig: getDynamicPublicProjectConfigAsync,
|
||||
opts: { env: profile.env },
|
||||
});
|
||||
|
||||
if (flags.json) {
|
||||
|
||||
+7
-6
@@ -114,11 +114,10 @@ export default class EnvironmentVariableCreate extends EasCommand {
|
||||
if (!environment) {
|
||||
environment = await promptVariableEnvironmentAsync(nonInteractive);
|
||||
}
|
||||
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
environment
|
||||
);
|
||||
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
});
|
||||
const existingVariable = existingVariables.find(variable => variable.name === name);
|
||||
|
||||
if (existingVariable) {
|
||||
@@ -191,7 +190,9 @@ export default class EnvironmentVariableCreate extends EasCommand {
|
||||
`Created a new variable ${chalk.bold(name)} on project ${chalk.bold(projectDisplayName)}.`
|
||||
);
|
||||
} else if (scope === EnvironmentVariableScope.Shared) {
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId);
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
});
|
||||
const existingVariable = sharedVariables.find(variable => variable.name === name);
|
||||
if (existingVariable) {
|
||||
throw new Error(
|
||||
|
||||
+5
-2
@@ -58,8 +58,11 @@ export default class EnvironmentVariableDelete extends EasCommand {
|
||||
|
||||
const variables =
|
||||
scope === EnvironmentVariableScope.Project && environment
|
||||
? await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, projectId, environment)
|
||||
: await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId);
|
||||
? await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
})
|
||||
: await EnvironmentVariablesQuery.sharedAsync(graphqlClient, { appId: projectId });
|
||||
|
||||
if (!name) {
|
||||
({ name } = await promptAsync({
|
||||
|
||||
+15
-10
@@ -72,7 +72,6 @@ export default class EnvironmentVariableGet extends EasCommand {
|
||||
if (!environment && scope === EnvironmentVariableScope.Project) {
|
||||
environment = await promptVariableEnvironmentAsync(nonInteractive);
|
||||
}
|
||||
|
||||
const variable = await getVariableAsync(graphqlClient, scope, projectId, name, environment);
|
||||
|
||||
if (!variable) {
|
||||
@@ -117,26 +116,32 @@ async function getVariableAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
scope: string,
|
||||
projectId: string,
|
||||
name: string,
|
||||
name: string | undefined,
|
||||
environment: string | undefined
|
||||
): Promise<EnvironmentVariableFragment | null> {
|
||||
if (!environment && scope === EnvironmentVariableScope.Project) {
|
||||
throw new Error('Environment is required.');
|
||||
}
|
||||
if (!name) {
|
||||
throw new Error("Variable name is required. Run the command with '--name VARIABLE_NAME' flag.");
|
||||
}
|
||||
if (environment && scope === EnvironmentVariableScope.Project) {
|
||||
const appVariables = await EnvironmentVariablesQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
const appVariables = await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
[name]
|
||||
);
|
||||
filterNames: [name],
|
||||
});
|
||||
return appVariables[0];
|
||||
}
|
||||
|
||||
if (scope === EnvironmentVariableScope.Shared) {
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId, [
|
||||
name,
|
||||
]);
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedWithSensitiveAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
appId: projectId,
|
||||
filterNames: [name],
|
||||
}
|
||||
);
|
||||
return sharedVariables[0];
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -40,7 +40,9 @@ export default class EnvironmentVariableLink extends EasCommand {
|
||||
});
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
const variables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId);
|
||||
const variables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
name = await selectAsync(
|
||||
|
||||
+52
-6
@@ -1,12 +1,18 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import {
|
||||
EASEnvironmentFlag,
|
||||
EASVariableFormatFlag,
|
||||
EASVariableScopeFlag,
|
||||
} from '../../commandUtils/flags';
|
||||
import { EnvironmentVariableScope } from '../../graphql/generated';
|
||||
import {
|
||||
EnvironmentVariableEnvironment,
|
||||
EnvironmentVariableFragment,
|
||||
EnvironmentVariableScope,
|
||||
} from '../../graphql/generated';
|
||||
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
|
||||
import Log from '../../log';
|
||||
import { formatVariable } from '../../utils/formatVariable';
|
||||
@@ -23,6 +29,10 @@ export default class EnvironmentValueList extends EasCommand {
|
||||
};
|
||||
|
||||
static override flags = {
|
||||
'include-sensitive': Flags.boolean({
|
||||
description: 'Display sensitive values in the output',
|
||||
default: false,
|
||||
}),
|
||||
...EASVariableFormatFlag,
|
||||
...EASVariableScopeFlag,
|
||||
...EASEnvironmentFlag,
|
||||
@@ -30,7 +40,7 @@ export default class EnvironmentValueList extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
let {
|
||||
flags: { environment, format, scope },
|
||||
flags: { environment, format, scope, 'include-sensitive': includeSensitive },
|
||||
} = await this.parse(EnvironmentValueList);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
@@ -43,10 +53,12 @@ export default class EnvironmentValueList extends EasCommand {
|
||||
environment = await promptVariableEnvironmentAsync(false);
|
||||
}
|
||||
|
||||
const variables =
|
||||
scope === EnvironmentVariableScope.Project && environment
|
||||
? await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, projectId, environment)
|
||||
: await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId);
|
||||
const variables = await this.getVariablesForScopeAsync(graphqlClient, {
|
||||
scope,
|
||||
includingSensitive: includeSensitive,
|
||||
environment,
|
||||
projectId,
|
||||
});
|
||||
|
||||
if (format === 'short') {
|
||||
for (const variable of variables) {
|
||||
@@ -69,4 +81,38 @@ export default class EnvironmentValueList extends EasCommand {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async getVariablesForScopeAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
scope,
|
||||
includingSensitive,
|
||||
environment,
|
||||
projectId,
|
||||
}: {
|
||||
scope: EnvironmentVariableScope;
|
||||
includingSensitive: boolean;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
projectId: string;
|
||||
}
|
||||
): Promise<EnvironmentVariableFragment[]> {
|
||||
if (scope === EnvironmentVariableScope.Project && environment) {
|
||||
if (includingSensitive) {
|
||||
return await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
});
|
||||
}
|
||||
return await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
});
|
||||
}
|
||||
|
||||
return includingSensitive
|
||||
? await EnvironmentVariablesQuery.sharedWithSensitiveAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
})
|
||||
: await EnvironmentVariablesQuery.sharedAsync(graphqlClient, { appId: projectId });
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlag, EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||
import { EnvironmentVariableFragment } from '../../graphql/generated';
|
||||
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
|
||||
import Log from '../../log';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { promptVariableEnvironmentAsync } from '../../utils/prompts';
|
||||
|
||||
export default class EnvironmentVariablePull extends EasCommand {
|
||||
static override description = 'pull env file';
|
||||
|
||||
static override hidden = true;
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
static override flags = {
|
||||
...EASEnvironmentFlag,
|
||||
...EASNonInteractiveFlag,
|
||||
path: Flags.string({
|
||||
description: 'Path to the result `.env` file',
|
||||
default: '.env.local',
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
let {
|
||||
flags: { environment, path: targetPath, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(EnvironmentVariablePull);
|
||||
|
||||
if (!environment) {
|
||||
environment = await promptVariableEnvironmentAsync(nonInteractive);
|
||||
}
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(EnvironmentVariablePull, {
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
targetPath = targetPath ?? '.env.local';
|
||||
|
||||
const environmentVariables = await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
appId: projectId,
|
||||
environment,
|
||||
}
|
||||
);
|
||||
|
||||
if (!nonInteractive && (await fs.exists(targetPath))) {
|
||||
const result = await confirmAsync({
|
||||
message: `File ${targetPath} already exists. Do you want to overwrite it?`,
|
||||
});
|
||||
if (!result) {
|
||||
Log.log('Aborting...');
|
||||
throw new Error(`File ${targetPath} already exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
const filePrefix = `# Environment: ${environment}\n\n`;
|
||||
|
||||
const envFileContent = environmentVariables
|
||||
.map((variable: EnvironmentVariableFragment) => {
|
||||
if (variable.value === null) {
|
||||
return `# ${variable.name}=***** (secret variables are not available for reading)`;
|
||||
}
|
||||
return `${variable.name}=${variable.value}`;
|
||||
})
|
||||
.join('\n');
|
||||
await fs.writeFile(targetPath, filePrefix + envFileContent);
|
||||
|
||||
const secretEnvVariables = environmentVariables.filter(
|
||||
(variable: EnvironmentVariableFragment) => variable.value === null
|
||||
);
|
||||
if (secretEnvVariables.length > 0) {
|
||||
Log.warn(
|
||||
`The eas env:pull command tried to pull environment variables with "secret" visibility. The variables with "secret" visibility are not available for reading, therefore thet were marked as "*****" in the generated .env file. Provide values for these manually in ${targetPath} if needed. Skipped variables: ${secretEnvVariables
|
||||
.map(v => v.name)
|
||||
.join('\n')}`
|
||||
);
|
||||
Log.warn();
|
||||
}
|
||||
|
||||
Log.log(
|
||||
`Pulled environment variables from ${environment.toLowerCase()} environment to ${targetPath}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import dotenv from 'dotenv';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlag } from '../../commandUtils/flags';
|
||||
import {
|
||||
EnvironmentVariableEnvironment,
|
||||
EnvironmentVariableFragment,
|
||||
EnvironmentVariableScope,
|
||||
EnvironmentVariableVisibility,
|
||||
} from '../../graphql/generated';
|
||||
import {
|
||||
EnvironmentVariableMutation,
|
||||
EnvironmentVariablePushInput,
|
||||
} from '../../graphql/mutations/EnvironmentVariableMutation';
|
||||
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
|
||||
import Log from '../../log';
|
||||
import { confirmAsync, promptAsync } from '../../prompts';
|
||||
|
||||
type PushFlags = {
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
path: string;
|
||||
};
|
||||
|
||||
export default class EnvironmentVariablePush extends EasCommand {
|
||||
static override description = 'push env file';
|
||||
|
||||
static override hidden = true;
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
static override flags = {
|
||||
...EASEnvironmentFlag,
|
||||
path: Flags.string({
|
||||
description: 'Path to the input `.env` file',
|
||||
default: '.env.local',
|
||||
}),
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(EnvironmentVariablePush);
|
||||
|
||||
const { environment, path: envPath } = this.validateFlags(flags);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(EnvironmentVariablePush, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const updateVariables: Record<string, EnvironmentVariablePushInput> =
|
||||
await this.parseEnvFileAsync(envPath, environment);
|
||||
|
||||
const variableNames = Object.keys(updateVariables);
|
||||
|
||||
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
filterNames: variableNames,
|
||||
});
|
||||
|
||||
const existingDifferentVariables: EnvironmentVariableFragment[] = [];
|
||||
// Remove variables that are the same as the ones in the environment
|
||||
existingVariables.forEach(variable => {
|
||||
const existingVariableUpdate = updateVariables[variable.name];
|
||||
if (existingVariableUpdate && existingVariableUpdate.value !== variable.value) {
|
||||
existingDifferentVariables.push(variable);
|
||||
} else {
|
||||
delete updateVariables[variable.name];
|
||||
}
|
||||
});
|
||||
|
||||
const existingDifferentSharedVariables = existingDifferentVariables.filter(
|
||||
variable => variable.scope === EnvironmentVariableScope.Shared
|
||||
);
|
||||
|
||||
if (existingDifferentSharedVariables.length > 0) {
|
||||
const existingDifferentSharedVariablesNames = existingDifferentSharedVariables.map(
|
||||
variable => variable.name
|
||||
);
|
||||
Log.error('Shared variables cannot be overwritten by eas env:push command.');
|
||||
Log.error('Remove them from the env file or unlink them from the project to continue:');
|
||||
existingDifferentSharedVariablesNames.forEach(name => {
|
||||
Log.error(`- ${name}`);
|
||||
});
|
||||
throw new Error('Shared variables cannot be overwritten by eas env:push command');
|
||||
}
|
||||
|
||||
if (existingDifferentVariables.length > 0) {
|
||||
Log.warn('Some variables already exist in the environment.');
|
||||
const variableNames = existingDifferentVariables.map(variable => variable.name);
|
||||
|
||||
const confirmationMessage =
|
||||
variableNames.length > 1
|
||||
? `The ${variableNames.join(
|
||||
', '
|
||||
)} environment variables already exist in ${environment} environment. Do you want to override them all?`
|
||||
: `The ${variableNames[0]} environment variable already exists in ${environment} environment. Do you want to override it?`;
|
||||
|
||||
const confirm = await confirmAsync({
|
||||
message: confirmationMessage,
|
||||
});
|
||||
|
||||
let variablesToOverwrite: string[] = [];
|
||||
|
||||
if (!confirm && existingDifferentVariables.length === 1) {
|
||||
throw new Error('No new variables to push.');
|
||||
}
|
||||
|
||||
if (confirm) {
|
||||
variablesToOverwrite = existingDifferentVariables.map(variable => variable.name);
|
||||
} else {
|
||||
const promptResult = await promptAsync({
|
||||
type: 'multiselect',
|
||||
name: 'variablesToOverwrite',
|
||||
message: 'Select variables to overwrite:',
|
||||
// @ts-expect-error property missing from `@types/prompts`
|
||||
optionsPerPage: 20,
|
||||
choices: existingDifferentVariables.map(variable => ({
|
||||
title: `${variable.name}: ${updateVariables[variable.name].value} (was ${
|
||||
variable.value ?? '(secret)'
|
||||
})`,
|
||||
value: variable.name,
|
||||
})),
|
||||
});
|
||||
variablesToOverwrite = promptResult.variablesToOverwrite;
|
||||
}
|
||||
|
||||
for (const existingVariable of existingVariables) {
|
||||
const name = existingVariable.name;
|
||||
if (variablesToOverwrite.includes(name)) {
|
||||
updateVariables[name]['overwrite'] = true;
|
||||
} else {
|
||||
delete updateVariables[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any of the sensitive variables already exist in the environment. Prompt the user to overwrite them.
|
||||
const existingSensitiveVariables = existingVariables.filter(
|
||||
variable => variable.value === null
|
||||
);
|
||||
|
||||
if (existingSensitiveVariables.length > 0) {
|
||||
const existingSensitiveVariablesNames = existingSensitiveVariables.map(
|
||||
variable => `- ${variable.name}`
|
||||
);
|
||||
const confirm = await confirmAsync({
|
||||
message: `You are about to overwrite sensitive variables.\n${existingSensitiveVariablesNames.join(
|
||||
'\n'
|
||||
)}\n Do you want to continue?`,
|
||||
});
|
||||
if (!confirm) {
|
||||
throw new Error('Aborting...');
|
||||
}
|
||||
}
|
||||
|
||||
const variablesToPush = Object.values(updateVariables);
|
||||
|
||||
if (variablesToPush.length === 0) {
|
||||
Log.log('No new variables to push.');
|
||||
return;
|
||||
}
|
||||
|
||||
await EnvironmentVariableMutation.createBulkEnvironmentVariablesForAppAsync(
|
||||
graphqlClient,
|
||||
variablesToPush,
|
||||
projectId
|
||||
);
|
||||
|
||||
Log.log(`Uploaded env file to ${environment} environment.`);
|
||||
}
|
||||
|
||||
private async parseEnvFileAsync(
|
||||
envPath: string,
|
||||
environment: string
|
||||
): Promise<Record<string, EnvironmentVariablePushInput>> {
|
||||
if (!(await fs.exists(envPath))) {
|
||||
throw new Error(`File ${envPath} does not exist.`);
|
||||
}
|
||||
const pushInput: Record<string, EnvironmentVariablePushInput> = {};
|
||||
|
||||
const variables: Record<string, string> = dotenv.parse(await fs.readFile(envPath, 'utf8'));
|
||||
|
||||
for (const [name, value] of Object.entries(variables)) {
|
||||
pushInput[name] = {
|
||||
name,
|
||||
value,
|
||||
environment,
|
||||
visibility: name.startsWith('EXPO_SENSITIVE')
|
||||
? EnvironmentVariableVisibility.Sensitive
|
||||
: EnvironmentVariableVisibility.Public,
|
||||
};
|
||||
}
|
||||
|
||||
return pushInput;
|
||||
}
|
||||
|
||||
private validateFlags(flags: PushFlags): Required<PushFlags> {
|
||||
if (!flags.environment) {
|
||||
throw new Error('Please provide an environment to push the env file to.');
|
||||
}
|
||||
|
||||
return { ...flags, environment: flags.environment };
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -46,11 +46,10 @@ export default class EnvironmentVariableUnlink extends EasCommand {
|
||||
|
||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
|
||||
const appVariables = await EnvironmentVariablesQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
environment
|
||||
);
|
||||
const appVariables = await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
});
|
||||
const linkedVariables = appVariables.filter(
|
||||
({ scope }) => scope === EnvironmentVariableScope.Shared
|
||||
);
|
||||
|
||||
+7
-6
@@ -80,11 +80,10 @@ export default class EnvironmentVariableUpdate extends EasCommand {
|
||||
if (!environment) {
|
||||
environment = await promptVariableEnvironmentAsync(nonInteractive);
|
||||
}
|
||||
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
environment
|
||||
);
|
||||
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
environment,
|
||||
});
|
||||
if (!name) {
|
||||
name = await selectAsync(
|
||||
'Select variable',
|
||||
@@ -134,7 +133,9 @@ export default class EnvironmentVariableUpdate extends EasCommand {
|
||||
`Updated variable ${chalk.bold(name)} on project ${chalk.bold(projectDisplayName)}.`
|
||||
);
|
||||
} else if (scope === EnvironmentVariableScope.Shared) {
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, projectId);
|
||||
const sharedVariables = await EnvironmentVariablesQuery.sharedAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
name = await selectAsync(
|
||||
|
||||
@@ -8,6 +8,7 @@ import { getProjectDashboardUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { saveProjectIdToAppConfigAsync } from '../../commandUtils/context/contextUtils/getProjectIdAsync';
|
||||
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||
import { AppPrivacy, Role } from '../../graphql/generated';
|
||||
import { AppMutation } from '../../graphql/mutations/AppMutation';
|
||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
@@ -33,14 +34,10 @@ export default class ProjectInit extends EasCommand {
|
||||
description: 'ID of the EAS project to link',
|
||||
}),
|
||||
force: Flags.boolean({
|
||||
description: 'Whether to overwrite any existing project ID',
|
||||
dependsOn: ['id'],
|
||||
}),
|
||||
// this is the same as EASNonInteractiveFlag but with the dependsOn
|
||||
'non-interactive': Flags.boolean({
|
||||
description: 'Run the command in non-interactive mode.',
|
||||
dependsOn: ['id'],
|
||||
description:
|
||||
'Whether to create a new project/link an existing project without additional prompts or overwrite any existing project ID when running with --id flag',
|
||||
}),
|
||||
...EASNonInteractiveFlag,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -216,10 +213,11 @@ export default class ProjectInit extends EasCommand {
|
||||
});
|
||||
}
|
||||
|
||||
private static async initializeWithInteractiveSelectionAsync(
|
||||
private static async initializeWithoutExplicitIDAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
actor: Actor,
|
||||
projectDir: string
|
||||
projectDir: string,
|
||||
{ force, nonInteractive }: InitializeMethodOptions
|
||||
): Promise<string> {
|
||||
const exp = getPrivateExpoConfig(projectDir);
|
||||
const existingProjectId = exp.extra?.eas?.projectId;
|
||||
@@ -245,6 +243,20 @@ export default class ProjectInit extends EasCommand {
|
||||
if (!accountName) {
|
||||
if (allAccounts.length === 1) {
|
||||
accountName = allAccounts[0].name;
|
||||
} else if (nonInteractive) {
|
||||
if (!force) {
|
||||
throw new Error(
|
||||
`There are multiple accounts that you have access to: ${allAccounts
|
||||
.map(a => a.name)
|
||||
.join(
|
||||
', '
|
||||
)}. Explicitly set the owner property in your app config or run this command with the --force flag to proceed with a default account: ${
|
||||
allAccounts[0].name
|
||||
}.`
|
||||
);
|
||||
}
|
||||
accountName = allAccounts[0].name;
|
||||
Log.log(`Using default account ${accountName} for non-interactive and force mode`);
|
||||
} else {
|
||||
const choices = ProjectInit.getAccountChoices(
|
||||
actor,
|
||||
@@ -274,13 +286,21 @@ export default class ProjectInit extends EasCommand {
|
||||
projectName
|
||||
);
|
||||
if (existingProjectIdOnServer) {
|
||||
const affirmedLink = await confirmAsync({
|
||||
message: `Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Link this project?`,
|
||||
});
|
||||
if (!affirmedLink) {
|
||||
throw new Error(
|
||||
`Project ID configuration canceled. Re-run the command to select a different account/project.`
|
||||
);
|
||||
if (!force) {
|
||||
if (nonInteractive) {
|
||||
throw new Error(
|
||||
`Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Use --force flag to continue with this project.`
|
||||
);
|
||||
}
|
||||
|
||||
const affirmedLink = await confirmAsync({
|
||||
message: `Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Link this project?`,
|
||||
});
|
||||
if (!affirmedLink) {
|
||||
throw new Error(
|
||||
`Project ID configuration canceled. Re-run the command to select a different account/project.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await ProjectInit.saveProjectIdAndLogSuccessAsync(projectDir, existingProjectIdOnServer);
|
||||
@@ -293,11 +313,18 @@ export default class ProjectInit extends EasCommand {
|
||||
);
|
||||
}
|
||||
|
||||
const affirmedCreate = await confirmAsync({
|
||||
message: `Would you like to create a project for ${projectFullName}?`,
|
||||
});
|
||||
if (!affirmedCreate) {
|
||||
throw new Error(`Project ID configuration canceled for ${projectFullName}.`);
|
||||
if (!force) {
|
||||
if (nonInteractive) {
|
||||
throw new Error(
|
||||
`Project does not exist: ${projectFullName}. Use --force flag to create this project.`
|
||||
);
|
||||
}
|
||||
const affirmedCreate = await confirmAsync({
|
||||
message: `Would you like to create a project for ${projectFullName}?`,
|
||||
});
|
||||
if (!affirmedCreate) {
|
||||
throw new Error(`Project ID configuration canceled for ${projectFullName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
const projectDashboardUrl = getProjectDashboardUrl(accountName, projectName);
|
||||
@@ -405,10 +432,14 @@ export default class ProjectInit extends EasCommand {
|
||||
});
|
||||
idForConsistency = idArgument;
|
||||
} else {
|
||||
idForConsistency = await ProjectInit.initializeWithInteractiveSelectionAsync(
|
||||
idForConsistency = await ProjectInit.initializeWithoutExplicitIDAsync(
|
||||
graphqlClient,
|
||||
actor,
|
||||
projectDir
|
||||
projectDir,
|
||||
{
|
||||
force,
|
||||
nonInteractive,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJson } from '@expo/eas-json';
|
||||
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
@@ -411,6 +411,7 @@ async function configureProjectFromBareDefaultExpoTemplateAsync({
|
||||
const easJson: EasJson = {
|
||||
cli: {
|
||||
version: `>= ${easCliVersion}`,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
},
|
||||
build: {
|
||||
development: {
|
||||
@@ -431,6 +432,7 @@ async function configureProjectFromBareDefaultExpoTemplateAsync({
|
||||
},
|
||||
production: {
|
||||
channel: 'production',
|
||||
autoIncrement: true,
|
||||
...easBuildGitHubConfig,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -138,7 +138,16 @@ describe(UpdatePublish.name, () => {
|
||||
|
||||
// Add configuration to the project that should not be included in the update
|
||||
const { appJson } = mockTestProject({
|
||||
expoConfig: {},
|
||||
expoConfig: {
|
||||
hooks: {
|
||||
postPublish: [
|
||||
{
|
||||
file: 'custom-hook.js',
|
||||
config: { some: 'config' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { platforms, runtimeVersion } = mockTestExport({ platforms: ['ios'] });
|
||||
|
||||
@@ -144,7 +144,16 @@ describe(UpdateRollBackToEmbedded.name, () => {
|
||||
|
||||
// Add configuration to the project that should not be included in the update
|
||||
mockTestProject({
|
||||
expoConfig: {},
|
||||
expoConfig: {
|
||||
hooks: {
|
||||
postPublish: [
|
||||
{
|
||||
file: 'custom-hook.js',
|
||||
config: { some: 'config' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const platforms = ['ios'];
|
||||
|
||||
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { transformFingerprintSource } from '../../build/graphql';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
@@ -21,6 +22,7 @@ import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
RawAsset,
|
||||
@@ -32,9 +34,9 @@ import {
|
||||
filterCollectedAssetsByRequestedPlatforms,
|
||||
generateEasMetadataAsync,
|
||||
getBranchNameForCommandAsync,
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions,
|
||||
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects,
|
||||
getRuntimeToUpdateRolloutInfoGroupMappingAsync,
|
||||
getRuntimeVersionObjectAsync,
|
||||
getRuntimeVersionInfoObjectsAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
platformDisplayNames,
|
||||
@@ -374,7 +376,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
}
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync({
|
||||
const runtimeVersionInfoObjects = await getRuntimeVersionInfoObjectsAsync({
|
||||
exp,
|
||||
platforms: realizedPlatforms,
|
||||
projectDir,
|
||||
@@ -384,21 +386,40 @@ export default class UpdatePublish extends EasCommand {
|
||||
},
|
||||
env: undefined,
|
||||
});
|
||||
const runtimeToPlatformMapping =
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
|
||||
const runtimeToPlatformsAndFingerprintInfoMapping =
|
||||
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects(
|
||||
runtimeVersionInfoObjects
|
||||
);
|
||||
|
||||
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchName,
|
||||
});
|
||||
|
||||
const runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping = await Promise.all(
|
||||
runtimeToPlatformsAndFingerprintInfoMapping.map(async info => {
|
||||
return {
|
||||
...info,
|
||||
fingerprintSource: info.fingerprint
|
||||
? (
|
||||
await maybeUploadFingerprintAsync({
|
||||
runtimeVersion: info.runtimeVersion,
|
||||
fingerprint: info.fingerprint,
|
||||
graphqlClient,
|
||||
})
|
||||
).fingerprintSource ?? null
|
||||
: null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const runtimeVersionToRolloutInfoGroup =
|
||||
rolloutPercentage !== undefined
|
||||
? await getRuntimeToUpdateRolloutInfoGroupMappingAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchName,
|
||||
rolloutPercentage,
|
||||
runtimeToPlatformMapping,
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
@@ -406,39 +427,43 @@ export default class UpdatePublish extends EasCommand {
|
||||
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 updateGroups: PublishUpdateGroupInput[] =
|
||||
runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping.map(
|
||||
({ runtimeVersion, platforms, fingerprintSource }) => {
|
||||
const localUpdateInfoGroup = Object.fromEntries(
|
||||
platforms.map(platform => [
|
||||
platform,
|
||||
unsortedUpdateInfoGroups[platform as keyof UpdateInfoGroup],
|
||||
])
|
||||
);
|
||||
|
||||
const rolloutInfoGroupForRuntimeVersion = runtimeVersionToRolloutInfoGroup
|
||||
? runtimeVersionToRolloutInfoGroup.get(runtimeVersion)
|
||||
: null;
|
||||
const localRolloutInfoGroup = rolloutInfoGroupForRuntimeVersion
|
||||
? Object.fromEntries(
|
||||
platforms.map(platform => [
|
||||
platform,
|
||||
rolloutInfoGroupForRuntimeVersion[platform as keyof UpdateRolloutInfoGroup],
|
||||
])
|
||||
)
|
||||
: null;
|
||||
const rolloutInfoGroupForRuntimeVersion = runtimeVersionToRolloutInfoGroup
|
||||
? runtimeVersionToRolloutInfoGroup.get(runtimeVersion)
|
||||
: null;
|
||||
const localRolloutInfoGroup = rolloutInfoGroupForRuntimeVersion
|
||||
? Object.fromEntries(
|
||||
platforms.map(platform => [
|
||||
platform,
|
||||
rolloutInfoGroupForRuntimeVersion[platform as keyof UpdateRolloutInfoGroup],
|
||||
])
|
||||
)
|
||||
: null;
|
||||
|
||||
return {
|
||||
branchId,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
rolloutInfoGroup: localRolloutInfoGroup,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
branchId,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
rolloutInfoGroup: localRolloutInfoGroup,
|
||||
runtimeFingerprintSource: fingerprintSource
|
||||
? transformFingerprintSource(fingerprintSource)
|
||||
: null,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
try {
|
||||
@@ -513,7 +538,10 @@ export default class UpdatePublish extends EasCommand {
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
for (const runtime of uniqBy(runtimeVersions, version => version.runtimeVersion)) {
|
||||
for (const runtime of uniqBy(
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
version => version.runtimeVersion
|
||||
)) {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime.runtimeVersion
|
||||
);
|
||||
|
||||
@@ -24,11 +24,12 @@ import {
|
||||
getOwnerAccountForProjectIdAsync,
|
||||
} from '../../project/projectUtils';
|
||||
import {
|
||||
RuntimeVersionInfo,
|
||||
UpdatePublishPlatform,
|
||||
defaultPublishPlatforms,
|
||||
getBranchNameForCommandAsync,
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions,
|
||||
getRuntimeVersionObjectAsync,
|
||||
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects,
|
||||
getRuntimeVersionInfoObjectsAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
|
||||
@@ -193,7 +194,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync({
|
||||
const runtimeVersionInfoObjects = await getRuntimeVersionInfoObjectsAsync({
|
||||
exp,
|
||||
platforms: realizedPlatforms,
|
||||
projectDir,
|
||||
@@ -203,6 +204,10 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
},
|
||||
env: undefined,
|
||||
});
|
||||
const runtimeToPlatformsAndFingerprintInfoMapping =
|
||||
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects(
|
||||
runtimeVersionInfoObjects
|
||||
);
|
||||
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
@@ -214,7 +219,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
updateMessage,
|
||||
branchId,
|
||||
codeSigningInfo,
|
||||
runtimeVersions,
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
realizedPlatforms,
|
||||
});
|
||||
publishSpinner.succeed('Published!');
|
||||
@@ -235,7 +240,10 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
for (const runtime of uniqBy(runtimeVersions, version => version.runtimeVersion)) {
|
||||
for (const runtime of uniqBy(
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
version => version.runtimeVersion
|
||||
)) {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime.runtimeVersion
|
||||
);
|
||||
@@ -290,7 +298,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
updateMessage,
|
||||
branchId,
|
||||
codeSigningInfo,
|
||||
runtimeVersions,
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
realizedPlatforms,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
@@ -299,17 +307,17 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
updateMessage: string | undefined;
|
||||
branchId: string;
|
||||
codeSigningInfo: CodeSigningInfo | undefined;
|
||||
runtimeVersions: { platform: UpdatePublishPlatform; runtimeVersion: string }[];
|
||||
runtimeToPlatformsAndFingerprintInfoMapping: (RuntimeVersionInfo & {
|
||||
platforms: UpdatePublishPlatform[];
|
||||
})[];
|
||||
realizedPlatforms: PublishPlatform[];
|
||||
}): Promise<UpdatePublishMutation['updateBranch']['publishUpdateGroups']> {
|
||||
const runtimeToPlatformMapping =
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
|
||||
const rollbackInfoGroups = Object.fromEntries(
|
||||
realizedPlatforms.map(platform => [platform, true])
|
||||
);
|
||||
|
||||
// Sort the updates into different groups based on their platform specific runtime versions
|
||||
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformMapping.map(
|
||||
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformsAndFingerprintInfoMapping.map(
|
||||
({ runtimeVersion, platforms }) => {
|
||||
const localRollbackInfoGroup = Object.fromEntries(
|
||||
platforms.map(platform => [platform, rollbackInfoGroups[platform]])
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { WorkerDeploymentAliasFragment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { Ora, ora } from '../../ora';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import {
|
||||
assignWorkerDeploymentAliasAsync,
|
||||
assignWorkerDeploymentProductionAsync,
|
||||
selectWorkerDeploymentOnAppAsync,
|
||||
} from '../../worker/deployment';
|
||||
import { formatWorkerDeploymentJson, formatWorkerDeploymentTable } from '../../worker/utils/logs';
|
||||
|
||||
interface DeployAliasFlags {
|
||||
nonInteractive: boolean;
|
||||
json: boolean;
|
||||
aliasName?: string | null;
|
||||
deploymentIdentifier?: string | null;
|
||||
isProduction: boolean;
|
||||
}
|
||||
|
||||
interface RawDeployAliasFlags {
|
||||
'non-interactive': boolean;
|
||||
json: boolean;
|
||||
alias?: string;
|
||||
id?: string;
|
||||
prod: boolean;
|
||||
}
|
||||
|
||||
export default class WorkerAlias extends EasCommand {
|
||||
static override description = 'Assign deployment aliases';
|
||||
static override aliases = ['deploy:alias', 'deploy:promote'];
|
||||
|
||||
// TODO(@kitten): Keep command hidden until worker deployments are live
|
||||
static override hidden = true;
|
||||
static override state = 'beta';
|
||||
|
||||
static override flags = {
|
||||
prod: Flags.boolean({
|
||||
aliases: ['production'],
|
||||
description: 'Promote an existing deployment to production',
|
||||
default: false,
|
||||
}),
|
||||
alias: Flags.string({
|
||||
description: 'Custom alias to assign to the existing deployment',
|
||||
helpValue: 'name',
|
||||
required: false,
|
||||
}),
|
||||
id: Flags.string({
|
||||
description: 'Unique identifier of an existing deployment',
|
||||
helpValue: 'xyz123',
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.ProjectDir,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
override async runAsync(): Promise<void> {
|
||||
// NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
chalk.yellow('EAS Worker Deployments are in beta and subject to breaking changes.')
|
||||
);
|
||||
|
||||
const { flags: rawFlags } = await this.parse(WorkerAlias);
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
|
||||
if (flags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const {
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(WorkerAlias, {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const { projectId } = await getDynamicPrivateProjectConfigAsync();
|
||||
const aliasName = await resolveDeploymentAliasAsync(flags);
|
||||
const deploymentId = await resolveDeploymentIdAsync({
|
||||
...flags,
|
||||
graphqlClient,
|
||||
projectId,
|
||||
aliasName,
|
||||
});
|
||||
|
||||
let progress: null | Ora = null;
|
||||
let deploymentAlias: null | Awaited<ReturnType<typeof assignWorkerDeploymentAliasAsync>> = null;
|
||||
|
||||
if (aliasName) {
|
||||
try {
|
||||
progress = ora(chalk`Assigning alias {bold ${aliasName}} to deployment`).start();
|
||||
deploymentAlias = await assignWorkerDeploymentAliasAsync({
|
||||
graphqlClient,
|
||||
appId: projectId,
|
||||
deploymentId,
|
||||
aliasName,
|
||||
});
|
||||
progress.text = chalk`Assigned alias {bold ${aliasName}} to deployment`;
|
||||
} catch (error: any) {
|
||||
progress?.fail(chalk`Failed to assign {bold ${aliasName}} alias to deployment`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
let deploymentProdAlias: null | Awaited<
|
||||
ReturnType<typeof assignWorkerDeploymentProductionAsync>
|
||||
> = null;
|
||||
|
||||
if (flags.isProduction) {
|
||||
try {
|
||||
progress = (progress ?? ora()).start(chalk`Promoting deployment to {bold production}`);
|
||||
deploymentProdAlias = await assignWorkerDeploymentProductionAsync({
|
||||
graphqlClient,
|
||||
appId: projectId,
|
||||
deploymentId,
|
||||
});
|
||||
progress.text = chalk`Promoted deployment to {bold production}`;
|
||||
} catch (error: any) {
|
||||
progress?.fail(chalk`Failed to promote deployment to {bold production}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
progress?.succeed(
|
||||
!deploymentAlias
|
||||
? chalk`Promoted deployment to {bold production}`
|
||||
: chalk`Promoted deployment to {bold production} with alias {bold ${deploymentAlias.aliasName}}`
|
||||
);
|
||||
|
||||
// Either use the alias, or production deployment information
|
||||
const deployment = deploymentAlias?.workerDeployment ?? deploymentProdAlias?.workerDeployment;
|
||||
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(
|
||||
formatWorkerDeploymentJson({
|
||||
projectId,
|
||||
deployment: deployment!,
|
||||
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
|
||||
production: deploymentProdAlias,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`🎉 Your deployment is modified`);
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
formatWorkerDeploymentTable({
|
||||
projectId,
|
||||
deployment: deployment!,
|
||||
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
|
||||
production: deploymentProdAlias,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private sanitizeFlags(flags: RawDeployAliasFlags): DeployAliasFlags {
|
||||
return {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
json: flags['json'],
|
||||
aliasName: flags.alias?.trim().toLowerCase(),
|
||||
deploymentIdentifier: flags.id?.trim().toLowerCase(),
|
||||
isProduction: flags.prod,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveDeploymentAliasAsync(flags: DeployAliasFlags): Promise<string | null> {
|
||||
if (flags.aliasName?.trim()) {
|
||||
return flags.aliasName.trim().toLowerCase();
|
||||
}
|
||||
|
||||
// Skip alias prompt when promoting deployments to prod
|
||||
if (flags.isProduction) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (flags.nonInteractive) {
|
||||
throw new Error('The `--alias` flag must be set when running in `--non-interactive` mode.');
|
||||
}
|
||||
|
||||
const { alias: aliasName } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'alias',
|
||||
message: 'Enter the alias to assign to a deployment',
|
||||
validate: (value: string) => !!value.trim(),
|
||||
hint: 'The alias name is case insensitive and must be URL safe',
|
||||
});
|
||||
|
||||
return aliasName.trim().toLowerCase();
|
||||
}
|
||||
|
||||
async function resolveDeploymentIdAsync({
|
||||
graphqlClient,
|
||||
deploymentIdentifier,
|
||||
aliasName,
|
||||
projectId,
|
||||
nonInteractive,
|
||||
}: DeployAliasFlags & {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
}): Promise<string> {
|
||||
if (deploymentIdentifier) {
|
||||
return deploymentIdentifier;
|
||||
}
|
||||
|
||||
if (nonInteractive) {
|
||||
throw new Error('The `--id` flag must be set when running in `--non-interactive` mode.');
|
||||
}
|
||||
|
||||
const deployment = await selectWorkerDeploymentOnAppAsync({
|
||||
graphqlClient,
|
||||
appId: projectId,
|
||||
selectTitle: chalk`deployment to assign the {underline ${aliasName}} alias`,
|
||||
});
|
||||
|
||||
if (!deployment) {
|
||||
throw new Error(
|
||||
'No deployments found for this project, create a new deployment with "eas deploy"'
|
||||
);
|
||||
}
|
||||
|
||||
return deployment.deploymentIdentifier;
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
import { format as formatTimeAgo } from '@expo/timeago.js';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlag, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import {
|
||||
EnvironmentVariableEnvironment,
|
||||
WorkerDeploymentAliasFragment,
|
||||
} from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { createProgressTracker } from '../../utils/progress';
|
||||
import * as WorkerAssets from '../../worker/assets';
|
||||
import {
|
||||
assignWorkerDeploymentAliasAsync,
|
||||
assignWorkerDeploymentProductionAsync,
|
||||
getSignedDeploymentUrlAsync,
|
||||
} from '../../worker/deployment';
|
||||
import { UploadParams, batchUploadAsync, uploadAsync } from '../../worker/upload';
|
||||
import {
|
||||
formatWorkerDeploymentJson,
|
||||
formatWorkerDeploymentTable,
|
||||
getDeploymentUrlFromFullName,
|
||||
} from '../../worker/utils/logs';
|
||||
|
||||
const isDirectory = (directoryPath: string): Promise<boolean> =>
|
||||
fs.promises
|
||||
.stat(directoryPath)
|
||||
.then(stat => stat.isDirectory())
|
||||
.catch(() => false);
|
||||
|
||||
interface DeployFlags {
|
||||
nonInteractive: boolean;
|
||||
json: boolean;
|
||||
isProduction: boolean;
|
||||
aliasName?: string;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
deploymentIdentifier?: string;
|
||||
exportDir: string;
|
||||
}
|
||||
|
||||
interface RawDeployFlags {
|
||||
'non-interactive': boolean;
|
||||
environment?: string;
|
||||
json: boolean;
|
||||
prod: boolean;
|
||||
alias?: string;
|
||||
id?: string;
|
||||
'export-dir': string;
|
||||
}
|
||||
|
||||
export default class WorkerDeploy extends EasCommand {
|
||||
static override description = 'Deploy your Expo web build';
|
||||
static override aliases = ['deploy'];
|
||||
static override usage = [chalk`deploy {dim [options]}`, `deploy --prod`];
|
||||
|
||||
// TODO(@kitten): Keep command hidden until worker deployments are live
|
||||
static override hidden = true;
|
||||
static override state = 'beta';
|
||||
|
||||
static override flags = {
|
||||
prod: Flags.boolean({
|
||||
aliases: ['production'],
|
||||
description: 'Create a new production deployment',
|
||||
default: false,
|
||||
}),
|
||||
alias: Flags.string({
|
||||
description: 'Custom alias to assign to the new deployment',
|
||||
helpValue: 'name',
|
||||
}),
|
||||
id: Flags.string({
|
||||
description: 'Custom unique identifier for the new deployment',
|
||||
helpValue: 'xyz123',
|
||||
}),
|
||||
'export-dir': Flags.string({
|
||||
description: 'Directory where the Expo project was exported',
|
||||
helpValue: 'dir',
|
||||
default: 'dist',
|
||||
}),
|
||||
// TODO(@kitten): Allow deployment identifier to be specified
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
...EASEnvironmentFlag,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.ProjectDir,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
// NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
chalk.yellow('EAS Worker Deployments are in beta and subject to breaking changes.')
|
||||
);
|
||||
|
||||
const { flags: rawFlags } = await this.parse(WorkerDeploy);
|
||||
const flags = this.sanitizeFlags(rawFlags);
|
||||
|
||||
if (flags.json) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const {
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
loggedIn: { graphqlClient },
|
||||
projectDir,
|
||||
} = await this.getContextAsync(WorkerDeploy, flags);
|
||||
|
||||
const [{ projectId }, projectDist] = await Promise.all([
|
||||
getDynamicPrivateProjectConfigAsync(),
|
||||
resolveExportedProjectAsync(flags, projectDir),
|
||||
]);
|
||||
|
||||
logExportedProjectInfo(projectDist);
|
||||
|
||||
async function* emitWorkerTarballAsync(params: {
|
||||
assetMap: WorkerAssets.AssetMap;
|
||||
manifest: WorkerAssets.Manifest;
|
||||
}): AsyncGenerator<WorkerAssets.FileEntry> {
|
||||
yield ['assets.json', JSON.stringify(params.assetMap)];
|
||||
yield ['manifest.json', JSON.stringify(params.manifest)];
|
||||
if (projectDist.type === 'server' && projectDist.serverPath) {
|
||||
const workerFiles = WorkerAssets.listWorkerFilesAsync(projectDist.serverPath);
|
||||
for await (const workerFile of workerFiles) {
|
||||
yield [`server/${workerFile.normalizedPath}`, workerFile.data];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadTarballAsync(tarPath: string, uploadUrl: string): Promise<any> {
|
||||
const { response } = await uploadAsync({
|
||||
url: uploadUrl,
|
||||
filePath: tarPath,
|
||||
compress: false,
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
},
|
||||
});
|
||||
if (response.status === 413) {
|
||||
throw new Error(
|
||||
'Upload failed! (Payload too large)\n' +
|
||||
`The files in "${path.relative(
|
||||
projectDir,
|
||||
projectDist.path
|
||||
)}" (at: ${projectDir}) exceed the maximum file size (10MB gzip).`
|
||||
);
|
||||
} else if (!response.ok) {
|
||||
throw new Error(`Upload failed! (${response.statusText})`);
|
||||
} else {
|
||||
const json = await response.json();
|
||||
if (!json.success || !json.result || typeof json.result !== 'object') {
|
||||
throw new Error(json.message ? `Upload failed: ${json.message}` : 'Upload failed!');
|
||||
}
|
||||
return json.result;
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadAssetsAsync(
|
||||
assetMap: WorkerAssets.AssetMap,
|
||||
uploads: Record<string, string>
|
||||
): Promise<void> {
|
||||
if (typeof uploads !== 'object' || !uploads) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(@kitten): Batch and upload multiple files in parallel
|
||||
const uploadParams: UploadParams[] = [];
|
||||
const assetPath = projectDist.type === 'server' ? projectDist.clientPath : projectDist.path;
|
||||
for await (const asset of WorkerAssets.listAssetMapFilesAsync(assetPath, assetMap)) {
|
||||
const uploadURL = uploads[asset.normalizedPath];
|
||||
if (uploadURL) {
|
||||
uploadParams.push({ url: uploadURL, filePath: asset.path });
|
||||
}
|
||||
}
|
||||
|
||||
const progress = {
|
||||
total: uploadParams.length,
|
||||
pending: 0,
|
||||
percent: 0,
|
||||
transferred: 0,
|
||||
};
|
||||
|
||||
const updateProgress = createProgressTracker({
|
||||
total: progress.total,
|
||||
message(ratio) {
|
||||
const percent = `${Math.floor(ratio * 100)}`;
|
||||
const details = chalk.dim(
|
||||
`(${progress.pending} Pending, ${progress.transferred} Completed, ${progress.total} Total)`
|
||||
);
|
||||
return `Uploading assets: ${percent.padStart(3)}% ${details}`;
|
||||
},
|
||||
completedMessage: 'Uploaded assets',
|
||||
});
|
||||
|
||||
try {
|
||||
for await (const signal of batchUploadAsync(uploadParams)) {
|
||||
if ('response' in signal) {
|
||||
progress.pending--;
|
||||
progress.percent = ++progress.transferred / progress.total;
|
||||
} else {
|
||||
progress.pending++;
|
||||
}
|
||||
updateProgress({ progress });
|
||||
}
|
||||
} catch (error: any) {
|
||||
updateProgress({ isComplete: true, error });
|
||||
throw error;
|
||||
}
|
||||
updateProgress({ isComplete: true });
|
||||
}
|
||||
|
||||
let assetMap: WorkerAssets.AssetMap;
|
||||
let tarPath: string;
|
||||
let deployResult: any;
|
||||
let progress = ora('Preparing project').start();
|
||||
|
||||
try {
|
||||
const manifest = await WorkerAssets.createManifestAsync(
|
||||
{
|
||||
environment: flags.environment,
|
||||
projectDir,
|
||||
projectId,
|
||||
},
|
||||
graphqlClient
|
||||
);
|
||||
assetMap = await WorkerAssets.createAssetMapAsync(
|
||||
projectDist.type === 'server' ? projectDist.clientPath : projectDist.path
|
||||
);
|
||||
tarPath = await WorkerAssets.packFilesIterableAsync(
|
||||
emitWorkerTarballAsync({
|
||||
assetMap,
|
||||
manifest,
|
||||
})
|
||||
);
|
||||
|
||||
const uploadUrl = await getSignedDeploymentUrlAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
deploymentIdentifier: flags.deploymentIdentifier,
|
||||
// NOTE(cedric): this function might ask the user for a dev-domain name,
|
||||
// when that happens, no ora spinner should be running.
|
||||
onSetupDevDomain: () => progress.stop(),
|
||||
nonInteractive: flags.nonInteractive,
|
||||
});
|
||||
|
||||
progress.start('Creating deployment');
|
||||
deployResult = await uploadTarballAsync(tarPath, uploadUrl);
|
||||
progress.succeed('Created deployment');
|
||||
} catch (error: any) {
|
||||
progress.fail('Failed to create deployment');
|
||||
throw error;
|
||||
}
|
||||
|
||||
await uploadAssetsAsync(assetMap, deployResult.uploads);
|
||||
|
||||
let deploymentAlias: null | Awaited<ReturnType<typeof assignWorkerDeploymentAliasAsync>> = null;
|
||||
if (flags.aliasName) {
|
||||
progress = ora(chalk`Assigning alias {bold ${flags.aliasName}} to deployment`).start();
|
||||
try {
|
||||
deploymentAlias = await assignWorkerDeploymentAliasAsync({
|
||||
graphqlClient,
|
||||
appId: projectId,
|
||||
deploymentId: deployResult.id,
|
||||
aliasName: flags.aliasName,
|
||||
});
|
||||
|
||||
// Only stop the spinner when not promoting to production
|
||||
if (!flags.isProduction) {
|
||||
progress.succeed(chalk`Assigned alias {bold ${flags.aliasName}} to deployment`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
progress.fail(chalk`Failed to assign {bold ${flags.aliasName}} alias to deployment`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
let deploymentProdAlias: null | Awaited<
|
||||
ReturnType<typeof assignWorkerDeploymentProductionAsync>
|
||||
> = null;
|
||||
if (flags.isProduction) {
|
||||
try {
|
||||
if (!flags.aliasName) {
|
||||
progress = ora(chalk`Promoting deployment to {bold production}`).start();
|
||||
} else {
|
||||
progress.text = chalk`Promoting deployment to {bold production}`;
|
||||
}
|
||||
|
||||
deploymentProdAlias = await assignWorkerDeploymentProductionAsync({
|
||||
graphqlClient,
|
||||
appId: projectId,
|
||||
deploymentId: deployResult.id,
|
||||
});
|
||||
|
||||
progress.succeed(
|
||||
!flags.aliasName
|
||||
? chalk`Promoted deployment to {bold production}`
|
||||
: chalk`Promoted deployment to {bold production} with alias {bold ${flags.aliasName}}`
|
||||
);
|
||||
} catch (error: any) {
|
||||
progress.fail('Failed to promote deployment to production');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(
|
||||
formatWorkerDeploymentJson({
|
||||
projectId,
|
||||
deployment: {
|
||||
deploymentIdentifier: deployResult.id,
|
||||
url: getDeploymentUrlFromFullName(deployResult.fullName),
|
||||
},
|
||||
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
|
||||
production: deploymentProdAlias,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`🎉 Your deployment is ready`);
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
formatWorkerDeploymentTable({
|
||||
projectId,
|
||||
deployment: {
|
||||
deploymentIdentifier: deployResult.id,
|
||||
url: getDeploymentUrlFromFullName(deployResult.fullName),
|
||||
},
|
||||
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
|
||||
production: deploymentProdAlias,
|
||||
})
|
||||
);
|
||||
|
||||
if (!deploymentProdAlias) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log('🚀 When you are ready to deploy to production:');
|
||||
Log.log(chalk` $ eas deploy {bold --prod}`);
|
||||
}
|
||||
}
|
||||
|
||||
private sanitizeFlags(flags: RawDeployFlags): DeployFlags {
|
||||
return {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
json: flags['json'],
|
||||
isProduction: !!flags.prod,
|
||||
aliasName: flags.alias?.trim().toLowerCase(),
|
||||
deploymentIdentifier: flags.id?.trim(),
|
||||
exportDir: flags['export-dir'],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveExportedProjectAsync(
|
||||
flags: DeployFlags,
|
||||
projectDir: string
|
||||
): Promise<
|
||||
| { type: 'static'; modifiedAt: Date; path: string }
|
||||
| { type: 'server'; modifiedAt: Date; path: string; serverPath: string; clientPath: string }
|
||||
> {
|
||||
const exportPath = path.join(projectDir, flags.exportDir);
|
||||
const serverPath = path.join(exportPath, 'server');
|
||||
const clientPath = path.join(exportPath, 'client');
|
||||
|
||||
const [hasServerPath, hasClientPath, modifiedAt] = await Promise.all([
|
||||
isDirectory(serverPath),
|
||||
isDirectory(clientPath),
|
||||
fs.promises.stat(exportPath).then(stat => stat.mtime),
|
||||
]);
|
||||
|
||||
if (hasServerPath && hasClientPath) {
|
||||
return { type: 'server', path: exportPath, modifiedAt, serverPath, clientPath };
|
||||
}
|
||||
|
||||
return { type: 'static', path: exportPath, modifiedAt };
|
||||
}
|
||||
|
||||
function logExportedProjectInfo(
|
||||
project: Awaited<ReturnType<typeof resolveExportedProjectAsync>>
|
||||
): void {
|
||||
const modifiedAgo = formatTimeAgo(project.modifiedAt);
|
||||
Log.log(chalk`{dim > Project export: ${project.type} - created ${modifiedAgo}}`);
|
||||
}
|
||||
@@ -747,7 +747,47 @@ export const CapabilityMapping: CapabilityClassifier[] = [
|
||||
validateOptions: validateBooleanOptions,
|
||||
getOptions: getBooleanOptions,
|
||||
},
|
||||
|
||||
{
|
||||
entitlement: 'com.apple.developer.journal.allow',
|
||||
name: 'Journaling Suggestions',
|
||||
capability: CapabilityType.JOURNALING_SUGGESTIONS,
|
||||
validateOptions: createValidateStringArrayOptions(['suggestions']),
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.managed-app-distribution.install-ui',
|
||||
name: 'Managed App Installation UI',
|
||||
capability: CapabilityType.MANAGED_APP_INSTALLATION_UI,
|
||||
validateOptions: createValidateStringArrayOptions(['managed-app']),
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.networking.slicing.appcategory',
|
||||
name: '5G Network Slicing',
|
||||
capability: CapabilityType.NETWORK_SLICING,
|
||||
validateOptions: createValidateStringArrayOptions([
|
||||
'gaming-6014',
|
||||
'communication-9000',
|
||||
'streaming-9001',
|
||||
]),
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
{
|
||||
entitlement: 'com.apple.developer.networking.slicing.trafficcategory',
|
||||
name: '5G Network Slicing',
|
||||
capability: CapabilityType.NETWORK_SLICING,
|
||||
validateOptions: createValidateStringArrayOptions([
|
||||
'defaultslice-1',
|
||||
'video-2',
|
||||
'background-3',
|
||||
'voice-4',
|
||||
'callsignaling-5',
|
||||
'responsivedata-6',
|
||||
'avstreaming-7',
|
||||
'responsiveav-8',
|
||||
]),
|
||||
getOptions: getDefinedOptions,
|
||||
},
|
||||
// VMNET
|
||||
|
||||
// These don't appear to have entitlements, so it's unclear how we can automatically enable / disable them at this time.
|
||||
|
||||
+619
-97
File diff suppressed because it is too large.
Load diff
@@ -4,6 +4,7 @@ import gql from 'graphql-tag';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
CreateBulkEnvironmentVariablesForAppMutation,
|
||||
CreateEnvironmentVariableForAccountMutation,
|
||||
CreateEnvironmentVariableForAppMutation,
|
||||
DeleteEnvironmentVariableMutation,
|
||||
@@ -196,4 +197,34 @@ export const EnvironmentVariableMutation = {
|
||||
|
||||
return data.environmentVariable.deleteEnvironmentVariable;
|
||||
},
|
||||
async createBulkEnvironmentVariablesForAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
input: EnvironmentVariablePushInput[],
|
||||
appId: string
|
||||
): Promise<boolean> {
|
||||
await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateBulkEnvironmentVariablesForAppMutation>(
|
||||
gql`
|
||||
mutation CreateBulkEnvironmentVariablesForApp(
|
||||
$input: [CreateEnvironmentVariableInput!]!
|
||||
$appId: ID!
|
||||
) {
|
||||
environmentVariable {
|
||||
createBulkEnvironmentVariablesForApp(
|
||||
environmentVariablesData: $input
|
||||
appId: $appId
|
||||
) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ input, appId }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -4,7 +4,6 @@ import gql from 'graphql-tag';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../client';
|
||||
import {
|
||||
EnvironmentVariableEnvironment,
|
||||
EnvironmentVariableFragment,
|
||||
EnvironmentVariablesByAppIdQuery,
|
||||
EnvironmentVariablesSharedQuery,
|
||||
@@ -21,12 +20,10 @@ export const EnvironmentVariablesQuery = {
|
||||
filterNames,
|
||||
}: {
|
||||
appId: string;
|
||||
environment: EnvironmentVariableEnvironment;
|
||||
environment: string;
|
||||
filterNames?: string[];
|
||||
}
|
||||
): Promise<{
|
||||
appVariables: EnvironmentVariableFragment[];
|
||||
}> {
|
||||
): Promise<EnvironmentVariableFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query(
|
||||
@@ -52,20 +49,24 @@ export const EnvironmentVariablesQuery = {
|
||||
}
|
||||
`,
|
||||
{ appId, filterNames, environment },
|
||||
{ additionalTypenames: ['EnvironmentVariable'] }
|
||||
{ additionalTypenames: ['EnvironmentVariableWithSecret'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return {
|
||||
appVariables: data.app?.byId.environmentVariablesIncludingSensitive ?? [],
|
||||
};
|
||||
return data.app?.byId.environmentVariablesIncludingSensitive ?? [];
|
||||
},
|
||||
async byAppIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
appId: string,
|
||||
environment: string,
|
||||
filterNames?: string[]
|
||||
{
|
||||
appId,
|
||||
environment,
|
||||
filterNames,
|
||||
}: {
|
||||
appId: string;
|
||||
environment: string;
|
||||
filterNames?: string[];
|
||||
}
|
||||
): Promise<EnvironmentVariableFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
@@ -98,8 +99,7 @@ export const EnvironmentVariablesQuery = {
|
||||
},
|
||||
async sharedAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
appId: string,
|
||||
filterNames?: string[]
|
||||
{ appId, filterNames }: { appId: string; filterNames?: string[] }
|
||||
): Promise<EnvironmentVariableFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
@@ -129,4 +129,39 @@ export const EnvironmentVariablesQuery = {
|
||||
|
||||
return data.app?.byId.ownerAccount.environmentVariables ?? [];
|
||||
},
|
||||
async sharedWithSensitiveAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, filterNames }: { appId: string; filterNames?: string[] }
|
||||
): Promise<EnvironmentVariableFragment[]> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query(
|
||||
gql`
|
||||
query EnvironmentVariablesSharedWithSensitive(
|
||||
$appId: String!
|
||||
$filterNames: [String!]
|
||||
) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
ownerAccount {
|
||||
id
|
||||
environmentVariablesIncludingSensitive(filterNames: $filterNames) {
|
||||
id
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ appId, filterNames },
|
||||
{ additionalTypenames: ['EnvironmentVariableWithSecret'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app?.byId.ownerAccount.environmentVariablesIncludingSensitive ?? [];
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
|
||||
import {
|
||||
@@ -26,6 +26,7 @@ export async function getApplicationIdentifierAsync({
|
||||
platform,
|
||||
vcsClient,
|
||||
nonInteractive,
|
||||
env,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
@@ -35,6 +36,7 @@ export async function getApplicationIdentifierAsync({
|
||||
platform: Platform;
|
||||
vcsClient: Client;
|
||||
nonInteractive: boolean;
|
||||
env: Env;
|
||||
}): Promise<string> {
|
||||
if (platform === Platform.ANDROID) {
|
||||
const profile = buildProfile as BuildProfile<Platform.ANDROID>;
|
||||
@@ -71,11 +73,12 @@ export async function getApplicationIdentifierAsync({
|
||||
{ exp, projectDir, nonInteractive: false, vcsClient },
|
||||
profile
|
||||
);
|
||||
|
||||
const targets = await resolveTargetsAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
xcodeBuildContext,
|
||||
env: profile.env,
|
||||
env,
|
||||
vcsClient,
|
||||
});
|
||||
const applicationTarget = findApplicationTarget(targets);
|
||||
|
||||
@@ -18,6 +18,7 @@ export type PublicExpoConfig = Omit<
|
||||
export interface ExpoConfigOptions {
|
||||
env?: Env;
|
||||
skipSDKVersionRequirement?: boolean;
|
||||
skipPlugins?: boolean;
|
||||
}
|
||||
|
||||
interface ExpoConfigOptionsInternal extends ExpoConfigOptions {
|
||||
@@ -52,6 +53,7 @@ function getExpoConfigInternal(
|
||||
const { exp } = getConfig(projectDir, {
|
||||
skipSDKVersionRequirement: true,
|
||||
...(opts.isPublicConfig ? { isPublicConfig: true } : {}),
|
||||
...(opts.skipPlugins ? { skipPlugins: true } : {}),
|
||||
});
|
||||
|
||||
const { error } = MinimalAppConfigSchema.validate(exp, {
|
||||
|
||||
@@ -189,7 +189,7 @@ async function resolveBareProjectDependenciesAsync({
|
||||
}
|
||||
);
|
||||
const entitlements = await getNativeTargetEntitlementsAsync(projectDir, {
|
||||
targetName: target.name,
|
||||
targetName: dependency.name,
|
||||
buildConfiguration,
|
||||
});
|
||||
result.push({
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { FingerprintSource, FingerprintSourceType } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { LocalBuildMode } from '../build/local';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { UploadSessionType } from '../graphql/generated';
|
||||
import Log from '../log';
|
||||
import { uploadFileAtPathToGCSAsync } from '../uploads';
|
||||
import { getTmpDirectory } from '../utils/paths';
|
||||
|
||||
export async function maybeUploadFingerprintAsync({
|
||||
runtimeVersion,
|
||||
fingerprint,
|
||||
graphqlClient,
|
||||
localBuildMode,
|
||||
}: {
|
||||
runtimeVersion: string;
|
||||
fingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
};
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
localBuildMode?: LocalBuildMode;
|
||||
}): Promise<{
|
||||
runtimeVersion: string;
|
||||
fingerprintSource?: FingerprintSource;
|
||||
}> {
|
||||
await fs.mkdirp(getTmpDirectory());
|
||||
const fingerprintLocation = path.join(getTmpDirectory(), `${uuidv4()}-runtime-fingerprint.json`);
|
||||
|
||||
await fs.writeJSON(fingerprintLocation, {
|
||||
hash: runtimeVersion,
|
||||
sources: fingerprint.fingerprintSources,
|
||||
});
|
||||
|
||||
if (localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
return {
|
||||
runtimeVersion,
|
||||
fingerprintSource: {
|
||||
type: FingerprintSourceType.PATH,
|
||||
path: fingerprintLocation,
|
||||
isDebugFingerprint: fingerprint.isDebugFingerprintSource,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let fingerprintGCSBucketKey = undefined;
|
||||
try {
|
||||
fingerprintGCSBucketKey = await uploadFileAtPathToGCSAsync(
|
||||
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,
|
||||
};
|
||||
} finally {
|
||||
await fs.remove(fingerprintLocation);
|
||||
}
|
||||
|
||||
return {
|
||||
runtimeVersion,
|
||||
fingerprintSource: {
|
||||
type: FingerprintSourceType.GCS,
|
||||
bucketKey: fingerprintGCSBucketKey,
|
||||
isDebugFingerprint: fingerprint.isDebugFingerprintSource,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import path from 'path';
|
||||
import promiseLimit from 'promise-limit';
|
||||
|
||||
import { isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync } from './projectUtils';
|
||||
import { resolveRuntimeVersionUsingCLIAsync } from './resolveRuntimeVersionAsync';
|
||||
import { selectBranchOnAppAsync } from '../branch/queries';
|
||||
import { getDefaultBranchNameAsync } from '../branch/utils';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
@@ -42,12 +43,10 @@ import {
|
||||
shouldUseVersionedExpoCLI,
|
||||
shouldUseVersionedExpoCLIWithExplicitPlatforms,
|
||||
} from '../utils/expoCli';
|
||||
import {
|
||||
ExpoUpdatesCLIModuleNotFoundError,
|
||||
expoUpdatesCommandAsync,
|
||||
} from '../utils/expoUpdatesCli';
|
||||
import { ExpoUpdatesCLIModuleNotFoundError } from '../utils/expoUpdatesCli';
|
||||
import chunk from '../utils/expodash/chunk';
|
||||
import { truthy } from '../utils/expodash/filter';
|
||||
import groupBy from '../utils/expodash/groupBy';
|
||||
import uniqBy from '../utils/expodash/uniqBy';
|
||||
import { Client } from '../vcs/vcs';
|
||||
|
||||
@@ -698,7 +697,15 @@ export async function getUpdateMessageForCommandAsync(
|
||||
|
||||
export const defaultPublishPlatforms: UpdatePublishPlatform[] = ['android', 'ios'];
|
||||
|
||||
export async function getRuntimeVersionObjectAsync({
|
||||
export type RuntimeVersionInfo = {
|
||||
runtimeVersion: string;
|
||||
fingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export async function getRuntimeVersionInfoObjectsAsync({
|
||||
exp,
|
||||
platforms,
|
||||
workflows,
|
||||
@@ -710,12 +717,17 @@ export async function getRuntimeVersionObjectAsync({
|
||||
workflows: Record<ExpoConfigPlatform, Workflow>;
|
||||
projectDir: string;
|
||||
env: Env | undefined;
|
||||
}): Promise<{ platform: UpdatePublishPlatform; runtimeVersion: string }[]> {
|
||||
}): Promise<
|
||||
{
|
||||
platform: UpdatePublishPlatform;
|
||||
runtimeVersionInfo: RuntimeVersionInfo;
|
||||
}[]
|
||||
> {
|
||||
return await Promise.all(
|
||||
platforms.map(async platform => {
|
||||
return {
|
||||
platform,
|
||||
runtimeVersion: await getRuntimeVersionForPlatformAsync({
|
||||
runtimeVersionInfo: await getRuntimeVersionInfoForPlatformAsync({
|
||||
exp,
|
||||
platform,
|
||||
workflow: workflows[platform],
|
||||
@@ -727,7 +739,7 @@ export async function getRuntimeVersionObjectAsync({
|
||||
);
|
||||
}
|
||||
|
||||
async function getRuntimeVersionForPlatformAsync({
|
||||
async function getRuntimeVersionInfoForPlatformAsync({
|
||||
exp,
|
||||
platform,
|
||||
workflow,
|
||||
@@ -739,29 +751,31 @@ async function getRuntimeVersionForPlatformAsync({
|
||||
workflow: Workflow;
|
||||
projectDir: string;
|
||||
env: Env | undefined;
|
||||
}): Promise<string> {
|
||||
}): Promise<{
|
||||
runtimeVersion: string;
|
||||
fingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
}> {
|
||||
if (await isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync(projectDir)) {
|
||||
try {
|
||||
Log.debug('Using expo-updates runtimeversion:resolve CLI for runtime version resolution');
|
||||
|
||||
const extraArgs = Log.isDebug ? ['--debug'] : [];
|
||||
|
||||
const resolvedRuntimeVersionJSONResult = await expoUpdatesCommandAsync(
|
||||
const runtimeVersionResult = await resolveRuntimeVersionUsingCLIAsync({
|
||||
platform,
|
||||
workflow,
|
||||
projectDir,
|
||||
['runtimeversion:resolve', '--platform', platform, '--workflow', workflow, ...extraArgs],
|
||||
{ env }
|
||||
);
|
||||
const runtimeVersionResult = JSON.parse(resolvedRuntimeVersionJSONResult);
|
||||
env,
|
||||
});
|
||||
|
||||
Log.debug('runtimeversion:resolve output:');
|
||||
Log.debug(resolvedRuntimeVersionJSONResult);
|
||||
|
||||
return nullthrows(
|
||||
runtimeVersionResult.runtimeVersion,
|
||||
`Unable to determine runtime version for ${
|
||||
requestedPlatformDisplayNames[platform]
|
||||
}. ${learnMore('https://docs.expo.dev/eas-update/runtime-versions/')}`
|
||||
);
|
||||
return {
|
||||
...runtimeVersionResult,
|
||||
runtimeVersion: nullthrows(
|
||||
runtimeVersionResult.runtimeVersion,
|
||||
`Unable to determine runtime version for ${
|
||||
requestedPlatformDisplayNames[platform]
|
||||
}. ${learnMore('https://docs.expo.dev/eas-update/runtime-versions/')}`
|
||||
),
|
||||
};
|
||||
} catch (e: any) {
|
||||
// if it's a known set of errors thrown by the CLI it means that we need to default back to the
|
||||
// previous behavior, otherwise we throw the error since something is wrong
|
||||
@@ -791,23 +805,37 @@ async function getRuntimeVersionForPlatformAsync({
|
||||
);
|
||||
}
|
||||
|
||||
return resolvedRuntimeVersion;
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion,
|
||||
fingerprint: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function getRuntimeToPlatformMappingFromRuntimeVersions(
|
||||
runtimeVersions: { platform: UpdatePublishPlatform; runtimeVersion: string }[]
|
||||
): { runtimeVersion: string; platforms: UpdatePublishPlatform[] }[] {
|
||||
const runtimeToPlatformMapping: { runtimeVersion: string; platforms: UpdatePublishPlatform[] }[] =
|
||||
[];
|
||||
for (const runtime of runtimeVersions) {
|
||||
const platforms = runtimeVersions
|
||||
.filter(({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion)
|
||||
.map(({ platform }) => platform);
|
||||
if (!runtimeToPlatformMapping.find(item => item.runtimeVersion === runtime.runtimeVersion)) {
|
||||
runtimeToPlatformMapping.push({ runtimeVersion: runtime.runtimeVersion, platforms });
|
||||
export function getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects(
|
||||
runtimeVersionInfoObjects: {
|
||||
platform: UpdatePublishPlatform;
|
||||
runtimeVersionInfo: RuntimeVersionInfo;
|
||||
}[]
|
||||
): (RuntimeVersionInfo & { platforms: UpdatePublishPlatform[] })[] {
|
||||
const groupedRuntimeVersionInfoObjects = groupBy(
|
||||
runtimeVersionInfoObjects,
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.runtimeVersionInfo.runtimeVersion
|
||||
);
|
||||
|
||||
return Object.entries(groupedRuntimeVersionInfoObjects).map(
|
||||
([runtimeVersion, runtimeVersionInfoObjects]) => {
|
||||
return {
|
||||
runtimeVersion,
|
||||
platforms: runtimeVersionInfoObjects.map(
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.platform
|
||||
),
|
||||
fingerprint:
|
||||
runtimeVersionInfoObjects.map(
|
||||
runtimeVersionInfoObject => runtimeVersionInfoObject.runtimeVersionInfo.fingerprint
|
||||
)[0] ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
return runtimeToPlatformMapping;
|
||||
);
|
||||
}
|
||||
|
||||
export const platformDisplayNames: Record<UpdatePublishPlatform, string> = {
|
||||
@@ -841,19 +869,18 @@ export async function getRuntimeToUpdateRolloutInfoGroupMappingAsync(
|
||||
appId,
|
||||
branchName,
|
||||
rolloutPercentage,
|
||||
runtimeToPlatformMapping,
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
}: {
|
||||
appId: string;
|
||||
branchName: string;
|
||||
rolloutPercentage: number;
|
||||
runtimeToPlatformMapping: { runtimeVersion: string; platforms: UpdatePublishPlatform[] }[];
|
||||
runtimeToPlatformsAndFingerprintInfoMapping: (RuntimeVersionInfo & {
|
||||
platforms: UpdatePublishPlatform[];
|
||||
})[];
|
||||
}
|
||||
): Promise<Map<string, UpdateRolloutInfoGroup>> {
|
||||
const runtimeToPlatformsMap = new Map(
|
||||
runtimeToPlatformMapping.map<[string, UpdatePublishPlatform[]]>(r => [
|
||||
r.runtimeVersion,
|
||||
r.platforms,
|
||||
])
|
||||
runtimeToPlatformsAndFingerprintInfoMapping.map(r => [r.runtimeVersion, r.platforms])
|
||||
);
|
||||
return await mapMapAsync(runtimeToPlatformsMap, async (platforms, runtimeVersion) => {
|
||||
return Object.fromEntries(
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { AppVersionSource, EasJson, EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||
import { Errors } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import Log from '../log';
|
||||
import { confirmAsync } from '../prompts';
|
||||
import { BuildFlags } from '../build/types';
|
||||
import Log, { learnMore } from '../log';
|
||||
import { confirmAsync, selectAsync } from '../prompts';
|
||||
import { ProfileData } from '../utils/profiles';
|
||||
|
||||
export enum AppVersionSourceUpdateOption {
|
||||
SET_TO_REMOTE,
|
||||
SET_TO_LOCAL,
|
||||
ABORT,
|
||||
}
|
||||
|
||||
export async function ensureVersionSourceIsRemoteAsync(
|
||||
easJsonAccessor: EasJsonAccessor,
|
||||
{ nonInteractive }: { nonInteractive: boolean }
|
||||
): Promise<void> {
|
||||
const easJsonCliConfig = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||
let easJsonCliConfig = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||
if (easJsonCliConfig?.appVersionSource === undefined) {
|
||||
easJsonCliConfig = await ensureAppVersionSourceIsSetAsync(
|
||||
easJsonAccessor,
|
||||
easJsonCliConfig ?? undefined,
|
||||
nonInteractive
|
||||
);
|
||||
}
|
||||
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
||||
return;
|
||||
}
|
||||
@@ -45,10 +60,31 @@ export async function ensureVersionSourceIsRemoteAsync(
|
||||
Log.withTick('Updated eas.json');
|
||||
}
|
||||
|
||||
export function validateBuildProfileVersionSettings(
|
||||
export async function validateBuildProfileVersionSettingsAsync(
|
||||
profileInfo: ProfileData<'build'>,
|
||||
cliConfig: EasJson['cli']
|
||||
): void {
|
||||
cliConfig: EasJson['cli'],
|
||||
projectDir: string,
|
||||
flags: BuildFlags
|
||||
): Promise<void> {
|
||||
if (
|
||||
cliConfig?.appVersionSource === undefined &&
|
||||
profileInfo.profile.autoIncrement !== 'version'
|
||||
) {
|
||||
if (profileInfo.profile.autoIncrement !== true) {
|
||||
Log.warn(
|
||||
`The field "cli.appVersionSource" is not set, but it will be required in the future. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/'
|
||||
)}`
|
||||
);
|
||||
} else {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
cliConfig = await ensureAppVersionSourceIsSetAsync(
|
||||
easJsonAccessor,
|
||||
cliConfig,
|
||||
flags.nonInteractive
|
||||
);
|
||||
}
|
||||
}
|
||||
if (cliConfig?.appVersionSource !== AppVersionSource.REMOTE) {
|
||||
return;
|
||||
}
|
||||
@@ -98,3 +134,96 @@ export function getBuildVersionName(platform: Platform): string {
|
||||
return 'buildNumber';
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureAppVersionSourceIsSetAsync(
|
||||
easJsonAccessor: EasJsonAccessor,
|
||||
easJsonCliConfig: EasJson['cli'] | undefined,
|
||||
nonInteractive: boolean
|
||||
): Promise<EasJson['cli'] | undefined> {
|
||||
let selectOption, updateEasJson;
|
||||
if (nonInteractive) {
|
||||
Log.warn(
|
||||
`The field "cli.appVersionSource" is not set, but it will be required in the future Proceeding with the default "local" value. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/'
|
||||
)}`
|
||||
);
|
||||
selectOption = AppVersionSourceUpdateOption.SET_TO_LOCAL;
|
||||
updateEasJson = false;
|
||||
} else {
|
||||
Log.log(
|
||||
'Since EAS CLI version `12.0.0` explicitly specifying app version source is required. Please select your app version source:'
|
||||
);
|
||||
Log.log(
|
||||
`\t1) With the "local" app version source and "autoIncrement" option enabled, the build number/version code is sourced from local project files and incremented automatically if possible, by editing local project files. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/#local-version-source'
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
`\t2) With the "remote" app version source and "autoIncrement" option enabled, the build number/version code is stored on EAS servers and updated every time you create a new build. Remote auto-incrementation won't edit the version in the local project files, but instead, the new version will be injected automatically during the build process. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/#remote-version-source'
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
`Until now, this project has been using the "local" version source (which was the previous default). App version source can now be set for you automatically, or you can configure it manually by setting the "appVersionSource" value in your eas.json.`
|
||||
);
|
||||
|
||||
selectOption = await selectAsync(`What would you like to do?`, [
|
||||
{
|
||||
title: 'Update eas.json to use the default "remote" version source (recommended)',
|
||||
value: AppVersionSourceUpdateOption.SET_TO_REMOTE,
|
||||
},
|
||||
{
|
||||
title: 'Update eas.json to use "local" version source (old behavior)',
|
||||
value: AppVersionSourceUpdateOption.SET_TO_LOCAL,
|
||||
},
|
||||
{
|
||||
title: "Don't update eas.json, abort command and configure manually",
|
||||
value: AppVersionSourceUpdateOption.ABORT,
|
||||
},
|
||||
]);
|
||||
updateEasJson = true;
|
||||
}
|
||||
|
||||
if (selectOption === AppVersionSourceUpdateOption.SET_TO_LOCAL) {
|
||||
if (updateEasJson) {
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
easJsonRawObject.cli = {
|
||||
...easJsonRawObject?.cli,
|
||||
appVersionSource: AppVersionSource.LOCAL,
|
||||
};
|
||||
return easJsonRawObject;
|
||||
});
|
||||
await easJsonAccessor.writeAsync();
|
||||
}
|
||||
if (easJsonCliConfig) {
|
||||
easJsonCliConfig.appVersionSource = AppVersionSource.LOCAL;
|
||||
}
|
||||
Log.withTick('Updated eas.json');
|
||||
} else if (selectOption === AppVersionSourceUpdateOption.SET_TO_REMOTE) {
|
||||
if (updateEasJson) {
|
||||
await easJsonAccessor.readRawJsonAsync();
|
||||
easJsonAccessor.patch(easJsonRawObject => {
|
||||
easJsonRawObject.cli = {
|
||||
...easJsonRawObject?.cli,
|
||||
appVersionSource: AppVersionSource.REMOTE,
|
||||
};
|
||||
return easJsonRawObject;
|
||||
});
|
||||
await easJsonAccessor.writeAsync();
|
||||
}
|
||||
if (easJsonCliConfig) {
|
||||
easJsonCliConfig.appVersionSource = AppVersionSource.REMOTE;
|
||||
}
|
||||
Log.withTick('Updated eas.json');
|
||||
} else {
|
||||
Log.warn(
|
||||
`You'll need to configure ${chalk.bold('appVersionSource')} manually. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/app-versions/'
|
||||
)}`
|
||||
);
|
||||
Errors.error('Aborted.', { exit: 1 });
|
||||
}
|
||||
|
||||
return easJsonCliConfig;
|
||||
}
|
||||
@@ -9,6 +9,52 @@ import {
|
||||
expoUpdatesCommandAsync,
|
||||
} from '../utils/expoUpdatesCli';
|
||||
|
||||
export async function resolveRuntimeVersionUsingCLIAsync({
|
||||
platform,
|
||||
workflow,
|
||||
projectDir,
|
||||
env,
|
||||
cwd,
|
||||
}: {
|
||||
platform: 'ios' | 'android';
|
||||
workflow: Workflow;
|
||||
projectDir: string;
|
||||
env: Env | undefined;
|
||||
cwd?: string;
|
||||
}): Promise<{
|
||||
runtimeVersion: string | null;
|
||||
fingerprint: {
|
||||
fingerprintSources: object[];
|
||||
isDebugFingerprintSource: boolean;
|
||||
} | null;
|
||||
}> {
|
||||
Log.debug('Using expo-updates runtimeversion:resolve CLI for runtime version resolution');
|
||||
|
||||
const useDebugFingerprintSource = Log.isDebug;
|
||||
|
||||
const extraArgs = useDebugFingerprintSource ? ['--debug'] : [];
|
||||
|
||||
const resolvedRuntimeVersionJSONResult = await expoUpdatesCommandAsync(
|
||||
projectDir,
|
||||
['runtimeversion:resolve', '--platform', platform, '--workflow', workflow, ...extraArgs],
|
||||
{ env, cwd }
|
||||
);
|
||||
const runtimeVersionResult = JSON.parse(resolvedRuntimeVersionJSONResult);
|
||||
|
||||
Log.debug('runtimeversion:resolve output:');
|
||||
Log.debug(resolvedRuntimeVersionJSONResult);
|
||||
|
||||
return {
|
||||
runtimeVersion: runtimeVersionResult.runtimeVersion ?? null,
|
||||
fingerprint: runtimeVersionResult.fingerprintSources
|
||||
? {
|
||||
fingerprintSources: runtimeVersionResult.fingerprintSources,
|
||||
isDebugFingerprintSource: useDebugFingerprintSource,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveRuntimeVersionAsync({
|
||||
exp,
|
||||
platform,
|
||||
@@ -40,31 +86,7 @@ export async function resolveRuntimeVersionAsync({
|
||||
}
|
||||
|
||||
try {
|
||||
Log.debug('Using expo-updates runtimeversion:resolve CLI for runtime version resolution');
|
||||
|
||||
const useDebugFingerprintSource = Log.isDebug;
|
||||
|
||||
const extraArgs = useDebugFingerprintSource ? ['--debug'] : [];
|
||||
|
||||
const resolvedRuntimeVersionJSONResult = await expoUpdatesCommandAsync(
|
||||
projectDir,
|
||||
['runtimeversion:resolve', '--platform', platform, '--workflow', workflow, ...extraArgs],
|
||||
{ env, cwd }
|
||||
);
|
||||
const runtimeVersionResult = JSON.parse(resolvedRuntimeVersionJSONResult);
|
||||
|
||||
Log.debug('runtimeversion:resolve output:');
|
||||
Log.debug(resolvedRuntimeVersionJSONResult);
|
||||
|
||||
return {
|
||||
runtimeVersion: runtimeVersionResult.runtimeVersion ?? null,
|
||||
fingerprint: runtimeVersionResult.fingerprintSources
|
||||
? {
|
||||
fingerprintSources: runtimeVersionResult.fingerprintSources,
|
||||
isDebugFingerprintSource: useDebugFingerprintSource,
|
||||
}
|
||||
: null,
|
||||
};
|
||||
return await resolveRuntimeVersionUsingCLIAsync({ platform, workflow, projectDir, env, cwd });
|
||||
} catch (e: any) {
|
||||
// if expo-updates is not installed, there's no need for a runtime version in the build
|
||||
if (e instanceof ExpoUpdatesCLIModuleNotFoundError) {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { App, Runtime, UpdateBranch, UpdateChannel, UpdateInsights } from '../../graphql/generated';
|
||||
import {
|
||||
App,
|
||||
Runtime,
|
||||
UpdateBranch,
|
||||
UpdateChannel,
|
||||
UpdateDeploymentsConnection,
|
||||
UpdateInsights,
|
||||
} from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import { getBranchNameFromChannelNameAsync } from '../getBranchNameFromChannelNameAsync';
|
||||
|
||||
@@ -127,6 +134,7 @@ function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
|
||||
runtimeVersion: '1.0.0',
|
||||
runtime: {} as Runtime, // Temporary fix to resolve type errors
|
||||
insights: {} as UpdateInsights, // Temporary fix to resolve type errors
|
||||
deployments: {} as UpdateDeploymentsConnection, // Temporary fix to resolve type errors
|
||||
platform: 'ios',
|
||||
manifestFragment: '...',
|
||||
isRollBackToEmbedded: false,
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
import { get as getEnv } from '@expo/env';
|
||||
import { Gzip, GzipOptions } from 'minizlib';
|
||||
import { HashOptions, createHash, randomBytes } from 'node:crypto';
|
||||
import fs, { createWriteStream } from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
import { pack } from 'tar-stream';
|
||||
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EnvironmentVariableEnvironment } from '../graphql/generated';
|
||||
import { EnvironmentVariablesQuery } from '../graphql/queries/EnvironmentVariablesQuery';
|
||||
|
||||
/** Returns whether a file or folder is ignored */
|
||||
function isIgnoredName(name: string): boolean {
|
||||
switch (name) {
|
||||
// macOS system files
|
||||
case '.DS_Store':
|
||||
case '.AppleDouble':
|
||||
case '.Trashes':
|
||||
case '__MACOSX':
|
||||
case '.LSOverride':
|
||||
return true;
|
||||
default:
|
||||
// Backup file name convention
|
||||
return name.endsWith('~');
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a temporary file write path */
|
||||
async function createTempWritePathAsync(): Promise<string> {
|
||||
const basename = path.basename(__filename, path.extname(__filename));
|
||||
const tmpdir = await fs.promises.realpath(os.tmpdir());
|
||||
const random = randomBytes(4).toString('hex');
|
||||
return path.resolve(tmpdir, `tmp-${basename}-${process.pid}-${random}`);
|
||||
}
|
||||
|
||||
/** Computes a SHA512 hash for a file */
|
||||
async function computeSha512HashAsync(
|
||||
filePath: fs.PathLike,
|
||||
options?: HashOptions
|
||||
): Promise<string> {
|
||||
const hash = createHash('sha512', { encoding: 'hex', ...options });
|
||||
await pipeline(fs.createReadStream(filePath), hash);
|
||||
return `${hash.read()}`;
|
||||
}
|
||||
|
||||
/** A file entry with a gzip-safe (normalized) path and a filesystem path */
|
||||
interface RecursiveFileEntry {
|
||||
normalizedPath: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
/** Lists plain files in base path recursively and outputs normalized paths */
|
||||
function listFilesRecursively(basePath: string): AsyncGenerator<RecursiveFileEntry> {
|
||||
async function* recurseAsync(parentPath?: string): AsyncGenerator<RecursiveFileEntry> {
|
||||
const target = parentPath ? path.resolve(basePath, parentPath) : basePath;
|
||||
const entries = await fs.promises.readdir(target, { withFileTypes: true });
|
||||
for (const dirent of entries) {
|
||||
const normalizedPath = parentPath ? `${parentPath}/${dirent.name}` : dirent.name;
|
||||
if (isIgnoredName(dirent.name)) {
|
||||
continue;
|
||||
} else if (dirent.isFile()) {
|
||||
yield {
|
||||
normalizedPath,
|
||||
path: path.resolve(target, dirent.name),
|
||||
};
|
||||
} else if (dirent.isDirectory()) {
|
||||
yield* recurseAsync(normalizedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return recurseAsync();
|
||||
}
|
||||
|
||||
interface AssetMapOptions {
|
||||
hashOptions?: HashOptions;
|
||||
}
|
||||
|
||||
/** Mapping of normalized file paths to a SHA512 hash */
|
||||
export type AssetMap = Record<string, string>;
|
||||
|
||||
/** Creates an asset map of a given target path */
|
||||
async function createAssetMapAsync(
|
||||
assetPath: string,
|
||||
options?: AssetMapOptions
|
||||
): Promise<AssetMap> {
|
||||
const map: AssetMap = Object.create(null);
|
||||
for await (const file of listFilesRecursively(assetPath)) {
|
||||
map[file.normalizedPath] = await computeSha512HashAsync(file.path, options?.hashOptions);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
export interface Manifest {
|
||||
env: Record<string, string | undefined>;
|
||||
}
|
||||
|
||||
interface CreateManifestParams {
|
||||
projectId: string;
|
||||
projectDir: string;
|
||||
environment?: EnvironmentVariableEnvironment;
|
||||
}
|
||||
|
||||
/** Creates a manifest configuration sent up for deployment */
|
||||
export async function createManifestAsync(
|
||||
params: CreateManifestParams,
|
||||
graphqlClient: ExpoGraphqlClient
|
||||
): Promise<Manifest> {
|
||||
let env: Record<string, string | undefined>;
|
||||
if (params.environment) {
|
||||
env = Object.fromEntries(
|
||||
(
|
||||
await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
|
||||
appId: params.projectId,
|
||||
environment: params.environment,
|
||||
})
|
||||
).map(variable => [variable.name, variable.value ?? undefined])
|
||||
);
|
||||
} else {
|
||||
// NOTE: This is required for the .env resolution
|
||||
process.env.NODE_ENV = 'production';
|
||||
env = getEnv(params.projectDir).env;
|
||||
}
|
||||
return { env };
|
||||
}
|
||||
|
||||
interface WorkerFileEntry {
|
||||
normalizedPath: string;
|
||||
path: string;
|
||||
data: Buffer | string;
|
||||
}
|
||||
|
||||
/** Reads worker files while normalizing sourcemaps and providing normalized paths */
|
||||
async function* listWorkerFilesAsync(workerPath: string): AsyncGenerator<WorkerFileEntry> {
|
||||
for await (const file of listFilesRecursively(workerPath)) {
|
||||
yield {
|
||||
normalizedPath: file.normalizedPath,
|
||||
path: file.path,
|
||||
data: await fs.promises.readFile(file.path),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads files of an asset maps and enumerates normalized paths and data */
|
||||
async function* listAssetMapFilesAsync(
|
||||
assetPath: string,
|
||||
assetMap: AssetMap
|
||||
): AsyncGenerator<WorkerFileEntry> {
|
||||
for (const normalizedPath in assetMap) {
|
||||
const filePath = path.resolve(assetPath, normalizedPath.split('/').join(path.sep));
|
||||
const data = await fs.promises.readFile(filePath);
|
||||
yield {
|
||||
normalizedPath,
|
||||
path: filePath,
|
||||
data,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/** Entry of a normalized (gzip-safe) path and file data */
|
||||
export type FileEntry = readonly [normalizedPath: string, data: Buffer | string];
|
||||
|
||||
/** Packs file entries into a tar.gz file (path to tgz returned) */
|
||||
async function packFilesIterableAsync(
|
||||
iterable: Iterable<FileEntry> | AsyncIterable<FileEntry>,
|
||||
options?: GzipOptions
|
||||
): Promise<string> {
|
||||
const writePath = `${await createTempWritePathAsync()}.tar.gz`;
|
||||
const write = createWriteStream(writePath);
|
||||
const gzip = new Gzip({ portable: true, ...options });
|
||||
const tar = pack();
|
||||
const writeTask$ = pipeline(tar, gzip, write);
|
||||
for await (const file of iterable) {
|
||||
tar.entry({ name: file[0], type: 'file' }, file[1]);
|
||||
}
|
||||
tar.finalize();
|
||||
await writeTask$;
|
||||
return writePath;
|
||||
}
|
||||
|
||||
export {
|
||||
createAssetMapAsync,
|
||||
listWorkerFilesAsync,
|
||||
listAssetMapFilesAsync,
|
||||
packFilesIterableAsync,
|
||||
};
|
||||
@@ -0,0 +1,224 @@
|
||||
import { CombinedError as GraphqlError } from '@urql/core';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { DeploymentsMutation } from './mutations';
|
||||
import { DeploymentsQuery } from './queries';
|
||||
import { EXPO_BASE_DOMAIN } from './utils/logs';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { WorkerDeploymentFragment } from '../graphql/generated';
|
||||
import Log from '../log';
|
||||
import { promptAsync } from '../prompts';
|
||||
import { memoize } from '../utils/expodash/memoize';
|
||||
import { selectPaginatedAsync } from '../utils/relay';
|
||||
|
||||
export async function getSignedDeploymentUrlAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
options: {
|
||||
appId: string;
|
||||
deploymentIdentifier?: string | null;
|
||||
/** Callback which is invoked when the project is going to setup the dev domain */
|
||||
onSetupDevDomain?: () => any;
|
||||
/** If the terminal is running in non interactive mode or not */
|
||||
nonInteractive?: boolean;
|
||||
}
|
||||
): Promise<string> {
|
||||
try {
|
||||
return await DeploymentsMutation.createSignedDeploymentUrlAsync(graphqlClient, {
|
||||
appId: options.appId,
|
||||
deploymentIdentifier: options.deploymentIdentifier,
|
||||
});
|
||||
} catch (error: any) {
|
||||
const isMissingDevDomain = (error as GraphqlError)?.graphQLErrors?.some(e =>
|
||||
['APP_NO_DEV_DOMAIN_NAME'].includes(e?.extensions?.errorCode as string)
|
||||
);
|
||||
|
||||
if (!isMissingDevDomain) {
|
||||
throw error;
|
||||
}
|
||||
if (options.nonInteractive) {
|
||||
throw new Error(
|
||||
'The project URL needs to be set up, but the terminal is running in non-interactive mode.'
|
||||
);
|
||||
}
|
||||
|
||||
const suggestedDevDomainName = await DeploymentsQuery.getSuggestedDevDomainByAppIdAsync(
|
||||
graphqlClient,
|
||||
{ appId: options.appId }
|
||||
);
|
||||
|
||||
options.onSetupDevDomain?.();
|
||||
|
||||
await chooseDevDomainNameAsync({
|
||||
graphqlClient,
|
||||
appId: options.appId,
|
||||
initial: suggestedDevDomainName,
|
||||
});
|
||||
|
||||
return await DeploymentsMutation.createSignedDeploymentUrlAsync(graphqlClient, {
|
||||
appId: options.appId,
|
||||
deploymentIdentifier: options.deploymentIdentifier,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type PromptInstance = {
|
||||
cursorOffset: number;
|
||||
placeholder: boolean;
|
||||
rendered: string;
|
||||
initial: string;
|
||||
get value(): string;
|
||||
set value(input: string);
|
||||
};
|
||||
|
||||
const DEV_DOMAIN_INVALID_START_END_CHARACTERS = /^[^a-z0-9]+|[^a-z0-9-]+$/;
|
||||
const DEV_DOMAIN_INVALID_REPLACEMENT_HYPHEN = /[^a-z0-9-]+/;
|
||||
const DEV_DOMAIN_INVALID_MULTIPLE_HYPHENS = /(-{2,})/;
|
||||
|
||||
/**
|
||||
* Format a dev domain name to match whats allowed on the backend.
|
||||
* This is equal to our `DEV_DOMAIN_NAME_REGEX`, but implemented as a filtering function
|
||||
* to help users find a valid name while typing.
|
||||
*/
|
||||
function formatDevDomainName(name = ''): string {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.replace(DEV_DOMAIN_INVALID_REPLACEMENT_HYPHEN, '-')
|
||||
.replace(DEV_DOMAIN_INVALID_START_END_CHARACTERS, '')
|
||||
.replace(DEV_DOMAIN_INVALID_MULTIPLE_HYPHENS, '-')
|
||||
.trim();
|
||||
}
|
||||
|
||||
async function chooseDevDomainNameAsync({
|
||||
graphqlClient,
|
||||
appId,
|
||||
initial,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
appId: string;
|
||||
initial: string;
|
||||
}): Promise<void> {
|
||||
const rootDomain = `.${EXPO_BASE_DOMAIN}.app`;
|
||||
const memoizedFormatDevDomainName = memoize(formatDevDomainName);
|
||||
|
||||
const { name } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'Choose a URL for your project:',
|
||||
initial,
|
||||
validate: (value: string) => {
|
||||
if (!value) {
|
||||
return 'You have to choose a URL for your project';
|
||||
}
|
||||
if (value.length < 3) {
|
||||
return 'Project URLs must be at least 3 characters long';
|
||||
}
|
||||
if (value.endsWith('-')) {
|
||||
return 'Project URLs cannot end with a hyphen (-)';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
onState(this: PromptInstance, state: { value?: string }) {
|
||||
const value = memoizedFormatDevDomainName(state.value);
|
||||
if (value !== state.value) {
|
||||
this.value = value;
|
||||
}
|
||||
},
|
||||
onRender(this: PromptInstance, kleur) {
|
||||
this.cursorOffset = -rootDomain.length - 1;
|
||||
|
||||
if (this.placeholder) {
|
||||
this.rendered = kleur.dim(`${this.initial} ${rootDomain}`);
|
||||
} else {
|
||||
this.rendered = this.value + kleur.dim(` ${rootDomain}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
throw new Error('No project URL provided, aborting deployment.');
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await DeploymentsMutation.assignDevDomainNameAsync(graphqlClient, {
|
||||
appId,
|
||||
name,
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
throw new Error('Failed to assign project URL');
|
||||
}
|
||||
} catch (error: any) {
|
||||
const isChosenNameTaken = (error as GraphqlError)?.graphQLErrors?.some(e =>
|
||||
['DEV_DOMAIN_NAME_TAKEN'].includes(e?.extensions?.errorCode as string)
|
||||
);
|
||||
|
||||
if (isChosenNameTaken) {
|
||||
Log.error(`The project URL "${name}" is already taken, choose a different name.`);
|
||||
await chooseDevDomainNameAsync({ graphqlClient, appId, initial });
|
||||
}
|
||||
|
||||
if (!isChosenNameTaken) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function assignWorkerDeploymentAliasAsync({
|
||||
graphqlClient,
|
||||
appId,
|
||||
deploymentId,
|
||||
aliasName,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
appId: string;
|
||||
deploymentId: string;
|
||||
aliasName: string;
|
||||
}): ReturnType<typeof DeploymentsMutation.assignAliasAsync> {
|
||||
return await DeploymentsMutation.assignAliasAsync(graphqlClient, {
|
||||
appId,
|
||||
deploymentId,
|
||||
aliasName,
|
||||
});
|
||||
}
|
||||
|
||||
export async function assignWorkerDeploymentProductionAsync({
|
||||
graphqlClient,
|
||||
appId,
|
||||
deploymentId,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
appId: string;
|
||||
deploymentId: string;
|
||||
}): ReturnType<typeof DeploymentsMutation.assignAliasAsync> {
|
||||
return await DeploymentsMutation.assignAliasAsync(graphqlClient, {
|
||||
appId,
|
||||
deploymentId,
|
||||
aliasName: null, // this will assign the deployment as production
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectWorkerDeploymentOnAppAsync({
|
||||
graphqlClient,
|
||||
appId,
|
||||
selectTitle,
|
||||
pageSize,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
appId: string;
|
||||
selectTitle?: string;
|
||||
pageSize?: number;
|
||||
}): ReturnType<typeof selectPaginatedAsync<WorkerDeploymentFragment>> {
|
||||
return await selectPaginatedAsync({
|
||||
pageSize: pageSize ?? 25,
|
||||
printedType: selectTitle ?? 'worker deployment',
|
||||
queryAsync: async queryParams =>
|
||||
await DeploymentsQuery.getAllDeploymentsPaginatedAsync(graphqlClient, {
|
||||
...queryParams,
|
||||
appId,
|
||||
}),
|
||||
getTitleAsync: async (deployment: WorkerDeploymentFragment) =>
|
||||
chalk`${deployment.deploymentIdentifier}{dim - created at: ${new Date(
|
||||
deployment.createdAt
|
||||
).toLocaleString()}}`,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const WorkerDeploymentFragmentNode = gql`
|
||||
fragment WorkerDeploymentFragment on WorkerDeployment {
|
||||
id
|
||||
url
|
||||
deploymentIdentifier
|
||||
deploymentDomain
|
||||
createdAt
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,9 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const WorkerDeploymentAliasFragmentNode = gql`
|
||||
fragment WorkerDeploymentAliasFragment on WorkerDeploymentAlias {
|
||||
id
|
||||
aliasName
|
||||
url
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,116 @@
|
||||
import assert from 'assert';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { WorkerDeploymentFragmentNode } from './fragments/WorkerDeployment';
|
||||
import { WorkerDeploymentAliasFragmentNode } from './fragments/WorkerDeploymentAlias';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../graphql/client';
|
||||
import {
|
||||
AssignAliasMutation,
|
||||
AssignAliasMutationVariables,
|
||||
AssignDevDomainNameMutation,
|
||||
AssignDevDomainNameMutationVariables,
|
||||
CreateDeploymentUrlMutation,
|
||||
CreateDeploymentUrlMutationVariables,
|
||||
} from '../graphql/generated';
|
||||
|
||||
export const DeploymentsMutation = {
|
||||
async createSignedDeploymentUrlAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
deploymentVariables: {
|
||||
appId: string;
|
||||
deploymentIdentifier?: string | null;
|
||||
}
|
||||
): Promise<string> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateDeploymentUrlMutation, CreateDeploymentUrlMutationVariables>(
|
||||
gql`
|
||||
mutation createDeploymentUrlMutation($appId: ID!, $deploymentIdentifier: ID) {
|
||||
deployments {
|
||||
createSignedDeploymentUrl(
|
||||
appId: $appId
|
||||
deploymentIdentifier: $deploymentIdentifier
|
||||
) {
|
||||
pendingWorkerDeploymentId
|
||||
deploymentIdentifier
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
deploymentVariables
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
const url = data.deployments?.createSignedDeploymentUrl.url;
|
||||
assert(url, 'Deployment URL must be defined');
|
||||
return url;
|
||||
},
|
||||
|
||||
async assignDevDomainNameAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
devDomainNameVariables: { appId: string; name: string }
|
||||
): Promise<boolean> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<AssignDevDomainNameMutation, AssignDevDomainNameMutationVariables>(
|
||||
gql`
|
||||
mutation AssignDevDomainName($appId: ID!, $name: DevDomainName!) {
|
||||
devDomainName {
|
||||
assignDevDomainName(appId: $appId, name: $name) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
devDomainNameVariables
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.devDomainName.assignDevDomainName.name === devDomainNameVariables.name;
|
||||
},
|
||||
|
||||
async assignAliasAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
aliasVariables: { appId: string; deploymentId: string; aliasName: string | null }
|
||||
): Promise<AssignAliasMutation['deployments']['assignAlias']> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<AssignAliasMutation, AssignAliasMutationVariables>(
|
||||
gql`
|
||||
mutation AssignAlias(
|
||||
$appId: ID!
|
||||
$deploymentId: ID!
|
||||
$aliasName: WorkerDeploymentIdentifier
|
||||
) {
|
||||
deployments {
|
||||
assignAlias(
|
||||
appId: $appId
|
||||
deploymentIdentifier: $deploymentId
|
||||
aliasName: $aliasName
|
||||
) {
|
||||
id
|
||||
...WorkerDeploymentAliasFragment
|
||||
workerDeployment {
|
||||
id
|
||||
...WorkerDeploymentFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${print(WorkerDeploymentFragmentNode)}
|
||||
${print(WorkerDeploymentAliasFragmentNode)}
|
||||
`,
|
||||
aliasVariables
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.deployments.assignAlias;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { WorkerDeploymentFragmentNode } from './fragments/WorkerDeployment';
|
||||
import type { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../graphql/client';
|
||||
import {
|
||||
type PaginatedWorkerDeploymentsQuery,
|
||||
type PaginatedWorkerDeploymentsQueryVariables,
|
||||
SuggestedDevDomainNameQuery,
|
||||
SuggestedDevDomainNameQueryVariables,
|
||||
type WorkerDeploymentFragment,
|
||||
} from '../graphql/generated';
|
||||
import type { Connection } from '../utils/relay';
|
||||
|
||||
export const DeploymentsQuery = {
|
||||
async getAllDeploymentsPaginatedAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId, first, after, last, before }: PaginatedWorkerDeploymentsQueryVariables
|
||||
): Promise<Connection<WorkerDeploymentFragment>> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<PaginatedWorkerDeploymentsQuery, PaginatedWorkerDeploymentsQueryVariables>(
|
||||
gql`
|
||||
query PaginatedWorkerDeployments(
|
||||
$appId: String!
|
||||
$first: Int
|
||||
$after: String
|
||||
$last: Int
|
||||
$before: String
|
||||
) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
workerDeployments(first: $first, after: $after, last: $last, before: $before) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
...WorkerDeploymentFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(WorkerDeploymentFragmentNode)}
|
||||
`,
|
||||
{ appId, first, after, last, before },
|
||||
{ additionalTypenames: ['WorkerDeployment'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app.byId.workerDeployments;
|
||||
},
|
||||
|
||||
async getSuggestedDevDomainByAppIdAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ appId }: SuggestedDevDomainNameQueryVariables
|
||||
): Promise<string> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<SuggestedDevDomainNameQuery, SuggestedDevDomainNameQueryVariables>(
|
||||
gql`
|
||||
query SuggestedDevDomainName($appId: String!) {
|
||||
app {
|
||||
byId(appId: $appId) {
|
||||
id
|
||||
suggestedDevDomainName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ appId },
|
||||
{ additionalTypenames: ['App'] }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
||||
return data.app.byId.suggestedDevDomainName;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,177 @@
|
||||
import * as https from 'https';
|
||||
import createHttpsProxyAgent from 'https-proxy-agent';
|
||||
import mime from 'mime';
|
||||
import { Gzip } from 'minizlib';
|
||||
import fetch, { Headers, HeadersInit, RequestInit, Response } from 'node-fetch';
|
||||
import fs, { createReadStream } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import promiseRetry from 'promise-retry';
|
||||
|
||||
const MAX_RETRIES = 4;
|
||||
const MAX_CONCURRENCY = 10;
|
||||
const MIN_RETRY_TIMEOUT = 100;
|
||||
const MAX_UPLOAD_SIZE = 5e8; // 5MB
|
||||
const MIN_COMPRESSION_SIZE = 5e4; // 50kB
|
||||
|
||||
const isCompressible = (contentType: string | null, size: number): boolean => {
|
||||
if (size < MIN_COMPRESSION_SIZE) {
|
||||
// Don't compress small files
|
||||
return false;
|
||||
} else if (contentType && /^(?:audio|video|image)\//i.test(contentType)) {
|
||||
// Never compress images, audio, or videos as they're presumably precompressed
|
||||
return false;
|
||||
} else if (contentType && /^application\//i.test(contentType)) {
|
||||
// Only compress `application/` files if they're marked as XML/JSON/JS
|
||||
return /(?:xml|json5?|javascript)$/i.test(contentType);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export interface UploadParams extends Omit<RequestInit, 'signal' | 'body'> {
|
||||
filePath: string;
|
||||
compress?: boolean;
|
||||
url: string;
|
||||
method?: string;
|
||||
headers?: HeadersInit;
|
||||
body?: undefined;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
export interface UploadResult {
|
||||
params: UploadParams;
|
||||
response: Response;
|
||||
}
|
||||
|
||||
let sharedAgent: https.Agent | undefined;
|
||||
const getAgent = (): https.Agent => {
|
||||
if (sharedAgent) {
|
||||
return sharedAgent;
|
||||
} else if (process.env.https_proxy) {
|
||||
return (sharedAgent = createHttpsProxyAgent(process.env.https_proxy));
|
||||
} else {
|
||||
return (sharedAgent = new https.Agent({
|
||||
keepAlive: true,
|
||||
maxSockets: MAX_CONCURRENCY,
|
||||
maxTotalSockets: MAX_CONCURRENCY,
|
||||
scheduling: 'lifo',
|
||||
timeout: 4_000,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
|
||||
const {
|
||||
filePath,
|
||||
signal,
|
||||
compress,
|
||||
method = 'POST',
|
||||
url,
|
||||
headers: headersInit,
|
||||
...requestInit
|
||||
} = params;
|
||||
const stat = await fs.promises.stat(params.filePath);
|
||||
if (stat.size > MAX_UPLOAD_SIZE) {
|
||||
throw new Error(
|
||||
`Upload of "${params.filePath}" aborted: File size is greater than the upload limit (>500MB)`
|
||||
);
|
||||
}
|
||||
|
||||
const contentType = mime.getType(path.basename(params.filePath));
|
||||
return await promiseRetry(
|
||||
async retry => {
|
||||
const headers = new Headers(headersInit);
|
||||
if (contentType) {
|
||||
headers.set('content-type', contentType);
|
||||
}
|
||||
|
||||
let bodyStream: NodeJS.ReadableStream = createReadStream(filePath);
|
||||
if (compress && isCompressible(contentType, stat.size)) {
|
||||
const gzip = new Gzip({ portable: true });
|
||||
bodyStream.on('error', error => gzip.emit('error', error));
|
||||
// @ts-expect-error: Gzip implements a Readable-like interface
|
||||
bodyStream = bodyStream.pipe(gzip) as NodeJS.ReadableStream;
|
||||
headers.set('content-encoding', 'gzip');
|
||||
}
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(params.url, {
|
||||
...requestInit,
|
||||
method,
|
||||
body: bodyStream,
|
||||
headers,
|
||||
agent: getAgent(),
|
||||
// @ts-expect-error: Internal types don't match
|
||||
signal,
|
||||
});
|
||||
} catch (error) {
|
||||
return retry(error);
|
||||
}
|
||||
|
||||
if (
|
||||
response.status === 408 ||
|
||||
response.status === 409 ||
|
||||
response.status === 429 ||
|
||||
(response.status >= 500 && response.status <= 599)
|
||||
) {
|
||||
const message = `Upload of "${filePath}" failed: ${response.statusText}`;
|
||||
const text = await response.text().catch(() => null);
|
||||
return retry(new Error(text ? `${message}\n${text}` : message));
|
||||
} else if (response.status === 413) {
|
||||
const message = `Upload of "${filePath}" failed: File size exceeded the upload limit`;
|
||||
throw new Error(message);
|
||||
} else if (!response.ok) {
|
||||
throw new Error(`Upload of "${filePath}" failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return {
|
||||
params,
|
||||
response,
|
||||
};
|
||||
},
|
||||
{
|
||||
retries: MAX_RETRIES,
|
||||
minTimeout: MIN_RETRY_TIMEOUT,
|
||||
randomize: true,
|
||||
factor: 2,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export interface UploadPending {
|
||||
params: UploadParams;
|
||||
}
|
||||
|
||||
export type BatchUploadSignal = UploadResult | UploadPending;
|
||||
|
||||
export async function* batchUploadAsync(
|
||||
uploads: readonly UploadParams[]
|
||||
): AsyncGenerator<BatchUploadSignal> {
|
||||
const controller = new AbortController();
|
||||
const queue = new Set<Promise<UploadResult>>();
|
||||
try {
|
||||
let index = 0;
|
||||
while (index < uploads.length || queue.size > 0) {
|
||||
while (queue.size < MAX_CONCURRENCY && index < uploads.length) {
|
||||
const uploadParams = uploads[index++];
|
||||
let uploadPromise: Promise<UploadResult>;
|
||||
queue.add(
|
||||
(uploadPromise = uploadAsync({ ...uploadParams, signal: controller.signal }).finally(() =>
|
||||
queue.delete(uploadPromise)
|
||||
))
|
||||
);
|
||||
yield { params: uploadParams };
|
||||
}
|
||||
yield await Promise.race(queue);
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (typeof error !== 'object' || error.name !== 'AbortError') {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
if (queue.size > 0) {
|
||||
controller.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import type {
|
||||
WorkerDeploymentAliasFragment,
|
||||
WorkerDeploymentFragment,
|
||||
} from '../../graphql/generated';
|
||||
import formatFields, { type FormatFieldsItem } from '../../utils/formatFields';
|
||||
|
||||
export const EXPO_BASE_DOMAIN = process.env.EXPO_STAGING ? 'staging.expo' : 'expo';
|
||||
|
||||
export function getDeploymentUrlFromFullName(deploymentFullName: string): string {
|
||||
return `https://${deploymentFullName}.${EXPO_BASE_DOMAIN}.app`;
|
||||
}
|
||||
|
||||
export function getDashboardUrl(projectId: string): string {
|
||||
return `https://${EXPO_BASE_DOMAIN}.dev/projects/${projectId}/hosting/deployments`;
|
||||
}
|
||||
|
||||
type WorkerDeploymentData = {
|
||||
/** Used to generate the dashboard URL to `expo.dev` */
|
||||
projectId: string;
|
||||
/** The actual deployment information */
|
||||
deployment: Pick<WorkerDeploymentFragment, 'deploymentIdentifier' | 'url'>;
|
||||
/** All modified aliases of the deployment, if any */
|
||||
aliases?: WorkerDeploymentAliasFragment[];
|
||||
/** The production promoting alias of the deployment, if any */
|
||||
production?: WorkerDeploymentAliasFragment | null;
|
||||
};
|
||||
|
||||
export function formatWorkerDeploymentTable(data: WorkerDeploymentData): string {
|
||||
const fields: FormatFieldsItem[] = [
|
||||
{ label: 'Dashboard', value: getDashboardUrl(data.projectId) },
|
||||
{ label: 'Deployment URL', value: data.deployment.url },
|
||||
];
|
||||
|
||||
if (data.aliases?.length) {
|
||||
fields.push({ label: 'Alias URL', value: data.aliases[0].url });
|
||||
}
|
||||
if (data.production) {
|
||||
fields.push({ label: 'Production URL', value: data.production.url });
|
||||
}
|
||||
|
||||
const lastUrlField = fields[fields.length - 1];
|
||||
lastUrlField.value = chalk.cyan(lastUrlField.value);
|
||||
|
||||
return formatFields(fields);
|
||||
}
|
||||
|
||||
type WorkerDeploymentOutput = {
|
||||
/** The absolute URL to the dashboard on `expo.dev` */
|
||||
dashboardUrl: string;
|
||||
/** The deployment information */
|
||||
deployment: {
|
||||
identifier: string;
|
||||
url: string;
|
||||
aliases?: { id: string; name: string; url: string }[];
|
||||
production?: { id: string; url: string };
|
||||
};
|
||||
};
|
||||
|
||||
export function formatWorkerDeploymentJson(data: WorkerDeploymentData): WorkerDeploymentOutput {
|
||||
return {
|
||||
dashboardUrl: getDashboardUrl(data.projectId),
|
||||
deployment: {
|
||||
identifier: data.deployment.deploymentIdentifier,
|
||||
url: data.deployment.url,
|
||||
aliases: !data.aliases?.length
|
||||
? undefined
|
||||
: data.aliases.map(alias => ({
|
||||
id: alias.id,
|
||||
name: alias.aliasName,
|
||||
url: alias.url,
|
||||
})),
|
||||
production: !data.production
|
||||
? undefined
|
||||
: {
|
||||
id: data.production.id,
|
||||
url: data.production.url,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "11.0.0",
|
||||
"version": "12.0.0",
|
||||
"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.130",
|
||||
"@expo/eas-build-job": "1.0.133",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "11.2.0",
|
||||
|
||||
@@ -1359,10 +1359,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
|
||||
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
|
||||
|
||||
"@expo/apple-utils@1.7.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/apple-utils@1.8.0":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-1.8.0.tgz#8a359cefbad000be66caa18235ecd4c03406a15d"
|
||||
integrity sha512-JKGfPCCGko7GtLcvCj9VeyrSJGimgWsBJnR4G10kGY9sUQo3ykEgVrUBUaG2HJrCLzbeDUExmsbuZRWqJha77g==
|
||||
|
||||
"@expo/bunyan@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1382,58 +1382,90 @@
|
||||
node-forge "^1.2.1"
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
"@expo/config-plugins@8.0.8", "@expo/config-plugins@~8.0.8":
|
||||
version "8.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.8.tgz#294a71905a498ea02c8b79bea950b5e37ab5d748"
|
||||
integrity sha512-Fvu6IO13EUw0R9WeqxUO37FkM62YJBNcZb9DyJAOgMz7Ez/vaKQGEjKt9cwT+Q6uirtCATMgaq6VWAW7YW8xXw==
|
||||
"@expo/config-plugins@7.8.4":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.4.tgz#533b5d536c1dc8b5544d64878b51bda28f2e1a1f"
|
||||
integrity sha512-hv03HYxb/5kX8Gxv/BTI8TLc9L06WzqAfHRRXdbar4zkLcP2oTzvsLEF4/L/TIpD3rsnYa0KU42d0gWRxzPCJg==
|
||||
dependencies:
|
||||
"@expo/config-types" "^51.0.0-unreleased"
|
||||
"@expo/config-types" "^50.0.0-alpha.1"
|
||||
"@expo/fingerprint" "^0.6.0"
|
||||
"@expo/json-file" "~8.3.0"
|
||||
"@expo/plist" "^0.1.0"
|
||||
"@expo/sdk-runtime-versions" "^1.0.0"
|
||||
"@react-native/normalize-color" "^2.0.0"
|
||||
chalk "^4.1.2"
|
||||
debug "^4.3.1"
|
||||
find-up "~5.0.0"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.5.4"
|
||||
semver "^7.5.3"
|
||||
slash "^3.0.0"
|
||||
slugify "^1.6.6"
|
||||
xcode "^3.0.1"
|
||||
xml2js "0.6.0"
|
||||
|
||||
"@expo/config-[email protected]", "@expo/config-types@^51.0.0-unreleased":
|
||||
version "51.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-51.0.2.tgz#7385451b180d34d8f2a4eeb5feabe1fe3c5d4f32"
|
||||
integrity sha512-IglkIoiDwJMY01lYkF/ZSBoe/5cR+O3+Gx6fpLFjLfgZGBTdyPkKa1g8NWoWQCk+D3cKL2MDbszT2DyRRB0YqQ==
|
||||
"@expo/config-plugins@~7.8.0", "@expo/config-plugins@~7.8.2":
|
||||
version "7.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.2.tgz#c00ce93c4d6c2cb9e345ed9cd56ceeea05ab8ddb"
|
||||
integrity sha512-XM2eXA5EvcpmXFCui48+bVy8GTskYSjPf2yC+LliYv8PDcedu7+pdgmbnvH4eZCyHfTMO8/UiF+w8e5WgOEj5A==
|
||||
dependencies:
|
||||
"@expo/config-types" "^50.0.0-alpha.1"
|
||||
"@expo/fingerprint" "^0.6.0"
|
||||
"@expo/json-file" "~8.3.0"
|
||||
"@expo/plist" "^0.1.0"
|
||||
"@expo/sdk-runtime-versions" "^1.0.0"
|
||||
"@react-native/normalize-color" "^2.0.0"
|
||||
chalk "^4.1.2"
|
||||
debug "^4.3.1"
|
||||
find-up "~5.0.0"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.5.3"
|
||||
slash "^3.0.0"
|
||||
slugify "^1.6.6"
|
||||
xcode "^3.0.1"
|
||||
xml2js "0.6.0"
|
||||
|
||||
"@expo/config@9.0.3", "@expo/config@~9.0.0-beta.0":
|
||||
version "9.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-9.0.3.tgz#4bc2ec654145e6242f4b1964db2962ee0fee1270"
|
||||
integrity sha512-eOTNM8eOC8gZNHgenySRlc/lwmYY1NOgvjwA8LHuvPT7/eUwD93zrxu3lPD1Cc/P6C/2BcVdfH4hf0tLmDxnsg==
|
||||
"@expo/config-types@50.0.0", "@expo/config-types@^50.0.0", "@expo/config-types@^50.0.0-alpha.1":
|
||||
version "50.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-50.0.0.tgz#b534d3ec997ec60f8af24f6ad56244c8afc71a0b"
|
||||
integrity sha512-0kkhIwXRT6EdFDwn+zTg9R2MZIAEYGn1MVkyRohAd+C9cXOb5RA8WLQi7vuxKF9m1SMtNAUrf0pO+ENK0+/KSw==
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "8.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.5.4.tgz#bb5eb06caa36e4e35dc8c7647fae63e147b830ca"
|
||||
integrity sha512-ggOLJPHGzJSJHVBC1LzwXwR6qUn8Mw7hkc5zEKRIdhFRuIQ6s2FE4eOvP87LrNfDF7eZGa6tJQYsiHSmZKG+8Q==
|
||||
dependencies:
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
"@expo/config-plugins" "~8.0.8"
|
||||
"@expo/config-types" "^51.0.0-unreleased"
|
||||
"@expo/json-file" "^8.3.0"
|
||||
"@expo/config-plugins" "~7.8.2"
|
||||
"@expo/config-types" "^50.0.0"
|
||||
"@expo/json-file" "^8.2.37"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
require-from-string "^2.0.2"
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.6.0"
|
||||
semver "7.5.3"
|
||||
slugify "^1.3.4"
|
||||
sucrase "3.34.0"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
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==
|
||||
"@expo/config@~8.5.0":
|
||||
version "8.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.5.2.tgz#4a0de82279337a37b5f1f0958711fe56e8ea31c2"
|
||||
integrity sha512-UYy9kWxjQAEDwlX7gwLk0+8IxGPcHJMLnYRYtR5C5xGIV+ML7tCA+qFz1p4gHZRPkJD+k7iZyqBtyqt8xm+zXw==
|
||||
dependencies:
|
||||
"@expo/logger" "1.0.117"
|
||||
joi "^17.13.1"
|
||||
semver "^7.6.2"
|
||||
zod "^3.23.8"
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
"@expo/config-plugins" "~7.8.2"
|
||||
"@expo/config-types" "^50.0.0"
|
||||
"@expo/json-file" "^8.2.37"
|
||||
getenv "^1.0.0"
|
||||
glob "7.1.6"
|
||||
require-from-string "^2.0.2"
|
||||
resolve-from "^5.0.0"
|
||||
semver "7.5.3"
|
||||
slugify "^1.3.4"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
version "1.0.133"
|
||||
@@ -1445,12 +1477,36 @@
|
||||
semver "^7.6.2"
|
||||
zod "^3.23.8"
|
||||
|
||||
"@expo/image-utils@^0.5.0":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.5.1.tgz#06fade141facebcd8431355923d30f3839309942"
|
||||
integrity sha512-U/GsFfFox88lXULmFJ9Shfl2aQGcwoKPF7fawSCLixIKtMCpsI+1r0h+5i0nQnmt9tHuzXZDL8+Dg1z6OhkI9A==
|
||||
"@expo/env@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/env/-/env-0.3.0.tgz#a66064e5656e0e48197525f47f3398034fdf579e"
|
||||
integrity sha512-OtB9XVHWaXidLbHvrVDeeXa09yvTl3+IQN884sO6PhIi2/StXfgSH/9zC7IvzrDB8kW3EBJ1PPLuCUJ2hxAT7Q==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
chalk "^4.0.0"
|
||||
debug "^4.3.4"
|
||||
dotenv "~16.4.5"
|
||||
dotenv-expand "~11.0.6"
|
||||
getenv "^1.0.0"
|
||||
|
||||
"@expo/fingerprint@^0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.6.0.tgz#77366934673d4ecea37284109b4dd67f9e6a7487"
|
||||
integrity sha512-KfpoVRTMwMNJ/Cf5o+Ou8M/Y0EGSTqK+rbi70M2Y0K2qgWNfMJ1gm6sYO9uc8lcTr7YSYM1Rme3dk7QXhpScNA==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
chalk "^4.1.2"
|
||||
debug "^4.3.4"
|
||||
find-up "^5.0.0"
|
||||
minimatch "^3.0.4"
|
||||
p-limit "^3.1.0"
|
||||
resolve-from "^5.0.0"
|
||||
|
||||
"@expo/image-utils@^0.4.0":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.4.1.tgz#78c54b8aaa974d0ac37fee5285683b54ff161b2c"
|
||||
integrity sha512-EZb+VHSmw+a5s2hS9qksTcWylY0FDaIAVufcxoaRS9tHIXLjW5zcKW7Rhj9dSEbZbRVy9yXXdHKa3GQdUQIOFw==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "1.5.0"
|
||||
chalk "^4.0.0"
|
||||
fs-extra "9.0.0"
|
||||
getenv "^1.0.0"
|
||||
@@ -1458,13 +1514,13 @@
|
||||
node-fetch "^2.6.0"
|
||||
parse-png "^2.1.0"
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.6.0"
|
||||
semver "7.3.2"
|
||||
tempy "0.3.0"
|
||||
|
||||
"@expo/json-file@8.3.3", "@expo/json-file@^8.3.0":
|
||||
version "8.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.3.3.tgz#7926e3592f76030ce63d6b1308ac8f5d4d9341f4"
|
||||
integrity sha512-eZ5dld9AD0PrVRiIWpRkm5aIoWBw3kAyd8VkuWEy92sEthBKDDDHAnK2a0dw0Eil6j7rK7lS/Qaq/Zzngv2h5A==
|
||||
"@expo/json-file@8.2.37", "@expo/json-file@^8.2.37":
|
||||
version "8.2.37"
|
||||
resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.37.tgz#9c02d3b42134907c69cc0a027b18671b69344049"
|
||||
integrity sha512-YaH6rVg11JoTS2P6LsW7ybS2CULjf40AbnAHw2F1eDPuheprNjARZMnyHFPkKv7GuxCy+B9GPcbOKgc4cgA80Q==
|
||||
dependencies:
|
||||
"@babel/code-frame" "~7.10.4"
|
||||
json5 "^2.2.2"
|
||||
@@ -1496,21 +1552,21 @@
|
||||
nullthrows "^1.1.1"
|
||||
structured-headers "^0.4.1"
|
||||
|
||||
"@expo/osascript@2.1.3":
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.1.3.tgz#912b74825cb83f3b958cad81034df9e19f1f2808"
|
||||
integrity sha512-aOEkhPzDsaAfolSswObGiYW0Pf0ROfR9J2NBRLQACdQ6uJlyAMiPF45DVEVknAU9juKh0y8ZyvC9LXqLEJYohA==
|
||||
"@expo/osascript@2.0.33":
|
||||
version "2.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.0.33.tgz#e9dcc8da54466c11939074aa71a006024ea884b1"
|
||||
integrity sha512-FQinlwHrTlJbntp8a7NAlCKedVXe06Va/0DSLXRO8lZVtgbEMrYYSUZWQNcOlNtc58c2elNph6z9dMOYwSo3JQ==
|
||||
dependencies:
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
exec-async "^2.2.0"
|
||||
|
||||
"@expo/package-manager@1.5.2":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.5.2.tgz#6015963669977a188bbbac930aa0dc103162ee73"
|
||||
integrity sha512-IuA9XtGBilce0q8cyxtWINqbzMB1Fia0Yrug/O53HNuRSwQguV/iqjV68bsa4z8mYerePhcFgtvISWLAlNEbUA==
|
||||
"@expo/package-manager@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.1.2.tgz#e58c9bed4cbb829ebf2cbb80b8542600a6609bd1"
|
||||
integrity sha512-JI9XzrxB0QVXysyuJ996FPCJGDCYRkbUvgG4QmMTTMFA1T+mv8YzazC3T9C1pHQUAAveVCre1+Pqv0nZXN24Xg==
|
||||
dependencies:
|
||||
"@expo/json-file" "^8.3.0"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
"@expo/json-file" "^8.2.37"
|
||||
"@expo/spawn-async" "^1.5.0"
|
||||
ansi-regex "^5.0.0"
|
||||
chalk "^4.0.0"
|
||||
find-up "^5.0.0"
|
||||
@@ -1518,21 +1574,20 @@
|
||||
js-yaml "^3.13.1"
|
||||
micromatch "^4.0.2"
|
||||
npm-package-arg "^7.0.0"
|
||||
ora "^3.4.0"
|
||||
split "^1.0.1"
|
||||
sudo-prompt "9.1.1"
|
||||
|
||||
"@expo/pkcs12@0.1.2":
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.1.2.tgz#9d88ed0d980e211690a9580fed425d9e3cc4c5b7"
|
||||
integrity sha512-CLoOKm35mcIJHV11bR7DunfAtw0HcQ2ir7fWWCw9gwvhrhi75cer2xlLqeHPhmvXDad437Mh8L1Bz+B1EIKdMA==
|
||||
"@expo/pkcs12@0.0.8":
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@expo/pkcs12/-/pkcs12-0.0.8.tgz#40ddde3007d90d4fd58779670f1aabfa82ce2950"
|
||||
integrity sha512-VNZnmsu3PgdvZRqYLB0Ja8dNjKrgKpcqMvtUPINI4fJbF/ihDNI0A/LkHvnR2/21WRMHk2tm4QgMIkOQTfZ5kg==
|
||||
dependencies:
|
||||
node-forge "^1.2.1"
|
||||
|
||||
"@expo/plist@0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.1.3.tgz#b4fbee2c4f7a88512a4853d85319f4d95713c529"
|
||||
integrity sha512-GW/7hVlAylYg1tUrEASclw1MMk9FP4ZwyFAY/SUTJIhPDQHtfOlXREyWV3hhrHdX/K+pS73GNgdfT6E/e+kBbg==
|
||||
"@expo/plist@0.0.20":
|
||||
version "0.0.20"
|
||||
resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.0.20.tgz#a6b3124438031c02b762bad5a47b70584d3c0072"
|
||||
integrity sha512-UXQ4LXCfTZ580LDHGJ5q62jSTwJFFJ1GqBu8duQMThiHKWbMJ+gajJh6rsB6EJ3aLUr9wcauxneL5LVRFxwBEA==
|
||||
dependencies:
|
||||
"@xmldom/xmldom" "~0.7.7"
|
||||
base64-js "^1.2.3"
|
||||
@@ -1568,21 +1623,20 @@
|
||||
semver "^7.3.7"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@expo/prebuild-config@7.0.8":
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-7.0.8.tgz#8af72b19c92f05f1ab6c6c70d31f33159dacac39"
|
||||
integrity sha512-wH9NVg6HiwF5y9x0TxiMEeBF+ITPGDXy5/i6OUheSrKpPgb0lF1Mwzl/f2fLPXBEpl+ZXOQ8LlLW32b7K9lrNg==
|
||||
"@expo/prebuild-config@6.7.3":
|
||||
version "6.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-6.7.3.tgz#8444c1630bd92931c2d1a510791535b7282d8fa6"
|
||||
integrity sha512-jZIHzlnvdg4Gnln06XR9tvirL3hSp/Jh48COhLKs51vb3THCWumUytZBS4DSMdvGwf8btnaB01Zg00xQhSDBsA==
|
||||
dependencies:
|
||||
"@expo/config" "~9.0.0-beta.0"
|
||||
"@expo/config-plugins" "~8.0.8"
|
||||
"@expo/config-types" "^51.0.0-unreleased"
|
||||
"@expo/image-utils" "^0.5.0"
|
||||
"@expo/json-file" "^8.3.0"
|
||||
"@react-native/normalize-colors" "0.74.85"
|
||||
"@expo/config" "~8.5.0"
|
||||
"@expo/config-plugins" "~7.8.0"
|
||||
"@expo/config-types" "^50.0.0-alpha.1"
|
||||
"@expo/image-utils" "^0.4.0"
|
||||
"@expo/json-file" "^8.2.37"
|
||||
debug "^4.3.1"
|
||||
fs-extra "^9.0.0"
|
||||
resolve-from "^5.0.0"
|
||||
semver "^7.6.0"
|
||||
semver "7.5.3"
|
||||
xml2js "0.6.0"
|
||||
|
||||
"@expo/[email protected]":
|
||||
@@ -1608,19 +1662,33 @@
|
||||
resolved "https://registry.yarnpkg.com/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c"
|
||||
integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==
|
||||
|
||||
"@expo/spawn-async@1.7.2", "@expo/spawn-async@^1.7.2":
|
||||
"@expo/spawn-async@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.5.0.tgz#799827edd8c10ef07eb1a2ff9dcfe081d596a395"
|
||||
integrity sha512-LB7jWkqrHo+5fJHNrLAFdimuSXQ2MQ4lA7SQW5bf/HbsXuV2VrT/jN/M8f/KoWt0uJMGN4k/j7Opx4AvOOxSew==
|
||||
dependencies:
|
||||
cross-spawn "^6.0.5"
|
||||
|
||||
"@expo/[email protected]", "@expo/spawn-async@^1.5.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.0.tgz#3ab6082b24318cccc4e73b13464da91325555500"
|
||||
integrity sha512-sqPAjOEFTrjaTybrh9SnPFLInDXcoMC06psEFmH68jLTmoipSQCq8GCEfIoHhxRDALWB+DsiwXJSbXlE/iVIIQ==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/spawn-async@^1.7.2":
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58"
|
||||
integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
version "1.0.130"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.130.tgz#91e05e7387f674d7c27cc610fa558e6cc2fb7ac1"
|
||||
integrity sha512-ZWjrPGj8rzPVDSwxhWi2i2FWIkKUZmJCNfs0p9DSPqdUOADg7XIFSqxefxBeygZ0R2dJMEjRQA8Gz0mqjFIkiQ==
|
||||
"@expo/[email protected]4":
|
||||
version "1.0.134"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.134.tgz#c7765a3fb2c0fdec458f168365b1c80cc59632cf"
|
||||
integrity sha512-9WZUhq1Jggipy+cTcN/pi92I911jaOS4VoEWjuKbhp5G5HU/oHBSfR3ZapEopA9N9xzBv9I/GYzRbPiMHPXaxw==
|
||||
dependencies:
|
||||
"@expo/eas-build-job" "1.0.130"
|
||||
"@expo/eas-build-job" "1.0.133"
|
||||
"@expo/logger" "1.0.117"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
arg "^5.0.2"
|
||||
@@ -3405,10 +3473,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.0.tgz#7d8dacb7fdef0e4387caf7396cbd77f179867d06"
|
||||
integrity sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==
|
||||
|
||||
"@react-native/normalize-color[email protected]":
|
||||
version "0.74.85"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz#62bcb9ab1b10b822ca0278fdfdf23d3b18e125da"
|
||||
integrity sha512-pcE4i0X7y3hsAE0SpIl7t6dUc0B0NZLd1yv7ssm4FrLhWG+CGyIq4eFDXpmPU1XHmL5PPySxTAjEMiwv6tAmOw==
|
||||
"@react-native/normalize-color@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.0.0.tgz#da955909432474a9a0fe1cbffc66576a0447f567"
|
||||
integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw==
|
||||
|
||||
"@repeaterjs/repeater@^3.0.4":
|
||||
version "3.0.5"
|
||||
@@ -3880,6 +3948,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/[email protected]":
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-3.1.3.tgz#f61427229691eda1b7d5719f34acdc4fc8a558ce"
|
||||
integrity sha512-Zbnx4wpkWBMBSu5CytMbrT5ZpMiF55qgM+EpHzR4yIDu7mv52cej8hTkOc6K+LzpkOAbxwn/m7j3iO+/l42YkQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/[email protected]":
|
||||
version "6.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.10.tgz#10b0e12129f4af5909af82a055837116ab06f860"
|
||||
@@ -4266,11 +4341,6 @@ ansi-escapes@^6.0.0:
|
||||
dependencies:
|
||||
type-fest "^3.0.0"
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed"
|
||||
integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==
|
||||
|
||||
ansi-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
@@ -4644,6 +4714,11 @@ axios@^1.0.0:
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
b4a@^1.6.4:
|
||||
version "1.6.6"
|
||||
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba"
|
||||
integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==
|
||||
|
||||
babel-jest@^29.7.0:
|
||||
version "29.7.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
|
||||
@@ -4759,6 +4834,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
bare-events@^2.2.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.4.2.tgz#3140cca7a0e11d49b3edc5041ab560659fd8e1f8"
|
||||
integrity sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==
|
||||
|
||||
base64-js@^1.0.2, base64-js@^1.2.3, base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
@@ -4807,10 +4887,10 @@ bl@^4.0.3, bl@^4.1.0:
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
[email protected].2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
[email protected].3:
|
||||
version "1.20.3"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
|
||||
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.5"
|
||||
@@ -4820,7 +4900,7 @@ [email protected]:
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
qs "6.13.0"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
@@ -5196,7 +5276,7 @@ [email protected], chalk@^4, chalk@^4.0.2, chalk@^4.1.1, chalk@^4.1.2:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2:
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
@@ -5303,13 +5383,6 @@ [email protected], cli-cursor@^3.1.0:
|
||||
dependencies:
|
||||
restore-cursor "^3.1.0"
|
||||
|
||||
cli-cursor@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||
integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==
|
||||
dependencies:
|
||||
restore-cursor "^2.0.0"
|
||||
|
||||
[email protected], cli-progress@^3.10.0, cli-progress@^3.12.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942"
|
||||
@@ -5322,11 +5395,6 @@ [email protected], cli-spinners@^2.5.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
|
||||
integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
|
||||
|
||||
cli-spinners@^2.0.0:
|
||||
version "2.9.2"
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
|
||||
integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
|
||||
|
||||
cli-spinners@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f"
|
||||
@@ -6131,6 +6199,13 @@ dotenv-expand@~10.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37"
|
||||
integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==
|
||||
|
||||
dotenv-expand@~11.0.6:
|
||||
version "11.0.6"
|
||||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-11.0.6.tgz#f2c840fd924d7c77a94eff98f153331d876882d3"
|
||||
integrity sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==
|
||||
dependencies:
|
||||
dotenv "^16.4.4"
|
||||
|
||||
[email protected], dotenv@~16.3.1:
|
||||
version "16.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
|
||||
@@ -6141,10 +6216,15 @@ dotenv@^16.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
|
||||
integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
|
||||
|
||||
dotenv@^16.4.4, dotenv@~16.4.5:
|
||||
version "16.4.5"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
|
||||
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
|
||||
|
||||
dset@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a"
|
||||
integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248"
|
||||
integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==
|
||||
|
||||
dtrace-provider@~0.8:
|
||||
version "0.8.8"
|
||||
@@ -6217,6 +6297,11 @@ encodeurl@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||
|
||||
encodeurl@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
|
||||
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||
|
||||
encoding@^0.1.12, encoding@^0.1.13:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
||||
@@ -6938,37 +7023,37 @@ expect@^29.7.0:
|
||||
jest-message-util "^29.7.0"
|
||||
jest-util "^29.7.0"
|
||||
|
||||
express@4.19.2:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
express@4.20.0:
|
||||
version "4.20.0"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
|
||||
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.2"
|
||||
body-parser "1.20.3"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
encodeurl "~1.0.2"
|
||||
encodeurl "~2.0.0"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "1.2.0"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
merge-descriptors "1.0.1"
|
||||
merge-descriptors "1.0.3"
|
||||
methods "~1.1.2"
|
||||
on-finished "2.4.1"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
path-to-regexp "0.1.10"
|
||||
proxy-addr "~2.0.7"
|
||||
qs "6.11.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.2.1"
|
||||
send "0.18.0"
|
||||
serve-static "1.15.0"
|
||||
send "0.19.0"
|
||||
serve-static "1.16.0"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
type-is "~1.6.18"
|
||||
@@ -7011,6 +7096,11 @@ fast-diff@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||
|
||||
fast-fifo@^1.2.0, fast-fifo@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
|
||||
integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==
|
||||
|
||||
[email protected]:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||
@@ -7674,6 +7764,18 @@ glob@^10.2.2:
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
path-scurry "^1.10.1"
|
||||
|
||||
glob@^10.3.7:
|
||||
version "10.4.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5"
|
||||
integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==
|
||||
dependencies:
|
||||
foreground-child "^3.1.0"
|
||||
jackspeak "^3.1.2"
|
||||
minimatch "^9.0.4"
|
||||
minipass "^7.1.2"
|
||||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^1.11.1"
|
||||
|
||||
glob@^6.0.1:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
|
||||
@@ -9013,6 +9115,15 @@ jackspeak@^2.3.5:
|
||||
optionalDependencies:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
jackspeak@^3.1.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a"
|
||||
integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==
|
||||
dependencies:
|
||||
"@isaacs/cliui" "^8.0.2"
|
||||
optionalDependencies:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
jake@^10.8.5:
|
||||
version "10.8.5"
|
||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
|
||||
@@ -9994,13 +10105,6 @@ [email protected], log-symbols@^4.1.0:
|
||||
chalk "^4.1.0"
|
||||
is-unicode-supported "^0.1.0"
|
||||
|
||||
log-symbols@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
|
||||
integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
|
||||
dependencies:
|
||||
chalk "^2.0.1"
|
||||
|
||||
log-symbols@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
|
||||
@@ -10051,6 +10155,11 @@ lowercase-keys@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lru-cache@^10.2.0:
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b"
|
||||
integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
@@ -10269,10 +10378,10 @@ meow@^8.1.2:
|
||||
type-fest "^0.18.0"
|
||||
yargs-parser "^20.2.3"
|
||||
|
||||
[email protected].1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||
[email protected].3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
|
||||
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -10336,11 +10445,6 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
|
||||
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
|
||||
|
||||
mimic-fn@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||
|
||||
mimic-fn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
@@ -10536,6 +10640,19 @@ minipass@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
|
||||
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
|
||||
|
||||
minipass@^7.0.4, minipass@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
[email protected]:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.0.1.tgz#46d5329d1eb3c83924eff1d3b858ca0a31581012"
|
||||
integrity sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==
|
||||
dependencies:
|
||||
minipass "^7.0.4"
|
||||
rimraf "^5.0.5"
|
||||
|
||||
minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||
@@ -11269,13 +11386,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
|
||||
integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
onetime@^5.1.0, onetime@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
|
||||
@@ -11318,18 +11428,6 @@ [email protected]:
|
||||
strip-ansi "^6.0.0"
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
ora@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
|
||||
integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
cli-cursor "^2.1.0"
|
||||
cli-spinners "^2.0.0"
|
||||
log-symbols "^2.2.0"
|
||||
strip-ansi "^5.2.0"
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
ora@^5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
|
||||
@@ -11484,6 +11582,11 @@ [email protected]:
|
||||
dependencies:
|
||||
p-reduce "^2.0.0"
|
||||
|
||||
package-json-from-dist@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
|
||||
integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
|
||||
|
||||
pacote@^12.0.0, pacote@^12.0.2:
|
||||
version "12.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pacote/-/pacote-12.0.2.tgz#14ae30a81fe62ec4fc18c071150e6763e932527c"
|
||||
@@ -11702,10 +11805,18 @@ path-scurry@^1.10.1, path-scurry@^1.6.1:
|
||||
lru-cache "^9.1.1 || ^10.0.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
path-scurry@^1.11.1:
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
|
||||
integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
|
||||
dependencies:
|
||||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
[email protected]:
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
|
||||
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
|
||||
|
||||
path-type@^3.0.0:
|
||||
version "3.0.0"
|
||||
@@ -12013,11 +12124,23 @@ [email protected]:
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
[email protected]:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||
dependencies:
|
||||
side-channel "^1.0.6"
|
||||
|
||||
[email protected]:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
|
||||
queue-tick@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
|
||||
integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
|
||||
|
||||
quick-lru@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||
@@ -12368,14 +12491,6 @@ responselike@^2.0.0:
|
||||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==
|
||||
dependencies:
|
||||
onetime "^2.0.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
restore-cursor@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
|
||||
@@ -12418,6 +12533,13 @@ rimraf@^4.4.1:
|
||||
dependencies:
|
||||
glob "^9.2.0"
|
||||
|
||||
rimraf@^5.0.5:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74"
|
||||
integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==
|
||||
dependencies:
|
||||
glob "^10.3.7"
|
||||
|
||||
rimraf@~2.4.0:
|
||||
version "2.4.5"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
|
||||
@@ -12539,6 +12661,11 @@ scuid@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
[email protected]:
|
||||
version "7.3.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||
|
||||
[email protected]:
|
||||
version "7.5.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb"
|
||||
@@ -12594,6 +12721,25 @@ [email protected]:
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
[email protected]:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
|
||||
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
mime "1.6.0"
|
||||
ms "2.1.3"
|
||||
on-finished "2.4.1"
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
sentence-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
|
||||
@@ -12603,10 +12749,10 @@ sentence-case@^3.0.4:
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
[email protected]5.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
||||
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
|
||||
[email protected]6.0:
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
|
||||
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
@@ -13008,6 +13154,17 @@ streamsearch@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
|
||||
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
|
||||
|
||||
streamx@^2.15.0:
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7"
|
||||
integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==
|
||||
dependencies:
|
||||
fast-fifo "^1.3.2"
|
||||
queue-tick "^1.0.1"
|
||||
text-decoder "^1.1.0"
|
||||
optionalDependencies:
|
||||
bare-events "^2.2.0"
|
||||
|
||||
[email protected], string-env-interpolation@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152"
|
||||
@@ -13201,13 +13358,6 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
|
||||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-ansi@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
|
||||
@@ -13316,6 +13466,18 @@ [email protected]:
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
sucrase@^3.20.0:
|
||||
version "3.20.1"
|
||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.20.1.tgz#1c055e97d0fab2f9857f02461364075b3a4ab226"
|
||||
integrity sha512-BIG59HaJOxNct9Va6KvT5yzBA/rcMGetzvZyTx0ZdCcspIbpJTPS64zuAfYlJuOj+3WaI5JOdA+F0bJQQi8ZiQ==
|
||||
dependencies:
|
||||
commander "^4.0.0"
|
||||
glob "7.1.6"
|
||||
lines-and-columns "^1.1.6"
|
||||
mz "^2.7.0"
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
[email protected]:
|
||||
version "9.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0"
|
||||
@@ -13391,6 +13553,15 @@ synckit@^0.9.1:
|
||||
"@pkgr/core" "^0.1.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
[email protected]:
|
||||
version "3.1.7"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b"
|
||||
integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==
|
||||
dependencies:
|
||||
b4a "^1.6.4"
|
||||
fast-fifo "^1.2.0"
|
||||
streamx "^2.15.0"
|
||||
|
||||
tar-stream@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
@@ -13457,6 +13628,13 @@ test-exclude@^6.0.0:
|
||||
glob "^7.1.4"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
text-decoder@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.1.0.tgz#3379e728fcf4d3893ec1aea35e8c2cac215ef190"
|
||||
integrity sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==
|
||||
dependencies:
|
||||
b4a "^1.6.4"
|
||||
|
||||
text-extensions@^1.0.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
|
||||
|
||||
Reference in new issue
Block a user