Compare commits

...
25 Commits
Author SHA1 Message Date
Szymon Dziedzic 88740682cd v12.6.1 2024-10-25 14:31:41 +02:00
Szymon Dziedzic 8c05a711fc [eas-cli] add simplified projectId-focused context (#2653)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

As suggested in the base PR we need a simple context focused on only getting the project ID, without specifying the server-side environment and resolving Expo config with it.

# How

Add such context. Make the `withServerSideEnvironment` argument required for standard project config context.

# Test Plan

Tests, test manually
2024-10-25 14:26:45 +02:00
Szymon Dziedzic d0f01cb58f [eas-cli] add --with-eas-environment-variables-set flag to eas update command (#2628)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

https://exponent-internal.slack.com/archives/C013ZK4SA12/p1725644220583309

We are planning to introduce enhancements to our env vars system soon. Currently, we only allow people to keep secrets on EAS servers. Secrets are not readable outside of EAS servers so of course EAS Update doesn't work with them. In a new system, people can also store readable env vars on our servers, which opens new possibilities about how EAS Update can interact with env vars!

In this PR I'm adding support for the `--with-eas-environment-variables-set production|preview|development` flag. If used, we use the flag's value to fetch readable env vars from our servers and use it in the `expo export` command to produce an update bundle based on these env vars. If the flag is used I'm using the `EXPO_NO_DOTENV` flag to disable Expo CLI from loading env vars from `.env` files and potentially unwillingly overwriting env vars fetched from our servers for a given environment.

Users seem to face big issues with using env vars with EAS Build & Update at the moment, originating from the fact that these 2 handle different, non-compatible ways of using env vars. Env vars in the build profile and server-side secrets vs local env vars + `.env`. With this flag it will be easy to avoid envs mismatch by doing `eas update ----with-eas-environment-variables-set environment` and `eas build` with `{ ..., environment: environment, ...} in the build profile!

# How

Add a new hidden (for now) flag `--with-eas-environment-variables-set` that indicates which environment to use. If used fetch env vars from server. Merge and overwrite `process.env` with newly fetched env vars. Use them to resolve the expo config. Don't use `.env` env var loading from Expo CLI if server-side env vars are used.

# Test Plan

Test manually. Run `eas update` with and without a new flag. Check that the expo config is resolved correctly.
2024-10-25 14:19:11 +02:00
Will Schurman a212662003 [eas-cli] Create dynamic logged in context field and clean up erroneous SessionManager context field uses (#2648) 2024-10-23 08:52:41 -07:00
Will Schurman 3139124daf [ci] Upgrade codemention and update comment configuration (#2647) 2024-10-23 08:48:35 -07:00
Piotr Szeremeta 8c4be1c556 Make eas env:pull pull file env vars (#2636)
# Why

File variables were not handled by the `env:pull` command. They should be downloaded just like the other values.

# How

* `env:pull` saves file type variables to `.eas/.env` directory and sets its value in `.env.local` file to its file path
* `env:push` skips file type variables

# Test Plan

Tested manually
2024-10-22 18:25:09 +02:00
Piotr Szeremeta 3a7dfb0001 Upload fileName with EnvVar create/update commands (#2646)
# Why

[ENG-13652: Show filename when value is a file](https://linear.app/expo/issue/ENG-13652/show-filename-when-value-is-a-file)

`eas env:create` and `eas:env:update` should upload fileName, just like website.

# How

* Added `fileName` parameter

# Test Plan

Tested manually
2024-10-22 17:31:19 +02:00
Szymon Dziedzic 214cff043c [eas-cli] improve logs announcing loading of env variables (#2643)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Improve logs announcing loading env vars.

https://exponent-internal.slack.com/archives/C06FK950085/p1729215938823339?thread_ts=1729186741.835629&cid=C06FK950085

# How

Improve logs

# Test Plan

Test manually.
2024-10-22 11:58:00 +02:00
Expo CI 0515c9a6fa update CHANGELOG.md 2024-10-21 21:33:03 +00:00
Will Schurman 367b763adf v12.6.0 2024-10-21 14:26:49 -07:00
Will Schurman 49dba20200 [eas-cli] Add interactivity to eas update:edit command (#2638) 2024-10-21 14:19:18 -07:00
Juwan Wheatley f0b14bf6a1 [eas-cli] add pause/resume commands for Update Channels (#2614)
# Why

We want to allow developers to prevent updates from being served to certain builds by "pausing" update channels.

# How

- I added two new CLI commands to toggle the channel pausing state
- I added the pause status to the channel details output in the `view` and `list` commands

# Test Plan

Build the CLI locally under the `neas` alias, and run `neas channel:pause development && neas channel:view development && neas channel:resume development && neas channel:view development`:

<img width="1364" alt="Screenshot 2024-10-17 at 05 33 36" src="https://github.com/user-attachments/assets/fd557e57-bbf7-463c-8cec-1febcb2fdde8">
2024-10-21 15:08:45 -04:00
Piotr Szeremeta 161acbb160 Allow user to select environments manually in env:push (#2637)
# Why

[ENG-13853: Prompt user to enter the environment when not provided vs fail and rerun when using `eas env:push`](https://linear.app/expo/issue/ENG-13853/prompt-user-to-enter-the-environment-when-not-provided-vs-fail-and)

If the environment is not provided, we ask the user about it in other commands, I think we can do the same here for consistency vs throwing an error and having the user rerun it

# Test Plan

Tested manually
2024-10-21 15:35:17 +02:00
Expo CI 5aad520c23 update CHANGELOG.md 2024-10-19 18:28:59 +00:00
Cedric van Putten d6ec3ae106 v12.5.4 2024-10-19 20:23:43 +02:00
Cedric van Putten feb8d2d8f4 fix(eas-cli): resolve Apple authentication error (#2641)
* fix(eas-cli): resolve Apple authentication error

* docs(eas-cli): fix changelog entry

* chore(eas-cli): bump `@expo/apple-utils` again
2024-10-19 20:22:39 +02:00
Piotr Szeremeta c07fd222d9 Ask for file path in env:* commands (#2635)
# Why

[ENG-13855: When creating `file` type env var in `env:create` ask about file path vs variable value](https://linear.app/expo/issue/ENG-13855/when-creating-file-type-env-var-in-envcreate-ask-about-file-path-vs)
2024-10-18 16:50:59 +02:00
Piotr Szeremeta 1873804c8b Display message when env:list returns an empty result (#2630)
# Why

[ENG-13847: Show `no env vars found for this environment` if 0 env vars exist for given environment in `eas env:list`](https://linear.app/expo/issue/ENG-13847/show-no-env-vars-found-for-this-environment-if-0-env-vars-exist-for)

<img width="455" alt="image" src="https://github.com/user-attachments/assets/d4286fcb-0adf-4806-a876-4cbbcf3d6157">
2024-10-18 13:16:20 +02:00
Piotr Szeremeta 1cc4fd8880 Use lower case environment across all eas env commands (#2632)
# Why

[ENG-13846: Use lower-case environment names across all `eas env:xyz` commands for consistency](https://linear.app/expo/issue/ENG-13846/use-lower-case-environment-names-across-all-eas-envxyz-commands-for)
2024-10-18 13:14:50 +02:00
Piotr Szeremeta f83d7e0be7 Always ask for environment in env:get (#2631)
# Why

[ENG-13848: Always ask for `environment` in `eas env:get` command](https://linear.app/expo/issue/ENG-13848/always-ask-for-environment-in-eas-envget-command)
2024-10-18 13:14:13 +02:00
Piotr Szeremeta b225ba4bf3 Use the same names for visibility options in eas cli as on (#2629)
# Why

[ENG-13849: When no `--visibility` input is provided `eas env:create` should ask about it by default](https://linear.app/expo/issue/ENG-13849/when-no-visibility-input-is-provided-eas-envcreate-should-ask-about-it)

[ENG-13850: Use the same names for visibility options in EAS CLI as on website](https://linear.app/expo/issue/ENG-13850/use-the-same-names-for-visibility-options-in-eas-cli-as-on-website)

`eas env:*` commands were using obsolete names for visibility options: `public` and `secret`, instead of `plaintext` and `encrypted`. It should be updated.
Also, user is prompted for the variable visibility if not provided.

# Test Plan

Updated tests
2024-10-18 10:29:52 +02:00
Piotr Szeremeta 4fb5972f18 Support multiple environments in env:push command (#2620) 2024-10-17 16:59:57 +02:00
Piotr Szeremeta 463f73eec2 Fix in env:link command: fail in non-interactive mode if variable name is ambigous (#2627) 2024-10-17 16:59:31 +02:00
Szymon Dziedzic 2dbaed5b2a [eas-cli] remove @expo/config-types dependency (#2528)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

Rely on ExpoConfig type from the `@expo/config` package in all places for consistency

# How

Rely on ExpoConfig type from the `@expo/config` package in all places for consistency

# Test Plan

Tests
2024-10-17 14:56:49 +02:00
Expo CI 20b76f72b0 update CHANGELOG.md 2024-10-16 15:30:02 +00:00
115 changed files with 2485 additions and 677 deletions

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
commentConfiguration:
preamble: 'Subscribed to pull request'
epilogue: 'Generated by [CodeMention](https://github.com/expo/expo/actions/workflows/codemention.yaml)'
rules:
- patterns: ['**/*']
mentions: ['szdziedzic', 'khamilowicz', 'sjchmiela']
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: tobyhs/codemention@v1.2.0
- uses: tobyhs/codemention@v1.4.0
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
+14
View File
@@ -12,11 +12,25 @@ This is the log of notable changes to EAS CLI and related packages.
### 🧹 Chores
- Create dynamic logged in context field and clean up erroneous SessionManager context field uses. ([#2648](https://github.com/expo/eas-cli/pull/2648) by [@wschurman](https://github.com/wschurman))
## [12.6.0](https://github.com/expo/eas-cli/releases/tag/v12.6.0) - 2024-10-21
### 🎉 New features
- Add `eas channel:pause` and `eas channel:resume` commands to pause/resume update delivery to builds using specific update channels and display paused status in channel details output. ([#2614](https://github.com/expo/eas-cli/pull/2614) by [@fiberjw](https://github.com/fiberjw))
- Add interactivity to eas update:edit command. ([#2638](https://github.com/expo/eas-cli/pull/2638) by [@wschurman](https://github.com/wschurman))
## [12.5.4](https://github.com/expo/eas-cli/releases/tag/v12.5.4) - 2024-10-19
## [12.5.3](https://github.com/expo/eas-cli/releases/tag/v12.5.3) - 2024-10-16
## [12.5.2](https://github.com/expo/eas-cli/releases/tag/v12.5.2) - 2024-10-11
### 🐛 Bug fixes
- Disallow republishing an update that is being rolled-out. ([#2602](https://github.com/expo/eas-cli/pull/2602) by [@wschurman](https://github.com/wschurman))
- Bump `@expo/apple-utils` to `2.0.2` resolving the Apple authentication error. ([#2641](https://github.com/expo/eas-cli/pull/2641) by [@byCedric](https://github.com/byCedric))
### 🧹 Chores
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "12.5.3",
"version": "12.6.1",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+131 -60
View File
@@ -81,6 +81,8 @@ eas --help COMMAND
* [`eas channel:create [NAME]`](#eas-channelcreate-name)
* [`eas channel:edit [NAME]`](#eas-channeledit-name)
* [`eas channel:list`](#eas-channellist)
* [`eas channel:pause [NAME]`](#eas-channelpause-name)
* [`eas channel:resume [NAME]`](#eas-channelresume-name)
* [`eas channel:rollout [CHANNEL]`](#eas-channelrollout-channel)
* [`eas channel:view [NAME]`](#eas-channelview-name)
* [`eas config`](#eas-config)
@@ -113,6 +115,7 @@ eas --help COMMAND
* [`eas update`](#eas-update)
* [`eas update:configure`](#eas-updateconfigure)
* [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
* [`eas update:edit [GROUPID]`](#eas-updateedit-groupid)
* [`eas update:list`](#eas-updatelist)
* [`eas update:republish`](#eas-updaterepublish)
* [`eas update:roll-back-to-embedded`](#eas-updateroll-back-to-embedded)
@@ -143,7 +146,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -160,7 +163,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -177,7 +180,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -191,7 +194,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v12.5.3/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -243,7 +246,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -264,7 +267,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -284,7 +287,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -304,7 +307,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -327,7 +330,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -364,7 +367,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -383,7 +386,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -400,7 +403,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -419,7 +422,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:inspect`
@@ -454,7 +457,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -504,7 +507,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -533,7 +536,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -559,7 +562,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -609,7 +612,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -628,7 +631,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -647,7 +650,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -664,7 +667,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -685,7 +688,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -707,7 +710,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -727,7 +730,51 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:pause [NAME]`
pause a channel to stop it from sending updates
```
USAGE
$ eas channel:pause [NAME] [--branch <value>] [--json --non-interactive]
ARGUMENTS
NAME Name of the channel to edit
FLAGS
--branch=<value> Name of the branch to point to
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
DESCRIPTION
pause a channel to stop it from sending updates
```
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/channel/pause.ts)_
## `eas channel:resume [NAME]`
resume a channel to start sending updates
```
USAGE
$ eas channel:resume [NAME] [--branch <value>] [--json --non-interactive]
ARGUMENTS
NAME Name of the channel to edit
FLAGS
--branch=<value> Name of the branch to point to
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
DESCRIPTION
resume a channel to start sending updates
```
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/channel/resume.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -760,7 +807,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -783,7 +830,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -804,7 +851,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/v12.5.3/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -821,7 +868,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -839,7 +886,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas device:create`
@@ -853,7 +900,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -873,7 +920,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -894,7 +941,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -915,7 +962,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -929,7 +976,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -943,7 +990,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v12.5.3/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -1051,7 +1098,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1068,7 +1115,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1085,7 +1132,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1115,7 +1162,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/v12.5.3/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1129,7 +1176,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1152,7 +1199,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1170,7 +1217,7 @@ ALIASES
$ eas onboarding
```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas secret:create`
@@ -1193,7 +1240,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1211,7 +1258,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1225,7 +1272,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1245,7 +1292,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/secret/push.ts)_
## `eas submit`
@@ -1276,7 +1323,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v12.5.3/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1314,7 +1361,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1332,7 +1379,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1353,7 +1400,31 @@ 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/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:edit [GROUPID]`
edit all the updates in an update group
```
USAGE
$ eas update:edit [GROUPID] [--rollout-percentage <value>] [--branch <value>] [--json --non-interactive]
ARGUMENTS
GROUPID The ID of an update group to edit.
FLAGS
--branch=<value> Branch for which to list updates to select from
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
--rollout-percentage=<value> Rollout percentage to set for a rollout update. The specified number must be an integer
between 1 and 100.
DESCRIPTION
edit all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v12.6.1/packages/eas-cli/src/commands/update/edit.ts)_
## `eas update:list`
@@ -1375,7 +1446,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1407,7 +1478,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded`
@@ -1435,7 +1506,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -1455,7 +1526,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/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -1475,7 +1546,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1496,7 +1567,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1516,7 +1587,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1534,7 +1605,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1556,7 +1627,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1573,7 +1644,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v12.5.3/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.6.1/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+942 -180
View File
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -1,18 +1,17 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "12.5.3",
"version": "12.6.1",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
},
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "1.8.0",
"@expo/apple-utils": "2.0.2",
"@expo/code-signing-certificates": "0.0.5",
"@expo/config": "8.5.4",
"@expo/config-plugins": "7.8.4",
"@expo/config-types": "50.0.0",
"@expo/eas-build-job": "1.0.136",
"@expo/eas-json": "12.5.2",
"@expo/env": "^0.3.0",
@@ -1,4 +1,4 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import { AppVersionSource, EasJson } from '@expo/eas-json';
import { Command, Config } from '@oclif/core';
import { vol } from 'memfs';
@@ -81,21 +81,59 @@ async function resolveEnvVarsAsync({
.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/')}`
);
if (Object.keys(serverEnvVars).length > 0) {
Log.log(
`Environment variables loaded from the "${environment.toLowerCase()}" environment on EAS servers: ${Object.keys(
serverEnvVars
).join(', ')}.`
);
} else {
Log.log(
`No environment variables found for the "${environment.toLowerCase()}" environment on EAS servers.`
);
}
const encryptedEnvVars = environmentVariables.filter(({ name, value }) => name && !value);
if (encryptedEnvVars.length > 0) {
Log.warn(
`Some environment variables defined in the "${environment.toLowerCase()}" environment on EAS servers are of "encrypted" type and cannot be read outside of the EAS servers (including EAS CLI): ${encryptedEnvVars
.map(({ name }) => name)
.join(
', '
)}. However, they will be available during the build process happening on the EAS servers. This can lead to potential configuration mismatches between the local development environment and the build environment if the encrypted environment variables are used to resolve the app config.`
);
}
if (buildProfile.env && Object.keys(buildProfile.env).length > 0) {
Log.log(
`Environment variables loaded from the "${buildProfileName}" build profile "env" configuration: ${
buildProfile.env && Object.keys(buildProfile.env).join(', ')
}.`
);
} else {
Log.log(
`No environment variables specified in the "${buildProfileName}" build profile "env" configuration.`
);
}
if (
buildProfile.env &&
Object.keys(buildProfile.env).length > 0 &&
Object.keys(serverEnvVars).length > 0
) {
const overlappingKeys = Object.keys(serverEnvVars).filter(
key => buildProfile.env && Object.keys(buildProfile.env).includes(key)
);
if (overlappingKeys.length > 0) {
Log.warn(
`The following environment variables are defined in both the "${buildProfileName}" build profile "env" configuration and the "${environment.toLowerCase()}" environment on EAS servers: ${overlappingKeys.join(
', '
)}. The values from the build profile configuration will be used.`
);
}
}
Log.newLine();
return { ...serverEnvVars, ...buildProfile.env };
} catch (e) {
@@ -1,4 +1,4 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import { Env, Platform, Workflow } from '@expo/eas-build-job';
import {
AppVersionSource,
@@ -128,6 +128,7 @@ export const testChannelObject: UpdateChannelObject = {
branchMapping:
'{"data":[{"branchId":"754bf17f-efc0-46ab-8a59-a03f20e53e9b","branchMappingLogic":{"operand":0.15,"clientKey":"rolloutToken","branchMappingOperator":"hash_lt"}},{"branchId":"6941a8dd-5c0a-48bc-8876-f49c88ed419f","branchMappingLogic":"true"}],"version":0}',
updateBranches: [testUpdateBranch1, testUpdateBranch2],
isPaused: false,
__typename: 'UpdateChannel',
};
+13 -2
View File
@@ -110,7 +110,11 @@ export async function listAndRenderBranchesAndUpdatesOnChannelAsync(
}
): Promise<void> {
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, { appId, channelName });
renderChannelHeaderContent({ channelName: channel.name, channelId: channel.id });
renderChannelHeaderContent({
channelName: channel.name,
channelId: channel.id,
isPaused: channel.isPaused,
});
if (paginatedQueryOptions.nonInteractive) {
const branches = await queryBranchesAndUpdateGroupsOnChannelAsync(graphqlClient, {
@@ -167,7 +171,11 @@ function renderPageOfChannels(
printJsonOnlyOutput({ currentPage });
} else {
for (const channel of currentPage) {
renderChannelHeaderContent({ channelName: channel.name, channelId: channel.id });
renderChannelHeaderContent({
channelName: channel.name,
channelId: channel.id,
isPaused: channel.isPaused,
});
Log.addNewLineIfNone();
logChannelDetails(channel);
@@ -196,9 +204,11 @@ function renderPageOfBranchesOnChannel(
function renderChannelHeaderContent({
channelName,
channelId,
isPaused,
}: {
channelName: string;
channelId: string;
isPaused: boolean;
}): void {
Log.addNewLineIfNone();
Log.log(chalk.bold('Channel:'));
@@ -206,6 +216,7 @@ function renderChannelHeaderContent({
formatFields([
{ label: 'Name', value: channelName },
{ label: 'ID', value: channelId },
{ label: 'Status', value: isPaused ? 'Paused' : 'Active' },
])
);
Log.addNewLineIfNone();
@@ -6,6 +6,7 @@ import nullthrows from 'nullthrows';
import AnalyticsContextField from './context/AnalyticsContextField';
import ContextField from './context/ContextField';
import DynamicLoggedInContextField from './context/DynamicLoggedInContextField';
import {
DynamicPrivateProjectConfigContextField,
DynamicPublicProjectConfigContextField,
@@ -15,6 +16,8 @@ import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
import { OptionalPrivateProjectConfigContextField } from './context/OptionalPrivateProjectConfigContextField';
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
import ProjectDirContextField from './context/ProjectDirContextField';
import { ProjectIdContextField } from './context/ProjectIdContextField';
import { ServerSideEnvironmentVariablesContextField } from './context/ServerSideEnvironmentVariablesContextField';
import SessionManagementContextField from './context/SessionManagementContextField';
import VcsClientContextField from './context/VcsClientContextField';
import { EasCommandError } from './errors';
@@ -23,6 +26,7 @@ import {
CommandEvent,
createAnalyticsAsync,
} from '../analytics/AnalyticsManager';
import { EnvironmentVariableEnvironment } from '../graphql/generated';
import Log from '../log';
import SessionManager from '../user/SessionManager';
import { Client } from '../vcs/vcs';
@@ -43,8 +47,26 @@ export type ContextOutput<
[P in keyof T]: T[P];
};
type GetContextType<Type> = {
[Property in keyof Type]: any;
};
const BASE_GRAPHQL_ERROR_MESSAGE: string = 'GraphQL request failed.';
interface BaseGetContextAsyncArgs {
nonInteractive: boolean;
vcsClientOverride?: Client;
}
interface GetContextAsyncArgsWithRequiredServerSideEnvironmentArgument
extends BaseGetContextAsyncArgs {
withServerSideEnvironment: EnvironmentVariableEnvironment | null;
}
interface GetContextAsyncArgsWithoutServerSideEnvironmentArgument extends BaseGetContextAsyncArgs {
withServerSideEnvironment?: never;
}
export default abstract class EasCommand extends Command {
protected static readonly ContextOptions = {
/**
@@ -61,8 +83,16 @@ export default abstract class EasCommand extends Command {
MaybeLoggedIn: {
maybeLoggedIn: new MaybeLoggedInContextField(),
},
/**
* Specify this context if the logged-in requirement is only necessary in a particular execution of the command.
*/
DynamicLoggedIn: {
// eslint-disable-next-line async-protect/async-suffix
getDynamicLoggedInAsync: new DynamicLoggedInContextField(),
},
/**
* Specify this context requirement if the command needs to mutate the user session.
* @deprecated Should not be used outside of session management commands, which currently only includes `login` and `logout`.
*/
SessionManagment: {
sessionManager: new SessionManagementContextField(),
@@ -72,7 +102,7 @@ export default abstract class EasCommand extends Command {
* run within a project directory, null otherwise.
*/
OptionalProjectConfig: {
privateProjectConfig: new OptionalPrivateProjectConfigContextField(),
optionalPrivateProjectConfig: new OptionalPrivateProjectConfigContextField(),
},
/**
* Require this command to be run in a project directory. Return the project directory in the context.
@@ -107,6 +137,16 @@ export default abstract class EasCommand extends Command {
Vcs: {
vcsClient: new VcsClientContextField(),
},
ServerSideEnvironmentVariables: {
// eslint-disable-next-line async-protect/async-suffix
getServerSideEnvironmentVariablesAsync: new ServerSideEnvironmentVariablesContextField(),
},
/**
* Require the project to be identified and registered on server. Returns the project ID evaluated from the app config.
*/
ProjectId: {
projectId: new ProjectIdContextField(),
},
};
/**
@@ -141,7 +181,19 @@ export default abstract class EasCommand extends Command {
} = object,
>(
commandClass: { contextDefinition: ContextInput<C> },
{ nonInteractive, vcsClientOverride }: { nonInteractive: boolean; vcsClientOverride?: Client }
{
nonInteractive,
vcsClientOverride,
// if specified and not null, the env vars from the selected environment will be fetched from the server
// to resolve dynamic config (if dynamic config context is used) and enable getServerSideEnvironmentVariablesAsync function (if server side environment variables context is used)
withServerSideEnvironment,
}: C extends
| GetContextType<typeof EasCommand.ContextOptions.ProjectConfig>
| GetContextType<typeof EasCommand.ContextOptions.DynamicProjectConfig>
| GetContextType<typeof EasCommand.ContextOptions.OptionalProjectConfig>
| GetContextType<typeof EasCommand.ContextOptions.ServerSideEnvironmentVariables>
? GetContextAsyncArgsWithRequiredServerSideEnvironmentArgument
: GetContextAsyncArgsWithoutServerSideEnvironmentArgument
): Promise<ContextOutput<C>> {
const contextDefinition = commandClass.contextDefinition;
@@ -155,6 +207,7 @@ export default abstract class EasCommand extends Command {
sessionManager: this.sessionManager,
analytics: this.analytics,
vcsClientOverride,
withServerSideEnvironment,
}),
]);
}
@@ -1,4 +1,5 @@
import { Analytics } from '../../analytics/AnalyticsManager';
import { EnvironmentVariableEnvironment } from '../../graphql/generated';
import SessionManager from '../../user/SessionManager';
import { Client } from '../../vcs/vcs';
@@ -7,6 +8,10 @@ export interface ContextOptions {
analytics: Analytics;
nonInteractive: boolean;
vcsClientOverride?: Client;
/**
* If specified, env variables from the selected environment will be fetched from the server and used to evaluate the dynamic config.
*/
withServerSideEnvironment?: EnvironmentVariableEnvironment | null;
}
export default abstract class ContextField<T> {
@@ -0,0 +1,36 @@
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
import { LoggedInAuthenticationInfo } from '../../user/SessionManager';
import { Actor } from '../../user/User';
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
import FeatureGating from '../gating/FeatureGating';
export type DynamicLoggedInContextFn = () => Promise<{
actor: Actor;
featureGating: FeatureGating;
graphqlClient: ExpoGraphqlClient;
authenticationInfo: LoggedInAuthenticationInfo;
}>;
export default class DynamicLoggedInContextField extends ContextField<DynamicLoggedInContextFn> {
async getValueAsync({
nonInteractive,
sessionManager,
}: ContextOptions): Promise<DynamicLoggedInContextFn> {
return async () => {
const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const featureGateServerValues: { [key: string]: boolean } = actor?.featureGates ?? {};
const graphqlClient = createGraphqlClient(authenticationInfo);
return {
actor,
featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo,
};
};
}
}
@@ -1,8 +1,10 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import ContextField, { ContextOptions } from './ContextField';
import { createGraphqlClient } from './contextUtils/createGraphqlClient';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
import {
ExpoConfigOptions,
getPrivateExpoConfig,
@@ -19,6 +21,7 @@ export class DynamicPublicProjectConfigContextField extends ContextField<Dynamic
async getValueAsync({
nonInteractive,
sessionManager,
withServerSideEnvironment,
}: ContextOptions): Promise<DynamicConfigContextFn> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
return async (options?: ExpoConfigOptions) => {
@@ -27,6 +30,24 @@ export class DynamicPublicProjectConfigContextField extends ContextField<Dynamic
nonInteractive,
env: options?.env,
});
if (withServerSideEnvironment) {
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const serverSideEnvironmentVariables = await loadServerSideEnvironmentVariablesAsync({
environment: withServerSideEnvironment,
projectId,
graphqlClient,
});
options = {
...options,
env: {
...options?.env,
...serverSideEnvironmentVariables,
},
};
}
const exp = getPublicExpoConfig(projectDir, options);
return {
exp,
@@ -41,6 +62,7 @@ export class DynamicPrivateProjectConfigContextField extends ContextField<Dynami
async getValueAsync({
nonInteractive,
sessionManager,
withServerSideEnvironment,
}: ContextOptions): Promise<DynamicConfigContextFn> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
return async (options?: ExpoConfigOptions) => {
@@ -49,6 +71,24 @@ export class DynamicPrivateProjectConfigContextField extends ContextField<Dynami
nonInteractive,
env: options?.env,
});
if (withServerSideEnvironment) {
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const serverSideEnvironmentVariables = await loadServerSideEnvironmentVariablesAsync({
environment: withServerSideEnvironment,
projectId,
graphqlClient,
});
options = {
...options,
env: {
...options?.env,
...serverSideEnvironmentVariables,
},
};
}
const exp = getPrivateExpoConfig(projectDir, options);
return {
exp,
@@ -1,19 +1,22 @@
import ContextField, { ContextOptions } from './ContextField';
import { ExpoGraphqlClient, createGraphqlClient } from './contextUtils/createGraphqlClient';
import { LoggedInAuthenticationInfo } from '../../user/SessionManager';
import { Actor } from '../../user/User';
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
import FeatureGating from '../gating/FeatureGating';
export default class LoggedInContextField extends ContextField<{
type LoggedInContextType = {
actor: Actor;
featureGating: FeatureGating;
graphqlClient: ExpoGraphqlClient;
}> {
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{
actor: Actor;
featureGating: FeatureGating;
graphqlClient: ExpoGraphqlClient;
}> {
authenticationInfo: LoggedInAuthenticationInfo;
};
export default class LoggedInContextField extends ContextField<LoggedInContextType> {
async getValueAsync({
nonInteractive,
sessionManager,
}: ContextOptions): Promise<LoggedInContextType> {
const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
@@ -25,6 +28,7 @@ export default class LoggedInContextField extends ContextField<{
actor,
featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo,
};
}
}
@@ -4,16 +4,18 @@ import { Actor } from '../../user/User';
import FeatureGateEnvOverrides from '../gating/FeatureGateEnvOverrides';
import FeatureGating from '../gating/FeatureGating';
export default class MaybeLoggedInContextField extends ContextField<{
type MaybeLoggedInContextType = {
actor: Actor | null;
featureGating: FeatureGating;
graphqlClient: ExpoGraphqlClient;
}> {
async getValueAsync({ sessionManager }: ContextOptions): Promise<{
actor: Actor | null;
featureGating: FeatureGating;
graphqlClient: ExpoGraphqlClient;
}> {
authenticationInfo: {
accessToken: string | null;
sessionSecret: string | null;
};
};
export default class MaybeLoggedInContextField extends ContextField<MaybeLoggedInContextType> {
async getValueAsync({ sessionManager }: ContextOptions): Promise<MaybeLoggedInContextType> {
const authenticationInfo = {
accessToken: sessionManager.getAccessToken(),
sessionSecret: sessionManager.getSessionSecret(),
@@ -27,6 +29,7 @@ export default class MaybeLoggedInContextField extends ContextField<{
actor: actor ?? null,
featureGating: new FeatureGating(featureGateServerValues, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo,
};
}
}
@@ -1,9 +1,11 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import { InvalidEasJsonError } from '@expo/eas-json/build/errors';
import ContextField, { ContextOptions } from './ContextField';
import { createGraphqlClient } from './contextUtils/createGraphqlClient';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
import { getPrivateExpoConfig } from '../../project/expoConfig';
export class OptionalPrivateProjectConfigContextField extends ContextField<
@@ -14,7 +16,11 @@ export class OptionalPrivateProjectConfigContextField extends ContextField<
}
| undefined
> {
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<
async getValueAsync({
nonInteractive,
sessionManager,
withServerSideEnvironment,
}: ContextOptions): Promise<
| {
projectId: string;
exp: ExpoConfig;
@@ -39,7 +45,20 @@ export class OptionalPrivateProjectConfigContextField extends ContextField<
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
nonInteractive,
});
const exp = getPrivateExpoConfig(projectDir);
let serverSideEnvVars: Record<string, string> | undefined;
if (withServerSideEnvironment) {
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const serverSideEnvironmentVariables = await loadServerSideEnvironmentVariablesAsync({
environment: withServerSideEnvironment,
projectId,
graphqlClient,
});
serverSideEnvVars = serverSideEnvironmentVariables;
}
const exp = getPrivateExpoConfig(projectDir, { env: serverSideEnvVars });
return {
exp,
projectDir,
@@ -1,8 +1,10 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import ContextField, { ContextOptions } from './ContextField';
import { createGraphqlClient } from './contextUtils/createGraphqlClient';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
import { getPrivateExpoConfig } from '../../project/expoConfig';
export class PrivateProjectConfigContextField extends ContextField<{
@@ -10,7 +12,11 @@ export class PrivateProjectConfigContextField extends ContextField<{
exp: ExpoConfig;
projectDir: string;
}> {
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{
async getValueAsync({
nonInteractive,
sessionManager,
withServerSideEnvironment,
}: ContextOptions): Promise<{
projectId: string;
exp: ExpoConfig;
projectDir: string;
@@ -20,7 +26,20 @@ export class PrivateProjectConfigContextField extends ContextField<{
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
nonInteractive,
});
const exp = getPrivateExpoConfig(projectDir);
let serverSideEnvVars: Record<string, string> | undefined;
if (withServerSideEnvironment) {
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const serverSideEnvironmentVariables = await loadServerSideEnvironmentVariablesAsync({
environment: withServerSideEnvironment,
projectId,
graphqlClient,
});
serverSideEnvVars = serverSideEnvironmentVariables;
}
const exp = getPrivateExpoConfig(projectDir, { env: serverSideEnvVars });
return {
projectId,
@@ -0,0 +1,15 @@
import ContextField, { ContextOptions } from './ContextField';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { getPrivateExpoConfig } from '../../project/expoConfig';
export class ProjectIdContextField extends ContextField<string> {
async getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<string> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
const expBefore = getPrivateExpoConfig(projectDir);
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
nonInteractive,
});
return projectId;
}
}
@@ -0,0 +1,42 @@
import ContextField, { ContextOptions } from './ContextField';
import { createGraphqlClient } from './contextUtils/createGraphqlClient';
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
import { getPublicExpoConfig } from '../../project/expoConfig';
type GetServerSideEnvironmentVariablesFn = (
maybeEnv?: Record<string, string>
) => Promise<Record<string, string>>;
export class ServerSideEnvironmentVariablesContextField extends ContextField<GetServerSideEnvironmentVariablesFn> {
async getValueAsync({
nonInteractive,
sessionManager,
withServerSideEnvironment,
}: ContextOptions): Promise<GetServerSideEnvironmentVariablesFn> {
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
return async (maybeEnv?: Record<string, string>) => {
if (!withServerSideEnvironment) {
throw new Error(
'withServerSideEnvironment parameter is required to evaluate ServerSideEnvironmentVariablesContextField'
);
}
const exp = getPublicExpoConfig(projectDir, { env: maybeEnv });
const projectId = await getProjectIdAsync(sessionManager, exp, {
nonInteractive,
env: maybeEnv,
});
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const serverSideEnvironmentVariables = await loadServerSideEnvironmentVariablesAsync({
environment: withServerSideEnvironment,
projectId,
graphqlClient,
});
return serverSideEnvironmentVariables;
};
}
}
@@ -1,5 +1,4 @@
import { getProjectConfigDescription } from '@expo/config';
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig, getProjectConfigDescription } from '@expo/config';
import { Env } from '@expo/eas-build-job';
import chalk from 'chalk';
@@ -0,0 +1,68 @@
import { ExpoGraphqlClient } from './createGraphqlClient';
import { EnvironmentVariableEnvironment } from '../../../graphql/generated';
import { EnvironmentVariablesQuery } from '../../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../../log';
const cachedServerSideEnvironmentVariables: Record<
EnvironmentVariableEnvironment,
Record<string, string> | null
> = {
[EnvironmentVariableEnvironment.Development]: null,
[EnvironmentVariableEnvironment.Preview]: null,
[EnvironmentVariableEnvironment.Production]: null,
};
export async function loadServerSideEnvironmentVariablesAsync({
environment,
projectId,
graphqlClient,
}: {
environment: EnvironmentVariableEnvironment;
projectId: string;
graphqlClient: ExpoGraphqlClient;
}): Promise<Record<string, string>> {
// don't load environment variables if they were already loaded while executing a command
const cachedEnvVarsForEnvironment = cachedServerSideEnvironmentVariables[environment];
if (cachedEnvVarsForEnvironment) {
return cachedEnvVarsForEnvironment;
}
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>;
if (Object.keys(serverEnvVars).length > 0) {
Log.log(
`Environment variables loaded from the "${environment.toLowerCase()}" environment on EAS servers: ${Object.keys(
serverEnvVars
).join(', ')}.`
);
} else {
Log.log(
`No environment variables found for the "${environment.toLowerCase()}" environment on EAS servers.`
);
}
const encryptedEnvVars = environmentVariables.filter(({ name, value }) => name && !value);
if (encryptedEnvVars.length > 0) {
Log.warn(
`Some environment variables defined in the "${environment.toLowerCase()}" environment on EAS servers are of "encrypted" type and cannot be read outside of the EAS servers (including EAS CLI): ${encryptedEnvVars
.map(({ name }) => name)
.join(', ')}. `
);
}
Log.newLine();
cachedServerSideEnvironmentVariables[environment] = serverEnvVars;
return serverEnvVars;
}
+3 -12
View File
@@ -1,10 +1,6 @@
import { Flags } from '@oclif/core';
import {
EnvironmentVariableEnvironment,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../graphql/generated';
import { EnvironmentVariableEnvironment, EnvironmentVariableScope } from '../graphql/generated';
// NOTE: not exactly true, but, provided mapToLowercase and upperCaseAsync
// are used in tandem, it saves on unnecessary typying in commands
@@ -56,14 +52,9 @@ export const EASVariableFormatFlag = {
};
export const EASVariableVisibilityFlag = {
visibility: Flags.enum<EnvironmentVariableVisibility>({
visibility: Flags.enum<'plaintext' | 'sensitive' | 'encrypted'>({
description: 'Visibility of the variable',
options: mapToLowercase([
EnvironmentVariableVisibility.Secret,
EnvironmentVariableVisibility.Sensitive,
EnvironmentVariableVisibility.Public,
]),
parse: upperCaseAsync,
options: ['plaintext', 'sensitive', 'encrypted'],
}),
};
@@ -12,16 +12,14 @@ export default class AccountView extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.MaybeLoggedIn,
...this.ContextOptions.SessionManagment,
};
async runAsync(): Promise<void> {
const {
maybeLoggedIn: { actor },
sessionManager,
maybeLoggedIn: { actor, authenticationInfo },
} = await this.getContextAsync(AccountView, { nonInteractive: true });
if (actor) {
const loggedInAs = sessionManager.getAccessToken()
const loggedInAs = authenticationInfo.accessToken
? `${getActorDisplayName(actor)} (authenticated using EXPO_TOKEN)`
: getActorDisplayName(actor);
Log.log(chalk.green(loggedInAs));
@@ -25,7 +25,7 @@ export default class BranchCreate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
@@ -36,7 +36,7 @@ export default class BranchCreate extends EasCommand {
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(BranchCreate);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
vcsClient,
} = await this.getContextAsync(BranchCreate, {
@@ -79,7 +79,7 @@ export default class BranchDelete extends EasCommand {
static override description = 'delete a branch';
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -108,7 +108,7 @@ export default class BranchDelete extends EasCommand {
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BranchDelete, { nonInteractive });
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
+2 -2
View File
@@ -13,14 +13,14 @@ export default class BranchList extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BranchList);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BranchList, {
nonInteractive: flags['non-interactive'],
@@ -63,7 +63,7 @@ export default class BranchRename extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -72,7 +72,7 @@ export default class BranchRename extends EasCommand {
flags: { json: jsonFlag, from: currentName, to: newName, 'non-interactive': nonInteractive },
} = await this.parse(BranchRename);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BranchRename, {
nonInteractive,
+2 -2
View File
@@ -27,7 +27,7 @@ export default class BranchView extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -38,7 +38,7 @@ export default class BranchView extends EasCommand {
} = await this.parse(BranchView);
const { 'non-interactive': nonInteractive } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BranchView, {
nonInteractive,
@@ -110,7 +110,7 @@ export default class BuildCancel extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
@@ -128,7 +128,7 @@ export default class BuildCancel extends EasCommand {
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildCancel, {
nonInteractive,
@@ -38,6 +38,7 @@ export default class BuildConfigure extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildConfigure, {
nonInteractive: false,
withServerSideEnvironment: null,
});
Log.log(
@@ -94,7 +94,7 @@ export default class BuildDelete extends EasCommand {
}),
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
@@ -112,7 +112,7 @@ export default class BuildDelete extends EasCommand {
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildDelete, { nonInteractive });
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
@@ -142,6 +142,7 @@ export default class Build extends EasCommand {
vcsClient,
} = await this.getContextAsync(Build, {
nonInteractive: flags.nonInteractive,
withServerSideEnvironment: null,
});
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
@@ -79,6 +79,7 @@ export default class BuildInspect extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildInspect, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const outputDirectory = path.resolve(process.cwd(), flags.output);
@@ -64,6 +64,7 @@ export default class BuildInternal extends EasCommand {
} = await this.getContextAsync(BuildInternal, {
nonInteractive: true,
vcsClientOverride: new GitNoCommitClient(),
withServerSideEnvironment: null,
});
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
+2 -2
View File
@@ -72,7 +72,7 @@ export default class BuildList extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
@@ -99,7 +99,7 @@ export default class BuildList extends EasCommand {
process.exit(1);
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildList, {
nonInteractive,
@@ -120,6 +120,7 @@ export default class BuildResign extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildResign, {
nonInteractive: flags.nonInteractive,
withServerSideEnvironment: null,
});
const maybeBuild = flags.maybeBuildId
+2 -3
View File
@@ -79,8 +79,7 @@ export default class Run extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.ProjectId,
...this.ContextOptions.Vcs,
};
@@ -90,7 +89,7 @@ export default class Run extends EasCommand {
const queryOptions = getPaginatedQueryOptions(flags);
const {
loggedIn: { graphqlClient },
privateProjectConfig: { projectId },
projectId,
} = await this.getContextAsync(Run, {
nonInteractive: false,
});
@@ -54,6 +54,7 @@ export default class BuildVersionGetView extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildVersionGetView, {
nonInteractive: true,
withServerSideEnvironment: null,
});
if (!flags.platform && flags['non-interactive']) {
@@ -54,6 +54,7 @@ export default class BuildVersionSetView extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildVersionSetView, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const platform = await selectPlatformAsync(flags.platform);
@@ -73,6 +73,7 @@ export default class BuildVersionSyncView extends EasCommand {
vcsClient,
} = await this.getContextAsync(BuildVersionSyncView, {
nonInteractive: true,
withServerSideEnvironment: null,
});
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
+2 -2
View File
@@ -18,7 +18,7 @@ export default class BuildView extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
};
@@ -29,7 +29,7 @@ export default class BuildView extends EasCommand {
flags,
} = await this.parse(BuildView);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildView, {
nonInteractive: true,
@@ -23,7 +23,7 @@ export default class ChannelCreate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -33,7 +33,7 @@ export default class ChannelCreate extends EasCommand {
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(ChannelCreate);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelCreate, {
nonInteractive,
@@ -31,7 +31,7 @@ export default class ChannelDelete extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -41,7 +41,7 @@ export default class ChannelDelete extends EasCommand {
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
} = await this.parse(ChannelDelete);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelDelete, {
nonInteractive,
@@ -74,7 +74,7 @@ export default class ChannelEdit extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -84,7 +84,7 @@ export default class ChannelEdit extends EasCommand {
flags: { branch: branchFlag, json, 'non-interactive': nonInteractive },
} = await this.parse(ChannelEdit);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelEdit, {
nonInteractive,
@@ -18,7 +18,7 @@ export default class ChannelList extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -27,7 +27,7 @@ export default class ChannelList extends EasCommand {
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelList, {
nonInteractive,
@@ -0,0 +1,113 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { print } from 'graphql';
import gql from 'graphql-tag';
import { selectChannelOnAppAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { withErrorHandlingAsync } from '../../graphql/client';
import {
PauseUpdateChannelMutation,
PauseUpdateChannelMutationVariables,
UpdateChannelBasicInfoFragment,
} from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import { UpdateChannelBasicInfoFragmentNode } from '../../graphql/types/UpdateChannelBasicInfo';
import Log from '../../log';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export async function pauseUpdateChannelAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId }: PauseUpdateChannelMutationVariables
): Promise<UpdateChannelBasicInfoFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<PauseUpdateChannelMutation, PauseUpdateChannelMutationVariables>(
gql`
mutation PauseUpdateChannel($channelId: ID!) {
updateChannel {
pauseUpdateChannel(channelId: $channelId) {
id
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{ channelId }
)
.toPromise()
);
const channel = data.updateChannel.pauseUpdateChannel;
if (!channel) {
throw new Error(`Could not find a channel with id: ${channelId}`);
}
return channel;
}
export default class ChannelPause extends EasCommand {
static override description = 'pause a channel to stop it from sending updates';
static override args = [
{
name: 'name',
required: false,
description: 'Name of the channel to edit',
},
];
static override flags = {
branch: Flags.string({
description: 'Name of the branch to point to',
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args,
flags: { json, 'non-interactive': nonInteractive },
} = await this.parse(ChannelPause);
const {
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelPause, {
nonInteractive,
});
if (json) {
enableJsonOutput();
}
const existingChannel = args.name
? await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: args.name,
})
: await selectChannelOnAppAsync(graphqlClient, {
projectId,
selectionPromptTitle: 'Select a channel to edit',
paginatedQueryOptions: { json, nonInteractive, offset: 0 },
});
const channel = await pauseUpdateChannelAsync(graphqlClient, {
channelId: existingChannel.id,
});
if (json) {
printJsonOnlyOutput(channel);
} else {
Log.withTick(chalk`Channel {bold ${channel.name}} is now paused.\n`);
Log.addNewLineIfNone();
Log.log(
chalk`Users with builds on channel {bold ${channel.name}} will no longer receive updates.`
);
}
}
}
@@ -0,0 +1,111 @@
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import { print } from 'graphql';
import gql from 'graphql-tag';
import { selectChannelOnAppAsync } from '../../channel/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { withErrorHandlingAsync } from '../../graphql/client';
import {
ResumeUpdateChannelMutation,
ResumeUpdateChannelMutationVariables,
UpdateChannelBasicInfoFragment,
} from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import { UpdateChannelBasicInfoFragmentNode } from '../../graphql/types/UpdateChannelBasicInfo';
import Log from '../../log';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export async function resumeUpdateChannelAsync(
graphqlClient: ExpoGraphqlClient,
{ channelId }: ResumeUpdateChannelMutationVariables
): Promise<UpdateChannelBasicInfoFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<ResumeUpdateChannelMutation, ResumeUpdateChannelMutationVariables>(
gql`
mutation ResumeUpdateChannel($channelId: ID!) {
updateChannel {
resumeUpdateChannel(channelId: $channelId) {
id
...UpdateChannelBasicInfoFragment
}
}
}
${print(UpdateChannelBasicInfoFragmentNode)}
`,
{ channelId }
)
.toPromise()
);
const channel = data.updateChannel.resumeUpdateChannel;
if (!channel) {
throw new Error(`Could not find a channel with id: ${channelId}`);
}
return channel;
}
export default class ChannelResume extends EasCommand {
static override description = 'resume a channel to start sending updates';
static override args = [
{
name: 'name',
required: false,
description: 'Name of the channel to edit',
},
];
static override flags = {
branch: Flags.string({
description: 'Name of the branch to point to',
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args,
flags: { json, 'non-interactive': nonInteractive },
} = await this.parse(ChannelResume);
const {
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelResume, {
nonInteractive,
});
if (json) {
enableJsonOutput();
}
const existingChannel = args.name
? await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: args.name,
})
: await selectChannelOnAppAsync(graphqlClient, {
projectId,
selectionPromptTitle: 'Select a channel to edit',
paginatedQueryOptions: { json, nonInteractive, offset: 0 },
});
const channel = await resumeUpdateChannelAsync(graphqlClient, {
channelId: existingChannel.id,
});
if (json) {
printJsonOnlyOutput(channel);
} else {
Log.withTick(chalk`Channel {bold ${channel.name}} is now active.\n`);
Log.addNewLineIfNone();
Log.log(chalk`Users with builds on channel {bold ${channel.name}} will now receive updates.`);
}
}
}
@@ -138,6 +138,7 @@ export default class ChannelRollout extends EasCommand {
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelRollout, {
nonInteractive: argsAndFlags.nonInteractive,
withServerSideEnvironment: null,
});
if (argsAndFlags.json) {
enableJsonOutput();
@@ -26,7 +26,7 @@ export default class ChannelView extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -38,7 +38,7 @@ export default class ChannelView extends EasCommand {
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ChannelView, {
nonInteractive,
+4 -7
View File
@@ -6,7 +6,6 @@ import chalk from 'chalk';
import { evaluateConfigWithEnvVarsAsync } from '../build/evaluateConfigWithEnvVarsAsync';
import EasCommand from '../commandUtils/EasCommand';
import { createGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
@@ -35,7 +34,7 @@ export default class Config extends EasCommand {
static override contextDefinition = {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.SessionManagment,
...this.ContextOptions.DynamicLoggedIn,
};
async runAsync(): Promise<void> {
@@ -48,9 +47,10 @@ export default class Config extends EasCommand {
profile: maybeProfile,
'non-interactive': nonInteractive,
} = flags;
const { getDynamicPublicProjectConfigAsync, projectDir, sessionManager } =
const { getDynamicPublicProjectConfigAsync, projectDir, getDynamicLoggedInAsync } =
await this.getContextAsync(Config, {
nonInteractive,
withServerSideEnvironment: null,
});
const accessor = EasJsonAccessor.fromProjectPath(projectDir);
@@ -88,10 +88,7 @@ export default class Config extends EasCommand {
Log.log(JSON.stringify(profile, null, 2));
}
} else {
const { authenticationInfo } = await sessionManager.ensureLoggedInAsync({
nonInteractive,
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const { graphqlClient } = await getDynamicLoggedInAsync();
const { exp: appConfig } = await evaluateConfigWithEnvVarsAsync({
buildProfile: profile,
buildProfileName: profileName,
@@ -39,6 +39,7 @@ export default class InitializeBuildCredentials extends EasCommand {
vcsClient,
} = await this.getContextAsync(InitializeBuildCredentials, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const platform = await selectPlatformAsync(flags.platform);
@@ -22,12 +22,13 @@ export default class Credentials extends EasCommand {
const { flags } = await this.parse(Credentials);
const {
loggedIn: { actor, graphqlClient },
privateProjectConfig,
optionalPrivateProjectConfig: privateProjectConfig,
getDynamicPrivateProjectConfigAsync,
analytics,
vcsClient,
} = await this.getContextAsync(Credentials, {
nonInteractive: false,
withServerSideEnvironment: null,
});
await new SelectPlatform(
@@ -15,9 +15,10 @@ export default class DeviceCreate extends EasCommand {
// this command is interactive by design
const {
loggedIn: { actor, graphqlClient },
privateProjectConfig,
optionalPrivateProjectConfig: privateProjectConfig,
} = await this.getContextAsync(DeviceCreate, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const ctx = await createContextAsync({
@@ -34,7 +34,7 @@ export default class DeviceDelete extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -43,7 +43,7 @@ export default class DeviceDelete extends EasCommand {
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
let { 'apple-team-id': appleTeamIdentifier, udid } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(DeviceDelete, {
nonInteractive: paginatedQueryOptions.nonInteractive,
+2 -2
View File
@@ -21,7 +21,7 @@ export default class BuildList extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -29,7 +29,7 @@ export default class BuildList extends EasCommand {
const { flags } = await this.parse(BuildList);
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildList, {
nonInteractive: paginatedQueryOptions.nonInteractive,
@@ -35,7 +35,7 @@ export default class DeviceRename extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -44,7 +44,7 @@ export default class DeviceRename extends EasCommand {
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
let { 'apple-team-id': appleTeamIdentifier, udid, name } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(DeviceRename, {
nonInteractive: paginatedQueryOptions.nonInteractive,
+2 -2
View File
@@ -11,7 +11,7 @@ export default class DeviceView extends EasCommand {
static override args = [{ name: 'UDID' }];
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -32,7 +32,7 @@ If you are not sure what is the UDID of the device you are looking for, run:
throw new Error('Device UDID is missing');
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(DeviceView, {
nonInteractive: true,
@@ -13,6 +13,7 @@ import { EnvironmentVariableMutation } from '../../../graphql/mutations/Environm
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { EnvironmentVariablesQuery } from '../../../graphql/queries/EnvironmentVariablesQuery';
import {
parseVisibility,
promptVariableEnvironmentAsync,
promptVariableNameAsync,
promptVariableTypeAsync,
@@ -33,6 +34,10 @@ describe(EnvironmentVariableCreate, () => {
beforeEach(() => {
jest.resetAllMocks();
jest
.mocked(parseVisibility)
.mockImplementation(jest.requireActual('../../../utils/prompts').parseVisibility);
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
jest
.mocked(EnvironmentVariableMutation.createForAppAsync)
@@ -70,14 +75,23 @@ describe(EnvironmentVariableCreate, () => {
describe('in interactive mode', () => {
it('creates a project variable', async () => {
const command = new EnvironmentVariableCreate(
['--name', 'VarName', '--value', 'VarValue', '--environment', 'production'],
[
'--name',
'VarName',
'--value',
'VarValue',
'--environment',
'production',
'--visibility',
'encrypted',
],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -88,7 +102,7 @@ describe(EnvironmentVariableCreate, () => {
name: 'VarName',
value: 'VarValue',
environments: [EnvironmentVariableEnvironment.Production],
visibility: EnvironmentVariableVisibility.Public,
visibility: EnvironmentVariableVisibility.Secret,
type: EnvironmentSecretType.String,
},
testProjectId
@@ -97,14 +111,23 @@ describe(EnvironmentVariableCreate, () => {
it('updates an existing variable in the same environment', async () => {
const command = new EnvironmentVariableCreate(
['--name', 'VarName', '--value', 'VarValue', '--environment', 'production'],
[
'--name',
'VarName',
'--value',
'VarValue',
'--environment',
'production',
'--visibility',
'encrypted',
],
mockConfig
);
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
const otherVariableId = 'otherId';
@@ -144,7 +167,7 @@ describe(EnvironmentVariableCreate, () => {
name: 'VarName',
value: 'VarValue',
environments: [EnvironmentVariableEnvironment.Production],
visibility: EnvironmentVariableVisibility.Public,
visibility: EnvironmentVariableVisibility.Secret,
});
});
@@ -286,7 +309,7 @@ describe(EnvironmentVariableCreate, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -336,7 +359,7 @@ describe(EnvironmentVariableCreate, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -47,7 +47,7 @@ describe(EnvironmentVariableGet, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -25,7 +25,7 @@ describe(EnvironmentVariableLink, () => {
const graphqlClient = {};
const mockConfig = {} as unknown as Config;
const mockContext = {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
};
@@ -61,7 +61,7 @@ describe(EnvironmentVariableList, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -82,7 +82,7 @@ describe(EnvironmentVariableList, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -105,7 +105,7 @@ describe(EnvironmentVariableList, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -129,7 +129,7 @@ describe(EnvironmentVariableList, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -155,7 +155,7 @@ describe(EnvironmentVariableList, () => {
// @ts-expect-error
jest.spyOn(command, 'getContextAsync').mockReturnValue({
loggedIn: { graphqlClient },
privateProjectConfig: { projectId: testProjectId },
projectId: testProjectId,
});
await command.runAsync();
@@ -25,7 +25,7 @@ describe(EnvironmentVariableUnlink, () => {
const graphqlClient = {};
const mockConfig = {} as unknown as Config;
const mockContext = {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
};
@@ -5,6 +5,7 @@ import { getMockAppFragment } from '../../../__tests__/commands/utils';
import {
EnvironmentVariableEnvironment,
EnvironmentVariableScope,
EnvironmentVariableVisibility,
} from '../../../graphql/generated';
import { EnvironmentVariableMutation } from '../../../graphql/mutations/EnvironmentVariableMutation';
import { AppQuery } from '../../../graphql/queries/AppQuery';
@@ -59,6 +60,8 @@ describe(EnvironmentVariableUpdate, () => {
'NEW_VARIABLE',
'--environment',
'production',
'--visibility',
'plaintext',
],
mockConfig
);
@@ -71,6 +74,7 @@ describe(EnvironmentVariableUpdate, () => {
name: 'NEW_VARIABLE',
value: 'new-value',
environments: [EnvironmentVariableEnvironment.Production],
visibility: EnvironmentVariableVisibility.Public,
});
expect(Log.withTick).toHaveBeenCalledWith(
`Updated variable ${chalk.bold('TEST_VARIABLE')} on project @testuser/testpp.`
+35 -9
View File
@@ -25,10 +25,12 @@ import {
} from '../../project/projectUtils';
import { confirmAsync } from '../../prompts';
import {
parseVisibility,
promptVariableEnvironmentAsync,
promptVariableNameAsync,
promptVariableTypeAsync,
promptVariableValueAsync,
promptVariableVisibilityAsync,
} from '../../utils/prompts';
import { performForEnvironmentsAsync } from '../../utils/variableUtils';
@@ -38,7 +40,7 @@ type CreateFlags = {
link?: boolean;
force?: boolean;
type?: 'string' | 'file';
visibility?: EnvironmentVariableVisibility;
visibility?: 'plaintext' | 'sensitive' | 'encrypted';
scope?: EnvironmentVariableScope;
environment?: EnvironmentVariableEnvironment[];
'non-interactive': boolean;
@@ -75,7 +77,7 @@ export default class EnvironmentVariableCreate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.Analytics,
...this.ContextOptions.LoggedIn,
};
@@ -95,10 +97,11 @@ export default class EnvironmentVariableCreate extends EasCommand {
link,
force,
type,
fileName,
} = await this.promptForMissingFlagsAsync(validatedFlags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableCreate, {
nonInteractive,
@@ -165,6 +168,7 @@ export default class EnvironmentVariableCreate extends EasCommand {
visibility,
environments,
type,
fileName,
})
: await EnvironmentVariableMutation.createForAppAsync(
graphqlClient,
@@ -174,6 +178,7 @@ export default class EnvironmentVariableCreate extends EasCommand {
environments,
visibility,
type: type ?? EnvironmentSecretType.String,
fileName,
},
projectId
);
@@ -292,18 +297,24 @@ export default class EnvironmentVariableCreate extends EasCommand {
name,
value,
environment,
visibility = EnvironmentVariableVisibility.Public,
visibility,
'non-interactive': nonInteractive,
type,
...rest
}: CreateFlags): Promise<
Required<Omit<CreateFlags, 'type'> & { type: EnvironmentSecretType | undefined }>
Required<
Omit<CreateFlags, 'type' | 'visibility'> & {
type: EnvironmentSecretType | undefined;
visibility: EnvironmentVariableVisibility;
}
> & { fileName?: string }
> {
if (!name) {
name = await promptVariableNameAsync(nonInteractive);
}
let newType;
let newVisibility = visibility ? parseVisibility(visibility) : undefined;
if (type === 'file') {
newType = EnvironmentSecretType.FileBase64;
@@ -315,45 +326,60 @@ export default class EnvironmentVariableCreate extends EasCommand {
newType = await promptVariableTypeAsync(nonInteractive);
}
if (!newVisibility) {
newVisibility = await promptVariableVisibilityAsync(nonInteractive);
}
if (!value) {
value = await promptVariableValueAsync({
nonInteractive,
hidden: visibility !== EnvironmentVariableVisibility.Public,
hidden: newVisibility !== EnvironmentVariableVisibility.Public,
filePath: newType === EnvironmentSecretType.FileBase64,
});
}
let environmentFilePath: string | undefined;
let fileName: string | undefined;
if (newType === EnvironmentSecretType.FileBase64) {
environmentFilePath = path.resolve(value);
if (!(await fs.pathExists(environmentFilePath))) {
throw new Error(`File "${value}" does not exist`);
}
fileName = path.basename(environmentFilePath);
}
value = environmentFilePath ? await fs.readFile(environmentFilePath, 'base64') : value;
if (!environment) {
environment = await promptVariableEnvironmentAsync({ nonInteractive, multiple: true });
if (!environment || environment.length === 0) {
throw new Error('No environments selected');
}
}
newVisibility = newVisibility ?? EnvironmentVariableVisibility.Public;
return {
name,
value,
environment,
visibility,
visibility: newVisibility,
link: rest.link ?? false,
force: rest.force ?? false,
scope: rest.scope ?? EnvironmentVariableScope.Project,
'non-interactive': nonInteractive,
type: newType,
fileName,
...rest,
};
}
private validateFlags(flags: CreateFlags & { type?: string }): CreateFlags {
private validateFlags(flags: CreateFlags & { type?: string; visibility?: string }): CreateFlags {
if (flags.scope !== EnvironmentVariableScope.Shared && flags.link) {
throw new Error(
`Unexpected argument: --link can only be used when creating shared variables`
`Unexpected argument: --link can only be used when creating shared variables`
);
}
+2 -2
View File
@@ -40,7 +40,7 @@ export default class EnvironmentVariableDelete extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -53,7 +53,7 @@ export default class EnvironmentVariableDelete extends EasCommand {
scope,
} = this.validateFlags(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableDelete, {
nonInteractive,
+2 -2
View File
@@ -31,7 +31,7 @@ export default class EnvExec extends EasCommand {
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -57,7 +57,7 @@ export default class EnvExec extends EasCommand {
const parsedFlags = this.sanitizeFlags(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvExec, {
nonInteractive: parsedFlags.nonInteractive,
+12 -19
View File
@@ -1,5 +1,4 @@
import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
@@ -35,7 +34,7 @@ export default class EnvironmentVariableGet extends EasCommand {
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -64,7 +63,7 @@ export default class EnvironmentVariableGet extends EasCommand {
} = this.validateFlags(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableGet, {
nonInteractive,
@@ -74,6 +73,13 @@ export default class EnvironmentVariableGet extends EasCommand {
name = await promptVariableNameAsync(nonInteractive);
}
if (!environment) {
environment = await promptVariableEnvironmentAsync({
nonInteractive,
multiple: false,
});
}
const variables = await getVariablesAsync(graphqlClient, scope, projectId, name, environment);
if (variables.length === 0) {
@@ -84,24 +90,11 @@ export default class EnvironmentVariableGet extends EasCommand {
let variable;
if (variables.length > 1) {
if (!environment) {
const availableEnvironments = variables.reduce<EnvironmentVariableEnvironment[]>(
(acc, v) => [...acc, ...(v.environments ?? [])],
[] as EnvironmentVariableEnvironment[]
);
environment = await promptVariableEnvironmentAsync({
nonInteractive,
multiple: false,
availableEnvironments,
});
}
assert(environment, 'Environment is required.');
const variableInEnvironment = variables.find(v => v.environments?.includes(environment!));
if (!variableInEnvironment) {
throw new Error(`Variable with name "${name}" not found in environment "${environment}"`);
throw new Error(
`Variable with name "${name}" not found in environment "${environment.toLocaleLowerCase()}"`
);
}
variable = variableInEnvironment;
+7 -2
View File
@@ -34,7 +34,7 @@ export default class EnvironmentVariableLink extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -48,7 +48,7 @@ export default class EnvironmentVariableLink extends EasCommand {
},
} = await this.parse(EnvironmentVariableLink);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableLink, {
nonInteractive,
@@ -64,6 +64,11 @@ export default class EnvironmentVariableLink extends EasCommand {
let selectedVariable = variables[0];
if (variables.length > 1) {
if (nonInteractive) {
throw new Error(
'Multiple variables found, run command with --variable-name and --variable-environment arguments.'
);
}
selectedVariable = await selectAsync(
'Select shared variable',
variables.map(variable => ({
+8 -3
View File
@@ -71,7 +71,7 @@ export default class EnvironmentValueList extends EasCommand {
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -101,7 +101,7 @@ export default class EnvironmentValueList extends EasCommand {
},
} = await this.parse(EnvironmentValueList);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentValueList, {
nonInteractive: true,
@@ -122,7 +122,12 @@ export default class EnvironmentValueList extends EasCommand {
Log.addNewLineIfNone();
if (environment) {
Log.log(chalk.bold(`Environment: ${environment}`));
Log.log(chalk.bold(`Environment: ${environment.toLocaleLowerCase()}`));
}
if (variables.length === 0) {
Log.log('No variables found for this environment.');
return;
}
if (format === 'short') {
+36 -10
View File
@@ -1,10 +1,18 @@
import { Flags } from '@oclif/core';
import * as fs from 'fs-extra';
import path from 'path';
import EasCommand from '../../commandUtils/EasCommand';
import { EASEnvironmentFlag, EASNonInteractiveFlag } from '../../commandUtils/flags';
import { EnvironmentVariableFragment } from '../../graphql/generated';
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
import {
EnvironmentSecretType,
EnvironmentVariableFragment,
EnvironmentVariableVisibility,
} from '../../graphql/generated';
import {
EnvironmentVariableWithFileContent,
EnvironmentVariablesQuery,
} from '../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../log';
import { confirmAsync } from '../../prompts';
import { promptVariableEnvironmentAsync } from '../../utils/prompts';
@@ -15,8 +23,9 @@ export default class EnvironmentVariablePull extends EasCommand {
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.ProjectDir,
};
static override flags = {
@@ -37,8 +46,9 @@ export default class EnvironmentVariablePull extends EasCommand {
environment = await promptVariableEnvironmentAsync({ nonInteractive });
}
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
projectDir,
} = await this.getContextAsync(EnvironmentVariablePull, {
nonInteractive,
});
@@ -50,6 +60,7 @@ export default class EnvironmentVariablePull extends EasCommand {
{
appId: projectId,
environment,
includeFileContent: true,
}
);
@@ -63,17 +74,32 @@ export default class EnvironmentVariablePull extends EasCommand {
}
}
const filePrefix = `# Environment: ${environment}\n\n`;
const filePrefix = `# Environment: ${environment.toLocaleLowerCase()}\n\n`;
const envFileContent = environmentVariables
.map((variable: EnvironmentVariableFragment) => {
if (variable.value === null) {
const isFileVariablePresent = environmentVariables.some(v => {
return v.type === EnvironmentSecretType.FileBase64 && v.valueWithFileContent;
});
const envDir = path.join(projectDir, '.eas', '.env');
if (isFileVariablePresent) {
await fs.mkdir(envDir, { recursive: true });
}
const envFileContentLines = await Promise.all(
environmentVariables.map(async (variable: EnvironmentVariableWithFileContent) => {
if (variable.visibility === EnvironmentVariableVisibility.Secret) {
return `# ${variable.name}=***** (secret variables are not available for reading)`;
}
if (variable.type === EnvironmentSecretType.FileBase64 && variable.valueWithFileContent) {
const filePath = path.join(envDir, variable.name);
await fs.writeFile(filePath, variable.valueWithFileContent, 'base64');
return `${variable.name}=${filePath}`;
}
return `${variable.name}=${variable.value}`;
})
.join('\n');
await fs.writeFile(targetPath, filePrefix + envFileContent);
);
await fs.writeFile(targetPath, filePrefix + envFileContentLines.join('\n'));
const secretEnvVariables = environmentVariables.filter(
(variable: EnvironmentVariableFragment) => variable.value === null
+130 -110
View File
@@ -1,9 +1,10 @@
import { Flags } from '@oclif/core';
import dotenv from 'dotenv';
import fs from 'fs-extra';
import path from 'path';
import EasCommand from '../../commandUtils/EasCommand';
import { EASEnvironmentFlag } from '../../commandUtils/flags';
import { EASMultiEnvironmentFlag } from '../../commandUtils/flags';
import {
EnvironmentVariableEnvironment,
EnvironmentVariableFragment,
@@ -17,11 +18,7 @@ import {
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../log';
import { confirmAsync, promptAsync } from '../../prompts';
type PushFlags = {
environment?: EnvironmentVariableEnvironment;
path: string;
};
import { promptVariableEnvironmentAsync } from '../../utils/prompts';
export default class EnvironmentVariablePush extends EasCommand {
static override description = 'push env file';
@@ -29,12 +26,12 @@ export default class EnvironmentVariablePush extends EasCommand {
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
static override flags = {
...EASEnvironmentFlag,
...EASMultiEnvironmentFlag,
path: Flags.string({
description: 'Path to the input `.env` file',
default: '.env.local',
@@ -42,120 +39,138 @@ export default class EnvironmentVariablePush extends EasCommand {
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(EnvironmentVariablePush);
let {
flags: { environment: environments, path: envPath },
} = await this.parse(EnvironmentVariablePush);
const { environment, path: envPath } = this.validateFlags(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariablePush, {
nonInteractive: false,
});
if (!environments) {
environments = await promptVariableEnvironmentAsync({
nonInteractive: false,
multiple: true,
});
}
const updateVariables: Record<string, EnvironmentVariablePushInput> =
await this.parseEnvFileAsync(envPath, environment);
await this.parseEnvFileAsync(envPath, environments);
const variableNames = Object.keys(updateVariables);
const existingVariables = await EnvironmentVariablesQuery.byAppIdAsync(graphqlClient, {
appId: projectId,
environment,
filterNames: variableNames,
});
for (const environment of environments) {
const displayedEnvironment = environment.toLocaleLowerCase();
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 existingDifferentVariables: EnvironmentVariableFragment[] = [];
// Remove variables that are the same as the ones in the environment
existingVariables.forEach(existingVariable => {
const existingVariableUpdate = updateVariables[existingVariable.name];
const existingDifferentSharedVariables = existingDifferentVariables.filter(
variable => variable.scope === EnvironmentVariableScope.Shared
);
if (existingVariableUpdate) {
const hasMoreEnvironments = existingVariableUpdate.environments.some(
newEnv => !existingVariable.environments?.includes(newEnv)
);
if (existingDifferentSharedVariables.length > 0) {
const existingDifferentSharedVariablesNames = existingDifferentSharedVariables.map(
variable => variable.name
if (existingVariableUpdate.value !== existingVariable.value || hasMoreEnvironments) {
existingDifferentVariables.push(existingVariable);
} else {
delete updateVariables[existingVariable.name];
}
}
});
const existingDifferentSharedVariables = existingDifferentVariables.filter(
variable => variable.scope === EnvironmentVariableScope.Shared
);
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,
})),
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}`);
});
variablesToOverwrite = promptResult.variablesToOverwrite;
throw new Error('Shared variables cannot be overwritten by eas env:push command');
}
for (const existingVariable of existingVariables) {
const name = existingVariable.name;
if (variablesToOverwrite.includes(name)) {
updateVariables[name]['overwrite'] = true;
if (existingDifferentVariables.length > 0) {
Log.warn(`Some variables already exist in the ${displayedEnvironment} environment.`);
const variableNames = existingDifferentVariables.map(variable => variable.name);
const confirmationMessage =
variableNames.length > 1
? `The ${variableNames.join(
', '
)} environment variables already exist in ${displayedEnvironment} environment. Do you want to override them all?`
: `The ${variableNames[0]} environment variable already exists in ${displayedEnvironment} environment. Do you want to override it?`;
const confirm = await confirmAsync({
message: confirmationMessage,
});
let variablesToOverwrite: string[] = [];
if (!confirm && existingDifferentVariables.length === 0) {
throw new Error('No new variables to push.');
}
if (confirm) {
variablesToOverwrite = existingDifferentVariables.map(variable => variable.name);
} else {
delete updateVariables[name];
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}`
// Check if any of the sensitive variables already exist in the environment. Prompt the user to overwrite them.
const existingSensitiveVariables = existingVariables.filter(
variable => variable.visibility !== EnvironmentVariableVisibility.Public
);
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...');
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...');
}
}
}
@@ -171,13 +186,12 @@ export default class EnvironmentVariablePush extends EasCommand {
variablesToPush,
projectId
);
Log.log(`Uploaded env file to ${environment} environment.`);
Log.log(`Uploaded env file to ${environments.join(', ').toLocaleLowerCase()}.`);
}
private async parseEnvFileAsync(
envPath: string,
environment: string
environments: EnvironmentVariableEnvironment[]
): Promise<Record<string, EnvironmentVariablePushInput>> {
if (!(await fs.exists(envPath))) {
throw new Error(`File ${envPath} does not exist.`);
@@ -186,11 +200,25 @@ export default class EnvironmentVariablePush extends EasCommand {
const variables: Record<string, string> = dotenv.parse(await fs.readFile(envPath, 'utf8'));
const hasFileVariables = Object.values(variables).some(value =>
value.includes(path.join('.eas', '.env'))
);
if (hasFileVariables) {
Log.warn('File variables are not supported in this command.');
}
for (const [name, value] of Object.entries(variables)) {
// Skip file variables
const fileVariablePath = path.join('.eas', '.env', name);
if (value.endsWith(fileVariablePath)) {
Log.warn(`Skipping file variable ${name}`);
continue;
}
pushInput[name] = {
name,
value,
environment,
environments,
visibility: name.startsWith('EXPO_SENSITIVE')
? EnvironmentVariableVisibility.Sensitive
: EnvironmentVariableVisibility.Public,
@@ -199,12 +227,4 @@ export default class EnvironmentVariablePush extends EasCommand {
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 };
}
}
+2 -2
View File
@@ -26,7 +26,7 @@ export default class EnvironmentVariableUnlink extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -39,7 +39,7 @@ export default class EnvironmentVariableUnlink extends EasCommand {
},
} = await this.parse(EnvironmentVariableUnlink);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableUnlink, {
nonInteractive,
+29 -19
View File
@@ -28,6 +28,7 @@ import {
} from '../../project/projectUtils';
import { selectAsync } from '../../prompts';
import {
parseVisibility,
promptVariableEnvironmentAsync,
promptVariableNameAsync,
promptVariableTypeAsync,
@@ -41,7 +42,7 @@ type UpdateFlags = {
value?: string;
scope?: EnvironmentVariableScope;
environment?: EnvironmentVariableEnvironment[];
visibility?: EnvironmentVariableVisibility;
visibility?: 'plaintext' | 'sensitive' | 'encrypted';
type?: 'string' | 'file';
'variable-name'?: string;
'variable-environment'?: EnvironmentVariableEnvironment;
@@ -79,7 +80,7 @@ export default class EnvironmentVariableUpdate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.Analytics,
...this.ContextOptions.LoggedIn,
};
@@ -99,7 +100,7 @@ export default class EnvironmentVariableUpdate extends EasCommand {
} = this.validateFlags(flags);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentVariableUpdate, {
nonInteractive,
@@ -136,7 +137,7 @@ export default class EnvironmentVariableUpdate extends EasCommand {
if (existingVariables.length === 0) {
throw new Error(
`Variable with name ${currentName} ${
currentEnvironment ? `in environment ${currentEnvironment}` : ''
currentEnvironment ? `in environment ${currentEnvironment.toLocaleLowerCase()}` : ''
} does not exist ${suffix}.`
);
} else if (existingVariables.length > 1) {
@@ -159,6 +160,7 @@ export default class EnvironmentVariableUpdate extends EasCommand {
environment: newEnvironments,
visibility: newVisibility,
type: newType,
fileName,
} = await this.promptForMissingFlagsAsync(selectedVariable, {
name,
value: rawValue,
@@ -175,6 +177,7 @@ export default class EnvironmentVariableUpdate extends EasCommand {
environments: newEnvironments,
type: newType,
visibility: newVisibility,
fileName: newValue ? fileName : undefined,
});
if (!variable) {
throw new Error(`Could not update variable with name ${name} ${suffix}`);
@@ -208,8 +211,16 @@ export default class EnvironmentVariableUpdate extends EasCommand {
type,
...rest
}: UpdateFlags
): Promise<Omit<UpdateFlags, 'type'> & { type?: EnvironmentSecretType }> {
): Promise<
Omit<UpdateFlags, 'type' | 'visibility'> & {
type?: EnvironmentSecretType;
visibility?: EnvironmentVariableVisibility;
fileName?: string;
}
> {
let newType;
let newVisibility: EnvironmentVariableVisibility | undefined;
let fileName: string | undefined;
if (type === 'file') {
newType = EnvironmentSecretType.FileBase64;
@@ -226,23 +237,15 @@ export default class EnvironmentVariableUpdate extends EasCommand {
}
}
Log.log(
selectedVariable.type,
EnvironmentSecretType.String === selectedVariable.type,
EnvironmentSecretType.FileBase64 === selectedVariable.type
);
if (!type && !value && !nonInteractive) {
newType = await promptVariableTypeAsync(nonInteractive, selectedVariable.type);
if (!newType || newType === selectedVariable.type) {
newType = undefined;
}
}
if (!value) {
value = await promptVariableValueAsync({
nonInteractive,
required: false,
filePath: (newType ?? selectedVariable.type) === EnvironmentSecretType.FileBase64,
initial:
(newType ?? selectedVariable.type) === EnvironmentSecretType.FileBase64
? undefined
@@ -251,16 +254,18 @@ export default class EnvironmentVariableUpdate extends EasCommand {
if (!value || value.length === 0 || value === selectedVariable.value) {
value = undefined;
newType = undefined;
}
}
let environmentFilePath: string | undefined;
if (newType === EnvironmentSecretType.FileBase64 && value) {
if ((newType ?? selectedVariable.type) === EnvironmentSecretType.FileBase64 && value) {
environmentFilePath = path.resolve(value);
if (!(await fs.pathExists(environmentFilePath))) {
throw new Error(`File "${value}" does not exist`);
}
fileName = path.basename(environmentFilePath);
}
value = environmentFilePath ? await fs.readFile(environmentFilePath, 'base64') : value;
@@ -282,25 +287,30 @@ export default class EnvironmentVariableUpdate extends EasCommand {
}
if (!visibility) {
visibility = await promptVariableVisibilityAsync(
newVisibility = await promptVariableVisibilityAsync(
nonInteractive,
selectedVariable.visibility
);
if (!visibility || visibility === selectedVariable.visibility) {
visibility = undefined;
if (!newVisibility || newVisibility === selectedVariable.visibility) {
newVisibility = undefined;
}
}
}
if (visibility) {
newVisibility = parseVisibility(visibility);
}
return {
name,
value,
environment: environments,
visibility,
visibility: newVisibility,
scope: rest.scope ?? EnvironmentVariableScope.Project,
'non-interactive': nonInteractive,
type: newType,
fileName,
...rest,
};
}
@@ -41,6 +41,7 @@ export default class MetadataPull extends EasCommand {
vcsClient,
} = await this.getContextAsync(MetadataPull, {
nonInteractive: false,
withServerSideEnvironment: null,
});
// this command is interactive (all nonInteractive flags passed to utility functions are false)
@@ -39,6 +39,7 @@ export default class MetadataPush extends EasCommand {
vcsClient,
} = await this.getContextAsync(MetadataPush, {
nonInteractive: false,
withServerSideEnvironment: null,
});
// this command is interactive (all nonInteractive flags passed to utility functions are false)
+1
View File
@@ -20,6 +20,7 @@ export default class Open extends EasCommand {
loggedIn: { graphqlClient },
} = await this.getContextAsync(Open, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
@@ -90,6 +90,7 @@ function mockTestProject(options: {
actor: jester,
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo: { accessToken: null, sessionSecret: '1234' },
});
}
@@ -37,13 +37,13 @@ export default class ProjectInfo extends EasCommand {
static override description = 'information about the current project';
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(ProjectInfo, {
nonInteractive: true,
@@ -1,5 +1,4 @@
import { ConfigError, getProjectConfigDescription } from '@expo/config';
import { ExpoConfig } from '@expo/config-types';
import { ConfigError, ExpoConfig, getProjectConfigDescription } from '@expo/config';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import nullthrows from 'nullthrows';
@@ -1,4 +1,4 @@
import { ExpoConfig } from '@expo/config-types';
import { ExpoConfig } from '@expo/config';
import { Platform } from '@expo/eas-build-job';
import { AppVersionSource, EasJson } from '@expo/eas-json';
import chalk from 'chalk';
@@ -50,7 +50,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -66,7 +66,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
},
} = await this.parse(EnvironmentSecretCreate);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentSecretCreate, {
nonInteractive,
@@ -22,7 +22,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -31,7 +31,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
flags: { id, 'non-interactive': nonInteractive },
} = await this.parse(EnvironmentSecretDelete);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentSecretDelete, {
nonInteractive,
+2 -2
View File
@@ -13,13 +13,13 @@ export default class EnvironmentSecretList extends EasCommand {
static override description = 'list environment secrets available for your current app';
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentSecretList, {
nonInteractive: true,
+2 -2
View File
@@ -42,7 +42,7 @@ export default class EnvironmentSecretPush extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -51,7 +51,7 @@ export default class EnvironmentSecretPush extends EasCommand {
flags: { scope, force, 'env-file': maybeEnvFilePath, 'non-interactive': nonInteractive },
} = await this.parse(EnvironmentSecretPush);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvironmentSecretPush, {
nonInteractive,
+1
View File
@@ -112,6 +112,7 @@ export default class Submit extends EasCommand {
vcsClient,
} = await this.getContextAsync(Submit, {
nonInteractive: false,
withServerSideEnvironment: null,
});
const flags = this.sanitizeFlags(rawFlags);
@@ -65,6 +65,7 @@ export default class SubmitInternal extends EasCommand {
} = await this.getContextAsync(SubmitInternal, {
nonInteractive: true,
vcsClientOverride: new GitNoCommitClient(),
withServerSideEnvironment: null,
});
const submissionProfile = await EasJsonUtils.getSubmitProfileAsync(
@@ -12,6 +12,7 @@ import {
DynamicPublicProjectConfigContextField,
} from '../../../commandUtils/context/DynamicProjectConfigContextField';
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
import { ServerSideEnvironmentVariablesContextField } from '../../../commandUtils/context/ServerSideEnvironmentVariablesContextField';
import VcsClientContextField from '../../../commandUtils/context/VcsClientContextField';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
@@ -232,6 +233,11 @@ function mockTestProject({
projectId: configuredProjectId,
};
});
jest
.spyOn(ServerSideEnvironmentVariablesContextField.prototype, 'getValueAsync')
.mockResolvedValue(async () => {
return {};
});
jest
.spyOn(DynamicPublicProjectConfigContextField.prototype, 'getValueAsync')
.mockResolvedValue(async () => {
@@ -254,6 +260,7 @@ function mockTestProject({
actor: jester,
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo: { accessToken: null, sessionSecret: '1234' },
});
jest
.spyOn(VcsClientContextField.prototype, 'getValueAsync')
@@ -467,6 +467,7 @@ function mockTestProject({
actor: jester,
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo: { accessToken: null, sessionSecret: '1234' },
});
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
@@ -255,6 +255,7 @@ function mockTestProject({
actor: jester,
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
graphqlClient,
authenticationInfo: { accessToken: null, sessionSecret: '1234' },
});
jest
.spyOn(VcsClientContextField.prototype, 'getValueAsync')
@@ -38,6 +38,7 @@ export default class UpdateConfigure extends EasCommand {
vcsClient,
} = await this.getContextAsync(UpdateConfigure, {
nonInteractive: flags['non-interactive'],
withServerSideEnvironment: null,
});
Log.log(
+46 -3
View File
@@ -2,12 +2,14 @@ import { Flags } from '@oclif/core';
import assert from 'assert';
import chalk from 'chalk';
import { selectBranchOnAppAsync } from '../../branch/queries';
import EasCommand from '../../commandUtils/EasCommand';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { selectUpdateGroupOnBranchAsync } from '../../update/queries';
import {
formatUpdateGroup,
getUpdateGroupDescriptions,
@@ -17,12 +19,10 @@ import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class UpdateEdit extends EasCommand {
static override description = 'edit all the updates in an update group';
static override hidden = true;
static override args = [
{
name: 'groupId',
required: true,
description: 'The ID of an update group to edit.',
},
];
@@ -34,24 +34,30 @@ export default class UpdateEdit extends EasCommand {
min: 0,
max: 100,
}),
branch: Flags.string({
description: 'Branch for which to list updates to select from',
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const {
args: { groupId },
args: { groupId: maybeGroupId },
flags: {
'rollout-percentage': rolloutPercentage,
json: jsonFlag,
'non-interactive': nonInteractive,
branch: branchFlag,
},
} = await this.parse(UpdateEdit);
const {
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(UpdateEdit, { nonInteractive });
@@ -59,6 +65,43 @@ export default class UpdateEdit extends EasCommand {
enableJsonOutput();
}
let groupId: string | undefined = maybeGroupId;
if (!groupId) {
let branch = branchFlag;
if (!branch) {
const validationMessage = 'Branch name may not be empty.';
if (nonInteractive) {
throw new Error(validationMessage);
}
const selectedBranch = await selectBranchOnAppAsync(graphqlClient, {
projectId,
promptTitle: 'On which branch would you like search for an update to edit?',
displayTextForListItem: updateBranch => ({
title: updateBranch.name,
}),
paginatedQueryOptions: {
json: jsonFlag,
nonInteractive,
offset: 0,
},
});
branch = selectedBranch.name;
}
const selectedUpdateGroup = await selectUpdateGroupOnBranchAsync(graphqlClient, {
projectId,
branchName: branch,
paginatedQueryOptions: {
json: jsonFlag,
nonInteractive,
offset: 0,
},
});
groupId = selectedUpdateGroup[0].group;
}
const proposedUpdatesToEdit = (
await UpdateQuery.viewUpdateGroupAsync(graphqlClient, { groupId })
).map(u => ({ updateId: u.id, rolloutPercentage: u.rolloutPercentage }));
+44 -1
View File
@@ -12,6 +12,7 @@ import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import fetch from '../../fetch';
import {
EnvironmentVariableEnvironment,
PublishUpdateGroupInput,
StatuspageServiceName,
UpdateInfoGroup,
@@ -72,6 +73,7 @@ type RawUpdateFlags = {
'rollout-percentage'?: number;
'non-interactive': boolean;
json: boolean;
'with-eas-environment-variables-set'?: string;
};
type UpdateFlags = {
@@ -88,6 +90,7 @@ type UpdateFlags = {
rolloutPercentage?: number;
json: boolean;
nonInteractive: boolean;
withEasEnvironmentVariablesSet?: EnvironmentVariableEnvironment;
};
export default class UpdatePublish extends EasCommand {
@@ -149,6 +152,18 @@ export default class UpdatePublish extends EasCommand {
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
required: false,
}),
'with-eas-environment-variables-set': Flags.enum({
description: 'Environment to use for EAS environment variables',
options: [
EnvironmentVariableEnvironment.Development,
EnvironmentVariableEnvironment.Preview,
EnvironmentVariableEnvironment.Production,
].map(env => env.toLowerCase()),
// eslint-disable-next-line async-protect/async-suffix
parse: async input => input.toUpperCase(),
required: false,
hidden: true,
}),
...EasNonInteractiveAndJsonFlags,
};
@@ -156,6 +171,7 @@ export default class UpdatePublish extends EasCommand {
...this.ContextOptions.DynamicProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
...this.ContextOptions.ServerSideEnvironmentVariables,
};
async runAsync(): Promise<void> {
@@ -175,6 +191,7 @@ export default class UpdatePublish extends EasCommand {
branchName: branchNameArg,
emitMetadata,
rolloutPercentage,
withEasEnvironmentVariablesSet,
} = this.sanitizeFlags(rawFlags);
const {
@@ -182,8 +199,10 @@ export default class UpdatePublish extends EasCommand {
getDynamicPrivateProjectConfigAsync,
loggedIn: { graphqlClient },
vcsClient,
getServerSideEnvironmentVariablesAsync,
} = await this.getContextAsync(UpdatePublish, {
nonInteractive,
withServerSideEnvironment: withEasEnvironmentVariablesSet ?? null,
});
if (jsonFlag) {
@@ -242,6 +261,11 @@ export default class UpdatePublish extends EasCommand {
exp,
platformFlag: requestedPlatform,
clearCache,
extraEnv: {
...(withEasEnvironmentVariablesSet
? { ...(await getServerSideEnvironmentVariablesAsync()), EXPO_NO_DOTENV: '1' }
: {}),
},
});
bundleSpinner.succeed('Exported bundle(s)');
} catch (e) {
@@ -637,6 +661,22 @@ export default class UpdatePublish extends EasCommand {
);
}
if (
flags['with-eas-environment-variables-set'] &&
!Object.values(EnvironmentVariableEnvironment).includes(
flags['with-eas-environment-variables-set'] as EnvironmentVariableEnvironment
)
) {
Errors.error(
`--with-eas-environment-variables-set must be one of ${Object.values(
EnvironmentVariableEnvironment
)
.map(env => `"${env.toLocaleLowerCase()}"`)
.join(', ')}`,
{ exit: 1 }
);
}
return {
auto,
branchName,
@@ -644,13 +684,16 @@ export default class UpdatePublish extends EasCommand {
updateMessage,
inputDir: flags['input-dir'],
skipBundler,
clearCache: flags['clear-cache'],
clearCache: flags['clear-cache'] ? true : !!flags['with-eas-environment-variables-set'],
platform: flags.platform as RequestedPlatform,
privateKeyPath: flags['private-key-path'],
rolloutPercentage: flags['rollout-percentage'],
nonInteractive,
emitMetadata,
json: flags.json ?? false,
withEasEnvironmentVariablesSet: flags[
'with-eas-environment-variables-set'
] as EnvironmentVariableEnvironment,
};
}
}
+2 -2
View File
@@ -33,7 +33,7 @@ export default class UpdateList extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -41,7 +41,7 @@ export default class UpdateList extends EasCommand {
const { flags } = await this.parse(UpdateList);
const { branch: branchFlag, all, json: jsonFlag, 'non-interactive': nonInteractive } = flags;
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(UpdateList, {
nonInteractive,
@@ -103,6 +103,7 @@ export default class UpdateRepublish extends EasCommand {
loggedIn: { graphqlClient },
} = await this.getContextAsync(UpdateRepublish, {
nonInteractive: flags.nonInteractive,
withServerSideEnvironment: null,
});
if (flags.json) {
@@ -134,6 +134,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
vcsClient,
} = await this.getContextAsync(UpdateRollBackToEmbedded, {
nonInteractive,
withServerSideEnvironment: null,
});
if (jsonFlag) {
@@ -26,14 +26,14 @@ export default class WebhookCreate extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(WebhookCreate);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(WebhookCreate, {
nonInteractive: flags['non-interactive'],
@@ -29,7 +29,7 @@ export default class WebhookDelete extends EasCommand {
};
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
@@ -39,7 +39,7 @@ export default class WebhookDelete extends EasCommand {
flags: { 'non-interactive': nonInteractive },
} = await this.parse(WebhookDelete);
const {
privateProjectConfig: { projectId },
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(WebhookDelete, {
nonInteractive,
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff. Show more