Compare commits

...
22 Commits
Author SHA1 Message Date
Szymon Dziedzic 01676a4f88 v12.5.1 2024-09-26 22:16:28 -07:00
Szymon Dziedzic 7edc10feaf [eas-cli] add exit option to eas credentials interactive menu (#2570)
<!-- 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/C017N0N99RA/p1726226230206999

Right now the only way to exit the `eas credentials` command is to kill the process by doing ctrl+c. Let's add an `exit` option to the interactive menu.

# How

Add exit option to menu.

# Test Plan

Test manually
2024-09-27 06:21:08 +02:00
Will Schurman fba26e6b50 [eas-cli] Add destination branch arguments to update:republish command (#2575) 2024-09-26 09:32:55 -07:00
Szymon Dziedzic 69014fe21d [eas-cli] stop using environment flag in build related commands (#2547)
<!-- 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

On Friday's meeting, we agreed that having both a flag and a field in the build profile for setting the environment might be too much. Additionally, we already have a `--profile` flag that has a `-e` alias which makes it even more confusing. We decided to delete the flag to avoid confusion and make it as straightforward as possible.

# How

Delete the `--environment` flag for build-related commands, rely only on the `environment` field in eas.json.

# Test Plan

Tests
2024-09-26 16:45:03 +02:00
Szymon Dziedzic 217a1f4fdb [eas-cli] add eas env:exec command (#2549)
<!-- 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

Add the `eas env:exec` command. This command allows you to run any arbitrary bash command with env vars from a given environment.

# How

Add `eas env:exec` command

# Test Plan

Test manually

![Screenshot 2024-09-12 at 11.23.06.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/9YRXgAETSTRMfjZ0IP35/b1f2e6d3-7a4d-415d-a6e3-5754684c6349.png)
2024-09-26 16:44:16 +02:00
Cedric van Putten 0863b9a477 refactor(eas-cli): support assigning dev domain in non interactive mode (#2595)
* refactor(eas-cli): split prompt from dev domain assignment and support it non-interactive

* docs: add changelog entry

* docs: fix changelog entry

* fix(eas-cli): use proper name in "already taken" prompt

* chore: resolve linting issue
2024-09-26 12:34:14 +02:00
Stanisław Chmiela 081a254d9b [eas-cli] Replace logsUrl with logFiles (#2598)
# Why

Almost a year ago we've migrated from using a single file for submission logs to multi-file logs, accommodating need to display logs from both the worker and the scheduler that is starting the worker VM.

Companion to https://github.com/expo/universe/pull/16873.

# How

Replaced `logsUrl` with `logFiles`, adjusted code.

# Test Plan

Logs printed

<img width="1055" alt="Zrzut ekranu 2024-09-24 o 23 06 22" src="https://github.com/user-attachments/assets/6c9a45c0-6760-4629-82e3-a79d5ef99ebe">
2024-09-25 19:13:29 +02:00
Will Schurman ba7eb7ea07 [ci] Remove blurb about changelog bot since it is still disabled (#2592) 2024-09-24 13:19:46 -07:00
Cedric van Putten f5df6e6e77 refactor(eas-cli): allow Expo Router server deployments without client directory (#2597)
* refactor(eas-cli): allow Expo Router server deployments without client directories

* docs: fix changelog entry
2024-09-24 20:03:19 +02:00
Cedric van Putten c0c27559eb refactor(eas-cli): simplify the output of eas deploy --json (#2596)
* refactor(eas-cli): simplify the `eas deploy --json` output

* docs: fix changelog entry

* refactor(eas-cli): keep aliases as array in `--json` output

* docs: fix changelog entry
2024-09-24 16:45:07 +02:00
Phil Pluckthun 2e002ad709 Fix worker asset upload error not being shown (#2579) 2024-09-24 11:17:12 +01:00
Expo CI 2e211f4904 update CHANGELOG.md 2024-09-23 15:32:31 +00:00
Szymon Dziedzic 981f7c9727 v12.5.0 2024-09-23 17:25:42 +02:00
Stanisław Chmiela 24245d76cf [eas-cli] Add submit:internal (#2571)
<!-- If this PR requires a changelog entry, add it by commenting the PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`. -->
<!-- You can skip the changelog check by labeling the PR with "no changelog". -->

# Why

I want to add support for submissions as part of workflows, where, when we start the job, we don't have access to the repository. So, we need to resolve submission config _somewhere_, ideally where we are isolated, can easily clone project, install dependencies and resolve dynamic config.

As we do with `eas build:internal`, I expect it will make sense to do `eas submit:internal` which will do what `eas submit` does usually — resolve all the credentials, using local filesystem or Expo servers  and then fetch the _actual_ keys from Expo servers.

# How

First, I changed structure of `SubmitCommand`/`Submitter` a bit — previously, `SubmitCommand` would create the `Submitter` and call `submitAsync` _inside_ `runAsync()`. After my changes `SubmitCommand` will create _and return_ the `Submitter` and the call site will need to call `submitAsync`.

By doing it this way I was able to add a `getSubmissionInputAsync` to `BaseSubmitter` which returns all-resolved submission input, as-it-would-have-been-used-in-submission-mutation. This ensures the same behavior for `eas submit` and `eas submit:internal`.

Once we have that, `eas submit:internal` is relatively straightforward:
1. create `SubmitCommand`, get `Submitter`.
2. `getSubmissionInputAsync`.
3. fetch necessary credentials by IDs from `www` (https://github.com/expo/universe/pull/16793).
4. validate that we have it all (https://github.com/expo/eas-build/pull/443) and print the JSON.

# Test Plan

I was able to run `easd submit:internal` locally and got the JSON output.
2024-09-20 22:25:33 +02:00
Cedric van Putten d42cbe0146 fix(eas-cli): avoid malforming app.json with empty .expo object (#2573)
* fix(eas-cli): avoid malforming `app.json` with empty `.expo` object

* docs(cli): add changelog entry

* docs(cli): fix changelog entry

* test(eas-cli): drop the legacy test
2024-09-19 21:19:23 +02:00
Stanisław Chmiela 2cf6dd49db [eas-cli] Let iOS bundle identifier override always (#2576)
<!-- 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

In a submit job we're going to have an IPA/APK to submit. We don't want to resolve any bundle identifiers from the project, we can use one from that IPA/APK.

# How

Let the override env variable override bundle identifier resolution if the workflow is managed too.

# Test Plan

I used it to submit an IPA with workflows.
2024-09-19 16:17:01 +02:00
Expo Bot 09c5038c58 Upgrade eas-build dependencies (#2577)
⬆️ Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.
2024-09-19 15:50:53 +02:00
Cedric van Putten d33ebe1ae4 refactor(eas-cli): reword dev domain prompt to use "preview URL" (#2572)
* refactor(eas-cli): reword dev domain prompt to use "preview URL"

* docs(eas-cli): add changelog entry

* docs(eas-cli): fix changelog entry
2024-09-17 16:03:31 +02:00
Cedric van Putten 41fd76c154 fix(eas-cli): check if export is available before validating project (#2569)
* fix(eas-cli): check if export is available before validating project

* docs(eas-cli): add changelog entry

* docs(eas-cli): fix changelog entry
2024-09-16 12:20:01 +02:00
Radosław Krzemień dadf58fa70 [eas-cli] [ENG-10146] Allow submission of builds in progress (#2543)
* [eas-cli] Add tests

Added tests for allowing to select in progress builds

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Allow in progress builds

When prompting the user with a list of builds the in-progress builds are now returned first

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Adjust description

When prompting the user with a list of builds the build status is now displayed let the user know which are finished and which are still in progress

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Reformat

Adjusted formatting

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Reformat

Adjusted formatting

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Lint fix

lint --fix

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Add changelog

Added the changelog entry since the bot does not seem to be working

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Return more statuses and sort

Query returning the potential builds for submission now also returns build in `new` and `in-queue` statuses. All builds are then sorted decreasingly by `createdAt` and up to `limit` are returned

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Lint

yarn lint --fix

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to

* [eas-cli] Add mapping

Created build status mapping instead string modification

See: https://linear.app/expo/issue/ENG-10146/fr-add-option-to-trigger-auto-submit-while-waiting-for-build-to
2024-09-16 11:38:34 +02:00
Archimedes TrajanoandSzymon Dziedzic a7f939fb5f log command execution to assist in debugging local builds (#2526)
* log command execution to assist in debugging local builds

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

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

* import and changelog

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

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

---------

Co-authored-by: Szymon Dziedzic <[email protected]>
2024-09-16 11:03:23 +02:00
Expo CI e54b5a243c update CHANGELOG.md 2024-09-14 14:15:52 +00:00
59 changed files with 2650 additions and 603 deletions

No files matched your search

-48
View File
@@ -1,48 +0,0 @@
name: Changelog bot
on:
issue_comment:
types: [created]
jobs:
pr-commented:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request && github.event.issue.user.login == github.event.comment.user.login && startsWith(github.event.comment.body, '/changelog-entry ') }}
steps:
- uses: actions/checkout@v2
with:
token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/setup-node@v2
with:
node-version: 20
- name: Checkout Pull Request
run: gh pr checkout ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies
working-directory: ./scripts
run: yarn install --frozen-lockfile --check-files
- name: Add changelog entry
working-directory: ./scripts
run: |
PARAMS=${PR_COMMENT_BODY#/changelog-entry }
if [[ ! "$PARAMS" =~ " " ]]; then
echo "Invalid /changelog-entry command"
exit 1
fi
CATEGORY=$( cut -d ' ' -f 1 <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
MESSAGE=$( cut -d ' ' -f 2- <<< "$PARAMS" | sed -e 's/^[[:space:]]*//' )
yarn changelog-entry "$CATEGORY" "$MESSAGE"
env:
GITHUB_PR_AUTHOR: ${{ github.event.issue.user.login }}
GITHUB_PR_NUMBER: ${{ github.event.issue.number }}
PR_COMMENT_BODY: ${{ github.event.comment.body }}
- name: Commit and push
run: |
# use the last commit author name and email
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
git config --global user.email "$(git --no-pager log --format=format:'%ae' -n 1)"
git add CHANGELOG.md
git commit -m "update CHANGELOG.md"
git push
+1 -2
View File
@@ -36,6 +36,5 @@ jobs:
refresh-message-position: true
message: ${{ contains(github.event.pull_request.labels.*.name, 'no changelog') && '⏩ The changelog entry check has been skipped since the "no changelog" label is present.' || '✅ Thank you for adding the changelog entry!' }}
message-failure: |
❌ It looks like a changelog entry is missing for this PR. You have two options: you can add it manually, or you can use the changelog bot to do it for you.
🤖 To use the bot, simply comment on this PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`.
❌ It looks like a changelog entry is missing for this PR. Add it manually to CHANGELOG.md.
⏩ If this PR doesn't require a changelog entry, such as if it's an internal change that doesn't affect the user experience, you can add the "no changelog" label to the PR.
+24
View File
@@ -10,8 +10,30 @@ This is the log of notable changes to EAS CLI and related packages.
### 🐛 Bug fixes
- Support assigning dev domain names in non interactive mode. ([#2595](https://github.com/expo/eas-cli/pull/2595) by [@byCedric](https://github.com/byCedric))
### 🧹 Chores
- Simplify the output of `eas deploy --json`. ([#2596](https://github.com/expo/eas-cli/pull/2596) by [@byCedric](https://github.com/byCedric))
- Support deploying Expo Router server exports without client directory. ([#2597](https://github.com/expo/eas-cli/pull/2597) by [@byCedric](https://github.com/byCedric))
- Add exit option to `eas credentials` interactive menu. ([#2570](https://github.com/expo/eas-cli/pull/2570) by [@szdziedzic](https://github.com/szdziedzic))
## [12.5.0](https://github.com/expo/eas-cli/releases/tag/v12.5.0) - 2024-09-23
### 🎉 New features
- Log command execution to assist in debugging local builds. ([#2526](https://github.com/expo/eas-cli/pull/2526) by [@trajano](https://github.com/trajano))
- Allow submitting builds in progress ([#2543](https://github.com/expo/eas-cli/pull/2543) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
- Use `EAS_DANGEROUS_OVERRIDE_ANDROID_APPLICATION_ID` and `EAS_DANGEROUS_OVERRIDE_IOS_BUNDLE_IDENTIFIER` environment variables as overrides of the Android application ID and iOS bundle identifier in managed workflow too. ([#2576](https://github.com/expo/eas-cli/pull/2576) by [@sjchmiela](https://github.com/sjchmiela))
- Add destination branch arguments to update:republish command. ([#2575](https://github.com/expo/eas-cli/pull/2575) by [@wschurman](https://github.com/wschurman))
### 🐛 Bug fixes
- Avoid malforming `app.json` with empty `.expo` object. ([#2573](https://github.com/expo/eas-cli/pull/2573) by [@byCedric](https://github.com/byCedric))
- Fix typo causing `worker:deploy` asset upload errors not to be shown properly. ([#2579](https://github.com/expo/eas-cli/pull/2579) by [@kitten](https://github.com/kitten))
## [12.4.1](https://github.com/expo/eas-cli/releases/tag/v12.4.1) - 2024-09-14
## [12.4.0](https://github.com/expo/eas-cli/releases/tag/v12.4.0) - 2024-09-14
### 🎉 New features
@@ -29,6 +51,7 @@ This is the log of notable changes to EAS CLI and related packages.
- Avoid merging `expo.extra` plugin-generated data with `expo.extra.eas.projectId` in `eas init`. ([#2554](https://github.com/expo/eas-cli/pull/2554) by [@byCedric](https://github.com/byCedric)))
- Restore "export not found" error and hide recent export timestamps. ([#2566](https://github.com/expo/eas-cli/pull/2566) by [@byCedric](https://github.com/byCedric)))
- Check if export is available before validating project ID in `eas worker`. ([#2569](https://github.com/expo/eas-cli/pull/2569) by [@byCedric](https://github.com/byCedric)))
### 🧹 Chores
@@ -42,6 +65,7 @@ This is the log of notable changes to EAS CLI and related packages.
- Always assume `static` exports in `eas deploy` and add modified time. ([#2565](https://github.com/expo/eas-cli/pull/2565) by [@byCedric](https://github.com/byCedric)))
- Update the `eas worker --help` `--environment` description. ([#2567](https://github.com/expo/eas-cli/pull/2567) by [@byCedric](https://github.com/byCedric)))
- Remove the cursor space after selecting project dev domain. ([#2568](https://github.com/expo/eas-cli/pull/2568) by [@byCedric](https://github.com/byCedric)))
- Reword the dev domain prompt to mention "preview URL". ([#2572](https://github.com/expo/eas-cli/pull/2572) by [@byCedric](https://github.com/byCedric)))
## [12.3.0](https://github.com/expo/eas-cli/releases/tag/v12.3.0) - 2024-09-09
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "12.4.1",
"version": "12.5.1",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+66 -62
View File
@@ -143,7 +143,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/account/login.ts)_
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -160,7 +160,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -177,7 +177,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/account/view.ts)_
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -191,7 +191,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -243,7 +243,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -264,7 +264,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -284,7 +284,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -304,7 +304,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -327,7 +327,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -364,7 +364,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/index.ts)_
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -383,7 +383,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -400,7 +400,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -419,7 +419,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/delete.ts)_
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:inspect`
@@ -454,7 +454,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -504,7 +504,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/list.ts)_
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -533,7 +533,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/resign.ts)_
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -559,7 +559,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli
```
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/run.ts)_
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -609,7 +609,7 @@ DESCRIPTION
get the latest version from EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/version/get.ts)_
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -628,7 +628,7 @@ DESCRIPTION
update version of an app
```
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/version/set.ts)_
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -647,7 +647,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers
```
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/version/sync.ts)_
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -664,7 +664,7 @@ DESCRIPTION
view a build for your project
```
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/build/view.ts)_
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -685,7 +685,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -707,7 +707,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -727,7 +727,7 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -760,7 +760,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally.
```
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/channel/rollout.ts)_
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -783,7 +783,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -804,7 +804,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -821,7 +821,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/credentials/index.ts)_
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -839,7 +839,7 @@ DESCRIPTION
Set up credentials for building your project.
```
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/credentials/configure-build.ts)_
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas device:create`
@@ -853,7 +853,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/device/create.ts)_
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -873,7 +873,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -894,7 +894,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/device/list.ts)_
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -915,7 +915,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/device/rename.ts)_
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -929,7 +929,7 @@ DESCRIPTION
view a device for your project
```
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/device/view.ts)_
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -943,7 +943,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -1051,7 +1051,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/metadata/lint.ts)_
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1068,7 +1068,7 @@ DESCRIPTION
generate the local store configuration from the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/metadata/pull.ts)_
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1085,7 +1085,7 @@ DESCRIPTION
sync the local store configuration to the app stores
```
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/metadata/push.ts)_
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1115,7 +1115,7 @@ DESCRIPTION
open the project page in a web browser
```
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1129,7 +1129,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/project/info.ts)_
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1152,7 +1152,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/project/init.ts)_
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1170,7 +1170,7 @@ ALIASES
$ eas onboarding
```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/project/onboarding.ts)_
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas secret:create`
@@ -1193,7 +1193,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -1211,7 +1211,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -1225,7 +1225,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/secret/list.ts)_
## `eas secret:push`
@@ -1245,7 +1245,7 @@ DESCRIPTION
read environment secrets from env file and store on the server
```
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/secret/push.ts)_
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/secret/push.ts)_
## `eas submit`
@@ -1276,7 +1276,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1310,7 +1310,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/update/index.ts)_
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1328,7 +1328,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1349,7 +1349,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -1371,7 +1371,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/update/list.ts)_
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -1379,14 +1379,18 @@ roll back to an existing update
```
USAGE
$ eas update:republish [--channel <value> | --branch <value> | --group <value>] [-m <value>] [-p android|ios|all]
[--private-key-path <value>] [--json --non-interactive]
$ eas update:republish [--channel <value> | --branch <value> | --group <value>] [--destination-channel <value> |
--destination-branch <value>] [-m <value>] [-p android|ios|all] [--private-key-path <value>] [--json
--non-interactive]
FLAGS
-m, --message=<value> Short message describing the republished update group
-p, --platform=(android|ios|all) [default: all]
--branch=<value> Branch name to select an update group to republish from
--channel=<value> Channel name to select an update group to republish from
--destination-branch=<value> Branch name to republish to if republishing to a different branch
--destination-channel=<value> Channel name to select a branch to republish to if republishing to a different
branch
--group=<value> Update group ID to republish
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
@@ -1399,7 +1403,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.4.1/packages/eas-cli/src/commands/update/republish.ts)_
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded`
@@ -1427,7 +1431,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.4.1/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -1447,7 +1451,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.4.1/packages/eas-cli/src/commands/update/rollback.ts)_
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -1467,7 +1471,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/update/view.ts)_
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -1488,7 +1492,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -1508,7 +1512,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -1526,7 +1530,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -1548,7 +1552,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1565,7 +1569,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v12.4.1/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v12.5.1/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
+1108 -156
View File
File diff suppressed because it is too large. Load diff
+6 -5
View File
@@ -1,7 +1,7 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "12.4.1",
"version": "12.5.1",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
@@ -13,8 +13,8 @@
"@expo/config": "8.5.4",
"@expo/config-plugins": "7.8.4",
"@expo/config-types": "50.0.0",
"@expo/eas-build-job": "1.0.133",
"@expo/eas-json": "12.0.0",
"@expo/eas-build-job": "1.0.136",
"@expo/eas-json": "12.5.0",
"@expo/env": "^0.3.0",
"@expo/json-file": "8.2.37",
"@expo/logger": "1.0.117",
@@ -29,7 +29,7 @@
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.0",
"@expo/steps": "1.0.134",
"@expo/steps": "1.0.136",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "^1.26.2",
"@oclif/plugin-autocomplete": "^2.3.10",
@@ -86,7 +86,8 @@
"turndown": "7.1.2",
"untildify": "4.0.0",
"uuid": "9.0.1",
"wrap-ansi": "7.0.0"
"wrap-ansi": "7.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@graphql-codegen/cli": "5.0.0",
@@ -13,14 +13,12 @@ function isEnvironment(env: string): env is EnvironmentVariableEnvironment {
}
export async function evaluateConfigWithEnvVarsAsync<Config extends { projectId: string }, Opts>({
flags,
buildProfile,
buildProfileName,
graphqlClient,
getProjectConfig,
opts,
}: {
flags: { environment?: string };
buildProfile: BuildProfile;
buildProfileName: string;
graphqlClient: ExpoGraphqlClient | null;
@@ -34,7 +32,6 @@ export async function evaluateConfigWithEnvVarsAsync<Config extends { projectId:
}
const { projectId } = await getProjectConfig({ env: buildProfile.env, ...opts });
const env = await resolveEnvVarsAsync({
flags,
buildProfile,
buildProfileName,
graphqlClient,
@@ -46,22 +43,18 @@ export async function evaluateConfigWithEnvVarsAsync<Config extends { projectId:
}
async function resolveEnvVarsAsync({
flags,
buildProfile,
buildProfileName,
graphqlClient,
projectId,
}: {
flags: { environment?: string };
buildProfile: BuildProfile;
buildProfileName: string;
graphqlClient: ExpoGraphqlClient;
projectId: string;
}): Promise<Env> {
const environment =
flags.environment ??
buildProfile.environment?.toUpperCase() ??
process.env.EAS_CURRENT_ENVIRONMENT;
buildProfile.environment?.toUpperCase() ?? process.env.EAS_CURRENT_ENVIRONMENT;
if (!environment || !isEnvironment(environment)) {
Log.log(
+19 -11
View File
@@ -3,6 +3,7 @@ import spawnAsync from '@expo/spawn-async';
import { ChildProcess } from 'child_process';
import semver from 'semver';
import Log from '../log';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
@@ -55,19 +56,26 @@ export async function runLocalBuildAsync(
};
process.on('SIGINT', interruptHandler);
try {
const mergedEnv = {
...env,
...process.env,
EAS_LOCAL_BUILD_WORKINGDIR: options.workingdir ?? process.env.EAS_LOCAL_BUILD_WORKINGDIR,
...(options.skipCleanup || options.skipNativeBuild
? { EAS_LOCAL_BUILD_SKIP_CLEANUP: '1' }
: {}),
...(options.skipNativeBuild ? { EAS_LOCAL_BUILD_SKIP_NATIVE_BUILD: '1' } : {}),
...(options.artifactsDir ? { EAS_LOCAL_BUILD_ARTIFACTS_DIR: options.artifactsDir } : {}),
...(options.artifactPath ? { EAS_LOCAL_BUILD_ARTIFACT_PATH: options.artifactPath } : {}),
};
// log command execution to assist in debugging local builds
Log.debug('Running local build, using local-build-plugin', {
command,
args,
env: mergedEnv,
});
const spawnPromise = spawnAsync(command, args, {
stdio: options.verbose ? 'inherit' : 'pipe',
env: {
...env,
...process.env,
EAS_LOCAL_BUILD_WORKINGDIR: options.workingdir ?? process.env.EAS_LOCAL_BUILD_WORKINGDIR,
...(options.skipCleanup || options.skipNativeBuild
? { EAS_LOCAL_BUILD_SKIP_CLEANUP: '1' }
: {}),
...(options.skipNativeBuild ? { EAS_LOCAL_BUILD_SKIP_NATIVE_BUILD: '1' } : {}),
...(options.artifactsDir ? { EAS_LOCAL_BUILD_ARTIFACTS_DIR: options.artifactsDir } : {}),
...(options.artifactPath ? { EAS_LOCAL_BUILD_ARTIFACT_PATH: options.artifactPath } : {}),
},
env: mergedEnv,
});
childProcess = spawnPromise.child;
await spawnPromise;
@@ -33,7 +33,6 @@ import {
BuildFragment,
BuildStatus,
BuildWithSubmissionsFragment,
EnvironmentVariableEnvironment,
SubmissionFragment,
} from '../graphql/generated';
import { BuildQuery } from '../graphql/queries/BuildQuery';
@@ -98,7 +97,6 @@ export interface BuildFlags {
buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean;
repack: boolean;
environment?: EnvironmentVariableEnvironment;
}
export async function runBuildAndSubmitAsync(
@@ -195,7 +193,6 @@ export async function runBuildAndSubmitAsync(
const platform = toAppPlatform(buildProfile.platform);
const { env } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile: buildProfile.profile,
buildProfileName: buildProfile.profileName,
graphqlClient,
@@ -38,21 +38,6 @@ export const EASEnvironmentFlag = {
}),
};
// NOTE: Used in build commands, should be replaced with EASEnvironmentFlag when
// the feature is public
export const EASEnvironmentFlagHidden = {
environment: Flags.enum<EnvironmentVariableEnvironment>({
description: "Environment variable's environment",
parse: upperCaseAsync,
hidden: true,
options: mapToLowercase([
EnvironmentVariableEnvironment.Development,
EnvironmentVariableEnvironment.Preview,
EnvironmentVariableEnvironment.Production,
]),
}),
};
export const EASVariableFormatFlag = {
format: Flags.enum({
description: 'Output format',
+2 -5
View File
@@ -10,8 +10,8 @@ import path from 'path';
import { LocalBuildMode } from '../../build/local';
import { BuildFlags, runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
import EasCommand from '../../commandUtils/EasCommand';
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EnvironmentVariableEnvironment, StatuspageServiceName } from '../../graphql/generated';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { StatuspageServiceName } from '../../graphql/generated';
import Log, { link } from '../../log';
import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform';
import { selectAsync } from '../../prompts';
@@ -37,7 +37,6 @@ interface RawBuildFlags {
'build-logger-level'?: LoggerLevel;
'freeze-credentials': boolean;
repack: boolean;
environment?: EnvironmentVariableEnvironment;
}
export default class Build extends EasCommand {
@@ -117,7 +116,6 @@ export default class Build extends EasCommand {
description: 'Use the golden dev client build repack flow as it works for onboarding',
}),
...EasNonInteractiveAndJsonFlags,
...EASEnvironmentFlagHidden,
};
static override contextDefinition = {
@@ -232,7 +230,6 @@ export default class Build extends EasCommand {
buildLoggerLevel: flags['build-logger-level'],
freezeCredentials: flags['freeze-credentials'],
repack: flags.repack,
environment: flags.environment,
};
}
@@ -13,7 +13,7 @@ import { listAndSelectBuildOnAppAsync } from '../../build/queries';
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EasPaginatedQueryFlags } from '../../commandUtils/pagination';
import { CredentialsContext } from '../../credentials/context';
import {
@@ -93,7 +93,6 @@ export default class BuildResign extends EasCommand {
}),
...EasPaginatedQueryFlags,
...EasNonInteractiveAndJsonFlags,
...EASEnvironmentFlagHidden,
};
static override contextDefinition = {
@@ -147,7 +146,6 @@ export default class BuildResign extends EasCommand {
);
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile,
buildProfileName: flags.targetProfile ?? 'production',
graphqlClient,
@@ -5,10 +5,7 @@ import chalk from 'chalk';
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
import EasCommand from '../../../commandUtils/EasCommand';
import {
EASEnvironmentFlagHidden,
EasNonInteractiveAndJsonFlags,
} from '../../../commandUtils/flags';
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
import Log from '../../../log';
@@ -35,7 +32,6 @@ export default class BuildVersionGetView extends EasCommand {
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
...EASEnvironmentFlagHidden,
...EasNonInteractiveAndJsonFlags,
};
@@ -80,7 +76,6 @@ export default class BuildVersionGetView extends EasCommand {
const results: { [key in Platform]?: string } = {};
for (const { profile, platform } of buildProfiles) {
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile: profile,
buildProfileName: flags.profile ?? 'production',
graphqlClient,
@@ -6,7 +6,6 @@ import chalk from 'chalk';
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
import EasCommand from '../../../commandUtils/EasCommand';
import { EASEnvironmentFlagHidden } from '../../../commandUtils/flags';
import { AppVersionMutation } from '../../../graphql/mutations/AppVersionMutation';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
@@ -37,7 +36,6 @@ export default class BuildVersionSetView extends EasCommand {
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
...EASEnvironmentFlagHidden,
};
static override contextDefinition = {
@@ -68,7 +66,6 @@ export default class BuildVersionSetView extends EasCommand {
);
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile: profile,
buildProfileName: flags.profile ?? 'production',
graphqlClient,
@@ -8,7 +8,6 @@ import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '.
import { evaluateConfigWithEnvVarsAsync } from '../../../build/evaluateConfigWithEnvVarsAsync';
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../../build/ios/version';
import EasCommand from '../../../commandUtils/EasCommand';
import { EASEnvironmentFlagHidden } from '../../../commandUtils/flags';
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
import Log from '../../../log';
@@ -56,7 +55,6 @@ export default class BuildVersionSyncView extends EasCommand {
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
helpValue: 'PROFILE_NAME',
}),
...EASEnvironmentFlagHidden,
};
static override contextDefinition = {
@@ -91,7 +89,6 @@ export default class BuildVersionSyncView extends EasCommand {
});
for (const profileInfo of buildProfiles) {
const { exp, projectId, env } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile: profileInfo.profile,
buildProfileName: profileInfo.profileName,
graphqlClient,
+1 -3
View File
@@ -7,7 +7,7 @@ import chalk from 'chalk';
import { evaluateConfigWithEnvVarsAsync } from '../build/evaluateConfigWithEnvVarsAsync';
import EasCommand from '../commandUtils/EasCommand';
import { createGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EASEnvironmentFlagHidden, EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { appPlatformEmojis } from '../platform';
@@ -29,7 +29,6 @@ export default class Config extends EasCommand {
'eas-json-only': Flags.boolean({
hidden: true,
}),
...EASEnvironmentFlagHidden,
...EasNonInteractiveAndJsonFlags,
};
@@ -94,7 +93,6 @@ export default class Config extends EasCommand {
});
const graphqlClient = createGraphqlClient(authenticationInfo);
const { exp: appConfig } = await evaluateConfigWithEnvVarsAsync({
flags,
buildProfile: profile,
buildProfileName: profileName,
graphqlClient,
+185
View File
@@ -0,0 +1,185 @@
import spawnAsync from '@expo/spawn-async';
import chalk from 'chalk';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EASEnvironmentFlag, EASNonInteractiveFlag } from '../../commandUtils/flags';
import { EnvironmentVariableEnvironment } from '../../graphql/generated';
import { EnvironmentVariablesQuery } from '../../graphql/queries/EnvironmentVariablesQuery';
import Log from '../../log';
import { promptVariableEnvironmentAsync } from '../../utils/prompts';
type ParsedFlags =
| {
nonInteractive: true;
environment: EnvironmentVariableEnvironment;
}
| {
nonInteractive: false;
environment?: EnvironmentVariableEnvironment;
};
interface RawFlags {
environment?: EnvironmentVariableEnvironment;
'non-interactive': boolean;
}
export default class EnvExec extends EasCommand {
static override description =
'execute a bash command with environment variables from the selected environment';
static override hidden = true;
static override contextDefinition = {
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
};
static override flags = {
...EASEnvironmentFlag,
...EASNonInteractiveFlag,
};
static override args = [
{
name: 'BASH_COMMAND',
required: true,
description: 'bash command to execute with the environment variables from the environment',
},
];
async runAsync(): Promise<void> {
const {
flags,
args: { BASH_COMMAND: command },
} = await this.parse(EnvExec);
const parsedFlags = this.sanitizeFlags(flags);
const {
privateProjectConfig: { projectId },
loggedIn: { graphqlClient },
} = await this.getContextAsync(EnvExec, {
nonInteractive: parsedFlags.nonInteractive,
});
const environment =
parsedFlags.environment ?? (await promptVariableEnvironmentAsync(parsedFlags.nonInteractive));
const environmentVariables = await this.loadEnvironmentVariablesAsync({
graphqlClient,
projectId,
environment,
});
await this.runCommandWithEnvVarsAsync({ command, environmentVariables });
}
private sanitizeFlags(rawFlags: RawFlags): ParsedFlags {
const environment = rawFlags.environment;
if (rawFlags['non-interactive']) {
if (!environment) {
throw new Error(
'You must specify an environment when running in non-interactive mode. Use the --environment flag.'
);
}
return {
nonInteractive: true,
environment,
};
}
return {
nonInteractive: false,
environment: rawFlags.environment,
};
}
private async runCommandWithEnvVarsAsync({
command,
environmentVariables,
}: {
command: string;
environmentVariables: Record<string, string>;
}): Promise<void> {
Log.log(`Running command: ${chalk.bold(command)}`);
const spawnPromise = spawnAsync('bash', ['-c', command], {
stdio: ['inherit', 'pipe', 'pipe'],
env: {
...process.env,
...environmentVariables,
},
});
const {
child: { stdout, stderr },
} = spawnPromise;
if (!stdout || !stderr) {
throw new Error(`Failed to spawn ${command}`);
}
stdout.on('data', data => {
for (const line of data.toString().trim().split('\n')) {
Log.log(`${chalk.gray('[stdout]')} ${line}`);
}
});
stderr.on('data', data => {
for (const line of data.toString().trim().split('\n')) {
Log.warn(`${chalk.gray('[stderr]')} ${line}`);
}
});
try {
await spawnPromise;
} catch (error) {
Log.error(`❌ ${chalk.bold(command)} failed`);
throw error;
}
}
private async loadEnvironmentVariablesAsync({
graphqlClient,
projectId,
environment,
}: {
graphqlClient: ExpoGraphqlClient;
projectId: string;
environment: EnvironmentVariableEnvironment;
}): Promise<Record<string, string>> {
const environmentVariablesQueryResult =
await EnvironmentVariablesQuery.byAppIdWithSensitiveAsync(graphqlClient, {
appId: projectId,
environment,
});
const secretEnvironmentVariables = environmentVariablesQueryResult.filter(
({ value }) => !value
);
if (secretEnvironmentVariables.length > 0) {
Log.warn(`The following environment variables are secret and cannot be downloaded locally:`);
for (const { name } of secretEnvironmentVariables) {
Log.warn(`- ${name}`);
}
Log.warn('Proceeding with the rest of the environment variables.');
Log.newLine();
}
const nonSecretEnvironmentVariables = environmentVariablesQueryResult.filter(
({ value }) => !!value
);
if (nonSecretEnvironmentVariables.length === 0) {
throw new Error('No readable environment variables found for the selected environment.');
}
Log.log(
`Loaded environment variables for the selected environment "${environment.toLowerCase()}":`
);
for (const { name } of nonSecretEnvironmentVariables) {
Log.log(`- ${name}`);
}
Log.newLine();
const environmentVariables: Record<string, string> = {};
for (const { name, value } of nonSecretEnvironmentVariables) {
if (value) {
environmentVariables[name] = value;
}
}
return environmentVariables;
}
}
@@ -0,0 +1,217 @@
import { Platform, SubmissionConfig } from '@expo/eas-build-job';
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import nullthrows from 'nullthrows';
import { z } from 'zod';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import {
AndroidSubmissionConfigInput,
IosSubmissionConfigInput,
SubmissionAndroidReleaseStatus,
SubmissionAndroidTrack,
} from '../../graphql/generated';
import { AppStoreConnectApiKeyQuery } from '../../graphql/queries/AppStoreConnectApiKeyQuery';
import { GoogleServiceAccountKeyQuery } from '../../graphql/queries/GoogleServiceAccountKeyQuery';
import AndroidSubmitCommand from '../../submit/android/AndroidSubmitCommand';
import { SubmissionContext, createSubmissionContextAsync } from '../../submit/context';
import IosSubmitCommand from '../../submit/ios/IosSubmitCommand';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import GitNoCommitClient from '../../vcs/clients/gitNoCommit';
/**
* This command will be run on the EAS workers.
* This command resolves credentials and other
* configuration, that normally would be included in the
* job and metadata objects, and prints them to stdout.
*/
export default class SubmitInternal extends EasCommand {
static override hidden = true;
static override flags = {
platform: Flags.enum<Platform>({
options: [Platform.ANDROID, Platform.IOS],
required: true,
}),
profile: Flags.string({
description:
'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
}),
id: Flags.string({
description: 'ID of the build to submit',
required: true,
}),
};
static override contextDefinition = {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.ProjectDir,
...this.ContextOptions.Analytics,
...this.ContextOptions.Vcs,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(SubmitInternal);
// This command is always run with implicit --non-interactive and --json options
enableJsonOutput();
const {
loggedIn: { actor, graphqlClient },
privateProjectConfig: { exp, projectId, projectDir },
analytics,
vcsClient,
} = await this.getContextAsync(SubmitInternal, {
nonInteractive: true,
vcsClientOverride: new GitNoCommitClient(),
});
const submissionProfile = await EasJsonUtils.getSubmitProfileAsync(
EasJsonAccessor.fromProjectPath(projectDir),
flags.platform,
flags.profile
);
const ctx = await createSubmissionContextAsync({
platform: flags.platform,
projectDir,
profile: submissionProfile,
archiveFlags: {
id: flags.id,
},
nonInteractive: true,
isVerboseFastlaneEnabled: false,
actor,
graphqlClient,
analytics,
exp,
projectId,
vcsClient,
specifiedProfile: flags.profile,
});
let config;
if (ctx.platform === Platform.IOS) {
const command = new IosSubmitCommand(ctx as SubmissionContext<Platform.IOS>);
const submitter = await command.runAsync();
const mutationInput = await submitter.getSubmissionInputAsync();
const iosConfig = mutationInput.submissionConfig;
const ascApiKeyJson = await getAppStoreConnectApiKeyJsonAsync({
iosConfig,
graphqlClient,
});
const configInput: z.input<typeof SubmissionConfig.Ios.SchemaZ> = {
ascAppIdentifier: iosConfig.ascAppIdentifier,
isVerboseFastlaneEnabled: iosConfig.isVerboseFastlaneEnabled ?? undefined,
...(ascApiKeyJson
? { ascApiJsonKey: ascApiKeyJson }
: {
appleIdUsername: nullthrows(iosConfig.appleIdUsername),
appleAppSpecificPassword: nullthrows(iosConfig.appleAppSpecificPassword),
}),
};
config = SubmissionConfig.Ios.SchemaZ.parse(configInput);
} else if (ctx.platform === Platform.ANDROID) {
const command = new AndroidSubmitCommand(ctx as SubmissionContext<Platform.ANDROID>);
const submitter = await command.runAsync();
const mutationInput = await submitter.getSubmissionInputAsync();
const androidConfig = mutationInput.submissionConfig;
const changesNotSentForReview = androidConfig.changesNotSentForReview ?? undefined;
const releaseStatus = androidConfig.releaseStatus
? graphQlReleaseStatusToConfigReleaseStatus[androidConfig.releaseStatus]
: undefined;
const googleServiceAccountKeyJson = nullthrows(
await getGoogleServiceAccountKeyJsonAsync({
androidConfig,
graphqlClient,
})
);
const track = graphQlTrackToConfigTrack[androidConfig.track];
const configInput: z.input<typeof SubmissionConfig.Android.SchemaZ> = {
changesNotSentForReview,
googleServiceAccountKeyJson,
track,
...(releaseStatus === SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS
? {
releaseStatus: SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS,
rollout: androidConfig.rollout ?? undefined,
}
: { releaseStatus }),
};
config = SubmissionConfig.Android.SchemaZ.parse(configInput);
} else {
throw new Error(`Unsupported platform: ${ctx.platform}`);
}
printJsonOnlyOutput({ config });
}
}
async function getGoogleServiceAccountKeyJsonAsync({
androidConfig,
graphqlClient,
}: {
androidConfig: AndroidSubmissionConfigInput;
graphqlClient: ExpoGraphqlClient;
}): Promise<string | null> {
if (androidConfig.googleServiceAccountKeyJson) {
return androidConfig.googleServiceAccountKeyJson;
} else if (androidConfig.googleServiceAccountKeyId) {
const key = await GoogleServiceAccountKeyQuery.getByIdAsync(
graphqlClient,
androidConfig.googleServiceAccountKeyId
);
return key.keyJson;
}
return null;
}
async function getAppStoreConnectApiKeyJsonAsync({
iosConfig,
graphqlClient,
}: {
iosConfig: IosSubmissionConfigInput;
graphqlClient: ExpoGraphqlClient;
}): Promise<string | null> {
if (iosConfig.ascApiKey) {
return JSON.stringify({
key_id: iosConfig.ascApiKey.keyIdentifier,
issuer_id: iosConfig.ascApiKey.issuerIdentifier,
key: iosConfig.ascApiKey.keyP8,
});
} else if (iosConfig.ascApiKeyId) {
const key = await AppStoreConnectApiKeyQuery.getByIdAsync(graphqlClient, iosConfig.ascApiKeyId);
return JSON.stringify({
key_id: key.keyIdentifier,
issuer_id: key.issuerIdentifier,
key: key.keyP8,
});
}
return null;
}
const graphQlReleaseStatusToConfigReleaseStatus = {
[SubmissionAndroidReleaseStatus.Draft]: SubmissionConfig.Android.ReleaseStatus.DRAFT,
[SubmissionAndroidReleaseStatus.InProgress]: SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS,
[SubmissionAndroidReleaseStatus.Completed]: SubmissionConfig.Android.ReleaseStatus.COMPLETED,
[SubmissionAndroidReleaseStatus.Halted]: SubmissionConfig.Android.ReleaseStatus.HALTED,
};
const graphQlTrackToConfigTrack = {
[SubmissionAndroidTrack.Production]: SubmissionConfig.Android.ReleaseTrack.PRODUCTION,
[SubmissionAndroidTrack.Beta]: SubmissionConfig.Android.ReleaseTrack.BETA,
[SubmissionAndroidTrack.Alpha]: SubmissionConfig.Android.ReleaseTrack.ALPHA,
[SubmissionAndroidTrack.Internal]: SubmissionConfig.Android.ReleaseTrack.INTERNAL,
};
@@ -21,7 +21,7 @@ import { UpdateFragment } from '../../../graphql/generated';
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { collectAssetsAsync, uploadAssetsAsync } from '../../../project/publish';
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
import { resolveVcsClient } from '../../../vcs';
const projectRoot = '/test-project';
@@ -47,7 +47,7 @@ jest.mock('@expo/config-plugins');
jest.mock('../../../branch/queries');
jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync');
jest.mock('../../../update/configure');
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
jest.mock('../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync');
jest.mock('../../../graphql/mutations/PublishMutation');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../graphql/queries/UpdateQuery');
@@ -106,7 +106,9 @@ describe(UpdatePublish.name, () => {
const { projectId } = mockTestProject();
const { platforms, runtimeVersion } = mockTestExport();
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
jest
.mocked(getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync)
.mockResolvedValue({ branchId: 'branch123', branchName: 'branchFromChannel' });
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
branchId: 'branch123',
createdBranch: false,
@@ -11,8 +11,9 @@ import { jester } from '../../../credentials/__tests__/fixtures-constants';
import { UpdateFragment } from '../../../graphql/generated';
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { BranchQuery } from '../../../graphql/queries/BranchQuery';
import { UpdateQuery } from '../../../graphql/queries/UpdateQuery';
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
import { selectUpdateGroupOnBranchAsync } from '../../../update/queries';
import {
getCodeSigningInfoAsync,
@@ -44,7 +45,8 @@ jest.mock('../../../commandUtils/context/contextUtils/getProjectIdAsync');
jest.mock('../../../graphql/mutations/PublishMutation');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../graphql/queries/UpdateQuery');
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
jest.mock('../../../graphql/queries/BranchQuery');
jest.mock('../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync');
jest.mock('../../../update/queries');
jest.mock('../../../ora', () => ({
ora: () => ({
@@ -57,6 +59,7 @@ jest.mock('../../../fetch');
describe(UpdateRepublish.name, () => {
afterEach(() => {
vol.reset();
jest.resetAllMocks();
});
it('errors when providing both --group and --branch', async () => {
@@ -89,6 +92,16 @@ describe(UpdateRepublish.name, () => {
);
});
it('errors when providing both --destination-channel and --destination-branch', async () => {
const flags = ['--group=1234', '--destination-channel=test', '--destination-branch=wat'];
mockTestProject();
await expect(new UpdateRepublish(flags, commandOptions).run()).rejects.toThrow(
/--destination-branch=wat cannot also be provided when using --destination-channel/
);
});
it('errors when update group is empty', async () => {
const flags = ['--group=1234'];
@@ -271,7 +284,9 @@ describe(UpdateRepublish.name, () => {
mockTestProject();
// Mock resolving the channel to branch name, only valid for a single branch connected
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
jest
.mocked(getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync)
.mockResolvedValue({ branchId: updateStub.branch.id, branchName: 'branchFromChannel' });
// Mock the prompt to ask the user which update to republish, from branch
jest.mocked(selectUpdateGroupOnBranchAsync).mockResolvedValue([updateStub]);
// Mock mutations to store the new update
@@ -306,6 +321,94 @@ describe(UpdateRepublish.name, () => {
])
);
});
it('republishes to a different branch when --destination-branch is supplied', async () => {
const flags = ['--branch=branch123', '--message=test-republish', '--destination-branch=other'];
mockTestProject();
// Mock the prompt to ask the user which update to republish, from branch
jest.mocked(selectUpdateGroupOnBranchAsync).mockResolvedValue([updateStub]);
// Mock mutations to store the new update
jest.mocked(PublishMutation.publishUpdateGroupAsync).mockResolvedValue([
{
...updateStub,
branch: { id: 'other-id', name: 'other' },
id: 'update-new',
platform: 'ios',
manifestPermalink: 'https://expo.dev/@test/test-project/manifest',
},
]);
jest.mocked(BranchQuery.getBranchByNameAsync).mockResolvedValue({
id: 'other-id',
name: 'other',
});
await new UpdateRepublish(flags, commandOptions).run();
expect(selectUpdateGroupOnBranchAsync).toHaveBeenCalledWith(
expect.any(Object), // graphql client
expect.objectContaining({ branchName: 'branch123' })
);
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalledWith(
expect.any(Object), // graphql client
expect.arrayContaining([
expect.objectContaining({
branchId: 'other-id',
runtimeVersion: updateStub.runtimeVersion,
updateInfoGroup: expect.objectContaining({
ios: expect.any(Object),
}),
gitCommitHash: updateStub.gitCommitHash,
awaitingCodeSigningInfo: false,
}),
])
);
});
it('republishes to a different branch when --destination-channel is supplied', async () => {
const flags = ['--branch=branch123', '--message=test-republish', '--destination-channel=blah'];
mockTestProject();
// Mock the prompt to ask the user which update to republish, from branch
jest.mocked(selectUpdateGroupOnBranchAsync).mockResolvedValue([updateStub]);
// Mock mutations to store the new update
jest.mocked(PublishMutation.publishUpdateGroupAsync).mockResolvedValue([
{
...updateStub,
branch: { id: 'other-id', name: 'other' },
id: 'update-new',
platform: 'ios',
manifestPermalink: 'https://expo.dev/@test/test-project/manifest',
},
]);
jest
.mocked(getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync)
.mockResolvedValue({ branchId: 'blah-id', branchName: 'blah' });
await new UpdateRepublish(flags, commandOptions).run();
expect(selectUpdateGroupOnBranchAsync).toHaveBeenCalledWith(
expect.any(Object), // graphql client
expect.objectContaining({ branchName: 'branch123' })
);
expect(PublishMutation.publishUpdateGroupAsync).toHaveBeenCalledWith(
expect.any(Object), // graphql client
expect.arrayContaining([
expect.objectContaining({
branchId: 'blah-id',
runtimeVersion: updateStub.runtimeVersion,
updateInfoGroup: expect.objectContaining({
ios: expect.any(Object),
}),
gitCommitHash: updateStub.gitCommitHash,
awaitingCodeSigningInfo: false,
}),
])
);
});
});
/** Create a new in-memory project, based on src/commands/project/__tests__/init.test.ts */
@@ -19,7 +19,7 @@ import { jester } from '../../../credentials/__tests__/fixtures-constants';
import { UpdateFragment } from '../../../graphql/generated';
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
import { AppQuery } from '../../../graphql/queries/AppQuery';
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
import { resolveVcsClient } from '../../../vcs';
import UpdateRollBackToEmbedded from '../roll-back-to-embedded';
@@ -50,7 +50,7 @@ jest.mock('../../../project/projectUtils', () => ({
enforceRollBackToEmbeddedUpdateSupportAsync: jest.fn(),
}));
jest.mock('../../../update/configure');
jest.mock('../../../update/getBranchNameFromChannelNameAsync');
jest.mock('../../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync');
jest.mock('../../../graphql/mutations/PublishMutation');
jest.mock('../../../graphql/queries/AppQuery');
jest.mock('../../../graphql/queries/UpdateQuery');
@@ -112,7 +112,9 @@ describe(UpdateRollBackToEmbedded.name, () => {
const runtimeVersion = 'exposdk:47.0.0';
jest.mocked(Updates.getRuntimeVersionAsync).mockResolvedValue(runtimeVersion);
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
jest
.mocked(getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync)
.mockResolvedValue({ branchId: updateStub.branch.id, branchName: 'branchFromChannel' });
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
branchId: 'branch123',
createdBranch: false,
@@ -7,10 +7,11 @@ import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import { BranchQuery } from '../../graphql/queries/BranchQuery';
import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { getBranchNameFromChannelNameAsync } from '../../update/getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
import { selectUpdateGroupOnBranchAsync } from '../../update/queries';
import { UpdateToRepublish, republishAsync } from '../../update/republish';
import { truncateString as truncateUpdateMessage } from '../../update/utils';
@@ -22,6 +23,8 @@ const defaultRepublishPlatforms: Platform[] = ['android', 'ios'];
type UpdateRepublishRawFlags = {
branch?: string;
channel?: string;
'destination-channel'?: string;
'destination-branch'?: string;
group?: string;
message?: string;
platform: string;
@@ -33,6 +36,8 @@ type UpdateRepublishRawFlags = {
type UpdateRepublishFlags = {
branchName?: string;
channelName?: string;
destinationChannelName?: string;
destinationBranchName?: string;
groupId?: string;
updateMessage?: string;
platform: Platform[];
@@ -57,6 +62,15 @@ export default class UpdateRepublish extends EasCommand {
description: 'Update group ID to republish',
exclusive: ['branch', 'channel'],
}),
'destination-channel': Flags.string({
description:
'Channel name to select a branch to republish to if republishing to a different branch',
exclusive: ['destination-branch'],
}),
'destination-branch': Flags.string({
description: 'Branch name to republish to if republishing to a different branch',
exclusive: ['destination-channel'],
}),
message: Flags.string({
char: 'm',
description: 'Short message describing the republished update group',
@@ -130,13 +144,21 @@ export default class UpdateRepublish extends EasCommand {
);
}
const updateMessage = await getOrAskUpdateMessageAsync(updatesToPublish, flags);
const arbitraryUpdate = updatesToPublish[0];
const targetBranch = await getOrAskTargetBranchAsync(
graphqlClient,
projectId,
flags,
arbitraryUpdate
);
const updateMessage = await getOrAskUpdateMessageAsync(updatesToPublish, flags);
await republishAsync({
graphqlClient,
app: { exp, projectId },
updatesToPublish,
targetBranch: { branchId: arbitraryUpdate.branchId, branchName: arbitraryUpdate.branchName },
targetBranch,
updateMessage,
codeSigningInfo,
json: flags.json,
@@ -147,6 +169,8 @@ export default class UpdateRepublish extends EasCommand {
const branchName = rawFlags.branch;
const channelName = rawFlags.channel;
const groupId = rawFlags.group;
const destinationChannelName = rawFlags['destination-channel'];
const destinationBranchName = rawFlags['destination-branch'];
const nonInteractive = rawFlags['non-interactive'];
const privateKeyPath = rawFlags['private-key-path'];
@@ -160,6 +184,8 @@ export default class UpdateRepublish extends EasCommand {
return {
branchName,
channelName,
destinationChannelName,
destinationBranchName,
groupId,
platform,
updateMessage: rawFlags.message,
@@ -170,6 +196,34 @@ export default class UpdateRepublish extends EasCommand {
}
}
async function getOrAskTargetBranchAsync(
graphqlClient: ExpoGraphqlClient,
projectId: string,
flags: UpdateRepublishFlags,
arbitraryUpdate: UpdateToRepublish
): Promise<{ branchId: string; branchName: string }> {
// if branch name supplied, use that
if (flags.destinationBranchName) {
const branch = await BranchQuery.getBranchByNameAsync(graphqlClient, {
appId: projectId,
name: flags.destinationBranchName,
});
return { branchId: branch.id, branchName: branch.name };
}
// if provided channel name but was non-interactive
if (flags.destinationChannelName) {
return await getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
projectId,
flags.destinationChannelName
);
}
// if neither provided, assume republish on same branch
return { branchId: arbitraryUpdate.branchId, branchName: arbitraryUpdate.branchName };
}
/** Retrieve the update group from either the update group id, or select from branch name. */
async function getOrAskUpdatesAsync(
graphqlClient: ExpoGraphqlClient,
@@ -293,7 +347,11 @@ async function askUpdatesFromChannelNameAsync(
nonInteractive,
}: { projectId: string; channelName: string; json: boolean; nonInteractive: boolean }
): Promise<UpdateToRepublish[]> {
const branchName = await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelName);
const { branchName } = await getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
projectId,
channelName
);
return await askUpdatesFromBranchNameAsync(graphqlClient, {
projectId,
+18 -19
View File
@@ -6,10 +6,7 @@ import * as path from 'node:path';
import EasCommand from '../../commandUtils/EasCommand';
import { EASEnvironmentFlag, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import {
EnvironmentVariableEnvironment,
WorkerDeploymentAliasFragment,
} from '../../graphql/generated';
import { EnvironmentVariableEnvironment } from '../../graphql/generated';
import Log from '../../log';
import { ora } from '../../ora';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
@@ -110,10 +107,8 @@ export default class WorkerDeploy extends EasCommand {
projectDir,
} = await this.getContextAsync(WorkerDeploy, flags);
const [{ projectId }, projectDist] = await Promise.all([
getDynamicPrivateProjectConfigAsync(),
resolveExportedProjectAsync(flags, projectDir),
]);
const projectDist = await resolveExportedProjectAsync(flags, projectDir);
const { projectId } = await getDynamicPrivateProjectConfigAsync();
logExportedProjectInfo(projectDist);
@@ -170,6 +165,10 @@ export default class WorkerDeploy extends EasCommand {
// TODO(@kitten): Batch and upload multiple files in parallel
const uploadParams: UploadParams[] = [];
const assetPath = projectDist.type === 'server' ? projectDist.clientPath : projectDist.path;
if (!assetPath) {
return;
}
for await (const asset of WorkerAssets.listAssetMapFilesAsync(assetPath, assetMap)) {
const uploadURL = uploads[asset.normalizedPath];
if (uploadURL) {
@@ -313,7 +312,7 @@ export default class WorkerDeploy extends EasCommand {
deploymentIdentifier: deployResult.id,
url: getDeploymentUrlFromFullName(deployResult.fullName),
},
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
aliases: [deploymentAlias],
production: deploymentProdAlias,
})
);
@@ -330,7 +329,7 @@ export default class WorkerDeploy extends EasCommand {
deploymentIdentifier: deployResult.id,
url: getDeploymentUrlFromFullName(deployResult.fullName),
},
aliases: [deploymentAlias].filter(Boolean) as WorkerDeploymentAliasFragment[],
aliases: [deploymentAlias],
production: deploymentProdAlias,
})
);
@@ -364,36 +363,36 @@ async function resolveExportedProjectAsync(
modifiedAt: Date | null;
path: string;
serverPath: string;
clientPath: string;
clientPath?: string;
}
> {
const exportPath = path.join(projectDir, flags.exportDir);
const serverPath = path.join(exportPath, 'server');
const clientPath = path.join(exportPath, 'client');
const [hasServerPath, hasClientPath, exportStat] = await Promise.all([
isDirectory(serverPath),
isDirectory(clientPath),
const [exportDirStat, expoRoutesStat, hasClientDir] = await Promise.all([
fs.promises.stat(exportPath).catch(() => null),
fs.promises.stat(path.join(serverPath, '_expo/routes.json')).catch(() => null),
isDirectory(clientPath),
]);
if (!exportStat?.isDirectory()) {
if (!exportDirStat?.isDirectory()) {
throw new Error(
`No "${flags.exportDir}/" folder found. Prepare your project for deployment with "npx expo export --platform web"`
);
}
if (hasServerPath && hasClientPath) {
if (expoRoutesStat?.isFile()) {
return {
type: 'server',
path: exportPath,
modifiedAt: exportStat.mtime,
modifiedAt: exportDirStat.mtime,
serverPath,
clientPath,
clientPath: hasClientDir ? clientPath : undefined,
};
}
return { type: 'static', path: exportPath, modifiedAt: exportStat.mtime };
return { type: 'static', path: exportPath, modifiedAt: exportDirStat.mtime };
}
function logExportedProjectInfo(
@@ -36,6 +36,7 @@ export enum AndroidActionType {
UpdateCredentialsJson,
SetUpBuildCredentialsFromCredentialsJson,
SetUpBuildCredentials,
Exit,
}
export enum IosActionType {
@@ -60,4 +61,5 @@ export enum IosActionType {
UseExistingAscApiKeyForSubmissions,
CreateAscApiKeyForSubmissions,
RemoveAscApiKey,
Exit,
}
@@ -26,6 +26,11 @@ export const highLevelActions: ActionInfo[] = [
title: 'Go back',
scope: Scope.Manager,
},
{
value: AndroidActionType.Exit,
title: 'Exit',
scope: Scope.Manager,
},
];
export const credentialsJsonActions: ActionInfo[] = [
@@ -27,6 +27,11 @@ export const highLevelActions: ActionInfo[] = [
title: 'Go back',
scope: Scope.Manager,
},
{
value: IosActionType.Exit,
title: 'Exit',
scope: Scope.Manager,
},
];
export const credentialsJsonActions: ActionInfo[] = [
@@ -145,6 +145,8 @@ export class ManageAndroid {
} else if (chosenAction === AndroidActionType.GoBackToCaller) {
await this.callingAction.runAsync(ctx);
return;
} else if (chosenAction === AndroidActionType.Exit) {
return;
}
}
@@ -154,6 +154,8 @@ export class ManageIos {
} else if (chosenAction === IosActionType.GoBackToCaller) {
await this.callingAction.runAsync(ctx);
return;
} else if (chosenAction === IosActionType.Exit) {
return;
}
} else if (actionInfo.scope === Scope.Project) {
assert(
+177 -34
View File
@@ -1619,15 +1619,15 @@ export type AppWorkerDeploymentsArgs = {
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppWorkerDeploymentsCrashArgs = {
crashId: Scalars['ID']['input'];
crashKey: Scalars['ID']['input'];
sampleFor?: InputMaybe<CrashSampleFor>;
};
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppWorkerDeploymentsCrashesArgs = {
limit?: Scalars['Int']['input'];
timespan: CrashesTimespan;
filters?: InputMaybe<CrashesFilters>;
timespan: DatasetTimespan;
};
@@ -1640,7 +1640,7 @@ export type AppWorkerDeploymentsRequestArgs = {
/** Represents an Exponent App (or Experience in legacy terms) */
export type AppWorkerDeploymentsRequestsArgs = {
filters?: InputMaybe<RequestsFilters>;
timespan: RequestsTimespan;
timespan: DatasetTimespan;
};
export type AppBranchEdge = {
@@ -1973,6 +1973,16 @@ export type AppStoreConnectApiKeyMutationUpdateAppStoreConnectApiKeyArgs = {
id: Scalars['ID']['input'];
};
export type AppStoreConnectApiKeyQuery = {
__typename?: 'AppStoreConnectApiKeyQuery';
byId: AppStoreConnectApiKey;
};
export type AppStoreConnectApiKeyQueryByIdArgs = {
id: Scalars['ID']['input'];
};
export type AppStoreConnectApiKeyUpdateInput = {
appleTeamId?: InputMaybe<Scalars['ID']['input']>;
};
@@ -3244,9 +3254,8 @@ export enum CrashSampleFor {
Oldest = 'OLDEST'
}
export type CrashesTimespan = {
end: Scalars['DateTime']['input'];
start?: InputMaybe<Scalars['DateTime']['input']>;
export type CrashesFilters = {
name?: InputMaybe<Array<Scalars['String']['input']>>;
};
export type CreateAccessTokenInput = {
@@ -3366,12 +3375,24 @@ export type CreateSubmissionResult = {
submission: Submission;
};
export type CumulativeMetrics = {
__typename?: 'CumulativeMetrics';
/** Totals from the beginning of time till now */
totals: CumulativeMetricsTotals;
};
export type CumulativeMetricsOverTimeData = {
__typename?: 'CumulativeMetricsOverTimeData';
data: LineChartData;
metricsAtLastTimestamp: Array<LineDatapoint>;
};
export type CumulativeMetricsTotals = {
__typename?: 'CumulativeMetricsTotals';
totalFailedInstalls: Scalars['Int']['output'];
totalInstalls: Scalars['Int']['output'];
};
export type CustomBuildConfigInput = {
path: Scalars['String']['input'];
};
@@ -3430,6 +3451,11 @@ export enum CustomDomainStatus {
TimedOut = 'TIMED_OUT'
}
export type DatasetTimespan = {
end: Scalars['DateTime']['input'];
start: Scalars['DateTime']['input'];
};
export type DeleteAccessTokenResult = {
__typename?: 'DeleteAccessTokenResult';
id: Scalars['ID']['output'];
@@ -3553,6 +3579,12 @@ export type DeleteWebhookResult = {
id: Scalars['ID']['output'];
};
export type DeleteWorkerDeploymentResult = {
__typename?: 'DeleteWorkerDeploymentResult';
deploymentIdentifier: Scalars['WorkerDeploymentIdentifier']['output'];
id: Scalars['ID']['output'];
};
export type DeployServerlessFunctionResult = {
__typename?: 'DeployServerlessFunctionResult';
url: Scalars['String']['output'];
@@ -3561,6 +3593,7 @@ export type DeployServerlessFunctionResult = {
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type Deployment = {
__typename?: 'Deployment';
buildCount: Scalars['Int']['output'];
builds: DeploymentBuildsConnection;
channel: UpdateChannel;
id: Scalars['ID']['output'];
@@ -3572,6 +3605,12 @@ export type Deployment = {
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentBuildCountArgs = {
statuses?: InputMaybe<Array<BuildStatus>>;
};
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
export type DeploymentBuildsArgs = {
after?: InputMaybe<Scalars['String']['input']>;
@@ -3664,6 +3703,13 @@ export type DeploymentQueryByIdArgs = {
deploymentId: Scalars['ID']['input'];
};
export type DeploymentResult = {
__typename?: 'DeploymentResult';
data?: Maybe<UpdateDeploymentsConnection>;
error?: Maybe<Scalars['String']['output']>;
success: Scalars['Boolean']['output'];
};
export type DeploymentSignedUrlResult = {
__typename?: 'DeploymentSignedUrlResult';
deploymentIdentifier: Scalars['ID']['output'];
@@ -3684,6 +3730,7 @@ export type DeploymentsMutation = {
/** Create a signed deployment URL */
createSignedDeploymentUrl: DeploymentSignedUrlResult;
deleteAlias: DeleteAliasResult;
deleteWorkerDeployment: DeleteWorkerDeploymentResult;
};
@@ -3705,6 +3752,11 @@ export type DeploymentsMutationDeleteAliasArgs = {
appId: Scalars['ID']['input'];
};
export type DeploymentsMutationDeleteWorkerDeploymentArgs = {
workerDeploymentId: Scalars['ID']['input'];
};
export type DiscordUser = {
__typename?: 'DiscordUser';
discordIdentifier: Scalars['String']['output'];
@@ -4415,6 +4467,7 @@ export type GoogleServiceAccountKey = {
clientIdentifier: Scalars['String']['output'];
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
keyJson: Scalars['String']['output'];
privateKeyIdentifier: Scalars['String']['output'];
projectIdentifier: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
@@ -4443,6 +4496,16 @@ export type GoogleServiceAccountKeyMutationDeleteGoogleServiceAccountKeyArgs = {
id: Scalars['ID']['input'];
};
export type GoogleServiceAccountKeyQuery = {
__typename?: 'GoogleServiceAccountKeyQuery';
byId: GoogleServiceAccountKey;
};
export type GoogleServiceAccountKeyQueryByIdArgs = {
id: Scalars['ID']['input'];
};
/**
* The value field is always sent from the client as a string,
* and then it's parsed server-side according to the filterType
@@ -5359,11 +5422,6 @@ export type RequestsFilters = {
statusType?: InputMaybe<Array<ResponseStatusType>>;
};
export type RequestsTimespan = {
end: Scalars['DateTime']['input'];
start: Scalars['DateTime']['input'];
};
export type RescindUserInvitationResult = {
__typename?: 'RescindUserInvitationResult';
id: Scalars['ID']['output'];
@@ -5570,6 +5628,7 @@ export type RootMutation = {
webhook: WebhookMutation;
/** Mutations that modify a websiteNotification */
websiteNotifications: WebsiteNotificationMutation;
workflowJob: WorkflowJobMutation;
};
@@ -5614,6 +5673,8 @@ export type RootQuery = {
* @deprecated Use 'byId' field under 'app'.
*/
appByAppId?: Maybe<App>;
/** Top-level query object for querying App Store Connect API Keys. */
appStoreConnectApiKey: AppStoreConnectApiKeyQuery;
/** Top-level query object for querying Apple Device registration requests. */
appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestQuery;
/** Top-level query object for querying Apple Teams. */
@@ -5637,6 +5698,8 @@ export type RootQuery = {
experimentation: ExperimentationQuery;
/** Top-level query object for querying GitHub App information and resources it has access to. */
githubApp: GitHubAppQuery;
/** Top-level query object for querying Google Service Account Keys. */
googleServiceAccountKey: GoogleServiceAccountKeyQuery;
/** Top-level query object for querying Stripe Invoices. */
invoice: InvoiceQuery;
jobRun: JobRunQuery;
@@ -6353,7 +6416,7 @@ export type Update = ActivityTimelineProjectActivity & {
branchId: Scalars['ID']['output'];
codeSigningInfo?: Maybe<CodeSigningInfo>;
createdAt: Scalars['DateTime']['output'];
deployments: UpdateDeploymentsConnection;
deployments: DeploymentResult;
expoGoSDKVersion?: Maybe<Scalars['String']['output']>;
gitCommitHash?: Maybe<Scalars['String']['output']>;
group: Scalars['String']['output'];
@@ -6463,6 +6526,7 @@ export type UpdateChannel = {
branchMapping: Scalars['String']['output'];
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
isPaused: Scalars['Boolean']['output'];
name: Scalars['String']['output'];
updateBranches: Array<UpdateBranch>;
updatedAt: Scalars['DateTime']['output'];
@@ -6492,6 +6556,10 @@ export type UpdateChannelMutation = {
* stringified JSON supplied to the mutation as a variable.
*/
editUpdateChannel: UpdateChannel;
/** Pause updates for an EAS channel. */
pauseUpdateChannel: UpdateChannel;
/** Resume updates for an EAS channel. */
resumeUpdateChannel: UpdateChannel;
};
@@ -6512,6 +6580,16 @@ export type UpdateChannelMutationEditUpdateChannelArgs = {
channelId: Scalars['ID']['input'];
};
export type UpdateChannelMutationPauseUpdateChannelArgs = {
channelId: Scalars['ID']['input'];
};
export type UpdateChannelMutationResumeUpdateChannelArgs = {
channelId: Scalars['ID']['input'];
};
export type UpdateDeploymentEdge = {
__typename?: 'UpdateDeploymentEdge';
cursor: Scalars['String']['output'];
@@ -6555,6 +6633,7 @@ export type UpdateInfoGroup = {
export type UpdateInsights = {
__typename?: 'UpdateInsights';
cumulativeMetrics: CumulativeMetrics;
cumulativeMetricsOverTime: CumulativeMetricsOverTimeData;
id: Scalars['ID']['output'];
totalUniqueUsers: Scalars['Int']['output'];
@@ -7088,6 +7167,7 @@ export enum UserEntityTypeName {
Password = 'Password',
SsoUser = 'SSOUser',
User = 'User',
UserPermission = 'UserPermission',
UserSecondFactorBackupCodes = 'UserSecondFactorBackupCodes',
UserSecondFactorDevice = 'UserSecondFactorDevice'
}
@@ -7390,6 +7470,7 @@ export type WorkerCustomDomain = {
export type WorkerDeployment = {
__typename?: 'WorkerDeployment';
aliases?: Maybe<Array<WorkerDeploymentAlias>>;
crashes?: Maybe<WorkerDeploymentCrashes>;
createdAt: Scalars['DateTime']['output'];
deploymentDomain: Scalars['String']['output'];
deploymentIdentifier: Scalars['WorkerDeploymentIdentifier']['output'];
@@ -7403,6 +7484,12 @@ export type WorkerDeployment = {
};
export type WorkerDeploymentCrashesArgs = {
filters?: InputMaybe<CrashesFilters>;
timespan: DatasetTimespan;
};
export type WorkerDeploymentLogsArgs = {
limit?: InputMaybe<Scalars['Int']['input']>;
timespan: LogsTimespan;
@@ -7411,7 +7498,7 @@ export type WorkerDeploymentLogsArgs = {
export type WorkerDeploymentRequestsArgs = {
filters?: InputMaybe<RequestsFilters>;
timespan: RequestsTimespan;
timespan: DatasetTimespan;
};
export type WorkerDeploymentAlias = {
@@ -7442,33 +7529,65 @@ export type WorkerDeploymentAliasesConnection = {
export type WorkerDeploymentCrashEdge = {
__typename?: 'WorkerDeploymentCrashEdge';
logs: Array<WorkerDeploymentLogNode>;
node: WorkerDeploymentCrashNode;
request?: Maybe<WorkerDeploymentRequestNode>;
sample: WorkerDeploymentCrashSample;
};
export type WorkerDeploymentCrashNode = {
__typename?: 'WorkerDeploymentCrashNode';
id: Scalars['ID']['output'];
minOccurrences: Scalars['Int']['output'];
mostRecentlyOccurredAt: Scalars['DateTime']['output'];
oldestOccurredAt: Scalars['DateTime']['output'];
sample: WorkerDeploymentCrashSample;
};
export type WorkerDeploymentCrashSample = {
__typename?: 'WorkerDeploymentCrashSample';
crashHash: Scalars['ID']['output'];
crashTimestamp: Scalars['DateTime']['output'];
deploymentIdentifier: Scalars['String']['output'];
firstStackLine?: Maybe<Scalars['String']['output']>;
key: Scalars['ID']['output'];
message: Scalars['String']['output'];
name: Scalars['String']['output'];
requestTimestamp: Scalars['DateTime']['output'];
scriptName: Scalars['String']['output'];
stack?: Maybe<Array<Scalars['String']['output']>>;
timestamp: Scalars['DateTime']['output'];
};
export type WorkerDeploymentCrashes = {
__typename?: 'WorkerDeploymentCrashes';
minRowsWithoutLimit?: Maybe<Scalars['Int']['output']>;
byCrashHash: Array<WorkerDeploymentCrashesHashEdge>;
byName: Array<WorkerDeploymentCrashesNameEdge>;
interval: Scalars['Int']['output'];
minRowsWithoutLimit: Scalars['Int']['output'];
nodes: Array<WorkerDeploymentCrashNode>;
summary: WorkerDeploymentCrashesAggregationNode;
timeseries: Array<WorkerDeploymentCrashesTimeseriesEdge>;
};
export type WorkerDeploymentCrashesAggregationNode = {
__typename?: 'WorkerDeploymentCrashesAggregationNode';
crashesPerMs?: Maybe<Scalars['Float']['output']>;
crashesSum: Scalars['Int']['output'];
distinctCrashes: Scalars['Int']['output'];
firstOccurredAt: Scalars['DateTime']['output'];
mostRecentlyOccurredAt: Scalars['DateTime']['output'];
sampleRate: Scalars['Float']['output'];
};
export type WorkerDeploymentCrashesHashEdge = {
__typename?: 'WorkerDeploymentCrashesHashEdge';
crashHash: Scalars['ID']['output'];
node: WorkerDeploymentCrashesAggregationNode;
sample: WorkerDeploymentCrashNode;
timeseries: Array<WorkerDeploymentCrashesTimeseriesEdge>;
};
export type WorkerDeploymentCrashesNameEdge = {
__typename?: 'WorkerDeploymentCrashesNameEdge';
name: Scalars['String']['output'];
node: WorkerDeploymentCrashesAggregationNode;
sample: WorkerDeploymentCrashNode;
timeseries: Array<WorkerDeploymentCrashesTimeseriesEdge>;
};
export type WorkerDeploymentCrashesTimeseriesEdge = {
__typename?: 'WorkerDeploymentCrashesTimeseriesEdge';
node?: Maybe<WorkerDeploymentCrashesAggregationNode>;
timestamp: Scalars['DateTime']['output'];
};
export type WorkerDeploymentEdge = {
@@ -7511,7 +7630,7 @@ export type WorkerDeploymentQueryByIdArgs = {
export type WorkerDeploymentRequestEdge = {
__typename?: 'WorkerDeploymentRequestEdge';
crash?: Maybe<WorkerDeploymentCrashSample>;
crash?: Maybe<WorkerDeploymentCrashNode>;
logs: Array<WorkerDeploymentLogNode>;
node: WorkerDeploymentRequestNode;
};
@@ -7664,6 +7783,16 @@ export enum WorkerLoggerLevel {
Warn = 'WARN'
}
export type WorkflowJobMutation = {
__typename?: 'WorkflowJobMutation';
approveWorkflowJob: Scalars['ID']['output'];
};
export type WorkflowJobMutationApproveWorkflowJobArgs = {
workflowJobId: Scalars['ID']['input'];
};
export type DeleteAndroidAppBuildCredentialsResult = {
__typename?: 'deleteAndroidAppBuildCredentialsResult';
id: Scalars['ID']['output'];
@@ -8342,7 +8471,7 @@ export type CreateAndroidSubmissionMutationVariables = Exact<{
}>;
export type CreateAndroidSubmissionMutation = { __typename?: 'RootMutation', submission: { __typename?: 'SubmissionMutation', createAndroidSubmission: { __typename?: 'CreateSubmissionResult', submission: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } } };
export type CreateAndroidSubmissionMutation = { __typename?: 'RootMutation', submission: { __typename?: 'SubmissionMutation', createAndroidSubmission: { __typename?: 'CreateSubmissionResult', submission: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } } };
export type CreateIosSubmissionMutationVariables = Exact<{
appId: Scalars['ID']['input'];
@@ -8352,7 +8481,7 @@ export type CreateIosSubmissionMutationVariables = Exact<{
}>;
export type CreateIosSubmissionMutation = { __typename?: 'RootMutation', submission: { __typename?: 'SubmissionMutation', createIosSubmission: { __typename?: 'CreateSubmissionResult', submission: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } } };
export type CreateIosSubmissionMutation = { __typename?: 'RootMutation', submission: { __typename?: 'SubmissionMutation', createIosSubmission: { __typename?: 'CreateSubmissionResult', submission: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } } };
export type CreateUploadSessionMutationVariables = Exact<{
type: UploadSessionType;
@@ -8405,6 +8534,13 @@ export type AppByFullNameQueryVariables = Exact<{
export type AppByFullNameQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byFullName: { __typename?: 'App', id: string, name: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> }, githubRepository?: { __typename?: 'GitHubRepository', id: string, metadata: { __typename?: 'GitHubRepositoryMetadata', githubRepoOwnerName: string, githubRepoName: string } } | null } } };
export type AppStoreConnectApiKeyByIdQueryVariables = Exact<{
ascApiKeyId: Scalars['ID']['input'];
}>;
export type AppStoreConnectApiKeyByIdQuery = { __typename?: 'RootQuery', appStoreConnectApiKey: { __typename?: 'AppStoreConnectApiKeyQuery', byId: { __typename?: 'AppStoreConnectApiKey', id: string, issuerIdentifier: string, keyIdentifier: string, keyP8: string } } };
export type LatestAppVersionQueryVariables = Exact<{
appId: Scalars['String']['input'];
platform: AppPlatform;
@@ -8474,7 +8610,7 @@ export type BuildsWithSubmissionsByIdQueryVariables = Exact<{
}>;
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type BuildsWithSubmissionsByIdQuery = { __typename?: 'RootQuery', builds: { __typename?: 'BuildQuery', byId: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } } } };
export type ViewBuildsOnAppQueryVariables = Exact<{
appId: Scalars['String']['input'];
@@ -8556,6 +8692,13 @@ export type EnvironmentVariablesSharedWithSensitiveQueryVariables = Exact<{
export type EnvironmentVariablesSharedWithSensitiveQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, ownerAccount: { __typename?: 'Account', id: string, environmentVariablesIncludingSensitive: Array<{ __typename?: 'EnvironmentVariableWithSecret', id: string, name: string, value?: string | null }> } } } };
export type GoogleServiceAccountKeyByIdQueryVariables = Exact<{
ascApiKeyId: Scalars['ID']['input'];
}>;
export type GoogleServiceAccountKeyByIdQuery = { __typename?: 'RootQuery', googleServiceAccountKey: { __typename?: 'GoogleServiceAccountKeyQuery', byId: { __typename?: 'GoogleServiceAccountKey', id: string, keyJson: string } } };
export type GetAssetMetadataQueryVariables = Exact<{
storageKeys: Array<Scalars['String']['input']> | Scalars['String']['input'];
}>;
@@ -8595,7 +8738,7 @@ export type SubmissionsByIdQueryVariables = Exact<{
}>;
export type SubmissionsByIdQuery = { __typename?: 'RootQuery', submissions: { __typename?: 'SubmissionQuery', byId: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } };
export type SubmissionsByIdQuery = { __typename?: 'RootQuery', submissions: { __typename?: 'SubmissionQuery', byId: { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null } } };
export type GetAllSubmissionsForAppQueryVariables = Exact<{
appId: Scalars['String']['input'];
@@ -8606,7 +8749,7 @@ export type GetAllSubmissionsForAppQueryVariables = Exact<{
}>;
export type GetAllSubmissionsForAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }> } } };
export type GetAllSubmissionsForAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }> } } };
export type ViewUpdatesByGroupQueryVariables = Exact<{
groupId: Scalars['ID']['input'];
@@ -8662,7 +8805,7 @@ export type AppFragment = { __typename?: 'App', id: string, name: string, fullNa
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string } };
export type EnvironmentSecretFragment = { __typename?: 'EnvironmentSecret', id: string, name: string, type: EnvironmentSecretType, createdAt: any };
@@ -8672,7 +8815,7 @@ export type RuntimeFragment = { __typename?: 'Runtime', id: string, version: str
export type StatuspageServiceFragment = { __typename?: 'StatuspageService', id: string, name: StatuspageServiceName, status: StatuspageServiceStatus, incidents: Array<{ __typename?: 'StatuspageIncident', id: string, status: StatuspageIncidentStatus, name: string, impact: StatuspageIncidentImpact, shortlink: string }> };
export type SubmissionFragment = { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logsUrl?: string | null, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null };
export type SubmissionFragment = { __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null };
export type UpdateFragment = { __typename?: 'Update', id: string, group: string, message?: string | null, createdAt: any, runtimeVersion: string, platform: string, manifestFragment: string, isRollBackToEmbedded: boolean, manifestPermalink: string, gitCommitHash?: string | null, rolloutPercentage?: number | null, actor?: { __typename: 'Robot', firstName?: string | null, id: string } | { __typename: 'SSOUser', username: string, id: string } | { __typename: 'User', username: string, id: string } | null, branch: { __typename?: 'UpdateBranch', id: string, name: string }, codeSigningInfo?: { __typename?: 'CodeSigningInfo', keyid: string, sig: string, alg: string } | null, rolloutControlUpdate?: { __typename?: 'Update', id: string } | null };
@@ -0,0 +1,50 @@
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
AppStoreConnectApiKeyByIdQuery,
AppStoreConnectApiKeyByIdQueryVariables,
} from '../generated';
export const AppStoreConnectApiKeyQuery = {
async getByIdAsync(
graphqlClient: ExpoGraphqlClient,
ascApiKeyId: string
): Promise<{
issuerIdentifier: string;
keyIdentifier: string;
keyP8: string;
}> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<AppStoreConnectApiKeyByIdQuery, AppStoreConnectApiKeyByIdQueryVariables>(
gql`
query AppStoreConnectApiKeyById($ascApiKeyId: ID!) {
appStoreConnectApiKey {
byId(id: $ascApiKeyId) {
id
issuerIdentifier
keyIdentifier
keyP8
}
}
}
`,
{ ascApiKeyId },
{
additionalTypenames: ['AppStoreConnectApiKey'],
}
)
.toPromise()
);
const key = data.appStoreConnectApiKey.byId;
return {
issuerIdentifier: key.issuerIdentifier,
keyIdentifier: key.keyIdentifier,
keyP8: key.keyP8,
};
},
};
@@ -0,0 +1,38 @@
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
GoogleServiceAccountKeyByIdQuery,
GoogleServiceAccountKeyByIdQueryVariables,
} from '../generated';
export const GoogleServiceAccountKeyQuery = {
async getByIdAsync(
graphqlClient: ExpoGraphqlClient,
ascApiKeyId: string
): Promise<{ keyJson: string }> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<GoogleServiceAccountKeyByIdQuery, GoogleServiceAccountKeyByIdQueryVariables>(
gql`
query GoogleServiceAccountKeyById($ascApiKeyId: ID!) {
googleServiceAccountKey {
byId(id: $ascApiKeyId) {
id
keyJson
}
}
}
`,
{ ascApiKeyId },
{
additionalTypenames: ['GoogleServiceAccountKey'],
}
)
.toPromise()
);
return { keyJson: data.googleServiceAccountKey.byId.keyJson };
},
};
@@ -28,6 +28,6 @@ export const SubmissionFragmentNode = gql`
errorCode
message
}
logsUrl
logFiles
}
`;
@@ -46,17 +46,5 @@ describe('expoConfig', () => {
await createOrModifyExpoConfigAsync('/app', { owner: 'ccheever' });
expect(modifyConfigAsync).toHaveBeenCalledWith('/app', { owner: 'ccheever' });
});
it('adds expo key if app.json is missing it', async () => {
jest.mocked(getConfigFilePaths).mockReturnValue({
staticConfigPath: '/app/app.json',
dynamicConfigPath: null,
});
jest.mocked(JsonFile.readAsync).mockResolvedValue({ name: 'App' });
await createOrModifyExpoConfigAsync('/app', { owner: 'ccheever' });
expect(JsonFile.writeAsync).toHaveBeenCalledWith('/app/app.json', { name: 'App', expo: {} });
expect(modifyConfigAsync).toHaveBeenCalledWith('/app', { owner: 'ccheever' });
});
});
});
@@ -65,9 +65,6 @@ export async function getApplicationIdFromBareAsync(
projectDir: string,
gradleContext?: GradleBuildContext
): Promise<string> {
if (env.overrideAndroidApplicationId) {
return env.overrideAndroidApplicationId;
}
const errorMessage = 'Could not read applicationId from Android project.';
if (gradleContext) {
@@ -109,6 +106,10 @@ export async function getApplicationIdAsync(
vcsClient: Client,
gradleContext?: GradleBuildContext
): Promise<string> {
if (env.overrideAndroidApplicationId) {
return env.overrideAndroidApplicationId;
}
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
if (workflow === Workflow.GENERIC) {
warnIfAndroidPackageDefinedInAppConfigForBareWorkflowProject(projectDir, exp);
@@ -1,9 +1,7 @@
import { ExpoConfig, getConfig, getConfigFilePaths, modifyConfigAsync } from '@expo/config';
import { Env } from '@expo/eas-build-job';
import JsonFile from '@expo/json-file';
import fs from 'fs-extra';
import Joi from 'joi';
import nullthrows from 'nullthrows';
import path from 'path';
export type PublicExpoConfig = Omit<
@@ -31,7 +29,6 @@ export async function createOrModifyExpoConfigAsync(
readOptions?: { skipSDKVersionRequirement?: boolean }
): ReturnType<typeof modifyConfigAsync> {
ensureExpoConfigExists(projectDir);
await ensureStaticExpoConfigIsValidAsync(projectDir);
if (readOptions) {
return await modifyConfigAsync(projectDir, exp, readOptions);
@@ -95,20 +92,6 @@ export function ensureExpoConfigExists(projectDir: string): void {
}
}
async function ensureStaticExpoConfigIsValidAsync(projectDir: string): Promise<void> {
if (isUsingStaticExpoConfig(projectDir)) {
const staticConfigPath = nullthrows(getConfigFilePaths(projectDir).staticConfigPath);
const staticConfig = await JsonFile.readAsync(staticConfigPath);
// Add the "expo" key if it doesn't exist on app.json yet, such as in
// projects initialized with RNC CLI
if (!staticConfig?.expo) {
staticConfig.expo = {};
await JsonFile.writeAsync(staticConfigPath, staticConfig);
}
}
}
export function isUsingStaticExpoConfig(projectDir: string): boolean {
const paths = getConfigFilePaths(projectDir);
return !!(paths.staticConfigPath?.endsWith('app.json') && !paths.dynamicConfigPath);
@@ -59,14 +59,15 @@ export async function getBundleIdentifierAsync(
vcsClient: Client,
xcodeContext?: { targetName?: string; buildConfiguration?: string }
): Promise<string> {
if (env.overrideIosBundleIdentifier) {
return env.overrideIosBundleIdentifier;
}
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
if (workflow === Workflow.GENERIC) {
warnIfBundleIdentifierDefinedInAppConfigForBareWorkflowProject(projectDir, exp);
if (env.overrideIosBundleIdentifier) {
return env.overrideIosBundleIdentifier;
}
const xcodeProject = IOSConfig.XcodeUtils.getPbxproj(projectDir);
const isMultiScheme = IOSConfig.BuildScheme.getSchemesFromXcodeproj(projectDir).length > 1;
const isMultiTarget =
+7 -2
View File
@@ -31,7 +31,7 @@ import { PublishQuery } from '../graphql/queries/PublishQuery';
import Log, { learnMore } from '../log';
import { RequestedPlatform, requestedPlatformDisplayNames } from '../platform';
import { promptAsync } from '../prompts';
import { getBranchNameFromChannelNameAsync } from '../update/getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
import {
UpdateJsonInfo,
formatUpdateMessage,
@@ -598,7 +598,12 @@ export async function getBranchNameForCommandAsync({
}
if (channelNameArg) {
return await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelNameArg);
const { branchName } = await getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
projectId,
channelNameArg
);
return branchName;
}
if (branchNameArg) {
+17 -1
View File
@@ -7,7 +7,7 @@ import * as uuid from 'uuid';
import { getRecentBuildsForSubmissionAsync } from './utils/builds';
import { isExistingFileAsync, uploadAppArchiveAsync } from './utils/files';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { BuildFragment } from '../graphql/generated';
import { BuildFragment, BuildStatus } from '../graphql/generated';
import { BuildQuery } from '../graphql/queries/BuildQuery';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log, { learnMore } from '../log';
@@ -89,6 +89,16 @@ export type ArchiveSource =
export type ResolvedArchiveSource = ArchiveUrlSource | ArchiveGCSSource | ArchiveBuildSource;
const buildStatusMapping: Record<BuildStatus, string> = {
[BuildStatus.New]: 'new',
[BuildStatus.InQueue]: 'in queue',
[BuildStatus.InProgress]: 'in progress',
[BuildStatus.Finished]: 'finished',
[BuildStatus.Errored]: 'errored',
[BuildStatus.PendingCancel]: 'canceled',
[BuildStatus.Canceled]: 'canceled',
};
export async function getArchiveAsync(
ctx: ArchiveResolverContext,
source: ArchiveSource
@@ -344,6 +354,7 @@ function formatBuildChoice(build: BuildFragment): prompts.Choice {
gitCommitMessage,
channel,
message,
status,
} = build;
const buildDate = new Date(updatedAt);
@@ -368,6 +379,7 @@ function formatBuildChoice(build: BuildFragment): prompts.Choice {
? chalk.bold(message.length > 200 ? `${message.slice(0, 200)}...` : message)
: null,
},
{ name: 'Status', value: buildStatusMapping[status] },
];
const filteredDescriptionArray: string[] = descriptionItems
@@ -385,6 +397,10 @@ function formatBuildChoice(build: BuildFragment): prompts.Choice {
async function handlePromptSourceAsync(
ctx: ArchiveResolverContext
): Promise<ResolvedArchiveSource> {
if (ctx.nonInteractive) {
throw new Error('Please run this command with appropriate input.');
}
const { sourceType: sourceTypeRaw } = await promptAsync({
name: 'sourceType',
type: 'select',
+6 -2
View File
@@ -65,9 +65,13 @@ export default abstract class BaseSubmitter<
return resolvedSourceOptions;
}
public async submitAsync(): Promise<SubmissionFragment> {
public async getSubmissionInputAsync(): Promise<SubmissionInput<P>> {
const resolvedSourceOptions = await this.getSourceOptionsAsync();
const input = await this.createSubmissionInputAsync(resolvedSourceOptions);
return await this.createSubmissionInputAsync(resolvedSourceOptions);
}
public async submitAsync(): Promise<SubmissionFragment> {
const input = await this.getSubmissionInputAsync();
return await this.createSubmissionWithAnalyticsAsync(input);
}
@@ -7,6 +7,7 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
import {
AppPlatform,
BuildFragment,
BuildStatus,
SubmissionArchiveSourceType,
UploadSessionType,
} from '../../graphql/generated';
@@ -41,6 +42,17 @@ const MOCK_BUILD_FRAGMENT: Partial<BuildFragment> = {
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.Finished,
};
const MOCK_IN_PROGRESS_BUILD_FRAGMENT: Partial<BuildFragment> = {
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
status: BuildStatus.InProgress,
};
const SOURCE_STUB_INPUT = {
@@ -246,7 +258,7 @@ describe(getArchiveAsync, () => {
expect(archive.sourceType).toBe(ArchiveSourceType.url);
});
it('handles build-list-select source', async () => {
it('handles build-list-select source for finished builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
@@ -269,6 +281,57 @@ describe(getArchiveAsync, () => {
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});
it('handles build-list-select source for in-progress builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_IN_PROGRESS_BUILD_FRAGMENT as BuildFragment]);
jest
.mocked(promptAsync)
.mockResolvedValueOnce({ selectedBuild: MOCK_IN_PROGRESS_BUILD_FRAGMENT });
const archive = await getArchiveAsync(
{ ...SOURCE_STUB_INPUT, graphqlClient, projectId },
{
sourceType: ArchiveSourceType.buildList,
}
);
expect(getRecentBuildsForSubmissionAsync).toBeCalledWith(
graphqlClient,
toAppPlatform(SOURCE_STUB_INPUT.platform),
projectId,
{ limit: BUILD_LIST_ITEM_COUNT }
);
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});
it('handles build-list-select source for both finished and in-progress builds', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([
MOCK_IN_PROGRESS_BUILD_FRAGMENT as BuildFragment,
MOCK_BUILD_FRAGMENT as BuildFragment,
]);
jest.mocked(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });
const archive = await getArchiveAsync(
{ ...SOURCE_STUB_INPUT, graphqlClient, projectId },
{
sourceType: ArchiveSourceType.buildList,
}
);
expect(getRecentBuildsForSubmissionAsync).toBeCalledWith(
graphqlClient,
toAppPlatform(SOURCE_STUB_INPUT.platform),
projectId,
{ limit: BUILD_LIST_ITEM_COUNT }
);
expect(archive.sourceType).toBe(ArchiveSourceType.build);
});
it('prompts again if all builds have expired', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
{
@@ -4,11 +4,7 @@ import { Result, result } from '@expo/results';
import AndroidSubmitter, { AndroidSubmissionOptions } from './AndroidSubmitter';
import { ServiceAccountSource, ServiceAccountSourceType } from './ServiceAccountSource';
import {
SubmissionAndroidReleaseStatus,
SubmissionAndroidTrack,
SubmissionFragment,
} from '../../graphql/generated';
import { SubmissionAndroidReleaseStatus, SubmissionAndroidTrack } from '../../graphql/generated';
import Log from '../../log';
import {
AmbiguousApplicationIdError,
@@ -22,7 +18,7 @@ import { SubmissionContext } from '../context';
export default class AndroidSubmitCommand {
constructor(private ctx: SubmissionContext<Platform.ANDROID>) {}
async runAsync(): Promise<SubmissionFragment> {
async runAsync(): Promise<AndroidSubmitter> {
Log.addNewLineIfNone();
const archiveSource = this.resolveArchiveSource();
if (!archiveSource.ok) {
@@ -48,7 +44,7 @@ export default class AndroidSubmitCommand {
}
const submissionOptions = await this.getAndroidSubmissionOptionsAsync(archiveSourceValue);
const submitter = new AndroidSubmitter(this.ctx, submissionOptions, archive);
return await submitter.submitAsync();
return submitter;
}
private async getAndroidSubmissionOptionsAsync(
@@ -132,7 +132,9 @@ describe(AndroidSubmitCommand, () => {
vcsClient,
});
const command = new AndroidSubmitCommand(ctx);
await expect(command.runAsync()).rejects.toThrowError(
await expect(
command.runAsync().then(submitter => submitter.submitAsync())
).rejects.toThrowError(
'Google Service Account Keys cannot be set up in --non-interactive mode.'
);
});
@@ -170,7 +172,8 @@ describe(AndroidSubmitCommand, () => {
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -216,7 +219,8 @@ describe(AndroidSubmitCommand, () => {
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -264,7 +268,8 @@ describe(AndroidSubmitCommand, () => {
vcsClient,
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -329,7 +334,8 @@ describe(AndroidSubmitCommand, () => {
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(
graphqlClient,
@@ -391,7 +397,8 @@ describe(AndroidSubmitCommand, () => {
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(
graphqlClient,
@@ -461,7 +468,8 @@ describe(AndroidSubmitCommand, () => {
});
const command = new AndroidSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createAndroidSubmissionAsync).toHaveBeenCalledWith(
graphqlClient,
@@ -12,7 +12,6 @@ import {
import { AscApiKeySource, AscApiKeySourceType } from './AscApiKeySource';
import IosSubmitter, { IosSubmissionOptions } from './IosSubmitter';
import { MissingCredentialsError } from '../../credentials/errors';
import { SubmissionFragment } from '../../graphql/generated';
import Log, { learnMore } from '../../log';
import { ArchiveSource, ArchiveSourceType, getArchiveAsync } from '../ArchiveSource';
import { refreshContextSubmitProfileAsync, resolveArchiveSource } from '../commons';
@@ -21,7 +20,7 @@ import { SubmissionContext } from '../context';
export default class IosSubmitCommand {
constructor(private ctx: SubmissionContext<Platform.IOS>) {}
async runAsync(): Promise<SubmissionFragment> {
async runAsync(): Promise<IosSubmitter> {
Log.addNewLineIfNone();
const archiveSource = this.resolveArchiveSource();
if (!archiveSource.ok) {
@@ -47,7 +46,7 @@ export default class IosSubmitCommand {
}
const options = await this.resolveSubmissionOptionsAsync(archiveSourceValue);
const submitter = new IosSubmitter(this.ctx, options, archive);
return await submitter.submitAsync();
return submitter;
}
private async resolveSubmissionOptionsAsync(
@@ -109,7 +109,7 @@ describe(IosSubmitCommand, () => {
vcsClient,
});
const command = new IosSubmitCommand(ctx);
await expect(command.runAsync()).rejects.toThrow(
await expect(command.runAsync().then(submitter => submitter.submitAsync())).rejects.toThrow(
'EXPO_APPLE_APP_SPECIFIC_PASSWORD must be in the format xxxx-xxxx-xxxx-xxxx, where x is a lowercase letter.'
);
@@ -144,7 +144,9 @@ describe(IosSubmitCommand, () => {
vcsClient,
});
const command = new IosSubmitCommand(ctx);
await expect(command.runAsync()).rejects.toThrowError();
await expect(
command.runAsync().then(submitter => submitter.submitAsync())
).rejects.toThrowError();
});
});
@@ -180,7 +182,8 @@ describe(IosSubmitCommand, () => {
vcsClient,
});
const command = new IosSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createIosSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -245,7 +248,8 @@ describe(IosSubmitCommand, () => {
vcsClient,
});
const command = new IosSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createIosSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -304,7 +308,8 @@ describe(IosSubmitCommand, () => {
vcsClient,
});
const command = new IosSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createIosSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
@@ -369,7 +374,8 @@ describe(IosSubmitCommand, () => {
specifiedProfile: 'specificProfile',
});
const command = new IosSubmitCommand(ctx);
await command.runAsync();
const submitter = await command.runAsync();
await submitter.submitAsync();
expect(SubmissionMutation.createIosSubmissionAsync).toHaveBeenCalledWith(graphqlClient, {
appId: projectId,
+2 -1
View File
@@ -23,7 +23,8 @@ export async function submitAsync<T extends Platform>(
ctx.platform === Platform.ANDROID
? new AndroidSubmitCommand(ctx as SubmissionContext<Platform.ANDROID>)
: new IosSubmitCommand(ctx as SubmissionContext<Platform.IOS>);
return await command.runAsync();
const submitter = await command.runAsync();
return await submitter.submitAsync();
},
{
attemptEvent: SubmissionEvent.SUBMIT_COMMAND_ATTEMPT,
@@ -0,0 +1,226 @@
import { v4 as uuidv4 } from 'uuid';
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
import {
AppPlatform,
BuildFragment,
BuildStatus,
SubmissionArchiveSourceType,
} from '../../../graphql/generated';
import { BuildQuery } from '../../../graphql/queries/BuildQuery';
import { getRecentBuildsForSubmissionAsync } from '../builds';
jest.mock('../../../graphql/queries/BuildQuery', () => ({
BuildQuery: {
viewBuildsOnAppAsync: jest.fn(),
},
}));
const ARCHIVE_SOURCE = {
type: SubmissionArchiveSourceType.Url,
url: 'https://url.to/archive.tar.gz',
};
const MOCK_BUILD_FRAGMENTS: Partial<BuildFragment>[] = Array(5).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
createdAt: Date.now(),
status: BuildStatus.Finished,
}));
const MOCK_IN_PROGRESS_BUILD_FRAGMENTS: Partial<BuildFragment>[] = Array(2).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
createdAt: Date.now(),
status: BuildStatus.InProgress,
}));
const MOCK_IN_QUEUE_BUILD_FRAGMENTS = Array(2).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
createdAt: Date.now(),
status: BuildStatus.InQueue,
}));
const MOCK_NEW_BUILD_FRAGMENTS = Array(1).map(() => ({
id: uuidv4(),
artifacts: {
buildUrl: ARCHIVE_SOURCE.url,
},
appVersion: '1.2.3',
platform: AppPlatform.Android,
updatedAt: Date.now(),
createdAt: Date.now(),
status: BuildStatus.New,
}));
describe(getRecentBuildsForSubmissionAsync, () => {
let graphqlClient: ExpoGraphqlClient;
beforeEach(() => {
graphqlClient = {} as any as ExpoGraphqlClient;
});
it('returns finished builds', async () => {
const appId = uuidv4();
const limit = 2;
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce(
MOCK_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_BUILD_FRAGMENTS.length)
) as BuildFragment[]
);
const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit }
);
expect(result).toMatchObject(
MOCK_BUILD_FRAGMENTS.slice(0, Math.min(limit, MOCK_BUILD_FRAGMENTS.length))
);
});
it('returns in-progress builds', async () => {
const appId = uuidv4();
const limit = 2;
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce(
MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_IN_PROGRESS_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce([] as BuildFragment[]);
const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit }
);
expect(result).toMatchObject(
MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_IN_PROGRESS_BUILD_FRAGMENTS.length)
)
);
});
it('returns in-queue builds', async () => {
const appId = uuidv4();
const limit = 2;
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce(
MOCK_IN_QUEUE_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_IN_QUEUE_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[]);
const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit }
);
expect(result).toMatchObject(
MOCK_IN_QUEUE_BUILD_FRAGMENTS.slice(0, Math.min(limit, MOCK_IN_QUEUE_BUILD_FRAGMENTS.length))
);
});
it('returns new builds', async () => {
const appId = uuidv4();
const limit = 2;
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce(
MOCK_NEW_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_NEW_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[])
.mockResolvedValueOnce([] as BuildFragment[]);
const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit }
);
expect(result).toMatchObject(
MOCK_NEW_BUILD_FRAGMENTS.slice(0, Math.min(limit, MOCK_NEW_BUILD_FRAGMENTS.length))
);
});
it('returns up to "limit" newest builds regardless of status', async () => {
const appId = uuidv4();
const limit = 2;
jest
.mocked(BuildQuery.viewBuildsOnAppAsync)
.mockResolvedValueOnce(
MOCK_NEW_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_NEW_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce(
MOCK_IN_QUEUE_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_IN_QUEUE_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce(
MOCK_IN_PROGRESS_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_IN_PROGRESS_BUILD_FRAGMENTS.length)
) as BuildFragment[]
)
.mockResolvedValueOnce(
MOCK_BUILD_FRAGMENTS.slice(
0,
Math.min(limit, MOCK_BUILD_FRAGMENTS.length)
) as BuildFragment[]
);
const result = await getRecentBuildsForSubmissionAsync(
graphqlClient,
AppPlatform.Android,
appId,
{ limit }
);
expect(result).toMatchObject([MOCK_NEW_BUILD_FRAGMENTS[0], MOCK_IN_QUEUE_BUILD_FRAGMENTS[1]]);
});
});
+25 -10
View File
@@ -8,14 +8,29 @@ export async function getRecentBuildsForSubmissionAsync(
appId: string,
{ limit = 1 }: { limit?: number } = {}
): Promise<BuildFragment[]> {
return await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId,
limit,
offset: 0,
filter: {
platform,
distribution: DistributionType.Store,
status: BuildStatus.Finished,
},
});
const allowedStatuses = [
BuildStatus.New,
BuildStatus.InQueue,
BuildStatus.InProgress,
BuildStatus.Finished,
];
const buildsPromises: Promise<BuildFragment[]>[] = [];
for (const buildStatus of allowedStatuses) {
buildsPromises.push(
BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId,
limit,
offset: 0,
filter: {
platform,
distribution: DistributionType.Store,
status: buildStatus,
},
})
);
}
const builds = (await Promise.all(buildsPromises)).reduce((acc, value) => acc.concat(value), []);
builds.sort((buildA, buildB) => (buildA.createdAt > buildB.createdAt ? -1 : 1));
return builds.slice(0, limit);
}
+14 -15
View File
@@ -22,21 +22,20 @@ export async function displayLogsAsync(
}
async function downloadAndPrintSubmissionLogsAsync(submission: SubmissionFragment): Promise<void> {
if (!submission.logsUrl) {
return;
}
const response = await fetch(submission.logsUrl);
const logs = parseLogs(await response.text());
Log.addNewLineIfNone();
const prefix = chalk.blueBright('[logs] ');
for (const { level, msg } of logs) {
const msgWithPrefix = `${prefix}${msg}`;
if (level === 'error') {
Log.error(msgWithPrefix);
} else if (level === 'warn') {
Log.warn(msgWithPrefix);
} else {
Log.log(msgWithPrefix);
for (const logFile of submission.logFiles) {
const response = await fetch(logFile);
const logs = parseLogs(await response.text());
Log.addNewLineIfNone();
const prefix = chalk.blueBright('[logs] ');
for (const { level, msg } of logs) {
const msgWithPrefix = `${prefix}${msg}`;
if (level === 'error') {
Log.error(msgWithPrefix);
} else if (level === 'warn') {
Log.warn(msgWithPrefix);
} else {
Log.log(msgWithPrefix);
}
}
}
}
@@ -3,19 +3,19 @@ import { instance, mock } from 'ts-mockito';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import {
App,
DeploymentResult,
Runtime,
UpdateBranch,
UpdateChannel,
UpdateDeploymentsConnection,
UpdateInsights,
} from '../../graphql/generated';
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
import { getBranchNameFromChannelNameAsync } from '../getBranchNameFromChannelNameAsync';
import { getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync } from '../getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync';
jest.mock('../../graphql/queries/ChannelQuery');
jest.mock('../../graphql/queries/BranchQuery');
describe(getBranchNameFromChannelNameAsync, () => {
describe(getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync, () => {
beforeEach(() => {
jest.resetAllMocks();
});
@@ -30,13 +30,14 @@ describe(getBranchNameFromChannelNameAsync, () => {
}) as any
);
const result = await getBranchNameFromChannelNameAsync(
const result = await getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
'test-project-id',
'channel-name'
);
expect(result).toBe('test-branch-name');
expect(result.branchId).toBeTruthy();
expect(result.branchName).toBe('test-branch-name');
});
test('errors when no branch is connected to channel', async () => {
@@ -49,7 +50,11 @@ describe(getBranchNameFromChannelNameAsync, () => {
);
await expect(
getBranchNameFromChannelNameAsync(graphqlClient, 'test-project-id', 'test-channel-name')
getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
'test-project-id',
'test-channel-name'
)
).rejects.toThrow(
"Channel has no branches associated with it. Run 'eas channel:edit' to map a branch"
);
@@ -66,7 +71,11 @@ describe(getBranchNameFromChannelNameAsync, () => {
);
await expect(
getBranchNameFromChannelNameAsync(graphqlClient, 'test-project-id', 'test-channel-name')
getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
'test-project-id',
'test-channel-name'
)
).rejects.toThrow(
"Channel has multiple branches associated with it. Instead, use '--branch' instead of '--channel'"
);
@@ -82,13 +91,13 @@ describe(getBranchNameFromChannelNameAsync, () => {
}) as any
);
const result = await getBranchNameFromChannelNameAsync(
const result = await getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient,
'test-project-id',
'test-channel-name'
);
expect(result).toBe('test-branch-name');
expect(result.branchName).toBe('test-branch-name');
});
});
@@ -109,6 +118,7 @@ function mockUpdateChannel({
branchMapping:
'{"data":[{"branchId":"f9dc4dbb-663f-4a19-8cf2-a783b484d2db","branchMappingLogic":"true"}],"version":0}',
updateBranches: branchNames ? mockUpdateBranches(branchNames) : [],
isPaused: false,
__typename: 'UpdateChannel',
};
}
@@ -134,7 +144,7 @@ function mockUpdateBranches(branchNames: string[]): UpdateBranch[] {
runtimeVersion: '1.0.0',
runtime: {} as Runtime, // Temporary fix to resolve type errors
insights: {} as UpdateInsights, // Temporary fix to resolve type errors
deployments: {} as UpdateDeploymentsConnection, // Temporary fix to resolve type errors
deployments: {} as DeploymentResult, // Temporary fix to resolve type errors
platform: 'ios',
manifestFragment: '...',
isRollBackToEmbedded: false,
@@ -4,12 +4,12 @@ import { createChannelOnAppAsync } from '../channel/queries';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { ChannelQuery } from '../graphql/queries/ChannelQuery';
export async function getBranchNameFromChannelNameAsync(
export async function getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync(
graphqlClient: ExpoGraphqlClient,
projectId: string,
channelName: string
): Promise<string> {
let branchName;
): Promise<{ branchName: string; branchId: string }> {
let branchInfo: { branchName: string; branchId: string };
try {
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
@@ -18,7 +18,8 @@ export async function getBranchNameFromChannelNameAsync(
});
if (channel.updateBranches.length === 1) {
branchName = channel.updateBranches[0].name;
const branch = channel.updateBranches[0];
branchInfo = { branchId: branch.id, branchName: branch.name };
} else if (channel.updateBranches.length === 0) {
throw new Error(
"Channel has no branches associated with it. Run 'eas channel:edit' to map a branch"
@@ -51,8 +52,8 @@ export async function getBranchNameFromChannelNameAsync(
);
}
branchName = channelName;
branchInfo = { branchId, branchName: channelName };
}
return branchName;
return branchInfo;
}
+7 -2
View File
@@ -58,7 +58,10 @@ export async function republishAsync({
update.branchId === arbitraryUpdate.branchId &&
update.branchName === arbitraryUpdate.branchName &&
update.runtimeVersion === arbitraryUpdate.runtimeVersion;
assert(updatesToPublish.every(isSameGroup), 'All updates must belong to the same update group');
assert(
updatesToPublish.every(isSameGroup),
'All updates being republished must belong to the same update group'
);
assert(
updatesToPublish.every(u => u.isRollBackToEmbedded) ||
@@ -90,7 +93,9 @@ export async function republishAsync({
}
}
Log.withTick(`The republished update group will be signed`);
Log.withTick(
`The republished update group will be signed with the same code signing key and algorithm as the original update`
);
}
const publishIndicator = ora('Republishing...').start();
+4 -3
View File
@@ -12,11 +12,12 @@ export async function promptVariableEnvironmentAsync(
);
}
return await selectAsync('Select environment:', [
{ title: 'Development', value: EnvironmentVariableEnvironment.Development },
{ title: 'Preview', value: EnvironmentVariableEnvironment.Preview },
{ title: 'Production', value: EnvironmentVariableEnvironment.Production },
{ title: 'development', value: EnvironmentVariableEnvironment.Development },
{ title: 'preview', value: EnvironmentVariableEnvironment.Preview },
{ title: 'production', value: EnvironmentVariableEnvironment.Production },
]);
}
export async function promptVariableValueAsync({
nonInteractive,
required = true,
+5 -3
View File
@@ -82,12 +82,14 @@ export type AssetMap = Record<string, string>;
/** Creates an asset map of a given target path */
async function createAssetMapAsync(
assetPath: string,
assetPath?: string,
options?: AssetMapOptions
): Promise<AssetMap> {
const map: AssetMap = Object.create(null);
for await (const file of listFilesRecursively(assetPath)) {
map[file.normalizedPath] = await computeSha512HashAsync(file.path, options?.hashOptions);
if (assetPath) {
for await (const file of listFilesRecursively(assetPath)) {
map[file.normalizedPath] = await computeSha512HashAsync(file.path, options?.hashOptions);
}
}
return map;
}
+50 -45
View File
@@ -32,32 +32,21 @@ export async function getSignedDeploymentUrlAsync(
['APP_NO_DEV_DOMAIN_NAME'].includes(e?.extensions?.errorCode as string)
);
// Throw unexpected errors eagerly
if (!isMissingDevDomain) {
throw error;
}
if (options.nonInteractive) {
throw new Error(
'The project URL needs to be set up, but the terminal is running in non-interactive mode.'
);
}
const suggestedDevDomainName = await DeploymentsQuery.getSuggestedDevDomainByAppIdAsync(
graphqlClient,
{ appId: options.appId }
);
// Ensure the callback is invoked, containing cleanup logic for possible spinners
options.onSetupDevDomain?.();
await chooseDevDomainNameAsync({
// Assign the dev domain name by prompting the user
await assignDevDomainNameAsync({
graphqlClient,
appId: options.appId,
initial: suggestedDevDomainName,
});
return await DeploymentsMutation.createSignedDeploymentUrlAsync(graphqlClient, {
appId: options.appId,
deploymentIdentifier: options.deploymentIdentifier,
nonInteractive: options.nonInteractive,
});
// Retry creating the signed URL
return await getSignedDeploymentUrlAsync(graphqlClient, options);
}
}
@@ -89,32 +78,24 @@ function formatDevDomainName(name = ''): string {
.trim();
}
async function chooseDevDomainNameAsync({
graphqlClient,
appId,
initial,
}: {
graphqlClient: ExpoGraphqlClient;
appId: string;
initial: string;
}): Promise<void> {
async function promptDevDomainNameAsync(initialDevDomain: string): Promise<string> {
const rootDomain = `.${EXPO_BASE_DOMAIN}.app`;
const memoizedFormatDevDomainName = memoize(formatDevDomainName);
const { name } = await promptAsync({
type: 'text',
name: 'name',
message: 'Choose a URL for your project:',
initial,
message: 'Choose a preview URL for your project:',
initial: initialDevDomain,
validate: (value: string) => {
if (!value) {
return 'You have to choose a URL for your project';
return 'You have to choose a preview URL for your project';
}
if (value.length < 3) {
return 'Project URLs must be at least 3 characters long';
return 'Preview URLs must be at least 3 characters long';
}
if (value.endsWith('-')) {
return 'Project URLs cannot end with a hyphen (-)';
return 'Preview URLs cannot end with a hyphen (-)';
}
return true;
},
@@ -138,32 +119,56 @@ async function chooseDevDomainNameAsync({
},
});
// This should never happen due to the validation, if it does its an error
if (!name) {
throw new Error('No project URL provided, aborting deployment.');
throw new Error('No preview URL provided, aborting deployment.');
}
return name;
}
/**
* Assign a dev domain name to a project.
* - When running in interactive mode, it will prompt the user with a suggested domain name.
* - When running in non interactive mode, it will auto-assign the suggested domain name.
*/
export async function assignDevDomainNameAsync({
graphqlClient,
appId,
nonInteractive,
}: {
graphqlClient: ExpoGraphqlClient;
appId: string;
nonInteractive?: boolean;
}): ReturnType<typeof DeploymentsMutation.assignDevDomainNameAsync> {
let devDomainName = await DeploymentsQuery.getSuggestedDevDomainByAppIdAsync(graphqlClient, {
appId,
});
if (!nonInteractive) {
devDomainName = await promptDevDomainNameAsync(devDomainName);
}
try {
const success = await DeploymentsMutation.assignDevDomainNameAsync(graphqlClient, {
return await DeploymentsMutation.assignDevDomainNameAsync(graphqlClient, {
appId,
name,
name: devDomainName,
});
if (!success) {
throw new Error('Failed to assign project URL');
}
} catch (error: any) {
} catch (error) {
const isChosenNameTaken = (error as GraphqlError)?.graphQLErrors?.some(e =>
['DEV_DOMAIN_NAME_TAKEN'].includes(e?.extensions?.errorCode as string)
);
if (isChosenNameTaken) {
Log.error(`The project URL "${name}" is already taken, choose a different name.`);
await chooseDevDomainNameAsync({ graphqlClient, appId, initial });
}
// Throw unexpected errors eagerly
if (!isChosenNameTaken) {
throw error;
}
if (!nonInteractive) {
Log.error(`The preview URL "${devDomainName}" is already taken, choose a different URL.`);
}
return await assignDevDomainNameAsync({ graphqlClient, appId, nonInteractive });
}
}
+5 -5
View File
@@ -165,13 +165,13 @@ export async function* batchUploadAsync(
}
yield await Promise.race(queue);
}
} catch (error: any) {
if (typeof error !== 'object' || error.name !== 'AbortError') {
throw error;
}
} finally {
if (queue.size > 0) {
controller.abort();
}
} catch (error: any) {
if (error.name !== 'AbortError') {
throw error;
}
}
}
+28 -26
View File
@@ -22,7 +22,7 @@ type WorkerDeploymentData = {
/** The actual deployment information */
deployment: Pick<WorkerDeploymentFragment, 'deploymentIdentifier' | 'url'>;
/** All modified aliases of the deployment, if any */
aliases?: WorkerDeploymentAliasFragment[];
aliases?: (WorkerDeploymentAliasFragment | null)[];
/** The production promoting alias of the deployment, if any */
production?: WorkerDeploymentAliasFragment | null;
};
@@ -34,7 +34,10 @@ export function formatWorkerDeploymentTable(data: WorkerDeploymentData): string
];
if (data.aliases?.length) {
fields.push({ label: 'Alias URL', value: data.aliases[0].url });
const alias = data.aliases.filter(Boolean)[0];
if (alias) {
fields.push({ label: 'Alias URL', value: alias.url });
}
}
if (data.production) {
fields.push({ label: 'Production URL', value: data.production.url });
@@ -49,34 +52,33 @@ export function formatWorkerDeploymentTable(data: WorkerDeploymentData): string
type WorkerDeploymentOutput = {
/** The absolute URL to the dashboard on `expo.dev` */
dashboardUrl: string;
/** The deployment information */
deployment: {
identifier: string;
url: string;
aliases?: { id: string; name: string; url: string }[];
production?: { id: string; url: string };
};
/** The deployment identifier */
identifier: string;
/** The deployment URL */
url: string;
/** Custom aliases, if assigned */
aliases?: { id: string; url: string; name: string }[];
/** The production alias, if assigned */
production?: { id: string; url: string };
};
export function formatWorkerDeploymentJson(data: WorkerDeploymentData): WorkerDeploymentOutput {
const aliases = !data.aliases
? []
: (data.aliases.filter(Boolean) as WorkerDeploymentAliasFragment[]);
return {
dashboardUrl: getDashboardUrl(data.projectId),
deployment: {
identifier: data.deployment.deploymentIdentifier,
url: data.deployment.url,
aliases: !data.aliases?.length
? undefined
: data.aliases.map(alias => ({
id: alias.id,
name: alias.aliasName,
url: alias.url,
})),
production: !data.production
? undefined
: {
id: data.production.id,
url: data.production.url,
},
},
identifier: data.deployment.deploymentIdentifier,
url: data.deployment.url,
aliases: !aliases.length
? undefined
: aliases.map(alias => ({ id: alias.id, url: alias.url, name: alias.aliasName })),
production: !data.production
? undefined
: {
id: data.production.id,
url: data.production.url,
},
};
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@expo/eas-json",
"description": "A library for interacting with eas.json",
"version": "12.0.0",
"version": "12.5.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@babel/code-frame": "7.23.5",
"@expo/eas-build-job": "1.0.133",
"@expo/eas-build-job": "1.0.136",
"chalk": "4.1.2",
"env-string": "1.0.1",
"fs-extra": "11.2.0",
Generated
+9 -9
View File
@@ -1467,10 +1467,10 @@
slugify "^1.3.4"
sucrase "^3.20.0"
"@expo/[email protected]3":
version "1.0.133"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.133.tgz#342a58c79d523cb1364f6329a9aec07ffb019021"
integrity sha512-dlY3tl1A0GDIGs4WOOuvK08jvR6cfXCmCKTCqZ9ioePscZ3jbeGmadLa1nTvMSe9k8vFdfrHgkKmfs78hI7sbw==
"@expo/[email protected]6":
version "1.0.136"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.136.tgz#caae63772f80b6e013731e4f6e00900e7dc05522"
integrity sha512-IEQ3ttjLm5TpgrQ8WNlEBflbNghknLBewExCOyz8T+9busMh6jnFsKZgqLrDVWAG0cMPnQ3ybHVaRqNadP781Q==
dependencies:
"@expo/logger" "1.0.117"
joi "^17.13.1"
@@ -1683,12 +1683,12 @@
dependencies:
cross-spawn "^7.0.3"
"@expo/[email protected]4":
version "1.0.134"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.134.tgz#c7765a3fb2c0fdec458f168365b1c80cc59632cf"
integrity sha512-9WZUhq1Jggipy+cTcN/pi92I911jaOS4VoEWjuKbhp5G5HU/oHBSfR3ZapEopA9N9xzBv9I/GYzRbPiMHPXaxw==
"@expo/[email protected]6":
version "1.0.136"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.136.tgz#144f51c3c541b5c0b7813abf50c543c80413600a"
integrity sha512-5TQ/zYI9wQc5hAwIOC25K0Pa1wRCpbqkSB6iq5Z6I06gwle+ayaq2YWIy47S1XHBIJ9JSbQ2hsHJBYvJfbKYGQ==
dependencies:
"@expo/eas-build-job" "1.0.133"
"@expo/eas-build-job" "1.0.136"
"@expo/logger" "1.0.117"
"@expo/spawn-async" "^1.7.2"
arg "^5.0.2"