mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
87
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2889571fe | ||
|
|
224a5b288c | ||
|
|
16629e1ab5 | ||
|
|
d5a744b938 | ||
|
|
8f39b303c9 | ||
|
|
c199173704 | ||
|
|
800098b958 | ||
|
|
146468914f | ||
|
|
0e48417983 | ||
|
|
cf61f585e6 | ||
|
|
cc700ae263 | ||
|
|
4e94c60c7f | ||
|
|
37f8e117e1 | ||
|
|
46095d2da4 | ||
|
|
6c01856617 | ||
|
|
f8cd50e0ed | ||
|
|
fd0a129a4f | ||
|
|
c44e07bfeb | ||
|
|
951583e6ea | ||
|
|
3f9247ca84 | ||
|
|
c38638e94e | ||
|
|
3b46466a9d | ||
|
|
806085cbf1 | ||
|
|
398b41cc81 | ||
|
|
d23a97238b | ||
|
|
5b6f312ccb | ||
|
|
2bcab0f2b4 | ||
|
|
b90ce63067 | ||
|
|
be02aced9d | ||
|
|
f788b081c6 | ||
|
|
654f973d0e | ||
|
|
2e614cbde9 | ||
|
|
b7150163d9 | ||
|
|
0922121e84 | ||
|
|
4ace7d20fb | ||
|
|
4ae9aed8f6 | ||
|
|
54d6079406 | ||
|
|
f08adad1e7 | ||
|
|
346e47448a | ||
|
|
b0fde7b465 | ||
|
|
d42efadf51 | ||
|
|
e302390324 | ||
|
|
3a239f8cf9 | ||
|
|
b5bc8a8f6e | ||
|
|
6cbff89b75 | ||
|
|
118c008917 | ||
|
|
3b24d37f3c | ||
|
|
df425201b3 | ||
|
|
9636507130 | ||
|
|
2dd50ad21a | ||
|
|
30441b3db4 | ||
|
|
789c314a8d | ||
|
|
4760314ddf | ||
|
|
c8e57f000c | ||
|
|
3c82a2f8a8 | ||
|
|
0e7e926cec | ||
|
|
30364fd679 | ||
|
|
267fa0e43b | ||
|
|
e154f19751 | ||
|
|
4bca79f828 | ||
|
|
7e01661f70 | ||
|
|
ab18e3e27e | ||
|
|
3e7a112097 | ||
|
|
4cd6b8d228 | ||
|
|
400244194b | ||
|
|
dcf3a2d4fd | ||
|
|
0464ac27cd | ||
|
|
e0d2126f0a | ||
|
|
bd46da748d | ||
|
|
d8c68561c5 | ||
|
|
dfc314d5be | ||
|
|
2285d5771a | ||
|
|
aa22163688 | ||
|
|
5e5395b0c6 | ||
|
|
c94a1e7d9c | ||
|
|
aefadf9376 | ||
|
|
7acea869ff | ||
|
|
02af3f587f | ||
|
|
0d0da1a47a | ||
|
|
eec0b88258 | ||
|
|
98be401942 | ||
|
|
8c2689a27b | ||
|
|
1b34118d85 | ||
|
|
0f75fe407e | ||
|
|
efb7e534f4 | ||
|
|
53f89c0df1 | ||
|
|
1bbe7dfb72 |
No files matched your search
@@ -1,8 +1,5 @@
|
|||||||
<!-- Thanks for contributing to _EAS CLI_! Before you submit your pull request, please make sure to check the following boxes by putting an x in the [ ] (don't: [x ], [ x], do: [x]) -->
|
<!-- 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". -->
|
||||||
# Checklist
|
|
||||||
|
|
||||||
- [ ] I've added an entry to [CHANGELOG.md](https://github.com/expo/eas-cli/blob/main/CHANGELOG.md#main) if necessary. You can comment this pull request with `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]` and CHANGELOG.md will be updated automatically.
|
|
||||||
|
|
||||||
# Why
|
# Why
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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: 18
|
||||||
|
- name: Checkout Pull Request
|
||||||
|
run: hub 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,48 +1,37 @@
|
|||||||
on:
|
name: CHANGELOG.md PR check
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
name: Changelog
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- labeled
|
||||||
|
- unlabeled
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr-commented:
|
pr-changelog-entry:
|
||||||
|
name: Check CHANGELOG.md updated
|
||||||
runs-on: ubuntu-latest
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- name: Check if CHANGELOG.md was updated
|
||||||
|
id: changelog-updated
|
||||||
|
uses: tj-actions/changed-files@v35
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
files: CHANGELOG.md
|
||||||
- uses: actions/setup-node@v2
|
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
|
||||||
|
if: steps.changelog-updated.outputs.any_changed == 'false' && !contains(github.event.pull_request.labels.*.name, 'no changelog')
|
||||||
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
script: |
|
||||||
- name: Checkout Pull Request
|
core.setFailed('Please add a changelog entry!')
|
||||||
run: hub pr checkout ${{ github.event.issue.number }}
|
- uses: mshick/[email protected]
|
||||||
env:
|
if: always()
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
with:
|
||||||
- name: Install dependencies
|
message-id: changelog-entry-check
|
||||||
working-directory: ./scripts
|
refresh-message-position: true
|
||||||
run: yarn install --frozen-lockfile --check-files
|
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!' }}
|
||||||
- name: Add changelog entry
|
message-failure: |
|
||||||
working-directory: ./scripts
|
❌ 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.
|
||||||
run: |
|
🤖 To use the bot, simply comment on this PR with the command `/changelog-entry [breaking-change|new-feature|bug-fix|chore] [message]`.
|
||||||
PARAMS=${PR_COMMENT_BODY#/changelog-entry }
|
⏩ 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.
|
||||||
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,4 +1,5 @@
|
|||||||
name: Run tests
|
name: Run tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@@ -25,20 +26,23 @@ jobs:
|
|||||||
- run: yarn typecheck
|
- run: yarn typecheck
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
if: ${{ !matrix.coverage }}
|
if: ${{ !matrix.coverage }}
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: --max-old-space-size=8192
|
||||||
- run: yarn test --coverage
|
- run: yarn test --coverage
|
||||||
if: ${{ matrix.coverage }}
|
if: ${{ matrix.coverage }}
|
||||||
env:
|
env:
|
||||||
# Oclif seems to cause OOM errors when testing with coverage enabled.
|
# Oclif seems to cause OOM errors when testing with coverage enabled.
|
||||||
# Remove this whenever we solved those OOM errors.
|
# Remove this whenever we solved those OOM errors.
|
||||||
NODE_OPTIONS: --max-old-space-size=4096
|
NODE_OPTIONS: --max-old-space-size=8192
|
||||||
- run: yarn lint --max-warnings=0
|
- run: yarn lint --max-warnings=0
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
working-directory: ./scripts
|
working-directory: ./scripts
|
||||||
- run: yarn lint-changelog
|
|
||||||
- uses: codecov/codecov-action@v1
|
- uses: codecov/codecov-action@v1
|
||||||
if: ${{ matrix.coverage }}
|
if: ${{ matrix.coverage }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
- run: yarn lint-changelog
|
||||||
|
|
||||||
notify-slack:
|
notify-slack:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test
|
needs: test
|
||||||
|
|||||||
+128
-1
@@ -8,10 +8,137 @@ This is the log of notable changes to EAS CLI and related packages.
|
|||||||
|
|
||||||
### 🎉 New features
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Show build profile when selecting a build for eas build:run. ([#1901](https://github.com/expo/eas-cli/pull/1901) by [@keith-kurak](https://github.com/keith-kurak))
|
||||||
|
- Adds -m alias to --message in update/republish and removed README comment. ([#1905](https://github.com/expo/eas-cli/pull/1905) by [@pusongqi](https://github.com/pusongqi))
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
### 🧹 Chores
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Better runtimeVersion output. ([#1894](https://github.com/expo/eas-cli/pull/1894) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Print better error message when uploading project archive tarball fails. ([#1897](https://github.com/expo/eas-cli/pull/1897) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Set useClassicUpdates to false on `eas update` and `eas update:configure`. ([#1914](https://github.com/expo/eas-cli/pull/1914) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
|
||||||
|
## [3.14.0](https://github.com/expo/eas-cli/releases/tag/v3.14.0) - 2023-06-20
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Added support for SSO users. ([#1875](https://github.com/expo/eas-cli/pull/1875) by [@lzkb](https://github.com/lzkb))
|
||||||
|
- Added new bundle identifier capabilities and entitlements from WWDC23. ([#1870](https://github.com/expo/eas-cli/pull/1870) by [@EvanBacon](https://github.com/EvanBacon))
|
||||||
|
- Selecting default keystore via CLI. ([#1889](https://github.com/expo/eas-cli/pull/1889) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Change sso flag display. ([#1890](https://github.com/expo/eas-cli/pull/1890) by [@lzkb](https://github.com/lzkb))
|
||||||
|
- Build:configure -- add channels to eas.json if using eas updates. ([#1887](https://github.com/expo/eas-cli/pull/1887) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Eas update: Error gracefully if no git repo. ([#1884](https://github.com/expo/eas-cli/pull/1884) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Error gracefully if expo pkg not installed. ([#1883](https://github.com/expo/eas-cli/pull/1883) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Update `eas build:configure` command to show the link of `eas.json` when generated. ([#1878](https://github.com/expo/eas-cli/pull/1878) by [@amandeepmittal](https://github.com/amandeepmittal))
|
||||||
|
- Create app.json or add the "expo" key if either are missing, before modifying or reading the file. ([#1881](https://github.com/expo/eas-cli/pull/1881) by [@brentvatne](https://github.com/brentvatne))
|
||||||
|
- Include the original stack in re-thrown errors thrown from EAS CLI commands. ([#1882](https://github.com/expo/eas-cli/pull/1882) by [@brentvatne](https://github.com/brentvatne))
|
||||||
|
- Make `update:configure` less verbose. ([#1888](https://github.com/expo/eas-cli/pull/1888) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Improve validation for values from app config. ([#1893](https://github.com/expo/eas-cli/pull/1893) by [@wkozyra95](https://github.com/wkozyra95))
|
||||||
|
- Improve `expo-updates` validation for builds with `channel` property set. ([#1885](https://github.com/expo/eas-cli/pull/1885) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.13.3](https://github.com/expo/eas-cli/releases/tag/v3.13.3) - 2023-06-05
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Show original GraphQL error message in case of an unexpected error. ([#1862](https://github.com/expo/eas-cli/pull/1862) by [@dsokal](https://github.com/dsokal))
|
||||||
|
- Fix updates synchronization of native files to include strings.xml for bare projects. ([#1865](https://github.com/expo/eas-cli/pull/1865) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
## [3.13.2](https://github.com/expo/eas-cli/releases/tag/v3.13.2) - 2023-05-26
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Refactor getExpoConfig to remove dangerous default. ([#1857](https://github.com/expo/eas-cli/pull/1857) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Add support for pending-cancel build status. ([#1855](https://github.com/expo/eas-cli/pull/1855) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
## [3.13.1](https://github.com/expo/eas-cli/releases/tag/v3.13.1) - 2023-05-24
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Short format for selecting devices prompt. ([#1840](https://github.com/expo/eas-cli/pull/1840) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
- Improve typescript types for user display. ([#1851](https://github.com/expo/eas-cli/pull/1851) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Add error for `large` resource class not available on the free plan to server-side defined errors. ([#1848](https://github.com/expo/eas-cli/pull/1848) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.13.0](https://github.com/expo/eas-cli/releases/tag/v3.13.0) - 2023-05-17
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add clear cache flag to eas update. ([#1839](https://github.com/expo/eas-cli/pull/1839) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Print EAS Update assets that timed out during upload or processing. ([#1849](https://github.com/expo/eas-cli/pull/1849) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix building iOS projects with framework targets. ([#1835](https://github.com/expo/eas-cli/pull/1835) by [@dsokal](https://github.com/dsokal))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Ignore dirty workingdir when building from GitHub. ([#1842](https://github.com/expo/eas-cli/pull/1842) by [@wkozyra95](https://github.com/wkozyra95))
|
||||||
|
- Remove App Specific Password prompt. ([#1843](https://github.com/expo/eas-cli/pull/1843) by [@quinlanj](https://github.com/quinlanj))
|
||||||
|
- Validate `CFBundleShortVersionString`. ([#1846](https://github.com/expo/eas-cli/pull/1846) by [@khamilowicz](https://github.com/khamilowicz))
|
||||||
|
|
||||||
|
## [3.12.1](https://github.com/expo/eas-cli/releases/tag/v3.12.1) - 2023-05-15
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Don't submit expired builds. ([#1837](https://github.com/expo/eas-cli/pull/1837) by [@dsokal](https://github.com/dsokal))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Change red apple icon to green apple icon on successful build. ([#1828](https://github.com/expo/eas-cli/pull/1828) by [@dchhetri](https://github.com/dchhetri))
|
||||||
|
|
||||||
|
## [3.12.0](https://github.com/expo/eas-cli/releases/tag/v3.12.0) - 2023-05-08
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add `build:version:get` command. ([#1815](https://github.com/expo/eas-cli/pull/1815) by [@wkozyra95](https://github.com/wkozyra95))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Upgrade dependencies. ([#1824](https://github.com/expo/eas-cli/pull/1824) by [@dsokal](https://github.com/dsokal))
|
||||||
|
|
||||||
|
## [3.11.0](https://github.com/expo/eas-cli/releases/tag/v3.11.0) - 2023-05-05
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Add account list to `eas whoami`/`eas account:view`. ([#1814](https://github.com/expo/eas-cli/pull/1814) by [@wschurman](https://github.com/wschurman))
|
||||||
|
- Add `large` resource class for iOS as allowed value in `eas.json`. ([#1817](https://github.com/expo/eas-cli/pull/1817) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Print request ID on unforeseen GraphQL error for easier tracking and follow up. ([#1813](https://github.com/expo/eas-cli/pull/1813) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
## [3.10.2](https://github.com/expo/eas-cli/releases/tag/v3.10.2) - 2023-04-25
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add info about Xcode 14.3 image to `eas.schema.json`. ([#1808](https://github.com/expo/eas-cli/pull/1808) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Allow users to select an app to run using the `build:run` command if multiple apps are found in the tarball. ([#1807](https://github.com/expo/eas-cli/pull/1807) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
- Don't resolve `default` resource class for iOS in CLI. ([#1734](https://github.com/expo/eas-cli/pull/1734) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
|
## [3.10.1](https://github.com/expo/eas-cli/releases/tag/v3.10.1) - 2023-04-25
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Add `cache.paths` field and deprecate `cache.customPaths`. ([#1794](https://github.com/expo/eas-cli/pull/1794) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||||
|
|
||||||
|
## [3.10.0](https://github.com/expo/eas-cli/releases/tag/v3.10.0) - 2023-04-21
|
||||||
|
|
||||||
|
### 🎉 New features
|
||||||
|
|
||||||
|
- Added eas device:rename command. ([#1787](https://github.com/expo/eas-cli/pull/1787) by [@keith-kurak](https://github.com/keith-kurak))
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
- Fix `device:delete` not disabling on Apple. ([#1803](https://github.com/expo/eas-cli/pull/1803) by [@keith-kurak](https://github.com/keith-kurak))
|
||||||
|
- Fix message truncation for updates. ([#1801](https://github.com/expo/eas-cli/pull/1801) by [@wschurman](https://github.com/wschurman))
|
||||||
|
|
||||||
|
### 🧹 Chores
|
||||||
|
|
||||||
|
- Throw an error if somebody tries to start iOS build with the `large` resource class selected using the deprecated `--resource-class` flag. ([#1795](https://github.com/expo/eas-cli/pull/1795) by [@szdziedzic](https://github.com/szdziedzic))
|
||||||
|
|
||||||
## [3.9.3](https://github.com/expo/eas-cli/releases/tag/v3.9.3) - 2023-04-18
|
## [3.9.3](https://github.com/expo/eas-cli/releases/tag/v3.9.3) - 2023-04-18
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
@@ -89,7 +216,7 @@ This is the log of notable changes to EAS CLI and related packages.
|
|||||||
|
|
||||||
### 🎉 New features
|
### 🎉 New features
|
||||||
|
|
||||||
- Support variant detection for package*UniversalApk Gradle Commands. ([#1708](https://github.com/expo/eas-cli/pull/1708) by [@frw](https://github.com/frw))
|
- Support variant detection for package\*UniversalApk Gradle Commands. ([#1708](https://github.com/expo/eas-cli/pull/1708) by [@frw](https://github.com/frw))
|
||||||
|
|
||||||
## [3.6.1](https://github.com/expo/eas-cli/releases/tag/v3.6.1) - 2023-02-20
|
## [3.6.1](https://github.com/expo/eas-cli/releases/tag/v3.6.1) - 2023-02-20
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,10 +1,10 @@
|
|||||||
* @dsokal @wkozyra95
|
* @szdziedzic @khamilowicz
|
||||||
|
|
||||||
packages/eas-cli/src/build @dsokal @wkozyra95
|
packages/eas-cli/src/build @szdziedzic @khamilowicz
|
||||||
packages/eas-cli/src/commands/build @dsokal @wkozyra95
|
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz
|
||||||
|
|
||||||
packages/eas-cli/src/submit @dsokal
|
packages/eas-cli/src/submit @szdziedzic @khamilowicz
|
||||||
packages/eas-cli/src/commands/submit.ts @dsokal
|
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz
|
||||||
|
|
||||||
packages/eas-cli/src/update @EvanBacon @byCedric @wschurman
|
packages/eas-cli/src/update @EvanBacon @byCedric @wschurman
|
||||||
packages/eas-cli/src/commands/update @EvanBacon @byCedric @wschurman
|
packages/eas-cli/src/commands/update @EvanBacon @byCedric @wschurman
|
||||||
@@ -13,4 +13,4 @@ packages/eas-cli/schema @bycedric
|
|||||||
packages/eas-cli/src/metadata @bycedric
|
packages/eas-cli/src/metadata @bycedric
|
||||||
packages/eas-cli/src/commands/metadata @bycedric
|
packages/eas-cli/src/commands/metadata @bycedric
|
||||||
|
|
||||||
packages/eas-json @dsokal @wkozyra95
|
packages/eas-json @szdziedzic @khamilowicz
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"version": "3.9.3",
|
"version": "3.14.0",
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||||
}
|
}
|
||||||
+178
-130
@@ -55,66 +55,69 @@ eas --help COMMAND
|
|||||||
# Commands
|
# Commands
|
||||||
|
|
||||||
<!-- commands -->
|
<!-- commands -->
|
||||||
* [`eas account:login`](#eas-accountlogin)
|
|
||||||
* [`eas account:logout`](#eas-accountlogout)
|
- [`eas account:login`](#eas-accountlogin)
|
||||||
* [`eas account:view`](#eas-accountview)
|
- [`eas account:logout`](#eas-accountlogout)
|
||||||
* [`eas analytics [STATUS]`](#eas-analytics-status)
|
- [`eas account:view`](#eas-accountview)
|
||||||
* [`eas autocomplete [SHELL]`](#eas-autocomplete-shell)
|
- [`eas analytics [STATUS]`](#eas-analytics-status)
|
||||||
* [`eas branch:create [NAME]`](#eas-branchcreate-name)
|
- [`eas autocomplete [SHELL]`](#eas-autocomplete-shell)
|
||||||
* [`eas branch:delete [NAME]`](#eas-branchdelete-name)
|
- [`eas branch:create [NAME]`](#eas-branchcreate-name)
|
||||||
* [`eas branch:list`](#eas-branchlist)
|
- [`eas branch:delete [NAME]`](#eas-branchdelete-name)
|
||||||
* [`eas branch:rename`](#eas-branchrename)
|
- [`eas branch:list`](#eas-branchlist)
|
||||||
* [`eas branch:view [NAME]`](#eas-branchview-name)
|
- [`eas branch:rename`](#eas-branchrename)
|
||||||
* [`eas build`](#eas-build)
|
- [`eas branch:view [NAME]`](#eas-branchview-name)
|
||||||
* [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
- [`eas build`](#eas-build)
|
||||||
* [`eas build:configure`](#eas-buildconfigure)
|
- [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
||||||
* [`eas build:inspect`](#eas-buildinspect)
|
- [`eas build:configure`](#eas-buildconfigure)
|
||||||
* [`eas build:list`](#eas-buildlist)
|
- [`eas build:inspect`](#eas-buildinspect)
|
||||||
* [`eas build:resign`](#eas-buildresign)
|
- [`eas build:list`](#eas-buildlist)
|
||||||
* [`eas build:run`](#eas-buildrun)
|
- [`eas build:resign`](#eas-buildresign)
|
||||||
* [`eas build:submit`](#eas-buildsubmit)
|
- [`eas build:run`](#eas-buildrun)
|
||||||
* [`eas build:version:set`](#eas-buildversionset)
|
- [`eas build:submit`](#eas-buildsubmit)
|
||||||
* [`eas build:version:sync`](#eas-buildversionsync)
|
- [`eas build:version:get`](#eas-buildversionget)
|
||||||
* [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
- [`eas build:version:set`](#eas-buildversionset)
|
||||||
* [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
- [`eas build:version:sync`](#eas-buildversionsync)
|
||||||
* [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
- [`eas build:view [BUILD_ID]`](#eas-buildview-build_id)
|
||||||
* [`eas channel:list`](#eas-channellist)
|
- [`eas channel:create [NAME]`](#eas-channelcreate-name)
|
||||||
* [`eas channel:rollout [CHANNEL]`](#eas-channelrollout-channel)
|
- [`eas channel:edit [NAME]`](#eas-channeledit-name)
|
||||||
* [`eas channel:view [NAME]`](#eas-channelview-name)
|
- [`eas channel:list`](#eas-channellist)
|
||||||
* [`eas config`](#eas-config)
|
- [`eas channel:rollout [CHANNEL]`](#eas-channelrollout-channel)
|
||||||
* [`eas credentials`](#eas-credentials)
|
- [`eas channel:view [NAME]`](#eas-channelview-name)
|
||||||
* [`eas device:create`](#eas-devicecreate)
|
- [`eas config`](#eas-config)
|
||||||
* [`eas device:delete`](#eas-devicedelete)
|
- [`eas credentials`](#eas-credentials)
|
||||||
* [`eas device:list`](#eas-devicelist)
|
- [`eas device:create`](#eas-devicecreate)
|
||||||
* [`eas device:view [UDID]`](#eas-deviceview-udid)
|
- [`eas device:delete`](#eas-devicedelete)
|
||||||
* [`eas diagnostics`](#eas-diagnostics)
|
- [`eas device:list`](#eas-devicelist)
|
||||||
* [`eas help [COMMAND]`](#eas-help-command)
|
- [`eas device:rename`](#eas-devicerename)
|
||||||
* [`eas init`](#eas-init)
|
- [`eas device:view [UDID]`](#eas-deviceview-udid)
|
||||||
* [`eas login`](#eas-login)
|
- [`eas diagnostics`](#eas-diagnostics)
|
||||||
* [`eas logout`](#eas-logout)
|
- [`eas help [COMMAND]`](#eas-help-command)
|
||||||
* [`eas metadata:lint`](#eas-metadatalint)
|
- [`eas init`](#eas-init)
|
||||||
* [`eas metadata:pull`](#eas-metadatapull)
|
- [`eas login`](#eas-login)
|
||||||
* [`eas metadata:push`](#eas-metadatapush)
|
- [`eas logout`](#eas-logout)
|
||||||
* [`eas open`](#eas-open)
|
- [`eas metadata:lint`](#eas-metadatalint)
|
||||||
* [`eas project:info`](#eas-projectinfo)
|
- [`eas metadata:pull`](#eas-metadatapull)
|
||||||
* [`eas project:init`](#eas-projectinit)
|
- [`eas metadata:push`](#eas-metadatapush)
|
||||||
* [`eas secret:create`](#eas-secretcreate)
|
- [`eas open`](#eas-open)
|
||||||
* [`eas secret:delete`](#eas-secretdelete)
|
- [`eas project:info`](#eas-projectinfo)
|
||||||
* [`eas secret:list`](#eas-secretlist)
|
- [`eas project:init`](#eas-projectinit)
|
||||||
* [`eas secret:push`](#eas-secretpush)
|
- [`eas secret:create`](#eas-secretcreate)
|
||||||
* [`eas submit`](#eas-submit)
|
- [`eas secret:delete`](#eas-secretdelete)
|
||||||
* [`eas update`](#eas-update)
|
- [`eas secret:list`](#eas-secretlist)
|
||||||
* [`eas update:configure`](#eas-updateconfigure)
|
- [`eas secret:push`](#eas-secretpush)
|
||||||
* [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
|
- [`eas submit`](#eas-submit)
|
||||||
* [`eas update:list`](#eas-updatelist)
|
- [`eas update`](#eas-update)
|
||||||
* [`eas update:republish`](#eas-updaterepublish)
|
- [`eas update:configure`](#eas-updateconfigure)
|
||||||
* [`eas update:view GROUPID`](#eas-updateview-groupid)
|
- [`eas update:delete GROUPID`](#eas-updatedelete-groupid)
|
||||||
* [`eas webhook:create`](#eas-webhookcreate)
|
- [`eas update:list`](#eas-updatelist)
|
||||||
* [`eas webhook:delete [ID]`](#eas-webhookdelete-id)
|
- [`eas update:republish`](#eas-updaterepublish)
|
||||||
* [`eas webhook:list`](#eas-webhooklist)
|
- [`eas update:view GROUPID`](#eas-updateview-groupid)
|
||||||
* [`eas webhook:update`](#eas-webhookupdate)
|
- [`eas webhook:create`](#eas-webhookcreate)
|
||||||
* [`eas webhook:view ID`](#eas-webhookview-id)
|
- [`eas webhook:delete [ID]`](#eas-webhookdelete-id)
|
||||||
* [`eas whoami`](#eas-whoami)
|
- [`eas webhook:list`](#eas-webhooklist)
|
||||||
|
- [`eas webhook:update`](#eas-webhookupdate)
|
||||||
|
- [`eas webhook:view ID`](#eas-webhookview-id)
|
||||||
|
- [`eas whoami`](#eas-whoami)
|
||||||
|
|
||||||
## `eas account:login`
|
## `eas account:login`
|
||||||
|
|
||||||
@@ -131,7 +134,7 @@ ALIASES
|
|||||||
$ eas login
|
$ eas login
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/login.ts)_
|
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/account/login.ts)_
|
||||||
|
|
||||||
## `eas account:logout`
|
## `eas account:logout`
|
||||||
|
|
||||||
@@ -148,7 +151,7 @@ ALIASES
|
|||||||
$ eas logout
|
$ eas logout
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/logout.ts)_
|
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/account/logout.ts)_
|
||||||
|
|
||||||
## `eas account:view`
|
## `eas account:view`
|
||||||
|
|
||||||
@@ -165,7 +168,7 @@ ALIASES
|
|||||||
$ eas whoami
|
$ eas whoami
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/account/view.ts)_
|
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/account/view.ts)_
|
||||||
|
|
||||||
## `eas analytics [STATUS]`
|
## `eas analytics [STATUS]`
|
||||||
|
|
||||||
@@ -179,7 +182,7 @@ DESCRIPTION
|
|||||||
display or change analytics settings
|
display or change analytics settings
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/analytics.ts)_
|
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/analytics.ts)_
|
||||||
|
|
||||||
## `eas autocomplete [SHELL]`
|
## `eas autocomplete [SHELL]`
|
||||||
|
|
||||||
@@ -229,7 +232,7 @@ DESCRIPTION
|
|||||||
create a branch
|
create a branch
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/create.ts)_
|
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/branch/create.ts)_
|
||||||
|
|
||||||
## `eas branch:delete [NAME]`
|
## `eas branch:delete [NAME]`
|
||||||
|
|
||||||
@@ -250,7 +253,7 @@ DESCRIPTION
|
|||||||
delete a branch
|
delete a branch
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/delete.ts)_
|
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||||
|
|
||||||
## `eas branch:list`
|
## `eas branch:list`
|
||||||
|
|
||||||
@@ -270,7 +273,7 @@ DESCRIPTION
|
|||||||
list all branches
|
list all branches
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/list.ts)_
|
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/branch/list.ts)_
|
||||||
|
|
||||||
## `eas branch:rename`
|
## `eas branch:rename`
|
||||||
|
|
||||||
@@ -290,7 +293,7 @@ DESCRIPTION
|
|||||||
rename a branch
|
rename a branch
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/rename.ts)_
|
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||||
|
|
||||||
## `eas branch:view [NAME]`
|
## `eas branch:view [NAME]`
|
||||||
|
|
||||||
@@ -313,7 +316,7 @@ DESCRIPTION
|
|||||||
view a branch
|
view a branch
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/branch/view.ts)_
|
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/branch/view.ts)_
|
||||||
|
|
||||||
## `eas build`
|
## `eas build`
|
||||||
|
|
||||||
@@ -343,7 +346,7 @@ DESCRIPTION
|
|||||||
start a build
|
start a build
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/index.ts)_
|
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/index.ts)_
|
||||||
|
|
||||||
## `eas build:cancel [BUILD_ID]`
|
## `eas build:cancel [BUILD_ID]`
|
||||||
|
|
||||||
@@ -360,7 +363,7 @@ DESCRIPTION
|
|||||||
cancel a build
|
cancel a build
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/cancel.ts)_
|
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||||
|
|
||||||
## `eas build:configure`
|
## `eas build:configure`
|
||||||
|
|
||||||
@@ -377,7 +380,7 @@ DESCRIPTION
|
|||||||
configure the project to support EAS Build
|
configure the project to support EAS Build
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/configure.ts)_
|
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/configure.ts)_
|
||||||
|
|
||||||
## `eas build:inspect`
|
## `eas build:inspect`
|
||||||
|
|
||||||
@@ -412,7 +415,7 @@ DESCRIPTION
|
|||||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/inspect.ts)_
|
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||||
|
|
||||||
## `eas build:list`
|
## `eas build:list`
|
||||||
|
|
||||||
@@ -420,10 +423,11 @@ list all builds for your project
|
|||||||
|
|
||||||
```
|
```
|
||||||
USAGE
|
USAGE
|
||||||
$ eas build:list [--platform all|android|ios] [--status new|in-queue|in-progress|errored|finished|canceled]
|
$ eas build:list [--platform all|android|ios] [--status
|
||||||
[--distribution store|internal|simulator] [--channel <value>] [--appVersion <value>] [--appBuildVersion <value>]
|
new|in-queue|in-progress|pending-cancel|errored|finished|canceled] [--distribution store|internal|simulator]
|
||||||
[--sdkVersion <value>] [--runtimeVersion <value>] [--appIdentifier <value>] [--buildProfile <value>]
|
[--channel <value>] [--appVersion <value>] [--appBuildVersion <value>] [--sdkVersion <value>] [--runtimeVersion
|
||||||
[--gitCommitHash <value>] [--offset <value>] [--limit <value>] [--json --non-interactive]
|
<value>] [--appIdentifier <value>] [--buildProfile <value>] [--gitCommitHash <value>] [--offset <value>] [--limit
|
||||||
|
<value>] [--json --non-interactive]
|
||||||
|
|
||||||
FLAGS
|
FLAGS
|
||||||
--appBuildVersion=<value>
|
--appBuildVersion=<value>
|
||||||
@@ -433,23 +437,23 @@ FLAGS
|
|||||||
--channel=<value>
|
--channel=<value>
|
||||||
--distribution=(store|internal|simulator)
|
--distribution=(store|internal|simulator)
|
||||||
--gitCommitHash=<value>
|
--gitCommitHash=<value>
|
||||||
--json Enable JSON output, non-JSON messages will be printed
|
--json Enable JSON output, non-JSON messages
|
||||||
to stderr.
|
will be printed to stderr.
|
||||||
--limit=<value> The number of items to fetch each query. Defaults to 10
|
--limit=<value> The number of items to fetch each query.
|
||||||
and is capped at 50.
|
Defaults to 10 and is capped at 50.
|
||||||
--non-interactive Run the command in non-interactive mode.
|
--non-interactive Run the command in non-interactive mode.
|
||||||
--offset=<value> Start queries from specified index. Use for paginating
|
--offset=<value> Start queries from specified index. Use
|
||||||
results. Defaults to 0.
|
for paginating results. Defaults to 0.
|
||||||
--platform=(all|android|ios)
|
--platform=(all|android|ios)
|
||||||
--runtimeVersion=<value>
|
--runtimeVersion=<value>
|
||||||
--sdkVersion=<value>
|
--sdkVersion=<value>
|
||||||
--status=(new|in-queue|in-progress|errored|finished|canceled)
|
--status=(new|in-queue|in-progress|pending-cancel|errored|finished|canceled)
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
list all builds for your project
|
list all builds for your project
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/list.ts)_
|
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/list.ts)_
|
||||||
|
|
||||||
## `eas build:resign`
|
## `eas build:resign`
|
||||||
|
|
||||||
@@ -475,7 +479,7 @@ DESCRIPTION
|
|||||||
re-sign a build archive
|
re-sign a build archive
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/resign.ts)_
|
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/resign.ts)_
|
||||||
|
|
||||||
## `eas build:run`
|
## `eas build:run`
|
||||||
|
|
||||||
@@ -499,7 +503,7 @@ DESCRIPTION
|
|||||||
run simulator/emulator builds from eas-cli
|
run simulator/emulator builds from eas-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/run.ts)_
|
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/run.ts)_
|
||||||
|
|
||||||
## `eas build:submit`
|
## `eas build:submit`
|
||||||
|
|
||||||
@@ -529,9 +533,30 @@ ALIASES
|
|||||||
$ eas build:submit
|
$ eas build:submit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `eas build:version:get`
|
||||||
|
|
||||||
|
get the latest version from EAS servers
|
||||||
|
|
||||||
|
```
|
||||||
|
USAGE
|
||||||
|
$ eas build:version:get [-p android|ios|all] [-e <value>] [--json --non-interactive]
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
|
||||||
|
eas.json.
|
||||||
|
-p, --platform=(android|ios|all)
|
||||||
|
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||||
|
--non-interactive Run the command in non-interactive mode.
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
get the latest version from EAS servers
|
||||||
|
```
|
||||||
|
|
||||||
|
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||||
|
|
||||||
## `eas build:version:set`
|
## `eas build:version:set`
|
||||||
|
|
||||||
Update version of an app.
|
update version of an app
|
||||||
|
|
||||||
```
|
```
|
||||||
USAGE
|
USAGE
|
||||||
@@ -543,14 +568,14 @@ FLAGS
|
|||||||
-p, --platform=(android|ios)
|
-p, --platform=(android|ios)
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
Update version of an app.
|
update version of an app
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/version/set.ts)_
|
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||||
|
|
||||||
## `eas build:version:sync`
|
## `eas build:version:sync`
|
||||||
|
|
||||||
Update a version in native code with a value stored on EAS servers
|
update a version in native code with a value stored on EAS servers
|
||||||
|
|
||||||
```
|
```
|
||||||
USAGE
|
USAGE
|
||||||
@@ -562,10 +587,10 @@ FLAGS
|
|||||||
-p, --platform=(android|ios|all)
|
-p, --platform=(android|ios|all)
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
Update a version in native code with a value stored on EAS servers
|
update a version in native code with a value stored on EAS servers
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/version/sync.ts)_
|
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||||
|
|
||||||
## `eas build:view [BUILD_ID]`
|
## `eas build:view [BUILD_ID]`
|
||||||
|
|
||||||
@@ -582,7 +607,7 @@ DESCRIPTION
|
|||||||
view a build for your project
|
view a build for your project
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/build/view.ts)_
|
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/build/view.ts)_
|
||||||
|
|
||||||
## `eas channel:create [NAME]`
|
## `eas channel:create [NAME]`
|
||||||
|
|
||||||
@@ -603,7 +628,7 @@ DESCRIPTION
|
|||||||
create a channel
|
create a channel
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/create.ts)_
|
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/channel/create.ts)_
|
||||||
|
|
||||||
## `eas channel:edit [NAME]`
|
## `eas channel:edit [NAME]`
|
||||||
|
|
||||||
@@ -625,7 +650,7 @@ DESCRIPTION
|
|||||||
point a channel at a new branch
|
point a channel at a new branch
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/edit.ts)_
|
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||||
|
|
||||||
## `eas channel:list`
|
## `eas channel:list`
|
||||||
|
|
||||||
@@ -645,7 +670,7 @@ DESCRIPTION
|
|||||||
list all channels
|
list all channels
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/list.ts)_
|
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/channel/list.ts)_
|
||||||
|
|
||||||
## `eas channel:rollout [CHANNEL]`
|
## `eas channel:rollout [CHANNEL]`
|
||||||
|
|
||||||
@@ -669,7 +694,7 @@ DESCRIPTION
|
|||||||
Roll a new branch out on a channel incrementally.
|
Roll a new branch out on a channel incrementally.
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/rollout.ts)_
|
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||||
|
|
||||||
## `eas channel:view [NAME]`
|
## `eas channel:view [NAME]`
|
||||||
|
|
||||||
@@ -692,7 +717,7 @@ DESCRIPTION
|
|||||||
view a channel
|
view a channel
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/channel/view.ts)_
|
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/channel/view.ts)_
|
||||||
|
|
||||||
## `eas config`
|
## `eas config`
|
||||||
|
|
||||||
@@ -713,7 +738,7 @@ DESCRIPTION
|
|||||||
display project configuration (app.json + eas.json)
|
display project configuration (app.json + eas.json)
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/config.ts)_
|
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/config.ts)_
|
||||||
|
|
||||||
## `eas credentials`
|
## `eas credentials`
|
||||||
|
|
||||||
@@ -730,7 +755,7 @@ DESCRIPTION
|
|||||||
manage credentials
|
manage credentials
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/credentials.ts)_
|
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/credentials.ts)_
|
||||||
|
|
||||||
## `eas device:create`
|
## `eas device:create`
|
||||||
|
|
||||||
@@ -744,7 +769,7 @@ DESCRIPTION
|
|||||||
register new Apple Devices to use for internal distribution
|
register new Apple Devices to use for internal distribution
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/create.ts)_
|
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/device/create.ts)_
|
||||||
|
|
||||||
## `eas device:delete`
|
## `eas device:delete`
|
||||||
|
|
||||||
@@ -764,7 +789,7 @@ DESCRIPTION
|
|||||||
remove a registered device from your account
|
remove a registered device from your account
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/delete.ts)_
|
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/device/delete.ts)_
|
||||||
|
|
||||||
## `eas device:list`
|
## `eas device:list`
|
||||||
|
|
||||||
@@ -785,7 +810,28 @@ DESCRIPTION
|
|||||||
list all registered devices for your account
|
list all registered devices for your account
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/list.ts)_
|
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/device/list.ts)_
|
||||||
|
|
||||||
|
## `eas device:rename`
|
||||||
|
|
||||||
|
rename a registered device
|
||||||
|
|
||||||
|
```
|
||||||
|
USAGE
|
||||||
|
$ eas device:rename [--apple-team-id <value>] [--udid <value>] [--name <value>] [--json --non-interactive]
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--apple-team-id=<value> The Apple team ID on which to find the device
|
||||||
|
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||||
|
--name=<value> The new name for the device
|
||||||
|
--non-interactive Run the command in non-interactive mode.
|
||||||
|
--udid=<value> The Apple device ID to rename
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
rename a registered device
|
||||||
|
```
|
||||||
|
|
||||||
|
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/device/rename.ts)_
|
||||||
|
|
||||||
## `eas device:view [UDID]`
|
## `eas device:view [UDID]`
|
||||||
|
|
||||||
@@ -799,7 +845,7 @@ DESCRIPTION
|
|||||||
view a device for your project
|
view a device for your project
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/device/view.ts)_
|
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/device/view.ts)_
|
||||||
|
|
||||||
## `eas diagnostics`
|
## `eas diagnostics`
|
||||||
|
|
||||||
@@ -813,7 +859,7 @@ DESCRIPTION
|
|||||||
display environment info
|
display environment info
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/diagnostics.ts)_
|
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||||
|
|
||||||
## `eas help [COMMAND]`
|
## `eas help [COMMAND]`
|
||||||
|
|
||||||
@@ -901,7 +947,7 @@ DESCRIPTION
|
|||||||
validate the local store configuration
|
validate the local store configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/lint.ts)_
|
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||||
|
|
||||||
## `eas metadata:pull`
|
## `eas metadata:pull`
|
||||||
|
|
||||||
@@ -918,7 +964,7 @@ DESCRIPTION
|
|||||||
generate the local store configuration from the app stores
|
generate the local store configuration from the app stores
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/pull.ts)_
|
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||||
|
|
||||||
## `eas metadata:push`
|
## `eas metadata:push`
|
||||||
|
|
||||||
@@ -935,7 +981,7 @@ DESCRIPTION
|
|||||||
sync the local store configuration to the app stores
|
sync the local store configuration to the app stores
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/metadata/push.ts)_
|
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||||
|
|
||||||
## `eas open`
|
## `eas open`
|
||||||
|
|
||||||
@@ -949,7 +995,7 @@ DESCRIPTION
|
|||||||
open the project page in a web browser
|
open the project page in a web browser
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/open.ts)_
|
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/open.ts)_
|
||||||
|
|
||||||
## `eas project:info`
|
## `eas project:info`
|
||||||
|
|
||||||
@@ -963,7 +1009,7 @@ DESCRIPTION
|
|||||||
information about the current project
|
information about the current project
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/project/info.ts)_
|
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/project/info.ts)_
|
||||||
|
|
||||||
## `eas project:init`
|
## `eas project:init`
|
||||||
|
|
||||||
@@ -985,7 +1031,7 @@ ALIASES
|
|||||||
$ eas init
|
$ eas init
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/project/init.ts)_
|
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/project/init.ts)_
|
||||||
|
|
||||||
## `eas secret:create`
|
## `eas secret:create`
|
||||||
|
|
||||||
@@ -1008,7 +1054,7 @@ DESCRIPTION
|
|||||||
create an environment secret on the current project or owner account
|
create an environment secret on the current project or owner account
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/create.ts)_
|
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/secret/create.ts)_
|
||||||
|
|
||||||
## `eas secret:delete`
|
## `eas secret:delete`
|
||||||
|
|
||||||
@@ -1026,7 +1072,7 @@ DESCRIPTION
|
|||||||
delete an environment secret by ID
|
delete an environment secret by ID
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/delete.ts)_
|
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||||
|
|
||||||
## `eas secret:list`
|
## `eas secret:list`
|
||||||
|
|
||||||
@@ -1040,7 +1086,7 @@ DESCRIPTION
|
|||||||
list environment secrets available for your current app
|
list environment secrets available for your current app
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/list.ts)_
|
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/secret/list.ts)_
|
||||||
|
|
||||||
## `eas secret:push`
|
## `eas secret:push`
|
||||||
|
|
||||||
@@ -1060,7 +1106,7 @@ DESCRIPTION
|
|||||||
read environment secrets from env file and store on the server
|
read environment secrets from env file and store on the server
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/secret/push.ts)_
|
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/secret/push.ts)_
|
||||||
|
|
||||||
## `eas submit`
|
## `eas submit`
|
||||||
|
|
||||||
@@ -1090,7 +1136,7 @@ ALIASES
|
|||||||
$ eas build:submit
|
$ eas build:submit
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/submit.ts)_
|
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/submit.ts)_
|
||||||
|
|
||||||
## `eas update`
|
## `eas update`
|
||||||
|
|
||||||
@@ -1099,14 +1145,15 @@ publish an update group
|
|||||||
```
|
```
|
||||||
USAGE
|
USAGE
|
||||||
$ eas update [--branch <value>] [--channel <value>] [--message <value>] [--republish | --input-dir <value>
|
$ eas update [--branch <value>] [--channel <value>] [--message <value>] [--republish | --input-dir <value>
|
||||||
| --skip-bundler] [--group <value> | | ] [-p android|ios|all] [--auto] [--private-key-path <value>] [--json
|
| --skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--auto] [--private-key-path <value>]
|
||||||
--non-interactive]
|
[--json --non-interactive]
|
||||||
|
|
||||||
FLAGS
|
FLAGS
|
||||||
-p, --platform=(android|ios|all) [default: all]
|
-p, --platform=(android|ios|all) [default: all]
|
||||||
--auto Use the current git branch and commit message for the EAS branch and update message
|
--auto Use the current git branch and commit message for the EAS branch and update message
|
||||||
--branch=<value> Branch to publish the update group on
|
--branch=<value> Branch to publish the update group on
|
||||||
--channel=<value> Channel that the published update should affect
|
--channel=<value> Channel that the published update should affect
|
||||||
|
--clear-cache Clear the bundler cache before publishing
|
||||||
--group=<value> Update group to republish (deprecated, see republish command)
|
--group=<value> Update group to republish (deprecated, see republish command)
|
||||||
--input-dir=<value> [default: dist] Location of the bundle
|
--input-dir=<value> [default: dist] Location of the bundle
|
||||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||||
@@ -1122,7 +1169,7 @@ DESCRIPTION
|
|||||||
publish an update group
|
publish an update group
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/index.ts)_
|
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/index.ts)_
|
||||||
|
|
||||||
## `eas update:configure`
|
## `eas update:configure`
|
||||||
|
|
||||||
@@ -1140,7 +1187,7 @@ DESCRIPTION
|
|||||||
configure the project to support EAS Update
|
configure the project to support EAS Update
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/configure.ts)_
|
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/configure.ts)_
|
||||||
|
|
||||||
## `eas update:delete GROUPID`
|
## `eas update:delete GROUPID`
|
||||||
|
|
||||||
@@ -1161,7 +1208,7 @@ DESCRIPTION
|
|||||||
delete all the updates in an update group
|
delete all the updates in an update group
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/delete.ts)_
|
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/delete.ts)_
|
||||||
|
|
||||||
## `eas update:list`
|
## `eas update:list`
|
||||||
|
|
||||||
@@ -1183,7 +1230,7 @@ DESCRIPTION
|
|||||||
view the recent updates
|
view the recent updates
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/list.ts)_
|
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/list.ts)_
|
||||||
|
|
||||||
## `eas update:republish`
|
## `eas update:republish`
|
||||||
|
|
||||||
@@ -1207,7 +1254,7 @@ DESCRIPTION
|
|||||||
roll back to an existing update
|
roll back to an existing update
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/republish.ts)_
|
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/republish.ts)_
|
||||||
|
|
||||||
## `eas update:view GROUPID`
|
## `eas update:view GROUPID`
|
||||||
|
|
||||||
@@ -1227,7 +1274,7 @@ DESCRIPTION
|
|||||||
update group details
|
update group details
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/update/view.ts)_
|
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/update/view.ts)_
|
||||||
|
|
||||||
## `eas webhook:create`
|
## `eas webhook:create`
|
||||||
|
|
||||||
@@ -1248,7 +1295,7 @@ DESCRIPTION
|
|||||||
create a webhook
|
create a webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/create.ts)_
|
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||||
|
|
||||||
## `eas webhook:delete [ID]`
|
## `eas webhook:delete [ID]`
|
||||||
|
|
||||||
@@ -1268,7 +1315,7 @@ DESCRIPTION
|
|||||||
delete a webhook
|
delete a webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/delete.ts)_
|
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||||
|
|
||||||
## `eas webhook:list`
|
## `eas webhook:list`
|
||||||
|
|
||||||
@@ -1286,7 +1333,7 @@ DESCRIPTION
|
|||||||
list webhooks
|
list webhooks
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/list.ts)_
|
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||||
|
|
||||||
## `eas webhook:update`
|
## `eas webhook:update`
|
||||||
|
|
||||||
@@ -1308,7 +1355,7 @@ DESCRIPTION
|
|||||||
update a webhook
|
update a webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/update.ts)_
|
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||||
|
|
||||||
## `eas webhook:view ID`
|
## `eas webhook:view ID`
|
||||||
|
|
||||||
@@ -1325,7 +1372,7 @@ DESCRIPTION
|
|||||||
view a webhook
|
view a webhook
|
||||||
```
|
```
|
||||||
|
|
||||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.9.3/packages/eas-cli/src/commands/webhook/view.ts)_
|
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v3.14.0/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||||
|
|
||||||
## `eas whoami`
|
## `eas whoami`
|
||||||
|
|
||||||
@@ -1341,4 +1388,5 @@ DESCRIPTION
|
|||||||
ALIASES
|
ALIASES
|
||||||
$ eas whoami
|
$ eas whoami
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- commandsstop -->
|
<!-- commandsstop -->
|
||||||
Generated
+1939
-363
File diff suppressed because it is too large.
Load diff
@@ -2,4 +2,5 @@ module.exports = async () => {
|
|||||||
process.env.TZ = 'UTC';
|
process.env.TZ = 'UTC';
|
||||||
process.env.EAS_NO_VCS = '1';
|
process.env.EAS_NO_VCS = '1';
|
||||||
process.env.EAS_PROJECT_ROOT = '/app';
|
process.env.EAS_PROJECT_ROOT = '/app';
|
||||||
|
process.env.FORCE_COLOR = '1';
|
||||||
};
|
};
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "eas-cli",
|
"name": "eas-cli",
|
||||||
"description": "EAS command line tool",
|
"description": "EAS command line tool",
|
||||||
"version": "3.9.3",
|
"version": "3.14.0",
|
||||||
"author": "Expo <[email protected]>",
|
"author": "Expo <[email protected]>",
|
||||||
"bin": {
|
"bin": {
|
||||||
"eas": "./bin/run"
|
"eas": "./bin/run"
|
||||||
},
|
},
|
||||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/apple-utils": "1.2.0",
|
"@expo/apple-utils": "1.3.1",
|
||||||
"@expo/code-signing-certificates": "0.0.5",
|
"@expo/code-signing-certificates": "0.0.5",
|
||||||
"@expo/config": "7.0.3",
|
"@expo/config": "8.1.2",
|
||||||
"@expo/config-plugins": "5.0.4",
|
"@expo/config-plugins": "7.2.3",
|
||||||
"@expo/config-types": "47.0.0",
|
"@expo/config-types": "49.0.0",
|
||||||
"@expo/eas-build-job": "1.0.7",
|
"@expo/eas-build-job": "1.0.13",
|
||||||
"@expo/eas-json": "3.9.2",
|
"@expo/eas-json": "3.13.0",
|
||||||
"@expo/json-file": "8.2.37",
|
"@expo/json-file": "8.2.37",
|
||||||
"@expo/multipart-body-parser": "1.1.0",
|
"@expo/multipart-body-parser": "1.1.0",
|
||||||
"@expo/osascript": "2.0.33",
|
"@expo/osascript": "2.0.33",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"@expo/results": "1.0.0",
|
"@expo/results": "1.0.0",
|
||||||
"@expo/rudder-sdk-node": "1.1.1",
|
"@expo/rudder-sdk-node": "1.1.1",
|
||||||
"@expo/spawn-async": "1.7.0",
|
"@expo/spawn-async": "1.7.0",
|
||||||
"@expo/steps": "1.0.8",
|
"@expo/steps": "1.0.23",
|
||||||
"@expo/timeago.js": "1.0.0",
|
"@expo/timeago.js": "1.0.0",
|
||||||
"@oclif/core": "1.23.2",
|
"@oclif/core": "1.23.2",
|
||||||
"@oclif/plugin-autocomplete": "1.3.10",
|
"@oclif/plugin-autocomplete": "1.3.10",
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
"fast-glob": "3.2.12",
|
"fast-glob": "3.2.12",
|
||||||
"figures": "3.2.0",
|
"figures": "3.2.0",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
|
"freeport-async": "2.0.0",
|
||||||
"fs-extra": "10.1.0",
|
"fs-extra": "10.1.0",
|
||||||
"getenv": "1.0.0",
|
"getenv": "1.0.0",
|
||||||
"gradle-to-js": "2.0.1",
|
"gradle-to-js": "2.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`BuildVersionGetView reading version when appVersionSource is set to local 1`] = `"This project is not configured for using remote version source. Add [1m{"cli": { "appVersionSource": "remote" }}[22m in eas.json or re-run this command without "--non-interactive" flag."`;
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import BuildVersionGetView from '../../commands/build/version/get';
|
||||||
|
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||||
|
import Log from '../../log';
|
||||||
|
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||||
|
import { getMockEasJson, mockCommandContext, mockProjectId, mockTestCommand } from './utils';
|
||||||
|
|
||||||
|
jest.mock('../../project/applicationIdentifier');
|
||||||
|
jest.mock('fs');
|
||||||
|
jest.mock('../../log');
|
||||||
|
jest.mock('../../utils/json');
|
||||||
|
jest.mock('../../graphql/queries/AppVersionQuery');
|
||||||
|
|
||||||
|
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||||
|
return {
|
||||||
|
...easJson,
|
||||||
|
cli: {
|
||||||
|
...easJson.cli,
|
||||||
|
appVersionSource: AppVersionSource.REMOTE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(BuildVersionGetView, () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
test('reading version for platform android', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '100',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(Log.log).toHaveBeenCalledWith(`Android versionCode - ${chalk.bold('100')}`);
|
||||||
|
expect(enableJsonOutput).not.toHaveBeenCalled();
|
||||||
|
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reading version for platform android when no remote version is set', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionGetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(Log.log).toHaveBeenCalledWith(`No remote versions are configured for this project.`);
|
||||||
|
expect(enableJsonOutput).not.toHaveBeenCalled();
|
||||||
|
expect(printJsonOnlyOutput).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reading version with --json flag for platform android', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '100',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(
|
||||||
|
BuildVersionGetView,
|
||||||
|
['--non-interactive', '--json', '--platform=android'],
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(enableJsonOutput).toHaveBeenCalled();
|
||||||
|
expect(printJsonOnlyOutput).toHaveBeenCalledWith({
|
||||||
|
versionCode: '100',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reading version with --json flag for platform ios', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '100',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(
|
||||||
|
BuildVersionGetView,
|
||||||
|
['--non-interactive', '--json', '--platform=ios'],
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'IOS',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(enableJsonOutput).toHaveBeenCalled();
|
||||||
|
expect(printJsonOnlyOutput).toHaveBeenCalledWith({
|
||||||
|
buildNumber: '100',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reading version with --json flag when no remote version is set', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(
|
||||||
|
BuildVersionGetView,
|
||||||
|
['--non-interactive', '--json', '--platform=android'],
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(enableJsonOutput).toHaveBeenCalled();
|
||||||
|
expect(printJsonOnlyOutput).toHaveBeenCalledWith({});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reading version when appVersionSource is set to local ', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionGetView, {});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '100',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(
|
||||||
|
BuildVersionGetView,
|
||||||
|
['--non-interactive', '--json', '--platform=android'],
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
await expect(cmd.run()).rejects.toThrowErrorMatchingSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import BuildVersionSetView from '../../commands/build/version/set';
|
||||||
|
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||||
|
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||||
|
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||||
|
import Log from '../../log';
|
||||||
|
import * as prompts from '../../prompts';
|
||||||
|
import {
|
||||||
|
getMockAppFragment,
|
||||||
|
getMockEasJson,
|
||||||
|
mockCommandContext,
|
||||||
|
mockProjectId,
|
||||||
|
mockTestCommand,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
jest.mock('../../project/applicationIdentifier');
|
||||||
|
jest.mock('../../graphql/queries/AppVersionQuery');
|
||||||
|
jest.mock('../../graphql/queries/AppQuery');
|
||||||
|
jest.mock('../../graphql/mutations/AppVersionMutation');
|
||||||
|
jest.mock('fs');
|
||||||
|
jest.mock('../../log');
|
||||||
|
jest.mock('../../prompts');
|
||||||
|
jest.mock('../../utils/json');
|
||||||
|
|
||||||
|
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||||
|
return {
|
||||||
|
...easJson,
|
||||||
|
cli: {
|
||||||
|
...easJson.cli,
|
||||||
|
appVersionSource: AppVersionSource.REMOTE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(BuildVersionSetView, () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setting version for platform android', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(AppVersionMutation.createAppVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
expect.objectContaining({
|
||||||
|
buildVersion: '1000',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('printing current remote version before prompting for a new one', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '100',
|
||||||
|
storeVersion: '1.0.0',
|
||||||
|
}));
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
expect(Log.log).toHaveBeenCalledWith(expect.stringMatching('configured with versionCode 100'));
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('printing info that there is no remote version configured', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSetView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
expect(Log.log).toHaveBeenCalledWith(
|
||||||
|
expect.stringMatching('does not have any versionCode configured')
|
||||||
|
);
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setting version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||||
|
await expect(cmd.run()).rejects.toThrowError('Aborting...');
|
||||||
|
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setting version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSetView, {});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSetView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
|
||||||
|
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||||
|
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import { Workflow } from '@expo/eas-build-job';
|
||||||
|
import { AppVersionSource, EasJson } from '@expo/eas-json';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { updateNativeVersionsAsync as updateAndroidNativeVersionsAsync } from '../../build/android/version';
|
||||||
|
import { updateNativeVersionsAsync as updateIosNativeVersionsAsync } from '../../build/ios/version';
|
||||||
|
import BuildVersionSyncView from '../../commands/build/version/sync';
|
||||||
|
import { Target } from '../../credentials/ios/types';
|
||||||
|
import { AppVersionMutation } from '../../graphql/mutations/AppVersionMutation';
|
||||||
|
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||||
|
import { AppVersionQuery } from '../../graphql/queries/AppVersionQuery';
|
||||||
|
import { getAppBuildGradleAsync } from '../../project/android/gradleUtils';
|
||||||
|
import { resolveTargetsAsync } from '../../project/ios/target';
|
||||||
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
|
import * as prompts from '../../prompts';
|
||||||
|
import {
|
||||||
|
getMockAppFragment,
|
||||||
|
getMockEasJson,
|
||||||
|
mockCommandContext,
|
||||||
|
mockProjectId,
|
||||||
|
mockTestCommand,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
jest.mock('../../build/android/version');
|
||||||
|
jest.mock('../../build/ios/version');
|
||||||
|
jest.mock('../../project/applicationIdentifier');
|
||||||
|
jest.mock('../../graphql/queries/AppVersionQuery');
|
||||||
|
jest.mock('../../graphql/queries/AppQuery');
|
||||||
|
jest.mock('../../graphql/mutations/AppVersionMutation');
|
||||||
|
jest.mock('../../project/workflow');
|
||||||
|
jest.mock('../../project/android/gradleUtils');
|
||||||
|
jest.mock('../../project/ios/target');
|
||||||
|
jest.mock('../../project/ios/scheme');
|
||||||
|
jest.mock('fs');
|
||||||
|
jest.mock('../../log');
|
||||||
|
jest.mock('../../prompts');
|
||||||
|
jest.mock('../../utils/json');
|
||||||
|
|
||||||
|
function withRemoteVersionSource(easJson: EasJson): EasJson {
|
||||||
|
return {
|
||||||
|
...easJson,
|
||||||
|
cli: {
|
||||||
|
...easJson.cli,
|
||||||
|
appVersionSource: AppVersionSource.REMOTE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(BuildVersionSyncView, () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version for managed project on platform android', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '1000',
|
||||||
|
storeVersion: '0.0.1',
|
||||||
|
}));
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.MANAGED);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||||
|
const syncAndroidAsync = jest.spyOn(cmd, 'syncAndroidAsync' as any);
|
||||||
|
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(syncAndroidAsync).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version for bare project on platform android', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '1000',
|
||||||
|
storeVersion: '0.0.1',
|
||||||
|
}));
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.GENERIC);
|
||||||
|
jest.mocked(getAppBuildGradleAsync).mockImplementation(async () => ({
|
||||||
|
android: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||||
|
const syncAndroidAsync = jest.spyOn(cmd, 'syncAndroidAsync' as any);
|
||||||
|
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'ANDROID',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(syncAndroidAsync).toHaveBeenCalled();
|
||||||
|
expect(updateAndroidNativeVersionsAsync).toHaveBeenCalledWith({
|
||||||
|
projectDir: ctx.projectDir,
|
||||||
|
versionCode: 1000,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version for managed project on platform ios', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '1000',
|
||||||
|
storeVersion: '0.0.1',
|
||||||
|
}));
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.MANAGED);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=ios'], ctx);
|
||||||
|
const syncIosAsync = jest.spyOn(cmd, 'syncIosAsync' as any);
|
||||||
|
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'IOS',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(syncIosAsync).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version for bare project on platform ios', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {
|
||||||
|
easJson: withRemoteVersionSource(getMockEasJson()),
|
||||||
|
});
|
||||||
|
const fakeTarget: Target = {
|
||||||
|
targetName: 'testapp',
|
||||||
|
bundleIdentifier: 'eas.test.com',
|
||||||
|
entitlements: {},
|
||||||
|
};
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => ({
|
||||||
|
buildVersion: '1000',
|
||||||
|
storeVersion: '0.0.1',
|
||||||
|
}));
|
||||||
|
jest.mocked(prompts.promptAsync).mockImplementation(async () => ({
|
||||||
|
version: '1000',
|
||||||
|
}));
|
||||||
|
jest.mocked(resolveWorkflowAsync).mockImplementation(async () => Workflow.GENERIC);
|
||||||
|
jest.mocked(resolveTargetsAsync).mockImplementation(async () => [fakeTarget]);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=ios'], ctx);
|
||||||
|
const syncIosAsync = jest.spyOn(cmd, 'syncIosAsync' as any);
|
||||||
|
|
||||||
|
await cmd.run();
|
||||||
|
expect(AppVersionQuery.latestVersionAsync).toHaveBeenCalledWith(
|
||||||
|
ctx.loggedIn.graphqlClient,
|
||||||
|
mockProjectId,
|
||||||
|
'IOS',
|
||||||
|
'eas.test.com'
|
||||||
|
);
|
||||||
|
expect(syncIosAsync).toHaveBeenCalled();
|
||||||
|
expect(updateIosNativeVersionsAsync).toHaveBeenCalledWith({
|
||||||
|
buildNumber: '1000',
|
||||||
|
projectDir: ctx.projectDir,
|
||||||
|
targets: [fakeTarget],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version aborts when appVersionSource is set to local and users refuse auto configuration', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(prompts.confirmAsync).mockImplementation(async () => false);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||||
|
await expect(cmd.run()).rejects.toThrowError('Aborting...');
|
||||||
|
expect(AppVersionMutation.createAppVersionAsync).not.toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('syncing version when appVersionSource is set to local and user allows auto configuration', async () => {
|
||||||
|
const ctx = mockCommandContext(BuildVersionSyncView, {});
|
||||||
|
jest.mocked(AppVersionQuery.latestVersionAsync).mockImplementation(async () => null);
|
||||||
|
jest.mocked(AppQuery.byIdAsync).mockImplementation(async () => getMockAppFragment());
|
||||||
|
jest.mocked(prompts.confirmAsync).mockImplementation(async () => true);
|
||||||
|
|
||||||
|
const cmd = mockTestCommand(BuildVersionSyncView, ['--platform=android'], ctx);
|
||||||
|
await cmd.run();
|
||||||
|
|
||||||
|
const easJsonAfterCmd = await fs.readJson(path.join(ctx.projectDir, 'eas.json'));
|
||||||
|
expect(easJsonAfterCmd.cli.appVersionSource).toBe('remote');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
import { EasJson } from '@expo/eas-json';
|
||||||
|
import { Command, Config } from '@oclif/core';
|
||||||
|
import { vol } from 'memfs';
|
||||||
|
import path from 'path';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { ContextInput, ContextOutput } from '../../commandUtils/EasCommand';
|
||||||
|
import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
|
import { AppFragment, Role } from '../../graphql/generated';
|
||||||
|
|
||||||
|
export function getMockEasJson(): EasJson {
|
||||||
|
return {
|
||||||
|
cli: {},
|
||||||
|
build: {
|
||||||
|
production: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mockProjectId = 'fake-project-id';
|
||||||
|
|
||||||
|
export function getMockAppFragment(): AppFragment {
|
||||||
|
return {
|
||||||
|
id: mockProjectId,
|
||||||
|
slug: 'testapp',
|
||||||
|
fullName: '@testuser/testpp',
|
||||||
|
ownerAccount: {
|
||||||
|
id: 'test-account-id',
|
||||||
|
name: 'testuser',
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
role: Role.Owner,
|
||||||
|
actor: {
|
||||||
|
id: 'test-user-id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMockExpoConfig(): ExpoConfig {
|
||||||
|
return {
|
||||||
|
name: 'testapp',
|
||||||
|
slug: 'tetsapp',
|
||||||
|
extra: {
|
||||||
|
projectId: mockProjectId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocalProjectContext {
|
||||||
|
projectDir: string;
|
||||||
|
exp?: ExpoConfig;
|
||||||
|
appId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockCommandContext<
|
||||||
|
C extends {
|
||||||
|
[name: string]: any;
|
||||||
|
} = object
|
||||||
|
>(
|
||||||
|
commandClass: { contextDefinition: ContextInput<C> },
|
||||||
|
overrides: {
|
||||||
|
easJson?: EasJson;
|
||||||
|
exp?: ExpoConfig;
|
||||||
|
projectId?: string;
|
||||||
|
getDynamicPrivateProjectConfigAsync?: DynamicConfigContextFn;
|
||||||
|
getDynamicPublicProjectConfigAsync?: DynamicConfigContextFn;
|
||||||
|
}
|
||||||
|
): ContextOutput<C> {
|
||||||
|
const projectDir = path.join('/test', uuidv4());
|
||||||
|
vol.reset();
|
||||||
|
vol.fromJSON({ 'eas.json': JSON.stringify(overrides.easJson ?? getMockEasJson()) }, projectDir);
|
||||||
|
const contextDefinition = commandClass.contextDefinition;
|
||||||
|
|
||||||
|
const result: any = {};
|
||||||
|
for (const [contextKey] of Object.entries(contextDefinition)) {
|
||||||
|
if (contextKey === 'loggedIn') {
|
||||||
|
result.loggedIn = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contextKey === 'projectDir') {
|
||||||
|
result.projectDir = projectDir;
|
||||||
|
}
|
||||||
|
if (contextKey === 'maybeLoggedIn') {
|
||||||
|
result.maybeLoggedIn = {};
|
||||||
|
}
|
||||||
|
if (contextKey === 'sessionManager') {
|
||||||
|
result.sessionManager = {};
|
||||||
|
}
|
||||||
|
if (contextKey === 'projectConfig') {
|
||||||
|
result.projectConfig = {
|
||||||
|
exp: overrides.exp ?? getMockExpoConfig(),
|
||||||
|
projectId: overrides.projectId ?? mockProjectId,
|
||||||
|
projectDir,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (contextKey === 'getDynamicPrivateProjectConfigAsync') {
|
||||||
|
result.getDynamicPrivateProjectConfigAsync = () => ({
|
||||||
|
exp: overrides.exp ?? getMockExpoConfig(),
|
||||||
|
projectId: overrides.projectId ?? mockProjectId,
|
||||||
|
projectDir,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contextKey === 'getDynamicPublicProjectConfigAsync') {
|
||||||
|
result.getDynamicPublicProjectConfigAsync = () => ({
|
||||||
|
exp: overrides.exp ?? getMockExpoConfig(),
|
||||||
|
projectId: overrides.projectId ?? mockProjectId,
|
||||||
|
projectDir,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockTestCommand<T extends Command>(
|
||||||
|
commandConstructor: new (argv: string[], config: Config) => T,
|
||||||
|
argv: string[],
|
||||||
|
ctx: any
|
||||||
|
): T {
|
||||||
|
const cmd = new commandConstructor(argv, new Config({ root: __dirname }));
|
||||||
|
(cmd as any).getContextAsync = jest.fn(() => ctx);
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoErrorThrownError extends Error {}
|
||||||
|
|
||||||
|
export const getErrorAsync = async <TError = any>(
|
||||||
|
call: () => unknown
|
||||||
|
): Promise<TError | NoErrorThrownError> => {
|
||||||
|
try {
|
||||||
|
await call();
|
||||||
|
throw new NoErrorThrownError();
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return error as TError;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getError = <TError = any>(call: () => unknown): TError | NoErrorThrownError => {
|
||||||
|
try {
|
||||||
|
call();
|
||||||
|
throw new NoErrorThrownError();
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return error as TError;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,6 +2,7 @@ import { JSONValue } from '@expo/json-file';
|
|||||||
|
|
||||||
import { ApiV2Error } from './ApiV2Error';
|
import { ApiV2Error } from './ApiV2Error';
|
||||||
import fetch, { RequestError, RequestInit } from './fetch';
|
import fetch, { RequestError, RequestInit } from './fetch';
|
||||||
|
import { getFreePortAsync } from './utils/port';
|
||||||
|
|
||||||
interface RequestOptions {
|
interface RequestOptions {
|
||||||
body: JSONValue;
|
body: JSONValue;
|
||||||
@@ -104,3 +105,14 @@ export function getEASUpdateURL(projectId: string): string {
|
|||||||
return new URL(projectId, `https://u.expo.dev`).href;
|
return new URL(projectId, `https://u.expo.dev`).href;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getSsoLocalServerPortAsync(): Promise<number> {
|
||||||
|
let startPort: number;
|
||||||
|
if (process.env.SSO_LOCAL_SERVER_PORT) {
|
||||||
|
startPort = Number(process.env.SSO_LOCAL_SERVER_PORT);
|
||||||
|
} else {
|
||||||
|
startPort = 19300;
|
||||||
|
}
|
||||||
|
const port = await getFreePortAsync(startPort);
|
||||||
|
return port;
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Platform } from '@expo/eas-build-job';
|
import { Platform } from '@expo/eas-build-job';
|
||||||
import { CombinedError } from '@urql/core';
|
import { CombinedError } from '@urql/core';
|
||||||
import { GraphQLError } from 'graphql/error';
|
import { GraphQLError } from 'graphql/error';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { getError } from '../../__tests__/commands/utils';
|
||||||
import { EasCommandError } from '../../commandUtils/errors';
|
import { EasCommandError } from '../../commandUtils/errors';
|
||||||
import Build from '../../commands/build';
|
import Build from '../../commands/build';
|
||||||
import Log, { link } from '../../log';
|
import Log, { link } from '../../log';
|
||||||
@@ -11,6 +13,7 @@ import {
|
|||||||
EasBuildFreeTierDisabledAndroidError,
|
EasBuildFreeTierDisabledAndroidError,
|
||||||
EasBuildFreeTierDisabledError,
|
EasBuildFreeTierDisabledError,
|
||||||
EasBuildFreeTierDisabledIOSError,
|
EasBuildFreeTierDisabledIOSError,
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
EasBuildTooManyPendingBuildsError,
|
EasBuildTooManyPendingBuildsError,
|
||||||
RequestValidationError,
|
RequestValidationError,
|
||||||
TurtleDeprecatedJobFormatError,
|
TurtleDeprecatedJobFormatError,
|
||||||
@@ -42,9 +45,11 @@ const EXPECTED_MAX_BUILD_COUNT_MESSAGE_IOS = `You have already reached the maxim
|
|||||||
const EXPECTED_GENERIC_MESSAGE =
|
const EXPECTED_GENERIC_MESSAGE =
|
||||||
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.';
|
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.';
|
||||||
|
|
||||||
|
const mockRequestId = uuidv4();
|
||||||
const getGraphQLError = (message: string, errorCode: string): GraphQLError => {
|
const getGraphQLError = (message: string, errorCode: string): GraphQLError => {
|
||||||
return new GraphQLError(message, null, null, null, null, null, {
|
return new GraphQLError(message, null, null, null, null, null, {
|
||||||
errorCode,
|
errorCode,
|
||||||
|
requestId: mockRequestId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,64 +119,135 @@ describe(Build.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('error is server-side defined error', () => {
|
describe('error is server-side defined error', () => {
|
||||||
describe('throws correct EasCommandError subclass', () => {
|
|
||||||
describe('for TURTLE_DEPRECATED_JOB_FORMAT', () => {
|
describe('for TURTLE_DEPRECATED_JOB_FORMAT', () => {
|
||||||
it('does it with Android', async () => {
|
describe('with Android', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
|
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<TurtleDeprecatedJobFormatError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, TurtleDeprecatedJobFormatError);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
TurtleDeprecatedJobFormatError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does it with iOS', async () => {
|
it('throws correct EasBuildResourceClassNotAvailableInFreeTierError', async () => {
|
||||||
|
const platform = Platform.ANDROID;
|
||||||
|
const graphQLError = getGraphQLError(
|
||||||
|
'Error 1',
|
||||||
|
'EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER'
|
||||||
|
);
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError =
|
||||||
|
getError<EasBuildResourceClassNotAvailableInFreeTierError>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with iOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
|
const graphQLError = getGraphQLError('Error 1', 'TURTLE_DEPRECATED_JOB_FORMAT');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<TurtleDeprecatedJobFormatError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, TurtleDeprecatedJobFormatError);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
TurtleDeprecatedJobFormatError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws correct EasBuildResourceClassNotAvailableInFreeTierError', async () => {
|
||||||
|
const platform = Platform.IOS;
|
||||||
|
const graphQLError = getGraphQLError(
|
||||||
|
'Error 1',
|
||||||
|
'EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER'
|
||||||
|
);
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError =
|
||||||
|
getError<EasBuildResourceClassNotAvailableInFreeTierError>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for EAS_BUILD_FREE_TIER_DISABLED', () => {
|
describe('for EAS_BUILD_FREE_TIER_DISABLED', () => {
|
||||||
it('does it with Android', async () => {
|
describe('with Android', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledError);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
it('does it with iOS', async () => {
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildFreeTierDisabledError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with iOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledError);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('does it for EAS_BUILD_FREE_TIER_DISABLED_IOS', async () => {
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildFreeTierDisabledError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('for EAS_BUILD_FREE_TIER_DISABLED_IOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_IOS');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_IOS');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
@@ -180,181 +256,247 @@ describe(Build.name, () => {
|
|||||||
try {
|
try {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
} catch (caughtError) {
|
||||||
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledIOSError);
|
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledIOSError, 'Error 1');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('does it for EAS_BUILD_FREE_TIER_DISABLED_ANDROID', async () => {
|
describe('for EAS_BUILD_FREE_TIER_DISABLED_ANDROID', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_ANDROID');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_FREE_TIER_DISABLED_ANDROID');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildFreeTierDisabledAndroidError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, EasBuildFreeTierDisabledAndroidError);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
EasBuildFreeTierDisabledAndroidError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for VALIDATION_ERROR', () => {
|
describe('for VALIDATION_ERROR', () => {
|
||||||
it('does it with Android', async () => {
|
describe('with Android', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
|
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<RequestValidationError>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, RequestValidationError);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does it with iOS', async () => {
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
RequestValidationError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with iOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
|
const graphQLError = getGraphQLError('Error 1', 'VALIDATION_ERROR');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<RequestValidationError>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
});
|
||||||
assertReThrownError(caughtError as Error, RequestValidationError);
|
|
||||||
}
|
assertReThrownError(
|
||||||
|
handleBuildRequestErrorThrownError,
|
||||||
|
RequestValidationError,
|
||||||
|
'Error 1'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('error is other expected graphQL-related error', () => {
|
describe('error is other expected graphQL-related error', () => {
|
||||||
describe('throws correct EasCommandError subclass', () => {
|
|
||||||
describe('for EAS_BUILD_DOWN_FOR_MAINTENANCE', () => {
|
describe('for EAS_BUILD_DOWN_FOR_MAINTENANCE', () => {
|
||||||
it('does it with Android', async () => {
|
describe('with Android', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildDownForMaintenanceError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assertReThrownError(
|
assertReThrownError(
|
||||||
caughtError as Error,
|
handleBuildRequestErrorThrownError,
|
||||||
EasBuildDownForMaintenanceError,
|
EasBuildDownForMaintenanceError,
|
||||||
EXPECTED_EAS_BUILD_DOWN_MESSAGE
|
EXPECTED_EAS_BUILD_DOWN_MESSAGE
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('does it with iOS', async () => {
|
describe('with iOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_DOWN_FOR_MAINTENANCE');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildDownForMaintenanceError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assertReThrownError(
|
assertReThrownError(
|
||||||
caughtError as Error,
|
handleBuildRequestErrorThrownError,
|
||||||
EasBuildDownForMaintenanceError,
|
EasBuildDownForMaintenanceError,
|
||||||
EXPECTED_EAS_BUILD_DOWN_MESSAGE
|
EXPECTED_EAS_BUILD_DOWN_MESSAGE
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for EAS_BUILD_TOO_MANY_PENDING_BUILDS', () => {
|
describe('for EAS_BUILD_TOO_MANY_PENDING_BUILDS', () => {
|
||||||
it('does it with Android', async () => {
|
describe('with Android', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildTooManyPendingBuildsError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assertReThrownError(
|
assertReThrownError(
|
||||||
caughtError as Error,
|
handleBuildRequestErrorThrownError,
|
||||||
EasBuildTooManyPendingBuildsError,
|
EasBuildTooManyPendingBuildsError,
|
||||||
EXPECTED_MAX_BUILD_COUNT_MESSAGE_ANDROID
|
EXPECTED_MAX_BUILD_COUNT_MESSAGE_ANDROID
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('does it with iOS', async () => {
|
describe('with iOS', () => {
|
||||||
|
it('throws correct EasCommandError subclass', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
|
const graphQLError = getGraphQLError('Error 1', 'EAS_BUILD_TOO_MANY_PENDING_BUILDS');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<EasBuildTooManyPendingBuildsError>(
|
||||||
|
() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
}
|
||||||
|
);
|
||||||
|
|
||||||
assertReThrownError(
|
assertReThrownError(
|
||||||
caughtError as Error,
|
handleBuildRequestErrorThrownError,
|
||||||
EasBuildTooManyPendingBuildsError,
|
EasBuildTooManyPendingBuildsError,
|
||||||
EXPECTED_MAX_BUILD_COUNT_MESSAGE_IOS
|
EXPECTED_MAX_BUILD_COUNT_MESSAGE_IOS
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('error is unexpected graphQL-related error', () => {
|
describe('error is unexpected graphQL-related error', () => {
|
||||||
describe('throws base Error class with custom message', () => {
|
describe('with Android', () => {
|
||||||
it('does it with Android', async () => {
|
it('throws base Error class with custom message', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nRequest ID: ${mockRequestId}\nError message: Error 1`;
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
assertReThrownError(caughtError as Error, Error, EXPECTED_GENERIC_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does it with iOS', async () => {
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
|
describe('without request ID', () => {
|
||||||
|
it('throws base Error class with custom message without request ID line', async () => {
|
||||||
|
const platform = Platform.ANDROID;
|
||||||
|
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
||||||
|
delete graphQLError.extensions.requestId;
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nError message: Error 1`;
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with iOS', () => {
|
||||||
|
it('throws base Error class with custom message', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
||||||
const graphQLErrors = [graphQLError];
|
const graphQLErrors = [graphQLError];
|
||||||
const error = new CombinedError({ graphQLErrors });
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nRequest ID: ${mockRequestId}\nError message: Error 1`;
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
});
|
||||||
assertReThrownError(caughtError as Error, Error, EXPECTED_GENERIC_MESSAGE);
|
|
||||||
}
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
|
describe('without request ID', () => {
|
||||||
|
it('throws base Error class with custom message without request ID line', async () => {
|
||||||
|
const platform = Platform.IOS;
|
||||||
|
const graphQLError = getGraphQLError('Error 1', 'UNKNOWN_GRAPHQL_ERROR');
|
||||||
|
delete graphQLError.extensions.requestId;
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
const expectedMessage = `${EXPECTED_GENERIC_MESSAGE}\nError message: Error 1`;
|
||||||
|
|
||||||
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
|
handleBuildRequestError(error, platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertReThrownError(handleBuildRequestErrorThrownError, Error, expectedMessage);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('error is unexpected non-graphQL-related error', () => {
|
describe('error is unexpected non-graphQL-related error', () => {
|
||||||
describe('throws base Error class with propagated message', () => {
|
describe('with Android', () => {
|
||||||
it('does it with Android', async () => {
|
it('throws base Error class with propagated message', async () => {
|
||||||
const platform = Platform.ANDROID;
|
const platform = Platform.ANDROID;
|
||||||
const error = new Error('Non-graphQL-related error');
|
const error = new Error('Non-graphQL-related error');
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
|
||||||
expect(caughtError).toStrictEqual(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does it with iOS', async () => {
|
expect(handleBuildRequestErrorThrownError).toStrictEqual(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('with iOS', () => {
|
||||||
|
it('throws base Error class with propagated message', async () => {
|
||||||
const platform = Platform.IOS;
|
const platform = Platform.IOS;
|
||||||
const error = new Error('Non-graphQL-related error');
|
const error = new Error('Non-graphQL-related error');
|
||||||
|
|
||||||
try {
|
const handleBuildRequestErrorThrownError = getError<Error>(() => {
|
||||||
handleBuildRequestError(error, platform);
|
handleBuildRequestError(error, platform);
|
||||||
} catch (caughtError) {
|
});
|
||||||
expect(caughtError).toStrictEqual(error);
|
|
||||||
}
|
expect(handleBuildRequestErrorThrownError).toStrictEqual(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ interface JobData {
|
|||||||
|
|
||||||
const cacheDefaults = {
|
const cacheDefaults = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
customPaths: [],
|
paths: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function prepareJobAsync(
|
export async function prepareJobAsync(
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import {
|
|||||||
EasBuildFreeTierDisabledAndroidError,
|
EasBuildFreeTierDisabledAndroidError,
|
||||||
EasBuildFreeTierDisabledError,
|
EasBuildFreeTierDisabledError,
|
||||||
EasBuildFreeTierDisabledIOSError,
|
EasBuildFreeTierDisabledIOSError,
|
||||||
|
EasBuildProjectArchiveUploadError,
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
EasBuildTooManyPendingBuildsError,
|
EasBuildTooManyPendingBuildsError,
|
||||||
RequestValidationError,
|
RequestValidationError,
|
||||||
TurtleDeprecatedJobFormatError,
|
TurtleDeprecatedJobFormatError,
|
||||||
@@ -80,9 +82,14 @@ interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
|
|||||||
|
|
||||||
export type BuildRequestSender = () => Promise<BuildFragment | undefined>;
|
export type BuildRequestSender = () => Promise<BuildFragment | undefined>;
|
||||||
|
|
||||||
function resolveBuildParamsInput<T extends Platform>(ctx: BuildContext<T>): BuildParamsInput {
|
function resolveBuildParamsInput<T extends Platform>(
|
||||||
|
ctx: BuildContext<T>,
|
||||||
|
metadata: Metadata
|
||||||
|
): BuildParamsInput {
|
||||||
return {
|
return {
|
||||||
resourceClass: ctx.resourceClass,
|
resourceClass: ctx.resourceClass,
|
||||||
|
sdkVersion: metadata.sdkVersion,
|
||||||
|
reactNativeVersion: metadata.reactNativeVersion,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +149,7 @@ export async function prepareBuildRequestForPlatformAsync<
|
|||||||
assert(projectArchive);
|
assert(projectArchive);
|
||||||
|
|
||||||
const metadata = await collectMetadataAsync(ctx);
|
const metadata = await collectMetadataAsync(ctx);
|
||||||
const buildParams = resolveBuildParamsInput(ctx);
|
const buildParams = resolveBuildParamsInput(ctx, metadata);
|
||||||
const job = await builder.prepareJobAsync(ctx, {
|
const job = await builder.prepareJobAsync(ctx, {
|
||||||
projectArchive,
|
projectArchive,
|
||||||
credentials: credentialsResult?.credentials,
|
credentials: credentialsResult?.credentials,
|
||||||
@@ -176,6 +183,8 @@ const SERVER_SIDE_DEFINED_ERRORS: Record<string, typeof EasCommandError> = {
|
|||||||
EAS_BUILD_FREE_TIER_DISABLED: EasBuildFreeTierDisabledError,
|
EAS_BUILD_FREE_TIER_DISABLED: EasBuildFreeTierDisabledError,
|
||||||
EAS_BUILD_FREE_TIER_DISABLED_IOS: EasBuildFreeTierDisabledIOSError,
|
EAS_BUILD_FREE_TIER_DISABLED_IOS: EasBuildFreeTierDisabledIOSError,
|
||||||
EAS_BUILD_FREE_TIER_DISABLED_ANDROID: EasBuildFreeTierDisabledAndroidError,
|
EAS_BUILD_FREE_TIER_DISABLED_ANDROID: EasBuildFreeTierDisabledAndroidError,
|
||||||
|
EAS_BUILD_RESOURCE_CLASS_NOT_AVAILABLE_IN_FREE_TIER:
|
||||||
|
EasBuildResourceClassNotAvailableInFreeTierError,
|
||||||
VALIDATION_ERROR: RequestValidationError,
|
VALIDATION_ERROR: RequestValidationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -197,8 +206,15 @@ export function handleBuildRequestError(error: any, platform: Platform): never {
|
|||||||
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
|
`You have already reached the maximum number of pending ${requestedPlatformDisplayNames[platform]} builds for your account. Try again later.`
|
||||||
);
|
);
|
||||||
} else if (error?.graphQLErrors) {
|
} else if (error?.graphQLErrors) {
|
||||||
|
const graphQLError = error.graphQLErrors[0];
|
||||||
|
const requestIdLine = graphQLError?.extensions?.requestId
|
||||||
|
? `\nRequest ID: ${graphQLError.extensions.requestId}`
|
||||||
|
: '';
|
||||||
|
const originalErrorMessage = graphQLError?.message
|
||||||
|
? `\nError message: ${graphQLError.message}`
|
||||||
|
: '';
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.'
|
`Build request failed. Make sure you are using the latest eas-cli version. If the problem persists, report the issue.${requestIdLine}${originalErrorMessage}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
@@ -254,6 +270,12 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
|||||||
properties: ctx.analyticsEventProperties,
|
properties: ctx.analyticsEventProperties,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
let errMessage = 'Failed to upload the project tarball to EAS Build';
|
||||||
|
if (err.message) {
|
||||||
|
errMessage += `\n\nReason: ${err.message}`;
|
||||||
|
}
|
||||||
|
throw new EasBuildProjectArchiveUploadError(errMessage);
|
||||||
} finally {
|
} finally {
|
||||||
if (projectTarballPath) {
|
if (projectTarballPath) {
|
||||||
await fs.remove(projectTarballPath);
|
await fs.remove(projectTarballPath);
|
||||||
@@ -483,6 +505,7 @@ async function handleSingleBuildProgressAsync(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case BuildStatus.PendingCancel:
|
||||||
case BuildStatus.Canceled:
|
case BuildStatus.Canceled:
|
||||||
spinner.fail('Build canceled');
|
spinner.fail('Build canceled');
|
||||||
return { refetch: false };
|
return { refetch: false };
|
||||||
@@ -513,6 +536,7 @@ const statusToDisplayName: Record<BuildStatus, string> = {
|
|||||||
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
|
[BuildStatus.New]: 'waiting to enter the queue (concurrency limit reached)',
|
||||||
[BuildStatus.InQueue]: 'in queue',
|
[BuildStatus.InQueue]: 'in queue',
|
||||||
[BuildStatus.InProgress]: 'in progress',
|
[BuildStatus.InProgress]: 'in progress',
|
||||||
|
[BuildStatus.PendingCancel]: 'canceled',
|
||||||
[BuildStatus.Canceled]: 'canceled',
|
[BuildStatus.Canceled]: 'canceled',
|
||||||
[BuildStatus.Finished]: 'finished',
|
[BuildStatus.Finished]: 'finished',
|
||||||
[BuildStatus.Errored]: 'failed',
|
[BuildStatus.Errored]: 'failed',
|
||||||
@@ -531,7 +555,12 @@ async function handleMultipleBuildsProgressAsync(
|
|||||||
builds.filter(build => build.status === BuildStatus.Finished).length === buildCount;
|
builds.filter(build => build.status === BuildStatus.Finished).length === buildCount;
|
||||||
const allSettled =
|
const allSettled =
|
||||||
builds.filter(build =>
|
builds.filter(build =>
|
||||||
[BuildStatus.Finished, BuildStatus.Errored, BuildStatus.Canceled].includes(build.status)
|
[
|
||||||
|
BuildStatus.Finished,
|
||||||
|
BuildStatus.Errored,
|
||||||
|
BuildStatus.Canceled,
|
||||||
|
BuildStatus.PendingCancel,
|
||||||
|
].includes(build.status)
|
||||||
).length === buildCount;
|
).length === buildCount;
|
||||||
|
|
||||||
if (allSettled) {
|
if (allSettled) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||||
import { EasJson, EasJsonAccessor, ResourceClass } from '@expo/eas-json';
|
import { EasJson, EasJsonAccessor } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
import Log from '../log';
|
import Log, { learnMore } from '../log';
|
||||||
import { resolveWorkflowAsync } from '../project/workflow';
|
import { resolveWorkflowAsync } from '../project/workflow';
|
||||||
import { easCliVersion } from '../utils/easCli';
|
import { easCliVersion } from '../utils/easCli';
|
||||||
import { getVcsClient } from '../vcs';
|
import { getVcsClient } from '../vcs';
|
||||||
@@ -14,6 +14,10 @@ interface ConfigureParams {
|
|||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function easJsonExistsAsync(projectDir: string): Promise<boolean> {
|
||||||
|
return await fs.pathExists(EasJsonAccessor.formatEasJsonPath(projectDir));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates eas.json if it does not exist.
|
* Creates eas.json if it does not exist.
|
||||||
*
|
*
|
||||||
@@ -24,7 +28,7 @@ interface ConfigureParams {
|
|||||||
export async function ensureProjectConfiguredAsync(
|
export async function ensureProjectConfiguredAsync(
|
||||||
configureParams: ConfigureParams
|
configureParams: ConfigureParams
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (await fs.pathExists(EasJsonAccessor.formatEasJsonPath(configureParams.projectDir))) {
|
if (await easJsonExistsAsync(configureParams.projectDir)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,21 +57,11 @@ const EAS_JSON_MANAGED_DEFAULT: EasJson = {
|
|||||||
development: {
|
development: {
|
||||||
developmentClient: true,
|
developmentClient: true,
|
||||||
distribution: 'internal',
|
distribution: 'internal',
|
||||||
ios: {
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
distribution: 'internal',
|
distribution: 'internal',
|
||||||
ios: {
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
ios: {
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
production: {},
|
||||||
},
|
},
|
||||||
submit: {
|
submit: {
|
||||||
production: {},
|
production: {},
|
||||||
@@ -86,20 +80,12 @@ const EAS_JSON_BARE_DEFAULT: EasJson = {
|
|||||||
},
|
},
|
||||||
ios: {
|
ios: {
|
||||||
buildConfiguration: 'Debug',
|
buildConfiguration: 'Debug',
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
distribution: 'internal',
|
distribution: 'internal',
|
||||||
ios: {
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
ios: {
|
|
||||||
resourceClass: ResourceClass.M_MEDIUM,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
production: {},
|
||||||
},
|
},
|
||||||
submit: {
|
submit: {
|
||||||
production: {},
|
production: {},
|
||||||
@@ -120,5 +106,9 @@ async function createEasJsonAsync(projectDir: string): Promise<void> {
|
|||||||
|
|
||||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
||||||
await getVcsClient().trackFileAsync(easJsonPath);
|
await getVcsClient().trackFileAsync(easJsonPath);
|
||||||
Log.withTick(`Generated ${chalk.bold('eas.json')}`);
|
Log.withTick(
|
||||||
|
`Generated ${chalk.bold('eas.json')}. ${learnMore(
|
||||||
|
'https://docs.expo.dev/build-reference/eas-json/'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import { CredentialsContext } from '../credentials/context';
|
|||||||
import { Target } from '../credentials/ios/types';
|
import { Target } from '../credentials/ios/types';
|
||||||
import { BuildResourceClass } from '../graphql/generated';
|
import { BuildResourceClass } from '../graphql/generated';
|
||||||
import { GradleBuildContext } from '../project/android/gradle';
|
import { GradleBuildContext } from '../project/android/gradle';
|
||||||
|
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
|
||||||
import { XcodeBuildContext } from '../project/ios/scheme';
|
import { XcodeBuildContext } from '../project/ios/scheme';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
import { LocalBuildOptions } from './local';
|
import { LocalBuildOptions } from './local';
|
||||||
@@ -51,6 +52,7 @@ export interface BuildContext<T extends Platform> {
|
|||||||
graphqlClient: ExpoGraphqlClient;
|
graphqlClient: ExpoGraphqlClient;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
workflow: Workflow;
|
workflow: Workflow;
|
||||||
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
android: T extends Platform.ANDROID ? AndroidBuildContext : undefined;
|
||||||
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
ios: T extends Platform.IOS ? IosBuildContext : undefined;
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/An
|
|||||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { CredentialsContext } from '../credentials/context';
|
import { CredentialsContext } from '../credentials/context';
|
||||||
|
import { CustomBuildConfigMetadata } from '../project/customBuildConfig';
|
||||||
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
import { getOwnerAccountForProjectIdAsync } from '../project/projectUtils';
|
||||||
import { resolveWorkflowAsync } from '../project/workflow';
|
import { resolveWorkflowAsync } from '../project/workflow';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
@@ -33,7 +34,8 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
|
customBuildConfigMetadata,
|
||||||
}: {
|
}: {
|
||||||
buildProfileName: string;
|
buildProfileName: string;
|
||||||
buildProfile: BuildProfile<T>;
|
buildProfile: BuildProfile<T>;
|
||||||
@@ -49,9 +51,10 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
actor: Actor;
|
actor: Actor;
|
||||||
graphqlClient: ExpoGraphqlClient;
|
graphqlClient: ExpoGraphqlClient;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
getDynamicProjectConfigAsync: DynamicConfigContextFn;
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||||
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
}): Promise<BuildContext<T>> {
|
}): Promise<BuildContext<T>> {
|
||||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||||
const projectName = exp.slug;
|
const projectName = exp.slug;
|
||||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||||
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
const workflow = await resolveWorkflowAsync(projectDir, platform);
|
||||||
@@ -90,8 +93,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
const resourceClass = await resolveBuildResourceClassAsync(
|
const resourceClass = await resolveBuildResourceClassAsync(
|
||||||
buildProfile,
|
buildProfile,
|
||||||
platform,
|
platform,
|
||||||
projectDir,
|
|
||||||
exp,
|
|
||||||
resourceClassFlag
|
resourceClassFlag
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
|||||||
workflow,
|
workflow,
|
||||||
message,
|
message,
|
||||||
runFromCI,
|
runFromCI,
|
||||||
|
customBuildConfigMetadata,
|
||||||
};
|
};
|
||||||
if (platform === Platform.ANDROID) {
|
if (platform === Platform.ANDROID) {
|
||||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||||
|
|||||||
@@ -8,8 +8,12 @@ export class EasBuildFreeTierDisabledIOSError extends EasCommandError {}
|
|||||||
|
|
||||||
export class EasBuildFreeTierDisabledAndroidError extends EasCommandError {}
|
export class EasBuildFreeTierDisabledAndroidError extends EasCommandError {}
|
||||||
|
|
||||||
|
export class EasBuildResourceClassNotAvailableInFreeTierError extends EasCommandError {}
|
||||||
|
|
||||||
export class RequestValidationError extends EasCommandError {}
|
export class RequestValidationError extends EasCommandError {}
|
||||||
|
|
||||||
export class EasBuildDownForMaintenanceError extends EasCommandError {}
|
export class EasBuildDownForMaintenanceError extends EasCommandError {}
|
||||||
|
|
||||||
export class EasBuildTooManyPendingBuildsError extends EasCommandError {}
|
export class EasBuildTooManyPendingBuildsError extends EasCommandError {}
|
||||||
|
|
||||||
|
export class EasBuildProjectArchiveUploadError extends EasCommandError {}
|
||||||
@@ -77,7 +77,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
@@ -88,9 +88,9 @@ describe(bumpVersionAsync, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
const infoPlist = (await readPlistAsync(
|
const infoPlist = (await readPlistAsync(
|
||||||
'/repo/ios/myproject/Info.plist'
|
'/app/ios/myproject/Info.plist'
|
||||||
)) as IOSConfig.InfoPlist;
|
)) as IOSConfig.InfoPlist;
|
||||||
expect(fakeExp.version).toBe('1.0.0');
|
expect(fakeExp.version).toBe('1.0.0');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('2');
|
expect(fakeExp.ios?.buildNumber).toBe('2');
|
||||||
@@ -113,7 +113,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
@@ -124,9 +124,9 @@ describe(bumpVersionAsync, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
const infoPlist = (await readPlistAsync(
|
const infoPlist = (await readPlistAsync(
|
||||||
'/repo/ios/myproject/Info2.plist'
|
'/app/ios/myproject/Info2.plist'
|
||||||
)) as IOSConfig.InfoPlist;
|
)) as IOSConfig.InfoPlist;
|
||||||
expect(fakeExp.version).toBe('1.0.0');
|
expect(fakeExp.version).toBe('1.0.0');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('2');
|
expect(fakeExp.ios?.buildNumber).toBe('2');
|
||||||
@@ -148,7 +148,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.APP_VERSION,
|
bumpStrategy: BumpStrategy.APP_VERSION,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
@@ -159,9 +159,9 @@ describe(bumpVersionAsync, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
const infoPlist = (await readPlistAsync(
|
const infoPlist = (await readPlistAsync(
|
||||||
'/repo/ios/myproject/Info.plist'
|
'/app/ios/myproject/Info.plist'
|
||||||
)) as IOSConfig.InfoPlist;
|
)) as IOSConfig.InfoPlist;
|
||||||
expect(fakeExp.version).toBe('1.0.1');
|
expect(fakeExp.version).toBe('1.0.1');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('1');
|
expect(fakeExp.ios?.buildNumber).toBe('1');
|
||||||
@@ -183,7 +183,7 @@ describe(bumpVersionAsync, () => {
|
|||||||
);
|
);
|
||||||
await bumpVersionAsync({
|
await bumpVersionAsync({
|
||||||
bumpStrategy: BumpStrategy.NOOP,
|
bumpStrategy: BumpStrategy.NOOP,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
@@ -194,9 +194,9 @@ describe(bumpVersionAsync, () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
const infoPlist = (await readPlistAsync(
|
const infoPlist = (await readPlistAsync(
|
||||||
'/repo/ios/myproject/Info.plist'
|
'/app/ios/myproject/Info.plist'
|
||||||
)) as IOSConfig.InfoPlist;
|
)) as IOSConfig.InfoPlist;
|
||||||
expect(fakeExp.version).toBe('1.0.0');
|
expect(fakeExp.version).toBe('1.0.0');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('1');
|
expect(fakeExp.ios?.buildNumber).toBe('1');
|
||||||
@@ -214,11 +214,11 @@ describe(bumpVersionInAppJsonAsync, () => {
|
|||||||
|
|
||||||
await bumpVersionInAppJsonAsync({
|
await bumpVersionInAppJsonAsync({
|
||||||
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
bumpStrategy: BumpStrategy.BUILD_NUMBER,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
expect(fakeExp.version).toBe('1.0.0');
|
expect(fakeExp.version).toBe('1.0.0');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('2');
|
expect(fakeExp.ios?.buildNumber).toBe('2');
|
||||||
expect(appJSON.expo.version).toBe('1.0.0');
|
expect(appJSON.expo.version).toBe('1.0.0');
|
||||||
@@ -230,11 +230,11 @@ describe(bumpVersionInAppJsonAsync, () => {
|
|||||||
|
|
||||||
await bumpVersionInAppJsonAsync({
|
await bumpVersionInAppJsonAsync({
|
||||||
bumpStrategy: BumpStrategy.APP_VERSION,
|
bumpStrategy: BumpStrategy.APP_VERSION,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
expect(fakeExp.version).toBe('1.0.1');
|
expect(fakeExp.version).toBe('1.0.1');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('1');
|
expect(fakeExp.ios?.buildNumber).toBe('1');
|
||||||
expect(appJSON.expo.version).toBe('1.0.1');
|
expect(appJSON.expo.version).toBe('1.0.1');
|
||||||
@@ -246,11 +246,11 @@ describe(bumpVersionInAppJsonAsync, () => {
|
|||||||
|
|
||||||
await bumpVersionInAppJsonAsync({
|
await bumpVersionInAppJsonAsync({
|
||||||
bumpStrategy: BumpStrategy.NOOP,
|
bumpStrategy: BumpStrategy.NOOP,
|
||||||
projectDir: '/repo',
|
projectDir: '/app',
|
||||||
exp: fakeExp,
|
exp: fakeExp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const appJSON = await fs.readJSON('/repo/app.json');
|
const appJSON = await fs.readJSON('/app/app.json');
|
||||||
expect(fakeExp.version).toBe('1.0.0');
|
expect(fakeExp.version).toBe('1.0.0');
|
||||||
expect(fakeExp.ios?.buildNumber).toBe('1');
|
expect(fakeExp.ios?.buildNumber).toBe('1');
|
||||||
expect(appJSON.expo.version).toBe('1.0.0');
|
expect(appJSON.expo.version).toBe('1.0.0');
|
||||||
@@ -262,7 +262,7 @@ describe(readBuildNumberAsync, () => {
|
|||||||
describe('bare project', () => {
|
describe('bare project', () => {
|
||||||
it('reads the build number from native code', async () => {
|
it('reads the build number from native code', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
|
const buildNumber = await readBuildNumberAsync('/app', exp, {});
|
||||||
expect(buildNumber).toBe('1');
|
expect(buildNumber).toBe('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -270,7 +270,7 @@ describe(readBuildNumberAsync, () => {
|
|||||||
describe('managed project', () => {
|
describe('managed project', () => {
|
||||||
it('reads the build number from expo config', async () => {
|
it('reads the build number from expo config', async () => {
|
||||||
const exp = initManagedProject();
|
const exp = initManagedProject();
|
||||||
const buildNumber = await readBuildNumberAsync('/repo', exp, {});
|
const buildNumber = await readBuildNumberAsync('/app', exp, {});
|
||||||
expect(buildNumber).toBe('1');
|
expect(buildNumber).toBe('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -280,24 +280,38 @@ describe(readShortVersionAsync, () => {
|
|||||||
describe('bare project', () => {
|
describe('bare project', () => {
|
||||||
it('reads the short version from native code', async () => {
|
it('reads the short version from native code', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const appVersion = await readShortVersionAsync('/repo', exp, {});
|
const appVersion = await readShortVersionAsync('/app', exp, {});
|
||||||
expect(appVersion).toBe('1.0.0');
|
expect(appVersion).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
it('evaluates interpolated build number', async () => {
|
it('evaluates interpolated build number', async () => {
|
||||||
const exp = initBareWorkflowProject({
|
const exp = initBareWorkflowProject({
|
||||||
appVersion: '$(CURRENT_PROJECT_VERSION)',
|
appVersion: '$(CURRENT_PROJECT_VERSION)',
|
||||||
});
|
});
|
||||||
const buildNumber = await readShortVersionAsync('/repo', exp, {
|
const buildNumber = await readShortVersionAsync('/app', exp, {
|
||||||
CURRENT_PROJECT_VERSION: '1.0.0',
|
CURRENT_PROJECT_VERSION: '1.0.0',
|
||||||
});
|
});
|
||||||
expect(buildNumber).toBe('1.0.0');
|
expect(buildNumber).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fails when build number is invalid', async () => {
|
||||||
|
const exp = initBareWorkflowProject({
|
||||||
|
appVersion: '$(CURRENT_PROJECT_VERSION)',
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
readShortVersionAsync('/app', exp, {
|
||||||
|
CURRENT_PROJECT_VERSION: '0.0.7.1.028',
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(
|
||||||
|
'CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: 0.0.7.1.028'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('managed project', () => {
|
describe('managed project', () => {
|
||||||
it('reads the version from app config', async () => {
|
it('reads the version from app config', async () => {
|
||||||
const exp = initBareWorkflowProject();
|
const exp = initBareWorkflowProject();
|
||||||
const appVersion = await readShortVersionAsync('/repo', exp, {});
|
const appVersion = await readShortVersionAsync('/app', exp, {});
|
||||||
expect(appVersion).toBe('1.0.0');
|
expect(appVersion).toBe('1.0.0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -309,66 +323,75 @@ describe(getInfoPlistPath, () => {
|
|||||||
{
|
{
|
||||||
'./ios/testapp/Info.plist': '',
|
'./ios/testapp/Info.plist': '',
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
const plistPath = getInfoPlistPath('/repo', {});
|
const plistPath = getInfoPlistPath('/app', {});
|
||||||
expect(plistPath).toBe('/repo/ios/testapp/Info.plist');
|
expect(plistPath).toBe('/app/ios/testapp/Info.plist');
|
||||||
});
|
});
|
||||||
it('returns INFOPLIST_FILE if specified', () => {
|
it('returns INFOPLIST_FILE if specified', () => {
|
||||||
vol.fromJSON(
|
vol.fromJSON(
|
||||||
{
|
{
|
||||||
'./ios/testapp/Info.plist': '',
|
'./ios/testapp/Info.plist': '',
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
const plistPath = getInfoPlistPath('/repo', { INFOPLIST_FILE: './qwert/NotInfo.plist' });
|
const plistPath = getInfoPlistPath('/app', { INFOPLIST_FILE: './qwert/NotInfo.plist' });
|
||||||
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
|
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
|
||||||
});
|
});
|
||||||
it('evaluates SRCROOT in Info.plist', () => {
|
it('evaluates SRCROOT in Info.plist', () => {
|
||||||
vol.fromJSON(
|
vol.fromJSON(
|
||||||
{
|
{
|
||||||
'./ios/testapp/Info.plist': '',
|
'./ios/testapp/Info.plist': '',
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
const plistPath = getInfoPlistPath('/repo', {
|
const plistPath = getInfoPlistPath('/app', {
|
||||||
INFOPLIST_FILE: '$(SRCROOT)/qwert/NotInfo.plist',
|
INFOPLIST_FILE: '$(SRCROOT)/qwert/NotInfo.plist',
|
||||||
});
|
});
|
||||||
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
|
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
|
||||||
});
|
});
|
||||||
it('evaluates BuildSettings in Info.plist', () => {
|
it('evaluates BuildSettings in Info.plist', () => {
|
||||||
vol.fromJSON(
|
vol.fromJSON(
|
||||||
{
|
{
|
||||||
'./ios/testapp/Info.plist': '',
|
'./ios/testapp/Info.plist': '',
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
const plistPath = getInfoPlistPath('/repo', {
|
const plistPath = getInfoPlistPath('/app', {
|
||||||
INFOPLIST_FILE: '$(SRCROOT)/qwert/$(TARGET_NAME).plist',
|
INFOPLIST_FILE: '$(SRCROOT)/qwert/$(TARGET_NAME).plist',
|
||||||
TARGET_NAME: 'NotInfo',
|
TARGET_NAME: 'NotInfo',
|
||||||
});
|
});
|
||||||
expect(plistPath).toBe('/repo/ios/qwert/NotInfo.plist');
|
expect(plistPath).toBe('/app/ios/qwert/NotInfo.plist');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function initBareWorkflowProject({
|
function initBareWorkflowProject({
|
||||||
appVersion = '1.0.0',
|
appVersion = '1.0.0',
|
||||||
buildNumber = '1',
|
buildNumber = '1',
|
||||||
|
expoConfig = {},
|
||||||
infoPlistName = 'Info.plist',
|
infoPlistName = 'Info.plist',
|
||||||
}: { appVersion?: string; buildNumber?: string; infoPlistName?: string } = {}): ExpoConfig {
|
}: {
|
||||||
|
appVersion?: string;
|
||||||
|
buildNumber?: string;
|
||||||
|
infoPlistName?: string;
|
||||||
|
expoConfig?: { version?: string; ios?: object };
|
||||||
|
} = {}): ExpoConfig {
|
||||||
const fakeExp: ExpoConfig = {
|
const fakeExp: ExpoConfig = {
|
||||||
name: 'myproject',
|
name: 'myproject',
|
||||||
slug: 'myproject',
|
slug: 'myproject',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
ios: {
|
ios: {
|
||||||
buildNumber: '1',
|
buildNumber: '1',
|
||||||
|
...expoConfig.ios,
|
||||||
},
|
},
|
||||||
|
...expoConfig,
|
||||||
};
|
};
|
||||||
vol.fromJSON(
|
vol.fromJSON(
|
||||||
{
|
{
|
||||||
'./app.json': JSON.stringify({
|
'./app.json': JSON.stringify({
|
||||||
expo: fakeExp,
|
expo: fakeExp,
|
||||||
}),
|
}),
|
||||||
|
'./ios/myproject.xcodeproj/project.pbxproj': '',
|
||||||
[`./ios/myproject/${infoPlistName}`]: `<?xml version="1.0" encoding="UTF-8"?>
|
[`./ios/myproject/${infoPlistName}`]: `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
@@ -380,7 +403,7 @@ function initBareWorkflowProject({
|
|||||||
</dict>
|
</dict>
|
||||||
</plist>`,
|
</plist>`,
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
|
|
||||||
return fakeExp;
|
return fakeExp;
|
||||||
@@ -402,7 +425,7 @@ function initManagedProject(): ExpoConfig {
|
|||||||
expo: fakeExp,
|
expo: fakeExp,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
'/repo'
|
'/app'
|
||||||
);
|
);
|
||||||
|
|
||||||
return fakeExp;
|
return fakeExp;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface JobData {
|
|||||||
|
|
||||||
const cacheDefaults = {
|
const cacheDefaults = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
customPaths: [],
|
paths: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function prepareJobAsync(
|
export async function prepareJobAsync(
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { readPlistAsync, writePlistAsync } from '../../utils/plist';
|
|||||||
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
import { updateAppJsonConfigAsync } from '../utils/appJson';
|
||||||
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
import { bumpAppVersionAsync, ensureStaticConfigExists } from '../utils/version';
|
||||||
|
|
||||||
|
const SHORT_VERSION_REGEX = /^\d+.\d+.\d+$/;
|
||||||
|
|
||||||
export enum BumpStrategy {
|
export enum BumpStrategy {
|
||||||
APP_VERSION,
|
APP_VERSION,
|
||||||
BUILD_NUMBER,
|
BUILD_NUMBER,
|
||||||
@@ -97,6 +99,14 @@ export async function bumpVersionInAppJsonAsync({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateShortVersion(shortVersion: string | undefined): void {
|
||||||
|
if (shortVersion && !SHORT_VERSION_REGEX.test(shortVersion)) {
|
||||||
|
throw new Error(
|
||||||
|
`CFBundleShortVersionString (version field in app.json/app.config.js) must be a period-separated list of three non-negative integers. Current value: ${shortVersion}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function readShortVersionAsync(
|
export async function readShortVersionAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
exp: ExpoConfig,
|
exp: ExpoConfig,
|
||||||
@@ -105,11 +115,15 @@ export async function readShortVersionAsync(
|
|||||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
|
||||||
if (workflow === Workflow.GENERIC) {
|
if (workflow === Workflow.GENERIC) {
|
||||||
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
const infoPlist = await readInfoPlistAsync(projectDir, buildSettings);
|
||||||
return (
|
|
||||||
|
const shortVersion =
|
||||||
infoPlist.CFBundleShortVersionString &&
|
infoPlist.CFBundleShortVersionString &&
|
||||||
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings)
|
evaluateTemplateString(infoPlist.CFBundleShortVersionString, buildSettings);
|
||||||
);
|
|
||||||
|
validateShortVersion(shortVersion);
|
||||||
|
return shortVersion;
|
||||||
} else {
|
} else {
|
||||||
|
validateShortVersion(exp.version);
|
||||||
return exp.version;
|
return exp.version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
|||||||
import { ora } from '../ora';
|
import { ora } from '../ora';
|
||||||
|
|
||||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||||
const PLUGIN_PACKAGE_VERSION = '0.0.128';
|
const PLUGIN_PACKAGE_VERSION = '1.0.24';
|
||||||
|
|
||||||
export enum LocalBuildMode {
|
export enum LocalBuildMode {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { getVcsClient } from '../vcs';
|
|||||||
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
|
import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from './android/version';
|
||||||
import { BuildContext } from './context';
|
import { BuildContext } from './context';
|
||||||
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
||||||
|
import { LocalBuildMode } from './local';
|
||||||
|
|
||||||
export async function collectMetadataAsync<T extends Platform>(
|
export async function collectMetadataAsync<T extends Platform>(
|
||||||
ctx: BuildContext<T>
|
ctx: BuildContext<T>
|
||||||
@@ -47,7 +48,10 @@ export async function collectMetadataAsync<T extends Platform>(
|
|||||||
gitCommitMessage: truncateGitCommitMessage(
|
gitCommitMessage: truncateGitCommitMessage(
|
||||||
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
|
(await vcsClient.getLastCommitMessageAsync()) ?? undefined
|
||||||
),
|
),
|
||||||
isGitWorkingTreeDirty: await vcsClient.hasUncommittedChangesAsync(),
|
isGitWorkingTreeDirty:
|
||||||
|
ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL
|
||||||
|
? false
|
||||||
|
: await vcsClient.hasUncommittedChangesAsync(),
|
||||||
username: getUsername(ctx.exp, ctx.user),
|
username: getUsername(ctx.exp, ctx.user),
|
||||||
message: ctx.message,
|
message: ctx.message,
|
||||||
...(ctx.platform === Platform.IOS && {
|
...(ctx.platform === Platform.IOS && {
|
||||||
@@ -58,6 +62,7 @@ export async function collectMetadataAsync<T extends Platform>(
|
|||||||
runWithNoWaitFlag: ctx.noWait,
|
runWithNoWaitFlag: ctx.noWait,
|
||||||
runFromCI: ctx.runFromCI,
|
runFromCI: ctx.runFromCI,
|
||||||
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
buildMode: ctx.buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||||
|
customWorkflowName: ctx.customBuildConfigMetadata?.workflowName,
|
||||||
};
|
};
|
||||||
return sanitizeMetadata(metadata);
|
return sanitizeMetadata(metadata);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ function createBuildToPartialChoiceMaker(
|
|||||||
name: build.platform === AppPlatform.Ios ? 'Build number' : 'Version code',
|
name: build.platform === AppPlatform.Ios ? 'Build number' : 'Version code',
|
||||||
value: build.appBuildVersion ? chalk.bold(build.appBuildVersion) : null,
|
value: build.appBuildVersion ? chalk.bold(build.appBuildVersion) : null,
|
||||||
},
|
},
|
||||||
|
{ name: 'Profile', value: build.buildProfile ? chalk.bold(build.buildProfile) : null },
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredDescriptionArray: string[] = descriptionItems
|
const filteredDescriptionArray: string[] = descriptionItems
|
||||||
|
|||||||
@@ -31,10 +31,18 @@ import {
|
|||||||
appPlatformEmojis,
|
appPlatformEmojis,
|
||||||
toPlatforms,
|
toPlatforms,
|
||||||
} from '../platform';
|
} from '../platform';
|
||||||
import { validateCustomBuildConfigAsync } from '../project/customBuildConfig';
|
import {
|
||||||
|
CustomBuildConfigMetadata,
|
||||||
|
validateCustomBuildConfigAsync,
|
||||||
|
} from '../project/customBuildConfig';
|
||||||
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
import { checkExpoSdkIsSupportedAsync } from '../project/expoSdk';
|
||||||
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
import { validateMetroConfigForManagedWorkflowAsync } from '../project/metroConfig';
|
||||||
import { validateAppVersionRuntimePolicySupportAsync } from '../project/projectUtils';
|
import {
|
||||||
|
installExpoUpdatesAsync,
|
||||||
|
isExpoUpdatesInstalledAsDevDependency,
|
||||||
|
isExpoUpdatesInstalledOrAvailable,
|
||||||
|
validateAppVersionRuntimePolicySupportAsync,
|
||||||
|
} from '../project/projectUtils';
|
||||||
import {
|
import {
|
||||||
validateAppConfigForRemoteVersionSource,
|
validateAppConfigForRemoteVersionSource,
|
||||||
validateBuildProfileVersionSettings,
|
validateBuildProfileVersionSettings,
|
||||||
@@ -90,7 +98,7 @@ export async function runBuildAndSubmitAsync(
|
|||||||
projectDir: string,
|
projectDir: string,
|
||||||
flags: BuildFlags,
|
flags: BuildFlags,
|
||||||
actor: Actor,
|
actor: Actor,
|
||||||
getDynamicProjectConfigAsync: DynamicConfigContextFn
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await getVcsClient().ensureRepoExistsAsync();
|
await getVcsClient().ensureRepoExistsAsync();
|
||||||
await ensureRepoIsCleanAsync(flags.nonInteractive);
|
await ensureRepoIsCleanAsync(flags.nonInteractive);
|
||||||
@@ -117,9 +125,14 @@ export async function runBuildAndSubmitAsync(
|
|||||||
buildProfiles,
|
buildProfiles,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const customBuildConfigMetadataByPlatform: { [p in AppPlatform]?: CustomBuildConfigMetadata } =
|
||||||
|
{};
|
||||||
for (const buildProfile of buildProfiles) {
|
for (const buildProfile of buildProfiles) {
|
||||||
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
validateBuildProfileVersionSettings(buildProfile, easJsonCliConfig);
|
||||||
await validateCustomBuildConfigAsync(projectDir, buildProfile.profile);
|
const maybeMetadata = await validateCustomBuildConfigAsync(projectDir, buildProfile.profile);
|
||||||
|
if (maybeMetadata) {
|
||||||
|
customBuildConfigMetadataByPlatform[toAppPlatform(buildProfile.platform)] = maybeMetadata;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const startedBuilds: {
|
const startedBuilds: {
|
||||||
@@ -129,6 +142,7 @@ export async function runBuildAndSubmitAsync(
|
|||||||
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
|
const buildCtxByPlatform: { [p in AppPlatform]?: BuildContext<Platform> } = {};
|
||||||
|
|
||||||
for (const buildProfile of buildProfiles) {
|
for (const buildProfile of buildProfiles) {
|
||||||
|
const platform = toAppPlatform(buildProfile.platform);
|
||||||
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
|
const { build: maybeBuild, buildCtx } = await prepareAndStartBuildAsync({
|
||||||
projectDir,
|
projectDir,
|
||||||
flags,
|
flags,
|
||||||
@@ -138,12 +152,13 @@ export async function runBuildAndSubmitAsync(
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
|
customBuildConfigMetadata: customBuildConfigMetadataByPlatform[platform],
|
||||||
});
|
});
|
||||||
if (maybeBuild) {
|
if (maybeBuild) {
|
||||||
startedBuilds.push({ build: maybeBuild, buildProfile });
|
startedBuilds.push({ build: maybeBuild, buildProfile });
|
||||||
}
|
}
|
||||||
buildCtxByPlatform[toAppPlatform(buildProfile.platform)] = buildCtx;
|
buildCtxByPlatform[platform] = buildCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.localBuildOptions.localBuildMode) {
|
if (flags.localBuildOptions.localBuildMode) {
|
||||||
@@ -208,7 +223,9 @@ export async function runBuildAndSubmitAsync(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const haveAllBuildsFailedOrCanceled = builds.every(
|
const haveAllBuildsFailedOrCanceled = builds.every(
|
||||||
build => build?.status && [BuildStatus.Errored, BuildStatus.Canceled].includes(build?.status)
|
build =>
|
||||||
|
build?.status &&
|
||||||
|
[BuildStatus.Errored, BuildStatus.Canceled, BuildStatus.PendingCancel].includes(build?.status)
|
||||||
);
|
);
|
||||||
|
|
||||||
await maybeDownloadAndRunSimulatorBuildsAsync(builds, flags);
|
await maybeDownloadAndRunSimulatorBuildsAsync(builds, flags);
|
||||||
@@ -245,7 +262,8 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
|
customBuildConfigMetadata,
|
||||||
}: {
|
}: {
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
flags: BuildFlags;
|
flags: BuildFlags;
|
||||||
@@ -255,7 +273,8 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor: Actor;
|
actor: Actor;
|
||||||
graphqlClient: ExpoGraphqlClient;
|
graphqlClient: ExpoGraphqlClient;
|
||||||
analytics: Analytics;
|
analytics: Analytics;
|
||||||
getDynamicProjectConfigAsync: DynamicConfigContextFn;
|
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||||
|
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||||
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
}): Promise<{ build: BuildFragment | undefined; buildCtx: BuildContext<Platform> }> {
|
||||||
const buildCtx = await createBuildContextAsync({
|
const buildCtx = await createBuildContextAsync({
|
||||||
buildProfileName: buildProfile.profileName,
|
buildProfileName: buildProfile.profileName,
|
||||||
@@ -272,7 +291,8 @@ async function prepareAndStartBuildAsync({
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
|
customBuildConfigMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (moreBuilds) {
|
if (moreBuilds) {
|
||||||
@@ -291,6 +311,14 @@ async function prepareAndStartBuildAsync({
|
|||||||
buildCtx.projectId,
|
buildCtx.projectId,
|
||||||
flags.nonInteractive
|
flags.nonInteractive
|
||||||
);
|
);
|
||||||
|
if (buildProfile.profile.channel) {
|
||||||
|
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||||
|
projectDir,
|
||||||
|
sdkVersion: buildCtx.exp.sdkVersion,
|
||||||
|
nonInteractive: flags.nonInteractive,
|
||||||
|
buildProfile,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
await validateAppVersionRuntimePolicySupportAsync(buildCtx.projectDir, buildCtx.exp);
|
||||||
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
if (easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE) {
|
||||||
@@ -410,3 +438,40 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||||
|
projectDir,
|
||||||
|
buildProfile,
|
||||||
|
nonInteractive,
|
||||||
|
sdkVersion,
|
||||||
|
}: {
|
||||||
|
projectDir: string;
|
||||||
|
buildProfile: ProfileData<'build'>;
|
||||||
|
nonInteractive: boolean;
|
||||||
|
sdkVersion?: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
if (isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExpoUpdatesInstalledAsDevDependency(projectDir)) {
|
||||||
|
Log.warn(
|
||||||
|
`The build profile "${buildProfile.profileName}" uses the channel "${buildProfile.profile.channel}", but you've added "expo-updates" as a dev dependency. To make channels work for your builds, move "expo-updates" from dev dependencies to the main dependencies in your project.`
|
||||||
|
);
|
||||||
|
} else if (nonInteractive) {
|
||||||
|
Log.warn(
|
||||||
|
`The build profile "${buildProfile.profileName}" has specified the channel "${buildProfile.profile.channel}", but the "expo-updates" package hasn't been installed. To use channels for your builds, install the "expo-updates" package by running "npx expo install expo-updates".`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Log.warn(
|
||||||
|
`The build profile "${buildProfile.profileName}" specifies the channel "${buildProfile.profile.channel}", but the "expo-updates" package is missing. To use channels in your builds, install the "expo-updates" package.`
|
||||||
|
);
|
||||||
|
const installExpoUpdates = await confirmAsync({
|
||||||
|
message: `Would you like to install the "expo-updates" package?`,
|
||||||
|
});
|
||||||
|
if (installExpoUpdates) {
|
||||||
|
await installExpoUpdatesAsync(projectDir, { silent: false });
|
||||||
|
Log.withTick('Installed expo-updates');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ export enum BuildStatus {
|
|||||||
NEW = 'new',
|
NEW = 'new',
|
||||||
IN_QUEUE = 'in-queue',
|
IN_QUEUE = 'in-queue',
|
||||||
IN_PROGRESS = 'in-progress',
|
IN_PROGRESS = 'in-progress',
|
||||||
|
PENDING_CANCEL = 'pending-cancel',
|
||||||
ERRORED = 'errored',
|
ERRORED = 'errored',
|
||||||
FINISHED = 'finished',
|
FINISHED = 'finished',
|
||||||
CANCELED = 'canceled',
|
CANCELED = 'canceled',
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
|||||||
return chalk.blue('in queue');
|
return chalk.blue('in queue');
|
||||||
case GraphQLBuildStatus.InProgress:
|
case GraphQLBuildStatus.InProgress:
|
||||||
return chalk.blue('in progress');
|
return chalk.blue('in progress');
|
||||||
|
case GraphQLBuildStatus.PendingCancel:
|
||||||
case GraphQLBuildStatus.Canceled:
|
case GraphQLBuildStatus.Canceled:
|
||||||
return chalk.gray('canceled');
|
return chalk.gray('canceled');
|
||||||
case GraphQLBuildStatus.Finished:
|
case GraphQLBuildStatus.Finished:
|
||||||
@@ -90,6 +91,7 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
|||||||
case GraphQLBuildStatus.InQueue:
|
case GraphQLBuildStatus.InQueue:
|
||||||
case GraphQLBuildStatus.InProgress:
|
case GraphQLBuildStatus.InProgress:
|
||||||
return '<in progress>';
|
return '<in progress>';
|
||||||
|
case GraphQLBuildStatus.PendingCancel:
|
||||||
case GraphQLBuildStatus.Canceled:
|
case GraphQLBuildStatus.Canceled:
|
||||||
case GraphQLBuildStatus.Errored:
|
case GraphQLBuildStatus.Errored:
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function printBuildResult(build: BuildFragment): void {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (build.status === BuildStatus.Canceled) {
|
if ([BuildStatus.Canceled, BuildStatus.PendingCancel].includes(build.status)) {
|
||||||
Log.addNewLineIfNone();
|
Log.addNewLineIfNone();
|
||||||
Log.error(
|
Log.error(
|
||||||
`${appPlatformEmojis[build.platform]} ${
|
`${appPlatformEmojis[build.platform]} ${
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
|
||||||
import { Platform } from '@expo/eas-build-job';
|
import { Platform } from '@expo/eas-build-job';
|
||||||
import { BuildProfile, ResourceClass } from '@expo/eas-json';
|
import { BuildProfile, ResourceClass } from '@expo/eas-json';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import semver from 'semver';
|
|
||||||
|
|
||||||
import { BuildResourceClass } from '../../graphql/generated';
|
import { BuildResourceClass } from '../../graphql/generated';
|
||||||
import Log from '../../log';
|
import Log, { link } from '../../log';
|
||||||
import { getReactNativeVersionAsync } from '../metadata';
|
|
||||||
|
|
||||||
type AndroidResourceClass = Exclude<
|
type AndroidResourceClass = Exclude<
|
||||||
ResourceClass,
|
ResourceClass,
|
||||||
@@ -42,22 +39,20 @@ const androidResourceClassToBuildResourceClassMapping: Record<
|
|||||||
export async function resolveBuildResourceClassAsync<T extends Platform>(
|
export async function resolveBuildResourceClassAsync<T extends Platform>(
|
||||||
profile: BuildProfile<T>,
|
profile: BuildProfile<T>,
|
||||||
platform: Platform,
|
platform: Platform,
|
||||||
projectDir: string,
|
|
||||||
exp: ExpoConfig,
|
|
||||||
resourceClassFlag?: ResourceClass
|
resourceClassFlag?: ResourceClass
|
||||||
): Promise<BuildResourceClass> {
|
): Promise<BuildResourceClass> {
|
||||||
const profileResourceClass = profile.resourceClass;
|
const profileResourceClass = profile.resourceClass;
|
||||||
|
|
||||||
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
|
if (profileResourceClass && resourceClassFlag && resourceClassFlag !== profileResourceClass) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing the "${resourceClassFlag}" as the override.`
|
`Build profile specifies the "${profileResourceClass}" resource class but you passed "${resourceClassFlag}" to --resource-class.\nUsing "${resourceClassFlag}" as the override.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedResourceClass = resourceClassFlag ?? profileResourceClass;
|
const selectedResourceClass = resourceClassFlag ?? profileResourceClass;
|
||||||
|
|
||||||
return platform === Platform.IOS
|
return platform === Platform.IOS
|
||||||
? await resolveIosResourceClassAsync(exp, projectDir, resourceClassFlag ?? profileResourceClass)
|
? resolveIosResourceClass(resourceClassFlag, profileResourceClass)
|
||||||
: resolveAndroidResourceClass(selectedResourceClass);
|
: resolveAndroidResourceClass(selectedResourceClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,13 +74,19 @@ function resolveAndroidResourceClass(selectedResourceClass?: ResourceClass): Bui
|
|||||||
return androidResourceClassToBuildResourceClassMapping[resourceClass as AndroidResourceClass];
|
return androidResourceClassToBuildResourceClassMapping[resourceClass as AndroidResourceClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveIosResourceClassAsync(
|
function resolveIosResourceClass(
|
||||||
exp: ExpoConfig,
|
resourceClassFlag?: ResourceClass,
|
||||||
projectDir: string,
|
profileResourceClass?: ResourceClass
|
||||||
selectedResourceClass?: ResourceClass
|
): BuildResourceClass {
|
||||||
): Promise<BuildResourceClass> {
|
const resourceClass = resourceClassFlag ?? profileResourceClass ?? ResourceClass.DEFAULT;
|
||||||
const resourceClass =
|
|
||||||
selectedResourceClass ?? (await resolveIosDefaultRequestedResourceClassAsync(exp, projectDir));
|
if (resourceClassFlag === ResourceClass.LARGE) {
|
||||||
|
throw new Error(
|
||||||
|
`Experimental "large" resource class for Intel iOS workers is no longer available. Remove the specified resource class to use the default, or learn more about all available resource classes: ${link(
|
||||||
|
'https://docs.expo.dev/build-reference/eas-json/'
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ([ResourceClass.M1_EXPERIMENTAL, ResourceClass.M1_MEDIUM].includes(resourceClass)) {
|
if ([ResourceClass.M1_EXPERIMENTAL, ResourceClass.M1_MEDIUM].includes(resourceClass)) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
@@ -95,29 +96,13 @@ async function resolveIosResourceClassAsync(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resourceClass === ResourceClass.M1_LARGE) {
|
if ([ResourceClass.M_LARGE, ResourceClass.M1_LARGE].includes(resourceClass)) {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
`Resource class ${chalk.bold('m1-large')} is deprecated. Use ${chalk.bold(
|
`Resource class ${chalk.bold(resourceClass)} is deprecated. Use ${chalk.bold(
|
||||||
'm-large'
|
'large'
|
||||||
)} instead.`
|
)} instead.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return iosResourceClassToBuildResourceClassMapping[resourceClass];
|
return iosResourceClassToBuildResourceClassMapping[resourceClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveIosDefaultRequestedResourceClassAsync(
|
|
||||||
exp: ExpoConfig,
|
|
||||||
projectDir: string
|
|
||||||
): Promise<ResourceClass> {
|
|
||||||
const { sdkVersion } = exp;
|
|
||||||
const reactNativeVersion = await getReactNativeVersionAsync(projectDir);
|
|
||||||
if (
|
|
||||||
(sdkVersion && semver.satisfies(sdkVersion, '>=48')) ||
|
|
||||||
(reactNativeVersion && semver.satisfies(reactNativeVersion, '>=0.71.0'))
|
|
||||||
) {
|
|
||||||
return ResourceClass.M_MEDIUM;
|
|
||||||
} else {
|
|
||||||
return ResourceClass.DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,16 +11,19 @@ import Log from '../log';
|
|||||||
import SessionManager from '../user/SessionManager';
|
import SessionManager from '../user/SessionManager';
|
||||||
import AnalyticsContextField from './context/AnalyticsContextField';
|
import AnalyticsContextField from './context/AnalyticsContextField';
|
||||||
import ContextField from './context/ContextField';
|
import ContextField from './context/ContextField';
|
||||||
import { DynamicProjectConfigContextField } from './context/DynamicProjectConfigContextField';
|
import {
|
||||||
|
DynamicPrivateProjectConfigContextField,
|
||||||
|
DynamicPublicProjectConfigContextField,
|
||||||
|
} from './context/DynamicProjectConfigContextField';
|
||||||
import LoggedInContextField from './context/LoggedInContextField';
|
import LoggedInContextField from './context/LoggedInContextField';
|
||||||
import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
|
import MaybeLoggedInContextField from './context/MaybeLoggedInContextField';
|
||||||
import { OptionalProjectConfigContextField } from './context/OptionalProjectConfigContextField';
|
import { OptionalPrivateProjectConfigContextField } from './context/OptionalPrivateProjectConfigContextField';
|
||||||
import ProjectConfigContextField from './context/ProjectConfigContextField';
|
import { PrivateProjectConfigContextField } from './context/PrivateProjectConfigContextField';
|
||||||
import ProjectDirContextField from './context/ProjectDirContextField';
|
import ProjectDirContextField from './context/ProjectDirContextField';
|
||||||
import SessionManagementContextField from './context/SessionManagementContextField';
|
import SessionManagementContextField from './context/SessionManagementContextField';
|
||||||
import { EasCommandError } from './errors';
|
import { EasCommandError } from './errors';
|
||||||
|
|
||||||
type ContextInput<
|
export type ContextInput<
|
||||||
T extends {
|
T extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object
|
||||||
@@ -28,7 +31,7 @@ type ContextInput<
|
|||||||
[P in keyof T]: ContextField<T[P]>;
|
[P in keyof T]: ContextField<T[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ContextOutput<
|
export type ContextOutput<
|
||||||
T extends {
|
T extends {
|
||||||
[name: string]: any;
|
[name: string]: any;
|
||||||
} = object
|
} = object
|
||||||
@@ -65,7 +68,7 @@ export default abstract class EasCommand extends Command {
|
|||||||
* run within a project directory, null otherwise.
|
* run within a project directory, null otherwise.
|
||||||
*/
|
*/
|
||||||
OptionalProjectConfig: {
|
OptionalProjectConfig: {
|
||||||
projectConfig: new OptionalProjectConfigContextField(),
|
privateProjectConfig: new OptionalPrivateProjectConfigContextField(),
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Require this command to be run in a project directory. Return the project directory in the context.
|
* Require this command to be run in a project directory. Return the project directory in the context.
|
||||||
@@ -78,7 +81,9 @@ export default abstract class EasCommand extends Command {
|
|||||||
*/
|
*/
|
||||||
DynamicProjectConfig: {
|
DynamicProjectConfig: {
|
||||||
// eslint-disable-next-line async-protect/async-suffix
|
// eslint-disable-next-line async-protect/async-suffix
|
||||||
getDynamicProjectConfigAsync: new DynamicProjectConfigContextField(),
|
getDynamicPublicProjectConfigAsync: new DynamicPublicProjectConfigContextField(),
|
||||||
|
// eslint-disable-next-line async-protect/async-suffix
|
||||||
|
getDynamicPrivateProjectConfigAsync: new DynamicPrivateProjectConfigContextField(),
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Require the project to be identified and registered on server. Returns the project config in the context.
|
* Require the project to be identified and registered on server. Returns the project config in the context.
|
||||||
@@ -87,7 +92,7 @@ export default abstract class EasCommand extends Command {
|
|||||||
*/
|
*/
|
||||||
ProjectConfig: {
|
ProjectConfig: {
|
||||||
loggedIn: new LoggedInContextField(),
|
loggedIn: new LoggedInContextField(),
|
||||||
projectConfig: new ProjectConfigContextField(),
|
privateProjectConfig: new PrivateProjectConfigContextField(),
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Analytics manager. Returns the analytics manager in the context for use by the command.
|
* Analytics manager. Returns the analytics manager in the context for use by the command.
|
||||||
@@ -188,13 +193,18 @@ export default abstract class EasCommand extends Command {
|
|||||||
if (err instanceof EasCommandError) {
|
if (err instanceof EasCommandError) {
|
||||||
Log.error(err.message);
|
Log.error(err.message);
|
||||||
} else if (err instanceof CombinedError && err?.graphQLErrors) {
|
} else if (err instanceof CombinedError && err?.graphQLErrors) {
|
||||||
const cleanMessage = err.message.replace('[GraphQL] ', '');
|
let cleanMessage = err.message.replace('[GraphQL] ', '');
|
||||||
|
cleanMessage += err?.graphQLErrors?.[0]?.extensions?.requestId
|
||||||
|
? `\nRequest ID: ${err.graphQLErrors[0].extensions.requestId}`
|
||||||
|
: '';
|
||||||
Log.error(cleanMessage);
|
Log.error(cleanMessage);
|
||||||
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
|
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
|
||||||
} else {
|
} else {
|
||||||
Log.error(err.message);
|
Log.error(err.message);
|
||||||
}
|
}
|
||||||
Log.debug(err);
|
Log.debug(err);
|
||||||
throw new Error(baseMessage);
|
const sanitizedError = new Error(baseMessage);
|
||||||
|
sanitizedError.stack = err.stack;
|
||||||
|
throw sanitizedError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { CombinedError } from '@urql/core';
|
import { CombinedError } from '@urql/core';
|
||||||
|
import { GraphQLError } from 'graphql/error';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { AnalyticsWithOrchestration, createAnalyticsAsync } from '../../analytics/AnalyticsManager';
|
import { AnalyticsWithOrchestration, createAnalyticsAsync } from '../../analytics/AnalyticsManager';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
@@ -17,6 +19,7 @@ jest.mock('../../analytics/AnalyticsManager', () => {
|
|||||||
jest.mock('../../log');
|
jest.mock('../../log');
|
||||||
|
|
||||||
let originalProcessArgv: string[];
|
let originalProcessArgv: string[];
|
||||||
|
const mockRequestId = uuidv4();
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
originalProcessArgv = process.argv;
|
originalProcessArgv = process.argv;
|
||||||
@@ -137,6 +140,38 @@ describe(EasCommand.name, () => {
|
|||||||
expect(logDebugSpy).toBeCalledWith(error);
|
expect(logDebugSpy).toBeCalledWith(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('logs the cleaned message with request ID if present', async () => {
|
||||||
|
const TestEasCommand = createTestEasCommand();
|
||||||
|
const logErrorSpy = jest.spyOn(Log, 'error');
|
||||||
|
const logDebugSpy = jest.spyOn(Log, 'debug');
|
||||||
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
|
const graphQLError = new GraphQLError(
|
||||||
|
'Unexpected GraphQL error message',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
errorCode: 'UNKNOWN_GRAPHQL_ERROR',
|
||||||
|
requestId: mockRequestId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const graphQLErrors = [graphQLError];
|
||||||
|
const error = new CombinedError({ graphQLErrors });
|
||||||
|
runAsyncMock.mockImplementation(() => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await TestEasCommand.run();
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
expect(logErrorSpy).toBeCalledWith(
|
||||||
|
`Unexpected GraphQL error message\nRequest ID: ${mockRequestId}`
|
||||||
|
);
|
||||||
|
expect(logDebugSpy).toBeCalledWith(error);
|
||||||
|
});
|
||||||
|
|
||||||
it('re-throws the error with default base message', async () => {
|
it('re-throws the error with default base message', async () => {
|
||||||
const TestEasCommand = createTestEasCommand();
|
const TestEasCommand = createTestEasCommand();
|
||||||
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
const runAsyncMock = jest.spyOn(TestEasCommand.prototype as any, 'runAsync');
|
||||||
|
|||||||
@@ -1,29 +1,55 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
|
||||||
import { ExpoConfigOptions, getExpoConfig } from '../../project/expoConfig';
|
import {
|
||||||
|
ExpoConfigOptions,
|
||||||
|
getPrivateExpoConfig,
|
||||||
|
getPublicExpoConfig,
|
||||||
|
} from '../../project/expoConfig';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
|
|
||||||
export type DynamicConfigContextFn = (options: ExpoConfigOptions) => Promise<{
|
export type DynamicConfigContextFn = (options?: ExpoConfigOptions) => Promise<{
|
||||||
projectId: string;
|
projectId: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export class DynamicProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
|
export class DynamicPublicProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
|
||||||
async getValueAsync({
|
async getValueAsync({
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
sessionManager,
|
sessionManager,
|
||||||
}: ContextOptions): Promise<DynamicConfigContextFn> {
|
}: ContextOptions): Promise<DynamicConfigContextFn> {
|
||||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||||
return async (options?: ExpoConfigOptions) => {
|
return async (options?: ExpoConfigOptions) => {
|
||||||
const expBefore = getExpoConfig(projectDir, options);
|
const expBefore = getPublicExpoConfig(projectDir, options);
|
||||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
env: options?.env,
|
env: options?.env,
|
||||||
});
|
});
|
||||||
const exp = getExpoConfig(projectDir, options);
|
const exp = getPublicExpoConfig(projectDir, options);
|
||||||
|
return {
|
||||||
|
exp,
|
||||||
|
projectDir,
|
||||||
|
projectId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DynamicPrivateProjectConfigContextField extends ContextField<DynamicConfigContextFn> {
|
||||||
|
async getValueAsync({
|
||||||
|
nonInteractive,
|
||||||
|
sessionManager,
|
||||||
|
}: ContextOptions): Promise<DynamicConfigContextFn> {
|
||||||
|
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||||
|
return async (options?: ExpoConfigOptions) => {
|
||||||
|
const expBefore = getPrivateExpoConfig(projectDir, options);
|
||||||
|
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||||
|
nonInteractive,
|
||||||
|
env: options?.env,
|
||||||
|
});
|
||||||
|
const exp = getPrivateExpoConfig(projectDir, options);
|
||||||
return {
|
return {
|
||||||
exp,
|
exp,
|
||||||
projectDir,
|
projectDir,
|
||||||
|
|||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
import { InvalidEasJsonError } from '@expo/eas-json/build/errors';
|
import { InvalidEasJsonError } from '@expo/eas-json/build/errors';
|
||||||
|
|
||||||
import { getExpoConfig } from '../../project/expoConfig';
|
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
|
|
||||||
export class OptionalProjectConfigContextField extends ContextField<
|
export class OptionalPrivateProjectConfigContextField extends ContextField<
|
||||||
| {
|
| {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
@@ -35,11 +35,11 @@ export class OptionalProjectConfigContextField extends ContextField<
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expBefore = getExpoConfig(projectDir);
|
const expBefore = getPrivateExpoConfig(projectDir);
|
||||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
});
|
});
|
||||||
const exp = getExpoConfig(projectDir);
|
const exp = getPrivateExpoConfig(projectDir);
|
||||||
return {
|
return {
|
||||||
exp,
|
exp,
|
||||||
projectDir,
|
projectDir,
|
||||||
+4
-4
@@ -1,11 +1,11 @@
|
|||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
|
|
||||||
import { getExpoConfig } from '../../project/expoConfig';
|
import { getPrivateExpoConfig } from '../../project/expoConfig';
|
||||||
import ContextField, { ContextOptions } from './ContextField';
|
import ContextField, { ContextOptions } from './ContextField';
|
||||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||||
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||||
|
|
||||||
export default class ProjectConfigContextField extends ContextField<{
|
export class PrivateProjectConfigContextField extends ContextField<{
|
||||||
projectId: string;
|
projectId: string;
|
||||||
exp: ExpoConfig;
|
exp: ExpoConfig;
|
||||||
projectDir: string;
|
projectDir: string;
|
||||||
@@ -16,11 +16,11 @@ export default class ProjectConfigContextField extends ContextField<{
|
|||||||
projectDir: string;
|
projectDir: string;
|
||||||
}> {
|
}> {
|
||||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||||
const expBefore = getExpoConfig(projectDir);
|
const expBefore = getPrivateExpoConfig(projectDir);
|
||||||
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
const projectId = await getProjectIdAsync(sessionManager, expBefore, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
});
|
});
|
||||||
const exp = getExpoConfig(projectDir);
|
const exp = getPrivateExpoConfig(projectDir);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projectId,
|
projectId,
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getProjectConfigDescription, modifyConfigAsync } from '@expo/config';
|
import { getProjectConfigDescription } from '@expo/config';
|
||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
import { Env } from '@expo/eas-build-job';
|
import { Env } from '@expo/eas-build-job';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
@@ -6,7 +6,7 @@ import chalk from 'chalk';
|
|||||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||||
import Log, { learnMore } from '../../../log';
|
import Log, { learnMore } from '../../../log';
|
||||||
import { ora } from '../../../ora';
|
import { ora } from '../../../ora';
|
||||||
import { getExpoConfig } from '../../../project/expoConfig';
|
import { createOrModifyExpoConfigAsync, getPrivateExpoConfig } from '../../../project/expoConfig';
|
||||||
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
import { fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||||
import { toAppPrivacy } from '../../../project/projectUtils';
|
import { toAppPrivacy } from '../../../project/projectUtils';
|
||||||
import SessionManager from '../../../user/SessionManager';
|
import SessionManager from '../../../user/SessionManager';
|
||||||
@@ -24,8 +24,8 @@ export async function saveProjectIdToAppConfigAsync(
|
|||||||
projectId: string,
|
projectId: string,
|
||||||
options: { env?: Env } = {}
|
options: { env?: Env } = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const exp = getExpoConfig(projectDir, options);
|
const exp = getPrivateExpoConfig(projectDir, options);
|
||||||
const result = await modifyConfigAsync(
|
const result = await createOrModifyExpoConfigAsync(
|
||||||
projectDir,
|
projectDir,
|
||||||
{
|
{
|
||||||
extra: { ...exp.extra, eas: { ...exp.extra?.eas, projectId } },
|
extra: { ...exp.extra, eas: { ...exp.extra?.eas, projectId } },
|
||||||
@@ -160,12 +160,12 @@ export async function getProjectIdAsync(
|
|||||||
switch (user.__typename) {
|
switch (user.__typename) {
|
||||||
case 'User':
|
case 'User':
|
||||||
return user.username;
|
return user.username;
|
||||||
|
case 'SSOUser':
|
||||||
|
return user.username;
|
||||||
case 'Robot':
|
case 'Robot':
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'The "owner" manifest property is required when using robot users. See: https://docs.expo.dev/versions/latest/config/app/#owner'
|
'The "owner" manifest property is required when using robot users. See: https://docs.expo.dev/versions/latest/config/app/#owner'
|
||||||
);
|
);
|
||||||
case 'SSOUser':
|
|
||||||
throw new Error('SSO users are not supported yet.');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Flags } from '@oclif/core';
|
||||||
|
|
||||||
import EasCommand from '../../commandUtils/EasCommand';
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
|
|
||||||
@@ -5,13 +7,27 @@ export default class AccountLogin extends EasCommand {
|
|||||||
static override description = 'log in with your Expo account';
|
static override description = 'log in with your Expo account';
|
||||||
static override aliases = ['login'];
|
static override aliases = ['login'];
|
||||||
|
|
||||||
|
static override flags = {
|
||||||
|
// can pass either --sso or -s
|
||||||
|
sso: Flags.boolean({
|
||||||
|
description: 'Login with SSO',
|
||||||
|
char: 's',
|
||||||
|
default: false,
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
static override contextDefinition = {
|
static override contextDefinition = {
|
||||||
...this.ContextOptions.SessionManagment,
|
...this.ContextOptions.SessionManagment,
|
||||||
};
|
};
|
||||||
|
|
||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
|
const {
|
||||||
|
flags: { sso },
|
||||||
|
} = await this.parse(AccountLogin);
|
||||||
|
|
||||||
const { sessionManager } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
|
const { sessionManager } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
|
||||||
await sessionManager.showLoginPromptAsync();
|
await sessionManager.showLoginPromptAsync({ sso });
|
||||||
Log.log('Logged in');
|
Log.log('Logged in');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import nullthrows from 'nullthrows';
|
||||||
|
|
||||||
import EasCommand from '../../commandUtils/EasCommand';
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
|
import { Role } from '../../graphql/generated';
|
||||||
import Log from '../../log';
|
import Log from '../../log';
|
||||||
import { getActorDisplayName } from '../../user/User';
|
import { Actor, getActorDisplayName } from '../../user/User';
|
||||||
|
|
||||||
export default class AccountView extends EasCommand {
|
export default class AccountView extends EasCommand {
|
||||||
static override description = 'show the username you are logged in as';
|
static override description = 'show the username you are logged in as';
|
||||||
@@ -18,9 +20,48 @@ export default class AccountView extends EasCommand {
|
|||||||
} = await this.getContextAsync(AccountView, { nonInteractive: true });
|
} = await this.getContextAsync(AccountView, { nonInteractive: true });
|
||||||
if (actor) {
|
if (actor) {
|
||||||
Log.log(chalk.green(getActorDisplayName(actor)));
|
Log.log(chalk.green(getActorDisplayName(actor)));
|
||||||
|
|
||||||
|
// personal account is included, only show if more accounts that personal account
|
||||||
|
// but do show personal account in list if there are more
|
||||||
|
const accountExcludingPersonalAccount = actor.accounts.filter(
|
||||||
|
account => !('username' in actor) || account.name !== actor.username
|
||||||
|
);
|
||||||
|
if (accountExcludingPersonalAccount.length > 0) {
|
||||||
|
Log.newLine();
|
||||||
|
Log.log('Accounts:');
|
||||||
|
actor.accounts.forEach(account => {
|
||||||
|
const roleOnAccount = AccountView.getRoleOnAccount(actor, account);
|
||||||
|
Log.log(`• ${account.name} (Role: ${AccountView.getLabelForRole(roleOnAccount)})`);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.warn('Not logged in');
|
Log.warn('Not logged in');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static getRoleOnAccount(actor: Actor, account: Actor['accounts'][0]): Role {
|
||||||
|
if ('username' in actor && account.name === actor.username) {
|
||||||
|
return Role.Owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullthrows(account.users.find(user => user.actor.id === actor.id)?.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getLabelForRole(role: Role): string {
|
||||||
|
switch (role) {
|
||||||
|
case Role.Owner:
|
||||||
|
return 'Owner';
|
||||||
|
case Role.Admin:
|
||||||
|
return 'Admin';
|
||||||
|
case Role.Developer:
|
||||||
|
return 'Developer';
|
||||||
|
case Role.ViewOnly:
|
||||||
|
return 'Viewer';
|
||||||
|
case Role.Custom:
|
||||||
|
case Role.HasAdmin:
|
||||||
|
case Role.NotAdmin:
|
||||||
|
return 'Custom';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ export default class BranchCreate extends EasCommand {
|
|||||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(BranchCreate);
|
} = await this.parse(BranchCreate);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BranchCreate, {
|
} = await this.getContextAsync(BranchCreate, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export default class BranchDelete extends EasCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BranchDelete, { nonInteractive });
|
} = await this.getContextAsync(BranchDelete, { nonInteractive });
|
||||||
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
const projectDisplayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default class BranchList extends EasCommand {
|
|||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
const { flags } = await this.parse(BranchList);
|
const { flags } = await this.parse(BranchList);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BranchList, {
|
} = await this.getContextAsync(BranchList, {
|
||||||
nonInteractive: flags['non-interactive'],
|
nonInteractive: flags['non-interactive'],
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default class BranchRename extends EasCommand {
|
|||||||
flags: { json: jsonFlag, from: currentName, to: newName, 'non-interactive': nonInteractive },
|
flags: { json: jsonFlag, from: currentName, to: newName, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(BranchRename);
|
} = await this.parse(BranchRename);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BranchRename, {
|
} = await this.getContextAsync(BranchRename, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default class BranchView extends EasCommand {
|
|||||||
} = await this.parse(BranchView);
|
} = await this.parse(BranchView);
|
||||||
const { 'non-interactive': nonInteractive } = flags;
|
const { 'non-interactive': nonInteractive } = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BranchView, {
|
} = await this.getContextAsync(BranchView, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export default class BuildCancel extends EasCommand {
|
|||||||
flags: { 'non-interactive': nonInteractive },
|
flags: { 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(BuildCancel);
|
} = await this.parse(BuildCancel);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BuildCancel, {
|
} = await this.getContextAsync(BuildCancel, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -174,7 +174,7 @@ export default class BuildCancel extends EasCommand {
|
|||||||
const spinner = ora().start('Canceling the build…');
|
const spinner = ora().start('Canceling the build…');
|
||||||
try {
|
try {
|
||||||
const { status } = await cancelBuildAsync(graphqlClient, buildId);
|
const { status } = await cancelBuildAsync(graphqlClient, buildId);
|
||||||
if (status === BuildStatus.Canceled) {
|
if ([BuildStatus.Canceled, BuildStatus.PendingCancel].includes(status)) {
|
||||||
spinner.succeed('Build canceled');
|
spinner.succeed('Build canceled');
|
||||||
} else {
|
} else {
|
||||||
spinner.text = 'Build is already completed';
|
spinner.text = 'Build is already completed';
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import { ensureProjectConfiguredAsync } from '../../build/configure';
|
|||||||
import EasCommand from '../../commandUtils/EasCommand';
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
import Log, { learnMore } from '../../log';
|
import Log, { learnMore } from '../../log';
|
||||||
import { RequestedPlatform } from '../../platform';
|
import { RequestedPlatform } from '../../platform';
|
||||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
import { isExpoUpdatesInstalled, isUsingEASUpdate } from '../../project/projectUtils';
|
||||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||||
import { promptAsync } from '../../prompts';
|
import { promptAsync } from '../../prompts';
|
||||||
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
||||||
|
import { ensureEASUpdateIsConfiguredInEasJsonAsync } from '../../update/configure';
|
||||||
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||||
import { getVcsClient } from '../../vcs';
|
import { getVcsClient } from '../../vcs';
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ export default class BuildConfigure extends EasCommand {
|
|||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
const { flags } = await this.parse(BuildConfigure);
|
const { flags } = await this.parse(BuildConfigure);
|
||||||
const {
|
const {
|
||||||
projectConfig: { exp, projectId, projectDir },
|
privateProjectConfig: { exp, projectId, projectDir },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BuildConfigure, {
|
} = await this.getContextAsync(BuildConfigure, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
@@ -57,10 +58,13 @@ export default class BuildConfigure extends EasCommand {
|
|||||||
|
|
||||||
// ensure eas.json exists
|
// ensure eas.json exists
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
await ensureProjectConfiguredAsync({
|
const didCreateEasJson = await ensureProjectConfiguredAsync({
|
||||||
projectDir,
|
projectDir,
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
});
|
});
|
||||||
|
if (didCreateEasJson && isUsingEASUpdate(exp, projectId)) {
|
||||||
|
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
|
||||||
|
}
|
||||||
|
|
||||||
// configure expo-updates
|
// configure expo-updates
|
||||||
if (expoUpdatesIsInstalled) {
|
if (expoUpdatesIsInstalled) {
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export default class Build extends EasCommand {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(Build, {
|
} = await this.getContextAsync(Build, {
|
||||||
@@ -144,7 +144,7 @@ export default class Build extends EasCommand {
|
|||||||
projectDir,
|
projectDir,
|
||||||
flagsWithPlatform,
|
flagsWithPlatform,
|
||||||
actor,
|
actor,
|
||||||
getDynamicProjectConfigAsync
|
getDynamicPrivateProjectConfigAsync
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default class BuildInspect extends EasCommand {
|
|||||||
const { flags } = await this.parse(BuildInspect);
|
const { flags } = await this.parse(BuildInspect);
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(BuildInspect, {
|
} = await this.getContextAsync(BuildInspect, {
|
||||||
@@ -120,7 +120,7 @@ export default class BuildInspect extends EasCommand {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actor,
|
actor,
|
||||||
getDynamicProjectConfigAsync
|
getDynamicPrivateProjectConfigAsync
|
||||||
);
|
);
|
||||||
if (!flags.verbose) {
|
if (!flags.verbose) {
|
||||||
Log.log(chalk.green('Build successful'));
|
Log.log(chalk.green('Build successful'));
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class BuildInternal extends EasCommand {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(BuildInternal, {
|
} = await this.getContextAsync(BuildInternal, {
|
||||||
@@ -74,7 +74,7 @@ export default class BuildInternal extends EasCommand {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actor,
|
actor,
|
||||||
getDynamicProjectConfigAsync
|
getDynamicPrivateProjectConfigAsync
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ export default class BuildList extends EasCommand {
|
|||||||
BuildStatus.NEW,
|
BuildStatus.NEW,
|
||||||
BuildStatus.IN_QUEUE,
|
BuildStatus.IN_QUEUE,
|
||||||
BuildStatus.IN_PROGRESS,
|
BuildStatus.IN_PROGRESS,
|
||||||
|
BuildStatus.PENDING_CANCEL,
|
||||||
BuildStatus.ERRORED,
|
BuildStatus.ERRORED,
|
||||||
BuildStatus.FINISHED,
|
BuildStatus.FINISHED,
|
||||||
BuildStatus.CANCELED,
|
BuildStatus.CANCELED,
|
||||||
@@ -68,7 +69,7 @@ export default class BuildList extends EasCommand {
|
|||||||
'non-interactive': nonInteractive,
|
'non-interactive': nonInteractive,
|
||||||
} = flags;
|
} = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BuildList, {
|
} = await this.getContextAsync(BuildList, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -123,6 +124,8 @@ const toGraphQLBuildStatus = (buildStatus?: BuildStatus): GraphQLBuildStatus | u
|
|||||||
return GraphQLBuildStatus.InQueue;
|
return GraphQLBuildStatus.InQueue;
|
||||||
} else if (buildStatus === BuildStatus.IN_PROGRESS) {
|
} else if (buildStatus === BuildStatus.IN_PROGRESS) {
|
||||||
return GraphQLBuildStatus.InProgress;
|
return GraphQLBuildStatus.InProgress;
|
||||||
|
} else if (buildStatus === BuildStatus.PENDING_CANCEL) {
|
||||||
|
return GraphQLBuildStatus.PendingCancel;
|
||||||
} else if (buildStatus === BuildStatus.ERRORED) {
|
} else if (buildStatus === BuildStatus.ERRORED) {
|
||||||
return GraphQLBuildStatus.Errored;
|
return GraphQLBuildStatus.Errored;
|
||||||
} else if (buildStatus === BuildStatus.FINISHED) {
|
} else if (buildStatus === BuildStatus.FINISHED) {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default class BuildResign extends EasCommand {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(BuildResign, {
|
} = await this.getContextAsync(BuildResign, {
|
||||||
@@ -130,7 +130,7 @@ export default class BuildResign extends EasCommand {
|
|||||||
platform,
|
platform,
|
||||||
flags.profile ?? 'production'
|
flags.profile ?? 'production'
|
||||||
);
|
);
|
||||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: buildProfile.env });
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||||
const build = await this.ensureBuildSelectedAsync(
|
const build = await this.ensureBuildSelectedAsync(
|
||||||
{ graphqlClient, projectId, platform, nonInteractive, limit, offset },
|
{ graphqlClient, projectId, platform, nonInteractive, limit, offset },
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export default class Run extends EasCommand {
|
|||||||
const queryOptions = getPaginatedQueryOptions(flags);
|
const queryOptions = getPaginatedQueryOptions(flags);
|
||||||
const {
|
const {
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
} = await this.getContextAsync(Run, {
|
} = await this.getContextAsync(Run, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
});
|
});
|
||||||
@@ -210,8 +210,9 @@ async function maybeGetBuildAsync(
|
|||||||
},
|
},
|
||||||
paginatedQueryOptions,
|
paginatedQueryOptions,
|
||||||
selectPromptDisabledFunction: build => !isRunnableOnSimulatorOrEmulator(build),
|
selectPromptDisabledFunction: build => !isRunnableOnSimulatorOrEmulator(build),
|
||||||
selectPromptWarningMessage:
|
selectPromptWarningMessage: `Artifacts for this build have expired and are no longer available, or this is not ${
|
||||||
'Artifacts for this build have expired and are no longer available, or this is not a simulator/emulator build.',
|
flags.selectedPlatform === AppPlatform.Ios ? 'a simulator' : 'an emulator'
|
||||||
|
} build.`,
|
||||||
});
|
});
|
||||||
return validateChosenBuild(build, flags.selectedPlatform);
|
return validateChosenBuild(build, flags.selectedPlatform);
|
||||||
} else if (flags.runArchiveFlags.latest) {
|
} else if (flags.runArchiveFlags.latest) {
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import { Platform } from '@expo/eas-build-job';
|
||||||
|
import { EasJsonAccessor } from '@expo/eas-json';
|
||||||
|
import { Flags } from '@oclif/core';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import EasCommand from '../../../commandUtils/EasCommand';
|
||||||
|
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||||
|
import { AppVersionQuery } from '../../../graphql/queries/AppVersionQuery';
|
||||||
|
import { toAppPlatform } from '../../../graphql/types/AppPlatform';
|
||||||
|
import Log from '../../../log';
|
||||||
|
import { selectRequestedPlatformAsync, toPlatforms } from '../../../platform';
|
||||||
|
import { getApplicationIdentifierAsync } from '../../../project/applicationIdentifier';
|
||||||
|
import {
|
||||||
|
ensureVersionSourceIsRemoteAsync,
|
||||||
|
validateAppConfigForRemoteVersionSource,
|
||||||
|
} from '../../../project/remoteVersionSource';
|
||||||
|
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
|
||||||
|
import { getProfilesAsync } from '../../../utils/profiles';
|
||||||
|
|
||||||
|
export default class BuildVersionGetView extends EasCommand {
|
||||||
|
public static override description = 'get the latest version from EAS servers';
|
||||||
|
|
||||||
|
public static override flags = {
|
||||||
|
platform: Flags.enum({
|
||||||
|
char: 'p',
|
||||||
|
options: ['android', 'ios', 'all'],
|
||||||
|
}),
|
||||||
|
profile: Flags.string({
|
||||||
|
char: 'e',
|
||||||
|
description:
|
||||||
|
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||||
|
helpValue: 'PROFILE_NAME',
|
||||||
|
}),
|
||||||
|
...EasNonInteractiveAndJsonFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
static override contextDefinition = {
|
||||||
|
...this.ContextOptions.LoggedIn,
|
||||||
|
...this.ContextOptions.DynamicProjectConfig,
|
||||||
|
...this.ContextOptions.ProjectDir,
|
||||||
|
};
|
||||||
|
|
||||||
|
public async runAsync(): Promise<void> {
|
||||||
|
const { flags } = await this.parse(BuildVersionGetView);
|
||||||
|
if (flags.json) {
|
||||||
|
enableJsonOutput();
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
loggedIn: { graphqlClient },
|
||||||
|
getDynamicPrivateProjectConfigAsync,
|
||||||
|
projectDir,
|
||||||
|
} = await this.getContextAsync(BuildVersionGetView, {
|
||||||
|
nonInteractive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!flags.platform && flags['non-interactive']) {
|
||||||
|
throw new Error('"--platform" flag is required in non-interactive mode.');
|
||||||
|
}
|
||||||
|
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||||
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
|
await ensureVersionSourceIsRemoteAsync(easJsonAccessor, {
|
||||||
|
nonInteractive: flags['non-interactive'],
|
||||||
|
});
|
||||||
|
|
||||||
|
const platforms = toPlatforms(requestedPlatform);
|
||||||
|
const buildProfiles = await getProfilesAsync({
|
||||||
|
type: 'build',
|
||||||
|
easJsonAccessor,
|
||||||
|
platforms,
|
||||||
|
profileName: flags.profile ?? undefined,
|
||||||
|
});
|
||||||
|
const results: { [key in Platform]?: string } = {};
|
||||||
|
for (const { profile, platform } of buildProfiles) {
|
||||||
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||||
|
env: profile.env,
|
||||||
|
});
|
||||||
|
|
||||||
|
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||||
|
|
||||||
|
const applicationIdentifier = await getApplicationIdentifierAsync({
|
||||||
|
graphqlClient,
|
||||||
|
projectDir,
|
||||||
|
projectId,
|
||||||
|
exp,
|
||||||
|
buildProfile: profile,
|
||||||
|
platform,
|
||||||
|
});
|
||||||
|
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||||
|
graphqlClient,
|
||||||
|
projectId,
|
||||||
|
toAppPlatform(platform),
|
||||||
|
applicationIdentifier
|
||||||
|
);
|
||||||
|
if (remoteVersions?.buildVersion) {
|
||||||
|
results[platform] = remoteVersions?.buildVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags.json) {
|
||||||
|
const jsonResults: { versionCode?: string; buildNumber?: string } = {};
|
||||||
|
if (results.android) {
|
||||||
|
jsonResults.versionCode = results.android;
|
||||||
|
}
|
||||||
|
if (results.ios) {
|
||||||
|
jsonResults.buildNumber = results.ios;
|
||||||
|
}
|
||||||
|
printJsonOnlyOutput(jsonResults);
|
||||||
|
} else {
|
||||||
|
if (Object.keys(results).length === 0) {
|
||||||
|
Log.log('No remote versions are configured for this project.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (results.android) {
|
||||||
|
Log.log(`Android versionCode - ${chalk.bold(results.android)}`);
|
||||||
|
}
|
||||||
|
if (results.ios) {
|
||||||
|
Log.log(`iOS buildNumber - ${chalk.bold(results.ios)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
import { promptAsync } from '../../../prompts';
|
import { promptAsync } from '../../../prompts';
|
||||||
|
|
||||||
export default class BuildVersionSetView extends EasCommand {
|
export default class BuildVersionSetView extends EasCommand {
|
||||||
static override description = 'Update version of an app.';
|
static override description = 'update version of an app';
|
||||||
|
|
||||||
static override flags = {
|
static override flags = {
|
||||||
platform: Flags.enum({
|
platform: Flags.enum({
|
||||||
@@ -47,7 +47,7 @@ export default class BuildVersionSetView extends EasCommand {
|
|||||||
const { flags } = await this.parse(BuildVersionSetView);
|
const { flags } = await this.parse(BuildVersionSetView);
|
||||||
const {
|
const {
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
} = await this.getContextAsync(BuildVersionSetView, {
|
} = await this.getContextAsync(BuildVersionSetView, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
@@ -55,14 +55,14 @@ export default class BuildVersionSetView extends EasCommand {
|
|||||||
|
|
||||||
const platform = await selectPlatformAsync(flags.platform);
|
const platform = await selectPlatformAsync(flags.platform);
|
||||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
|
await ensureVersionSourceIsRemoteAsync(easJsonAccessor, { nonInteractive: false });
|
||||||
const profile = await EasJsonUtils.getBuildProfileAsync(
|
const profile = await EasJsonUtils.getBuildProfileAsync(
|
||||||
easJsonAccessor,
|
easJsonAccessor,
|
||||||
platform,
|
platform,
|
||||||
flags.profile ?? undefined
|
flags.profile ?? undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
const { exp, projectId } = await getDynamicProjectConfigAsync({ env: profile.env });
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: profile.env });
|
||||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||||
|
|
||||||
validateAppConfigForRemoteVersionSource(exp, platform);
|
validateAppConfigForRemoteVersionSource(exp, platform);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface SyncContext<T extends Platform> {
|
|||||||
|
|
||||||
export default class BuildVersionSyncView extends EasCommand {
|
export default class BuildVersionSyncView extends EasCommand {
|
||||||
public static override description =
|
public static override description =
|
||||||
'Update a version in native code with a value stored on EAS servers';
|
'update a version in native code with a value stored on EAS servers';
|
||||||
|
|
||||||
public static override flags = {
|
public static override flags = {
|
||||||
platform: Flags.enum({
|
platform: Flags.enum({
|
||||||
@@ -64,7 +64,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
|||||||
const { flags } = await this.parse(BuildVersionSyncView);
|
const { flags } = await this.parse(BuildVersionSyncView);
|
||||||
const {
|
const {
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
projectDir,
|
projectDir,
|
||||||
} = await this.getContextAsync(BuildVersionSyncView, {
|
} = await this.getContextAsync(BuildVersionSyncView, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
@@ -72,7 +72,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
|||||||
|
|
||||||
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
const requestedPlatform = await selectRequestedPlatformAsync(flags.platform);
|
||||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||||
await ensureVersionSourceIsRemoteAsync(easJsonAccessor);
|
await ensureVersionSourceIsRemoteAsync(easJsonAccessor, { nonInteractive: false });
|
||||||
|
|
||||||
const platforms = toPlatforms(requestedPlatform);
|
const platforms = toPlatforms(requestedPlatform);
|
||||||
const buildProfiles = await getProfilesAsync({
|
const buildProfiles = await getProfilesAsync({
|
||||||
@@ -82,7 +82,7 @@ export default class BuildVersionSyncView extends EasCommand {
|
|||||||
profileName: flags.profile ?? undefined,
|
profileName: flags.profile ?? undefined,
|
||||||
});
|
});
|
||||||
for (const profileInfo of buildProfiles) {
|
for (const profileInfo of buildProfiles) {
|
||||||
const { exp, projectId } = await getDynamicProjectConfigAsync({
|
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||||
env: profileInfo.profile.env,
|
env: profileInfo.profile.env,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default class BuildView extends EasCommand {
|
|||||||
flags,
|
flags,
|
||||||
} = await this.parse(BuildView);
|
} = await this.parse(BuildView);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BuildView, {
|
} = await this.getContextAsync(BuildView, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default class ChannelCreate extends EasCommand {
|
|||||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(ChannelCreate);
|
} = await this.parse(ChannelCreate);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelCreate, {
|
} = await this.getContextAsync(ChannelCreate, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class ChannelDelete extends EasCommand {
|
|||||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(ChannelDelete);
|
} = await this.parse(ChannelDelete);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelDelete, {
|
} = await this.getContextAsync(ChannelDelete, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default class ChannelEdit extends EasCommand {
|
|||||||
flags: { branch: branchFlag, json, 'non-interactive': nonInteractive },
|
flags: { branch: branchFlag, json, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(ChannelEdit);
|
} = await this.parse(ChannelEdit);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelEdit, {
|
} = await this.getContextAsync(ChannelEdit, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default class ChannelList extends EasCommand {
|
|||||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||||
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelList, {
|
} = await this.getContextAsync(ChannelList, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -336,7 +336,7 @@ export default class ChannelRollout extends EasCommand {
|
|||||||
},
|
},
|
||||||
} = await this.parse(ChannelRollout);
|
} = await this.parse(ChannelRollout);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelRollout, {
|
} = await this.getContextAsync(ChannelRollout, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default class ChannelView extends EasCommand {
|
|||||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||||
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ChannelView, {
|
} = await this.getContextAsync(ChannelView, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class Config extends EasCommand {
|
|||||||
enableJsonOutput();
|
enableJsonOutput();
|
||||||
}
|
}
|
||||||
const { platform: maybePlatform, profile: maybeProfile } = flags;
|
const { platform: maybePlatform, profile: maybeProfile } = flags;
|
||||||
const { getDynamicProjectConfigAsync, projectDir } = await this.getContextAsync(Config, {
|
const { getDynamicPublicProjectConfigAsync, projectDir } = await this.getContextAsync(Config, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -82,9 +82,8 @@ export default class Config extends EasCommand {
|
|||||||
Log.log(JSON.stringify(profile, null, 2));
|
Log.log(JSON.stringify(profile, null, 2));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { exp: appConfig } = await getDynamicProjectConfigAsync({
|
const { exp: appConfig } = await getDynamicPublicProjectConfigAsync({
|
||||||
env: profile.env,
|
env: profile.env,
|
||||||
isPublicConfig: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (flags.json) {
|
if (flags.json) {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export default class Credentials extends EasCommand {
|
|||||||
const { flags } = await this.parse(Credentials);
|
const { flags } = await this.parse(Credentials);
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
projectConfig,
|
privateProjectConfig,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(Credentials, {
|
} = await this.getContextAsync(Credentials, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
@@ -32,8 +32,8 @@ export default class Credentials extends EasCommand {
|
|||||||
actor,
|
actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
analytics,
|
analytics,
|
||||||
projectConfig ?? null,
|
privateProjectConfig ?? null,
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPrivateProjectConfigAsync,
|
||||||
flags.platform
|
flags.platform
|
||||||
).runAsync();
|
).runAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default class DeviceCreate extends EasCommand {
|
|||||||
// this command is interactive by design
|
// this command is interactive by design
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
projectConfig,
|
privateProjectConfig,
|
||||||
} = await this.getContextAsync(DeviceCreate, {
|
} = await this.getContextAsync(DeviceCreate, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
});
|
});
|
||||||
@@ -24,7 +24,7 @@ export default class DeviceCreate extends EasCommand {
|
|||||||
appStore: new AppStoreApi(),
|
appStore: new AppStoreApi(),
|
||||||
user: actor,
|
user: actor,
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
projectId: projectConfig?.projectId,
|
projectId: privateProjectConfig?.projectId,
|
||||||
});
|
});
|
||||||
const manager = new DeviceManager(ctx);
|
const manager = new DeviceManager(ctx);
|
||||||
await manager.createAsync();
|
await manager.createAsync();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default class DeviceDelete extends EasCommand {
|
|||||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||||
let { 'apple-team-id': appleTeamIdentifier, udid } = flags;
|
let { 'apple-team-id': appleTeamIdentifier, udid } = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(DeviceDelete, {
|
} = await this.getContextAsync(DeviceDelete, {
|
||||||
nonInteractive: paginatedQueryOptions.nonInteractive,
|
nonInteractive: paginatedQueryOptions.nonInteractive,
|
||||||
@@ -112,11 +112,17 @@ export default class DeviceDelete extends EasCommand {
|
|||||||
const removeAppleSpinner = ora('Disabling device on Apple').start();
|
const removeAppleSpinner = ora('Disabling device on Apple').start();
|
||||||
try {
|
try {
|
||||||
const appleValidatedDevices = await Device.getAsync(context);
|
const appleValidatedDevices = await Device.getAsync(context);
|
||||||
const appleValidatedDevice = appleValidatedDevices.find(d => d.id === device.id);
|
const appleValidatedDevice = appleValidatedDevices.find(
|
||||||
|
d => d.attributes.udid === device.identifier
|
||||||
|
);
|
||||||
if (appleValidatedDevice) {
|
if (appleValidatedDevice) {
|
||||||
await appleValidatedDevice.updateAsync({ status: DeviceStatus.DISABLED });
|
await appleValidatedDevice.updateAsync({ status: DeviceStatus.DISABLED });
|
||||||
}
|
|
||||||
removeAppleSpinner.succeed('Disabled device on Apple');
|
removeAppleSpinner.succeed('Disabled device on Apple');
|
||||||
|
} else {
|
||||||
|
removeAppleSpinner.warn(
|
||||||
|
'Device not found on Apple Developer Portal. Expo-registered devices will not appear there until they are chosen for an internal distribution build.'
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
removeAppleSpinner.fail();
|
removeAppleSpinner.fail();
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default class BuildList extends EasCommand {
|
|||||||
const { flags } = await this.parse(BuildList);
|
const { flags } = await this.parse(BuildList);
|
||||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(BuildList, {
|
} = await this.getContextAsync(BuildList, {
|
||||||
nonInteractive: paginatedQueryOptions.nonInteractive,
|
nonInteractive: paginatedQueryOptions.nonInteractive,
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import { Device } from '@expo/apple-utils';
|
||||||
|
import { Flags } from '@oclif/core';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
|
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
|
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||||
|
import { PaginatedQueryOptions, getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||||
|
import { AppleDeviceMutation } from '../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
|
||||||
|
import {
|
||||||
|
AppleDeviceQuery,
|
||||||
|
AppleDeviceQueryResult,
|
||||||
|
} from '../../credentials/ios/api/graphql/queries/AppleDeviceQuery';
|
||||||
|
import { authenticateAsync, getRequestContext } from '../../credentials/ios/appstore/authenticate';
|
||||||
|
import {
|
||||||
|
selectAppleDeviceOnAppleTeamAsync,
|
||||||
|
selectAppleTeamOnAccountAsync,
|
||||||
|
} from '../../devices/queries';
|
||||||
|
import formatDevice from '../../devices/utils/formatDevice';
|
||||||
|
import { AppleDevice, AppleTeamFragment, Maybe } from '../../graphql/generated';
|
||||||
|
import Log from '../../log';
|
||||||
|
import { ora } from '../../ora';
|
||||||
|
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||||
|
import { promptAsync } from '../../prompts';
|
||||||
|
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||||
|
|
||||||
|
export default class DeviceRename extends EasCommand {
|
||||||
|
static override description = 'rename a registered device';
|
||||||
|
|
||||||
|
static override flags = {
|
||||||
|
'apple-team-id': Flags.string({ description: 'The Apple team ID on which to find the device' }),
|
||||||
|
udid: Flags.string({ description: 'The Apple device ID to rename' }),
|
||||||
|
name: Flags.string({ description: 'The new name for the device' }),
|
||||||
|
...EasNonInteractiveAndJsonFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
static override contextDefinition = {
|
||||||
|
...this.ContextOptions.ProjectConfig,
|
||||||
|
...this.ContextOptions.LoggedIn,
|
||||||
|
};
|
||||||
|
|
||||||
|
async runAsync(): Promise<void> {
|
||||||
|
const { flags } = await this.parse(DeviceRename);
|
||||||
|
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||||
|
let { 'apple-team-id': appleTeamIdentifier, udid, name } = flags;
|
||||||
|
const {
|
||||||
|
privateProjectConfig: { projectId },
|
||||||
|
loggedIn: { graphqlClient },
|
||||||
|
} = await this.getContextAsync(DeviceRename, {
|
||||||
|
nonInteractive: paginatedQueryOptions.nonInteractive,
|
||||||
|
});
|
||||||
|
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||||
|
let appleTeamName;
|
||||||
|
|
||||||
|
if (paginatedQueryOptions.json) {
|
||||||
|
enableJsonOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
let appleTeam: AppleTeamFragment;
|
||||||
|
|
||||||
|
if (!appleTeamIdentifier) {
|
||||||
|
appleTeam = await selectAppleTeamOnAccountAsync(graphqlClient, {
|
||||||
|
accountName: account.name,
|
||||||
|
selectionPromptTitle: `What Apple team would you like to list devices for?`,
|
||||||
|
paginatedQueryOptions,
|
||||||
|
});
|
||||||
|
appleTeamIdentifier = appleTeam.appleTeamIdentifier;
|
||||||
|
appleTeamName = appleTeam.appleTeamName;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(appleTeamIdentifier, 'No team identifier is specified');
|
||||||
|
|
||||||
|
const chosenDevice = udid
|
||||||
|
? await AppleDeviceQuery.getByDeviceIdentifierAsync(graphqlClient, account.name, udid)
|
||||||
|
: await selectAppleDeviceOnAppleTeamAsync(graphqlClient, {
|
||||||
|
accountName: account.name,
|
||||||
|
appleTeamIdentifier,
|
||||||
|
selectionPromptTitle: `Which device would you like to rename?`,
|
||||||
|
paginatedQueryOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newDeviceName = name ? name : await this.promptForNewDeviceNameAsync(chosenDevice.name);
|
||||||
|
|
||||||
|
this.logChosenDevice(chosenDevice, appleTeamName, appleTeamIdentifier, paginatedQueryOptions);
|
||||||
|
|
||||||
|
await this.renameDeviceOnExpoAsync(graphqlClient, chosenDevice, newDeviceName!);
|
||||||
|
|
||||||
|
await this.renameDeviceOnAppleAsync(chosenDevice, appleTeamIdentifier, newDeviceName!);
|
||||||
|
}
|
||||||
|
|
||||||
|
async promptForNewDeviceNameAsync(
|
||||||
|
initial: Maybe<string> | undefined
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
const { name } = await promptAsync({
|
||||||
|
type: 'text',
|
||||||
|
name: 'name',
|
||||||
|
message: 'New device name:',
|
||||||
|
initial: initial ?? undefined,
|
||||||
|
});
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
async renameDeviceOnExpoAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
chosenDevice: AppleDevice | AppleDeviceQueryResult,
|
||||||
|
newDeviceName: string
|
||||||
|
): Promise<void> {
|
||||||
|
const removalSpinner = ora(`Renaming Apple device on Expo`).start();
|
||||||
|
try {
|
||||||
|
await AppleDeviceMutation.updateAppleDeviceAsync(graphqlClient, chosenDevice.id, {
|
||||||
|
name: newDeviceName,
|
||||||
|
});
|
||||||
|
removalSpinner.succeed('Renamed Apple device on Expo');
|
||||||
|
} catch (err) {
|
||||||
|
removalSpinner.fail();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async renameDeviceOnAppleAsync(
|
||||||
|
device: AppleDevice | AppleDeviceQueryResult,
|
||||||
|
appleTeamIdentifier: string,
|
||||||
|
newDeviceName: string
|
||||||
|
): Promise<void> {
|
||||||
|
const ctx = await authenticateAsync({ teamId: appleTeamIdentifier });
|
||||||
|
const context = getRequestContext(ctx);
|
||||||
|
|
||||||
|
Log.addNewLineIfNone();
|
||||||
|
const removeAppleSpinner = ora('Renaming device on Apple').start();
|
||||||
|
try {
|
||||||
|
const appleValidatedDevices = await Device.getAsync(context);
|
||||||
|
const appleValidatedDevice = appleValidatedDevices.find(
|
||||||
|
d => d.attributes.udid === device.identifier
|
||||||
|
);
|
||||||
|
if (appleValidatedDevice) {
|
||||||
|
await appleValidatedDevice.updateAsync({ name: newDeviceName });
|
||||||
|
removeAppleSpinner.succeed('Renamed device on Apple');
|
||||||
|
} else {
|
||||||
|
removeAppleSpinner.warn(
|
||||||
|
'Device not found on Apple Developer Portal. Expo-registered devices will not appear there until they are chosen for an internal distribution build.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
removeAppleSpinner.fail();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logChosenDevice(
|
||||||
|
device: AppleDevice | AppleDeviceQueryResult,
|
||||||
|
appleTeamName: Maybe<string> | undefined,
|
||||||
|
appleTeamIdentifier: string,
|
||||||
|
{ json }: PaginatedQueryOptions
|
||||||
|
): void {
|
||||||
|
if (json) {
|
||||||
|
printJsonOnlyOutput(device);
|
||||||
|
} else {
|
||||||
|
Log.addNewLineIfNone();
|
||||||
|
Log.log(
|
||||||
|
formatDevice(device, {
|
||||||
|
appleTeamName,
|
||||||
|
appleTeamIdentifier: appleTeamIdentifier!,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
Log.newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ If you are not sure what is the UDID of the device you are looking for, run:
|
|||||||
throw new Error('Device UDID is missing');
|
throw new Error('Device UDID is missing');
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(DeviceView, {
|
} = await this.getContextAsync(DeviceView, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default class MetadataPull extends EasCommand {
|
|||||||
const { flags } = await this.parse(MetadataPull);
|
const { flags } = await this.parse(MetadataPull);
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
projectConfig: { exp, projectId, projectDir },
|
privateProjectConfig: { exp, projectId, projectDir },
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(MetadataPull, {
|
} = await this.getContextAsync(MetadataPull, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class MetadataPush extends EasCommand {
|
|||||||
const { flags } = await this.parse(MetadataPush);
|
const { flags } = await this.parse(MetadataPush);
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
projectConfig: { exp, projectId, projectDir },
|
privateProjectConfig: { exp, projectId, projectDir },
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(MetadataPush, {
|
} = await this.getContextAsync(MetadataPush, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default class Open extends EasCommand {
|
|||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
// this command is interactive by nature (only really run by humans in a terminal)
|
// this command is interactive by nature (only really run by humans in a terminal)
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId, exp },
|
privateProjectConfig: { projectId, exp },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(Open, {
|
} = await this.getContextAsync(Open, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppJSONConfig, PackageJSONConfig, getConfig, modifyConfigAsync } from '@expo/config';
|
import { AppJSONConfig, PackageJSONConfig, getConfig } from '@expo/config';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { vol } from 'memfs';
|
import { vol } from 'memfs';
|
||||||
import { instance, mock } from 'ts-mockito';
|
import { instance, mock } from 'ts-mockito';
|
||||||
@@ -12,12 +12,17 @@ import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
|||||||
import { jester, jester2 } from '../../../credentials/__tests__/fixtures-constants';
|
import { jester, jester2 } from '../../../credentials/__tests__/fixtures-constants';
|
||||||
import { AppMutation } from '../../../graphql/mutations/AppMutation';
|
import { AppMutation } from '../../../graphql/mutations/AppMutation';
|
||||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||||
|
import { createOrModifyExpoConfigAsync } from '../../../project/expoConfig';
|
||||||
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||||
import { confirmAsync, promptAsync } from '../../../prompts';
|
import { confirmAsync, promptAsync } from '../../../prompts';
|
||||||
import ProjectInit from '../init';
|
import ProjectInit from '../init';
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
jest.mock('@expo/config');
|
jest.mock('@expo/config');
|
||||||
|
jest.mock('../../../project/expoConfig', () => ({
|
||||||
|
...jest.requireActual('../../../project/expoConfig'),
|
||||||
|
createOrModifyExpoConfigAsync: jest.fn(),
|
||||||
|
}));
|
||||||
jest.mock('../../../prompts');
|
jest.mock('../../../prompts');
|
||||||
jest.mock('../../../user/actions');
|
jest.mock('../../../user/actions');
|
||||||
jest.mock('../../../graphql/mutations/AppMutation');
|
jest.mock('../../../graphql/mutations/AppMutation');
|
||||||
@@ -143,13 +148,16 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('force', () => {
|
describe('force', () => {
|
||||||
it('does not prompt to overwrite when different', async () => {
|
beforeEach(() => {
|
||||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||||
id: '1234',
|
id: '1234',
|
||||||
slug: 'testing-123',
|
slug: 'testing-123',
|
||||||
fullName: '@jester/testing-123',
|
fullName: '@jester/testing-123',
|
||||||
ownerAccount: jester.accounts[0],
|
ownerAccount: jester.accounts[0],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not prompt to overwrite when different', async () => {
|
||||||
await new ProjectInit(['--id', '12345', '--force'], {
|
await new ProjectInit(['--id', '12345', '--force'], {
|
||||||
root: '/test-project',
|
root: '/test-project',
|
||||||
} as any).run();
|
} as any).run();
|
||||||
@@ -183,7 +191,7 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
||||||
expect(modifyConfigAsync).not.toHaveBeenCalled();
|
expect(createOrModifyExpoConfigAsync).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prompts to configure if not consistent', async () => {
|
it('prompts to configure if not consistent', async () => {
|
||||||
@@ -195,14 +203,20 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
||||||
|
|
||||||
expect(confirmAsync).toHaveBeenCalled();
|
expect(confirmAsync).toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledTimes(2);
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
owner: 'jester2',
|
||||||
|
});
|
||||||
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
|
slug: 'testing-124',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('overrides if force flag is present and it is not consistent', async () => {
|
it('overrides if force flag is present and it is not consistent', async () => {
|
||||||
@@ -213,14 +227,20 @@ describe(ProjectInit.name, () => {
|
|||||||
ownerAccount: jester2.accounts[0],
|
ownerAccount: jester2.accounts[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234', '--force'], commandOptions).run();
|
await new ProjectInit(['--id', '1234', '--force'], commandOptions).run();
|
||||||
|
|
||||||
expect(confirmAsync).not.toHaveBeenCalled();
|
expect(confirmAsync).not.toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledTimes(2);
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
owner: 'jester2',
|
||||||
|
});
|
||||||
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
|
slug: 'testing-124',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if non-interactive is present and it is not consistent', async () => {
|
it('throws if non-interactive is present and it is not consistent', async () => {
|
||||||
@@ -231,7 +251,9 @@ describe(ProjectInit.name, () => {
|
|||||||
ownerAccount: jester2.accounts[0],
|
ownerAccount: jester2.accounts[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
new ProjectInit(['--id', '1234', '--non-interactive'], commandOptions).run()
|
new ProjectInit(['--id', '1234', '--non-interactive'], commandOptions).run()
|
||||||
@@ -240,7 +262,7 @@ describe(ProjectInit.name, () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(confirmAsync).not.toHaveBeenCalled();
|
expect(confirmAsync).not.toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).not.toHaveBeenCalled();
|
expect(createOrModifyExpoConfigAsync).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -271,7 +293,7 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
||||||
expect(modifyConfigAsync).not.toHaveBeenCalled();
|
expect(createOrModifyExpoConfigAsync).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prompts to configure if not consistent', async () => {
|
it('prompts to configure if not consistent', async () => {
|
||||||
@@ -285,14 +307,20 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
await new ProjectInit(['--id', '1234'], commandOptions).run();
|
||||||
|
|
||||||
expect(confirmAsync).toHaveBeenCalled();
|
expect(confirmAsync).toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledTimes(2);
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
owner: 'jester2',
|
||||||
|
});
|
||||||
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
|
slug: 'testing-124',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('overrides if force flag is present and it is not consistent', async () => {
|
it('overrides if force flag is present and it is not consistent', async () => {
|
||||||
@@ -305,14 +333,20 @@ describe(ProjectInit.name, () => {
|
|||||||
ownerAccount: jester2.accounts[0],
|
ownerAccount: jester2.accounts[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await new ProjectInit(['--id', '1234', '--force'], commandOptions).run();
|
await new ProjectInit(['--id', '1234', '--force'], commandOptions).run();
|
||||||
|
|
||||||
expect(confirmAsync).not.toHaveBeenCalled();
|
expect(confirmAsync).not.toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledTimes(2);
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
owner: 'jester2',
|
||||||
|
});
|
||||||
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
|
slug: 'testing-124',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if non-interactive is present and it is not consistent', async () => {
|
it('throws if non-interactive is present and it is not consistent', async () => {
|
||||||
@@ -325,7 +359,9 @@ describe(ProjectInit.name, () => {
|
|||||||
ownerAccount: jester2.accounts[0],
|
ownerAccount: jester2.accounts[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
new ProjectInit(['--id', '1234', '--non-interactive'], commandOptions).run()
|
new ProjectInit(['--id', '1234', '--non-interactive'], commandOptions).run()
|
||||||
@@ -334,7 +370,7 @@ describe(ProjectInit.name, () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(confirmAsync).not.toHaveBeenCalled();
|
expect(confirmAsync).not.toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).not.toHaveBeenCalled();
|
expect(createOrModifyExpoConfigAsync).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -367,14 +403,20 @@ describe(ProjectInit.name, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
jest.mocked(confirmAsync).mockResolvedValue(true);
|
jest.mocked(confirmAsync).mockResolvedValue(true);
|
||||||
jest.mocked(modifyConfigAsync).mockResolvedValue({ type: 'success', config: {} as any });
|
jest
|
||||||
|
.mocked(createOrModifyExpoConfigAsync)
|
||||||
|
.mockResolvedValue({ type: 'success', config: {} as any });
|
||||||
|
|
||||||
await new ProjectInit([], commandOptions).run();
|
await new ProjectInit([], commandOptions).run();
|
||||||
|
|
||||||
expect(confirmAsync).toHaveBeenCalled();
|
expect(confirmAsync).toHaveBeenCalled();
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledTimes(2);
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledTimes(2);
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { owner: 'jester2' });
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
expect(modifyConfigAsync).toHaveBeenCalledWith('/test-project', { slug: 'testing-124' });
|
owner: 'jester2',
|
||||||
|
});
|
||||||
|
expect(createOrModifyExpoConfigAsync).toHaveBeenCalledWith('/test-project', {
|
||||||
|
slug: 'testing-124',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default class ProjectInfo extends EasCommand {
|
|||||||
|
|
||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(ProjectInfo, {
|
} = await this.getContextAsync(ProjectInfo, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getProjectConfigDescription, modifyConfigAsync } from '@expo/config';
|
import { getProjectConfigDescription } from '@expo/config';
|
||||||
import { ExpoConfig } from '@expo/config-types';
|
import { ExpoConfig } from '@expo/config-types';
|
||||||
import { Flags } from '@oclif/core';
|
import { Flags } from '@oclif/core';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
@@ -13,7 +13,7 @@ import { AppMutation } from '../../graphql/mutations/AppMutation';
|
|||||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||||
import Log, { link } from '../../log';
|
import Log, { link } from '../../log';
|
||||||
import { ora } from '../../ora';
|
import { ora } from '../../ora';
|
||||||
import { getExpoConfig } from '../../project/expoConfig';
|
import { createOrModifyExpoConfigAsync, getPrivateExpoConfig } from '../../project/expoConfig';
|
||||||
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
import { findProjectIdByAccountNameAndSlugNullableAsync } from '../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync';
|
||||||
import { toAppPrivacy } from '../../project/projectUtils';
|
import { toAppPrivacy } from '../../project/projectUtils';
|
||||||
import { confirmAsync, promptAsync } from '../../prompts';
|
import { confirmAsync, promptAsync } from '../../prompts';
|
||||||
@@ -60,7 +60,7 @@ export default class ProjectInit extends EasCommand {
|
|||||||
projectDir: string,
|
projectDir: string,
|
||||||
modifications: Partial<ExpoConfig>
|
modifications: Partial<ExpoConfig>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const result = await modifyConfigAsync(projectDir, modifications);
|
const result = await createOrModifyExpoConfigAsync(projectDir, modifications);
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case 'success':
|
case 'success':
|
||||||
break;
|
break;
|
||||||
@@ -98,7 +98,7 @@ export default class ProjectInit extends EasCommand {
|
|||||||
projectDir: string,
|
projectDir: string,
|
||||||
{ force, nonInteractive }: InitializeMethodOptions
|
{ force, nonInteractive }: InitializeMethodOptions
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const exp = getExpoConfig(projectDir);
|
const exp = getPrivateExpoConfig(projectDir);
|
||||||
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, projectId);
|
const appForProjectId = await AppQuery.byIdAsync(graphqlClient, projectId);
|
||||||
const correctOwner = appForProjectId.ownerAccount.name;
|
const correctOwner = appForProjectId.ownerAccount.name;
|
||||||
const correctSlug = appForProjectId.slug;
|
const correctSlug = appForProjectId.slug;
|
||||||
@@ -153,7 +153,7 @@ export default class ProjectInit extends EasCommand {
|
|||||||
projectDir: string,
|
projectDir: string,
|
||||||
{ force, nonInteractive }: InitializeMethodOptions
|
{ force, nonInteractive }: InitializeMethodOptions
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const exp = getExpoConfig(projectDir);
|
const exp = getPrivateExpoConfig(projectDir);
|
||||||
const existingProjectId = exp.extra?.eas?.projectId;
|
const existingProjectId = exp.extra?.eas?.projectId;
|
||||||
|
|
||||||
if (projectId === existingProjectId) {
|
if (projectId === existingProjectId) {
|
||||||
@@ -209,7 +209,7 @@ export default class ProjectInit extends EasCommand {
|
|||||||
actor: Actor,
|
actor: Actor,
|
||||||
projectDir: string
|
projectDir: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const exp = getExpoConfig(projectDir);
|
const exp = getPrivateExpoConfig(projectDir);
|
||||||
const existingProjectId = exp.extra?.eas?.projectId;
|
const existingProjectId = exp.extra?.eas?.projectId;
|
||||||
|
|
||||||
if (existingProjectId) {
|
if (existingProjectId) {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default class EnvironmentSecretCreate extends EasCommand {
|
|||||||
},
|
},
|
||||||
} = await this.parse(EnvironmentSecretCreate);
|
} = await this.parse(EnvironmentSecretCreate);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(EnvironmentSecretCreate, {
|
} = await this.getContextAsync(EnvironmentSecretCreate, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default class EnvironmentSecretDelete extends EasCommand {
|
|||||||
flags: { id, 'non-interactive': nonInteractive },
|
flags: { id, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(EnvironmentSecretDelete);
|
} = await this.parse(EnvironmentSecretDelete);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(EnvironmentSecretDelete, {
|
} = await this.getContextAsync(EnvironmentSecretDelete, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default class EnvironmentSecretList extends EasCommand {
|
|||||||
|
|
||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(EnvironmentSecretList, {
|
} = await this.getContextAsync(EnvironmentSecretList, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default class EnvironmentSecretPush extends EasCommand {
|
|||||||
flags: { scope, force, 'env-file': maybeEnvFilePath, 'non-interactive': nonInteractive },
|
flags: { scope, force, 'env-file': maybeEnvFilePath, 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(EnvironmentSecretPush);
|
} = await this.parse(EnvironmentSecretPush);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(EnvironmentSecretPush, {
|
} = await this.getContextAsync(EnvironmentSecretPush, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export default class Submit extends EasCommand {
|
|||||||
const { flags: rawFlags } = await this.parse(Submit);
|
const { flags: rawFlags } = await this.parse(Submit);
|
||||||
const {
|
const {
|
||||||
loggedIn: { actor, graphqlClient },
|
loggedIn: { actor, graphqlClient },
|
||||||
projectConfig: { exp, projectId, projectDir },
|
privateProjectConfig: { exp, projectId, projectDir },
|
||||||
analytics,
|
analytics,
|
||||||
} = await this.getContextAsync(Submit, {
|
} = await this.getContextAsync(Submit, {
|
||||||
nonInteractive: false,
|
nonInteractive: false,
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { AppJSONConfig, ExpoConfig, PackageJSONConfig, Platform, getConfig } from '@expo/config';
|
||||||
AppJSONConfig,
|
|
||||||
ExpoConfig,
|
|
||||||
GetConfigOptions,
|
|
||||||
PackageJSONConfig,
|
|
||||||
Platform,
|
|
||||||
getConfig,
|
|
||||||
} from '@expo/config';
|
|
||||||
import { Updates } from '@expo/config-plugins';
|
import { Updates } from '@expo/config-plugins';
|
||||||
import { vol } from 'memfs';
|
import { vol } from 'memfs';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
@@ -14,7 +7,10 @@ import { instance, mock } from 'ts-mockito';
|
|||||||
|
|
||||||
import UpdatePublish from '..';
|
import UpdatePublish from '..';
|
||||||
import { ensureBranchExistsAsync } from '../../../branch/queries';
|
import { ensureBranchExistsAsync } from '../../../branch/queries';
|
||||||
import { DynamicProjectConfigContextField } from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
import {
|
||||||
|
DynamicPrivateProjectConfigContextField,
|
||||||
|
DynamicPublicProjectConfigContextField,
|
||||||
|
} from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||||
@@ -237,13 +233,19 @@ function mockTestProject({
|
|||||||
|
|
||||||
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
||||||
jest
|
jest
|
||||||
.spyOn(DynamicProjectConfigContextField.prototype, 'getValueAsync')
|
.spyOn(DynamicPrivateProjectConfigContextField.prototype, 'getValueAsync')
|
||||||
.mockResolvedValue(async (options?: GetConfigOptions) => {
|
.mockResolvedValue(async () => {
|
||||||
let exp = { ...mockManifest.exp };
|
const exp = { ...mockManifest.exp };
|
||||||
|
return {
|
||||||
// Fake a limited public config
|
exp,
|
||||||
if (options?.isPublicConfig) {
|
projectDir: projectRoot,
|
||||||
exp = {
|
projectId: configuredProjectId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(DynamicPublicProjectConfigContextField.prototype, 'getValueAsync')
|
||||||
|
.mockResolvedValue(async () => {
|
||||||
|
const exp = {
|
||||||
name: mockManifest.exp.name,
|
name: mockManifest.exp.name,
|
||||||
version: mockManifest.exp.version,
|
version: mockManifest.exp.version,
|
||||||
slug: mockManifest.exp.slug,
|
slug: mockManifest.exp.slug,
|
||||||
@@ -251,8 +253,6 @@ function mockTestProject({
|
|||||||
owner: mockManifest.exp.owner,
|
owner: mockManifest.exp.owner,
|
||||||
extra: mockManifest.exp.extra,
|
extra: mockManifest.exp.extra,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
exp,
|
exp,
|
||||||
projectDir: projectRoot,
|
projectDir: projectRoot,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { vol } from 'memfs';
|
|||||||
import { instance, mock } from 'ts-mockito';
|
import { instance, mock } from 'ts-mockito';
|
||||||
|
|
||||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||||
import ProjectConfigContextField from '../../../commandUtils/context/ProjectConfigContextField';
|
import { PrivateProjectConfigContextField } from '../../../commandUtils/context/PrivateProjectConfigContextField';
|
||||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||||
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
||||||
@@ -324,7 +324,7 @@ function mockTestProject({
|
|||||||
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
|
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
|
||||||
|
|
||||||
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
||||||
jest.spyOn(ProjectConfigContextField.prototype, 'getValueAsync').mockResolvedValue({
|
jest.spyOn(PrivateProjectConfigContextField.prototype, 'getValueAsync').mockResolvedValue({
|
||||||
exp: mockManifest.exp,
|
exp: mockManifest.exp,
|
||||||
projectDir: projectRoot,
|
projectDir: projectRoot,
|
||||||
projectId: configuredProjectId,
|
projectId: configuredProjectId,
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import { AppJSONConfig, ExpoConfig, PackageJSONConfig, getConfig } from '@expo/config';
|
||||||
AppJSONConfig,
|
|
||||||
ExpoConfig,
|
|
||||||
GetConfigOptions,
|
|
||||||
PackageJSONConfig,
|
|
||||||
getConfig,
|
|
||||||
} from '@expo/config';
|
|
||||||
import { Updates } from '@expo/config-plugins';
|
import { Updates } from '@expo/config-plugins';
|
||||||
import { vol } from 'memfs';
|
import { vol } from 'memfs';
|
||||||
import nullthrows from 'nullthrows';
|
import nullthrows from 'nullthrows';
|
||||||
@@ -12,7 +6,10 @@ import path from 'path';
|
|||||||
import { instance, mock } from 'ts-mockito';
|
import { instance, mock } from 'ts-mockito';
|
||||||
|
|
||||||
import { ensureBranchExistsAsync } from '../../../branch/queries';
|
import { ensureBranchExistsAsync } from '../../../branch/queries';
|
||||||
import { DynamicProjectConfigContextField } from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
import {
|
||||||
|
DynamicPrivateProjectConfigContextField,
|
||||||
|
DynamicPublicProjectConfigContextField,
|
||||||
|
} from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
||||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||||
@@ -217,13 +214,19 @@ function mockTestProject({
|
|||||||
|
|
||||||
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
jest.mocked(getConfig).mockReturnValue(mockManifest as any);
|
||||||
jest
|
jest
|
||||||
.spyOn(DynamicProjectConfigContextField.prototype, 'getValueAsync')
|
.spyOn(DynamicPrivateProjectConfigContextField.prototype, 'getValueAsync')
|
||||||
.mockResolvedValue(async (options?: GetConfigOptions) => {
|
.mockResolvedValue(async () => {
|
||||||
let exp = { ...mockManifest.exp };
|
const exp = { ...mockManifest.exp };
|
||||||
|
return {
|
||||||
// Fake a limited public config
|
exp,
|
||||||
if (options?.isPublicConfig) {
|
projectDir: projectRoot,
|
||||||
exp = {
|
projectId: configuredProjectId,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(DynamicPublicProjectConfigContextField.prototype, 'getValueAsync')
|
||||||
|
.mockResolvedValue(async () => {
|
||||||
|
const exp = {
|
||||||
name: mockManifest.exp.name,
|
name: mockManifest.exp.name,
|
||||||
version: mockManifest.exp.version,
|
version: mockManifest.exp.version,
|
||||||
slug: mockManifest.exp.slug,
|
slug: mockManifest.exp.slug,
|
||||||
@@ -231,8 +234,6 @@ function mockTestProject({
|
|||||||
owner: mockManifest.exp.owner,
|
owner: mockManifest.exp.owner,
|
||||||
extra: mockManifest.exp.extra,
|
extra: mockManifest.exp.extra,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
exp,
|
exp,
|
||||||
projectDir: projectRoot,
|
projectDir: projectRoot,
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { Flags } from '@oclif/core';
|
import { Flags } from '@oclif/core';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
|
||||||
|
import { easJsonExistsAsync } from '../../build/configure';
|
||||||
import EasCommand from '../../commandUtils/EasCommand';
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||||
import Log from '../../log';
|
import Log, { learnMore } from '../../log';
|
||||||
import { RequestedPlatform } from '../../platform';
|
import { RequestedPlatform } from '../../platform';
|
||||||
import {
|
import {
|
||||||
ensureEASUpdateIsConfiguredAsync,
|
ensureEASUpdateIsConfiguredAsync,
|
||||||
ensureEASUpdateIsConfiguredInEasJsonAsync,
|
ensureEASUpdateIsConfiguredInEasJsonAsync,
|
||||||
} from '../../update/configure';
|
} from '../../update/configure';
|
||||||
|
import { getVcsClient } from '../../vcs';
|
||||||
|
|
||||||
export default class UpdateConfigure extends EasCommand {
|
export default class UpdateConfigure extends EasCommand {
|
||||||
static override description = 'configure the project to support EAS Update';
|
static override description = 'configure the project to support EAS Update';
|
||||||
@@ -32,7 +34,7 @@ export default class UpdateConfigure extends EasCommand {
|
|||||||
const { flags } = await this.parse(UpdateConfigure);
|
const { flags } = await this.parse(UpdateConfigure);
|
||||||
const platform = flags.platform as RequestedPlatform;
|
const platform = flags.platform as RequestedPlatform;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId, exp, projectDir },
|
privateProjectConfig: { projectId, exp, projectDir },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(UpdateConfigure, {
|
} = await this.getContextAsync(UpdateConfigure, {
|
||||||
nonInteractive: flags['non-interactive'],
|
nonInteractive: flags['non-interactive'],
|
||||||
@@ -42,6 +44,8 @@ export default class UpdateConfigure extends EasCommand {
|
|||||||
'💡 The following process will configure your project to run EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
|
'💡 The following process will configure your project to run EAS Update. These changes only apply to your local project files and you can safely revert them at any time.'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await getVcsClient().ensureRepoExistsAsync();
|
||||||
|
|
||||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||||
exp,
|
exp,
|
||||||
projectId,
|
projectId,
|
||||||
@@ -54,21 +58,14 @@ export default class UpdateConfigure extends EasCommand {
|
|||||||
Log.addNewLineIfNone();
|
Log.addNewLineIfNone();
|
||||||
Log.log(`🎉 Your app is configured with EAS Update!`);
|
Log.log(`🎉 Your app is configured with EAS Update!`);
|
||||||
Log.newLine();
|
Log.newLine();
|
||||||
Log.log(`${chalk.bold('Next steps')}:`);
|
const easJsonExists = await easJsonExistsAsync(projectDir);
|
||||||
Log.newLine();
|
if (!easJsonExists) {
|
||||||
Log.log('Update a production build:');
|
Log.log(`- Run ${chalk.bold('eas build:configure')} to complete your installation`);
|
||||||
Log.log(`1. Create a new build. Example: ${chalk.bold('eas build --profile production')}.`);
|
}
|
||||||
Log.log('2. Make changes in your project.');
|
|
||||||
Log.log(`3. Publish an update. Example: ${chalk.bold('eas update --channel production')}.`);
|
|
||||||
Log.log('4. Force close and reopen the app at least twice to view the update.');
|
|
||||||
|
|
||||||
Log.newLine();
|
|
||||||
Log.log('Preview an update:');
|
|
||||||
Log.log(
|
Log.log(
|
||||||
`1. Publish an update to a branch. Example: ${chalk.bold('eas update --branch new-feature')}.`
|
`- ${learnMore('https://docs.expo.dev/eas-update/introduction/', {
|
||||||
);
|
learnMoreMessage: 'Learn more about other capabilities of EAS Update',
|
||||||
Log.log(
|
})}`
|
||||||
'2. In Expo Go or a development build, navigate to Projects > [project name] > Branch > Open.'
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import { RequestedPlatform } from '../../platform';
|
|||||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||||
import {
|
import {
|
||||||
ExpoCLIExportPlatformFlag,
|
ExpoCLIExportPlatformFlag,
|
||||||
|
RawAsset,
|
||||||
buildBundlesAsync,
|
buildBundlesAsync,
|
||||||
buildUnsortedUpdateInfoGroupAsync,
|
buildUnsortedUpdateInfoGroupAsync,
|
||||||
collectAssetsAsync,
|
collectAssetsAsync,
|
||||||
@@ -45,6 +46,7 @@ import {
|
|||||||
getManifestBodyAsync,
|
getManifestBodyAsync,
|
||||||
signBody,
|
signBody,
|
||||||
} from '../../utils/code-signing';
|
} from '../../utils/code-signing';
|
||||||
|
import areSetsEqual from '../../utils/expodash/areSetsEqual';
|
||||||
import uniqBy from '../../utils/expodash/uniqBy';
|
import uniqBy from '../../utils/expodash/uniqBy';
|
||||||
import formatFields from '../../utils/formatFields';
|
import formatFields from '../../utils/formatFields';
|
||||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||||
@@ -59,6 +61,7 @@ type RawUpdateFlags = {
|
|||||||
platform: string;
|
platform: string;
|
||||||
'input-dir': string;
|
'input-dir': string;
|
||||||
'skip-bundler': boolean;
|
'skip-bundler': boolean;
|
||||||
|
'clear-cache': boolean;
|
||||||
'private-key-path'?: string;
|
'private-key-path'?: string;
|
||||||
'non-interactive': boolean;
|
'non-interactive': boolean;
|
||||||
json: boolean;
|
json: boolean;
|
||||||
@@ -76,6 +79,7 @@ type UpdateFlags = {
|
|||||||
updateMessage?: string;
|
updateMessage?: string;
|
||||||
inputDir: string;
|
inputDir: string;
|
||||||
skipBundler: boolean;
|
skipBundler: boolean;
|
||||||
|
clearCache: boolean;
|
||||||
privateKeyPath?: string;
|
privateKeyPath?: string;
|
||||||
json: boolean;
|
json: boolean;
|
||||||
nonInteractive: boolean;
|
nonInteractive: boolean;
|
||||||
@@ -94,6 +98,7 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
required: false,
|
required: false,
|
||||||
}),
|
}),
|
||||||
message: Flags.string({
|
message: Flags.string({
|
||||||
|
char: 'm',
|
||||||
description: 'A short message describing the update',
|
description: 'A short message describing the update',
|
||||||
required: false,
|
required: false,
|
||||||
}),
|
}),
|
||||||
@@ -114,6 +119,10 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
description: `Skip running Expo CLI to bundle the app before publishing`,
|
description: `Skip running Expo CLI to bundle the app before publishing`,
|
||||||
default: false,
|
default: false,
|
||||||
}),
|
}),
|
||||||
|
'clear-cache': Flags.boolean({
|
||||||
|
description: `Clear the bundler cache before publishing`,
|
||||||
|
default: false,
|
||||||
|
}),
|
||||||
platform: Flags.enum({
|
platform: Flags.enum({
|
||||||
char: 'p',
|
char: 'p',
|
||||||
options: [
|
options: [
|
||||||
@@ -151,6 +160,7 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
updateMessage: updateMessageArg,
|
updateMessage: updateMessageArg,
|
||||||
inputDir,
|
inputDir,
|
||||||
skipBundler,
|
skipBundler,
|
||||||
|
clearCache,
|
||||||
privateKeyPath,
|
privateKeyPath,
|
||||||
json: jsonFlag,
|
json: jsonFlag,
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -158,7 +168,8 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
} = this.sanitizeFlags(rawFlags);
|
} = this.sanitizeFlags(rawFlags);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPublicProjectConfigAsync,
|
||||||
|
getDynamicPrivateProjectConfigAsync,
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(UpdatePublish, {
|
} = await this.getContextAsync(UpdatePublish, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -172,9 +183,7 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||||
projectId,
|
projectId,
|
||||||
projectDir,
|
projectDir,
|
||||||
} = await getDynamicProjectConfigAsync({
|
} = await getDynamicPublicProjectConfigAsync();
|
||||||
isPublicConfig: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||||
|
|
||||||
@@ -185,10 +194,8 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
projectId,
|
projectId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
const { exp } = await getDynamicPublicProjectConfigAsync();
|
||||||
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
const { exp: expPrivate } = await getDynamicPrivateProjectConfigAsync();
|
||||||
isPublicConfig: false,
|
|
||||||
});
|
|
||||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||||
|
|
||||||
const branchName = await getBranchNameForCommandAsync({
|
const branchName = await getBranchNameForCommandAsync({
|
||||||
@@ -212,7 +219,7 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
if (!skipBundler) {
|
if (!skipBundler) {
|
||||||
const bundleSpinner = ora().start('Exporting...');
|
const bundleSpinner = ora().start('Exporting...');
|
||||||
try {
|
try {
|
||||||
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag });
|
await buildBundlesAsync({ projectDir, inputDir, exp, platformFlag, clearCache });
|
||||||
bundleSpinner.succeed('Exported bundle(s)');
|
bundleSpinner.succeed('Exported bundle(s)');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
bundleSpinner.fail('Export failed');
|
bundleSpinner.fail('Export failed');
|
||||||
@@ -234,14 +241,58 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
const assets = filterExportedPlatformsByFlag(collectedAssets, platformFlag);
|
const assets = filterExportedPlatformsByFlag(collectedAssets, platformFlag);
|
||||||
realizedPlatforms = Object.keys(assets) as PublishPlatform[];
|
realizedPlatforms = Object.keys(assets) as PublishPlatform[];
|
||||||
|
|
||||||
const uploadResults = await uploadAssetsAsync(
|
// Timeout mechanism:
|
||||||
|
// - Start with 60 second timeout. 60 seconds is chosen because the cloud function that processes
|
||||||
|
// uploaded assets has a timeout of 60 seconds.
|
||||||
|
// - Each time one or more assets reports as ready, reset the timeout to 60 seconds.
|
||||||
|
// - Start upload. Internally, uploadAssetsAsync uploads them all and then checks for successful
|
||||||
|
// processing every (5 + n) seconds with a linear backoff of n + 1 second.
|
||||||
|
// - At the same time as upload is started, start timeout checker which checks every 1 second to see
|
||||||
|
// if timeout has been reached. When timeout expires, send a cancellation signal to currently running
|
||||||
|
// upload function call to instruct it to stop uploading or checking for successful processing.
|
||||||
|
let lastUploadedStorageKeys = new Set<string>();
|
||||||
|
let lastAssetUploadResults: {
|
||||||
|
asset: RawAsset & { storageKey: string };
|
||||||
|
finished: boolean;
|
||||||
|
}[] = [];
|
||||||
|
let timeAtWhichToTimeout = Date.now() + 60 * 1000; // sixty seconds from now
|
||||||
|
const cancelationToken = { isCanceledOrFinished: false };
|
||||||
|
|
||||||
|
const uploadResults = await Promise.race([
|
||||||
|
uploadAssetsAsync(
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
assets,
|
assets,
|
||||||
projectId,
|
projectId,
|
||||||
(totalAssets, missingAssets) => {
|
cancelationToken,
|
||||||
assetSpinner.text = `Uploading (${totalAssets - missingAssets}/${totalAssets})`;
|
assetUploadResults => {
|
||||||
}
|
const currentUploadedStorageKeys = new Set(
|
||||||
|
assetUploadResults.filter(r => r.finished).map(r => r.asset.storageKey)
|
||||||
);
|
);
|
||||||
|
if (!areSetsEqual(currentUploadedStorageKeys, lastUploadedStorageKeys)) {
|
||||||
|
timeAtWhichToTimeout = Date.now() + 60 * 1000; // reset timeout to sixty seconds from now
|
||||||
|
lastUploadedStorageKeys = currentUploadedStorageKeys;
|
||||||
|
lastAssetUploadResults = assetUploadResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalAssets = assetUploadResults.length;
|
||||||
|
const missingAssetCount = assetUploadResults.filter(a => !a.finished).length;
|
||||||
|
assetSpinner.text = `Uploading (${totalAssets - missingAssetCount}/${totalAssets})`;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
(async () => {
|
||||||
|
while (Date.now() < timeAtWhichToTimeout) {
|
||||||
|
if (cancelationToken.isCanceledOrFinished) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(res => setTimeout(res, 1000)); // wait 1 second
|
||||||
|
}
|
||||||
|
cancelationToken.isCanceledOrFinished = true;
|
||||||
|
const timedOutAssets = lastAssetUploadResults
|
||||||
|
.filter(r => !r.finished)
|
||||||
|
.map(r => `\n- ${r.asset.originalPath ?? r.asset.path}`);
|
||||||
|
throw new Error(`Asset processing timed out for assets: ${timedOutAssets}`);
|
||||||
|
})(),
|
||||||
|
]);
|
||||||
|
|
||||||
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
|
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
|
||||||
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
|
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
|
||||||
@@ -488,6 +539,7 @@ export default class UpdatePublish extends EasCommand {
|
|||||||
updateMessage,
|
updateMessage,
|
||||||
inputDir: flags['input-dir'],
|
inputDir: flags['input-dir'],
|
||||||
skipBundler: flags['skip-bundler'],
|
skipBundler: flags['skip-bundler'],
|
||||||
|
clearCache: flags['clear-cache'],
|
||||||
platform: flags.platform as RequestedPlatform,
|
platform: flags.platform as RequestedPlatform,
|
||||||
privateKeyPath: flags['private-key-path'],
|
privateKeyPath: flags['private-key-path'],
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class UpdateList extends EasCommand {
|
|||||||
const { flags } = await this.parse(UpdateList);
|
const { flags } = await this.parse(UpdateList);
|
||||||
const { branch: branchFlag, all, json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
const { branch: branchFlag, all, json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(UpdateList, {
|
} = await this.getContextAsync(UpdateList, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export default class UpdateRepublish extends EasCommand {
|
|||||||
exclusive: ['branch', 'channel'],
|
exclusive: ['branch', 'channel'],
|
||||||
}),
|
}),
|
||||||
message: Flags.string({
|
message: Flags.string({
|
||||||
|
char: 'm',
|
||||||
description: 'Short message describing the republished update',
|
description: 'Short message describing the republished update',
|
||||||
required: false,
|
required: false,
|
||||||
}),
|
}),
|
||||||
@@ -94,7 +95,7 @@ export default class UpdateRepublish extends EasCommand {
|
|||||||
const flags = this.sanitizeFlags(rawFlags);
|
const flags = this.sanitizeFlags(rawFlags);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectConfig: { exp, projectId },
|
privateProjectConfig: { exp, projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(UpdateRepublish, {
|
} = await this.getContextAsync(UpdateRepublish, {
|
||||||
nonInteractive: flags.nonInteractive,
|
nonInteractive: flags.nonInteractive,
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
|||||||
} = this.sanitizeFlags(rawFlags);
|
} = this.sanitizeFlags(rawFlags);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getDynamicProjectConfigAsync,
|
getDynamicPublicProjectConfigAsync,
|
||||||
|
getDynamicPrivateProjectConfigAsync,
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(UpdateRollBackToEmbedded, {
|
} = await this.getContextAsync(UpdateRollBackToEmbedded, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
@@ -136,9 +137,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
|||||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||||
projectId,
|
projectId,
|
||||||
projectDir,
|
projectDir,
|
||||||
} = await getDynamicProjectConfigAsync({
|
} = await getDynamicPublicProjectConfigAsync();
|
||||||
isPublicConfig: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||||
|
|
||||||
@@ -149,10 +148,8 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
|||||||
projectId,
|
projectId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
const { exp } = await getDynamicPublicProjectConfigAsync();
|
||||||
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
const { exp: expPrivate } = await getDynamicPrivateProjectConfigAsync();
|
||||||
isPublicConfig: false,
|
|
||||||
});
|
|
||||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||||
|
|
||||||
const branchName = await getBranchNameForCommandAsync({
|
const branchName = await getBranchNameForCommandAsync({
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default class WebhookCreate extends EasCommand {
|
|||||||
async runAsync(): Promise<void> {
|
async runAsync(): Promise<void> {
|
||||||
const { flags } = await this.parse(WebhookCreate);
|
const { flags } = await this.parse(WebhookCreate);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(WebhookCreate, {
|
} = await this.getContextAsync(WebhookCreate, {
|
||||||
nonInteractive: flags['non-interactive'],
|
nonInteractive: flags['non-interactive'],
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default class WebhookDelete extends EasCommand {
|
|||||||
flags: { 'non-interactive': nonInteractive },
|
flags: { 'non-interactive': nonInteractive },
|
||||||
} = await this.parse(WebhookDelete);
|
} = await this.parse(WebhookDelete);
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(WebhookDelete, {
|
} = await this.getContextAsync(WebhookDelete, {
|
||||||
nonInteractive,
|
nonInteractive,
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default class WebhookList extends EasCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectConfig: { projectId },
|
privateProjectConfig: { projectId },
|
||||||
loggedIn: { graphqlClient },
|
loggedIn: { graphqlClient },
|
||||||
} = await this.getContextAsync(WebhookList, {
|
} = await this.getContextAsync(WebhookList, {
|
||||||
nonInteractive: true,
|
nonInteractive: true,
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ export function getNewAndroidApiMock(): { [key in keyof typeof AndroidGraphqlCli
|
|||||||
updateAndroidAppCredentialsAsync: jest.fn(),
|
updateAndroidAppCredentialsAsync: jest.fn(),
|
||||||
updateAndroidAppBuildCredentialsAsync: jest.fn(),
|
updateAndroidAppBuildCredentialsAsync: jest.fn(),
|
||||||
createAndroidAppBuildCredentialsAsync: jest.fn(),
|
createAndroidAppBuildCredentialsAsync: jest.fn(),
|
||||||
|
setDefaultAndroidAppBuildCredentialsAsync: jest.fn(),
|
||||||
getDefaultAndroidAppBuildCredentialsAsync: jest.fn(),
|
getDefaultAndroidAppBuildCredentialsAsync: jest.fn(),
|
||||||
getAndroidAppBuildCredentialsByNameAsync: jest.fn(),
|
getAndroidAppBuildCredentialsByNameAsync: jest.fn(),
|
||||||
createOrUpdateAndroidAppBuildCredentialsByNameAsync: jest.fn(),
|
createOrUpdateAndroidAppBuildCredentialsByNameAsync: jest.fn(),
|
||||||
|
|||||||
@@ -146,6 +146,16 @@ export async function updateAndroidAppBuildCredentialsAsync(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setDefaultAndroidAppBuildCredentialsAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
buildCredentials: AndroidAppBuildCredentialsFragment
|
||||||
|
): Promise<AndroidAppBuildCredentialsFragment> {
|
||||||
|
return await AndroidAppBuildCredentialsMutation.setDefaultAndroidAppBuildCredentialsAsync(
|
||||||
|
graphqlClient,
|
||||||
|
buildCredentials.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function createAndroidAppBuildCredentialsAsync(
|
export async function createAndroidAppBuildCredentialsAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
appLookupParams: AppLookupParams,
|
appLookupParams: AppLookupParams,
|
||||||
@@ -164,14 +174,6 @@ export async function createAndroidAppBuildCredentialsAsync(
|
|||||||
graphqlClient,
|
graphqlClient,
|
||||||
appLookupParams
|
appLookupParams
|
||||||
);
|
);
|
||||||
const buildCredentialsList = androidAppCredentials.androidAppBuildCredentialsList;
|
|
||||||
const existingDefaultBuildCredentials =
|
|
||||||
buildCredentialsList.find(buildCredentials => buildCredentials.isDefault) ?? null;
|
|
||||||
if (existingDefaultBuildCredentials && isDefault) {
|
|
||||||
throw new Error(
|
|
||||||
'Cannot create new default Android Build Credentials. A set of default credentials exists already.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await AndroidAppBuildCredentialsMutation.createAndroidAppBuildCredentialsAsync(
|
return await AndroidAppBuildCredentialsMutation.createAndroidAppBuildCredentialsAsync(
|
||||||
graphqlClient,
|
graphqlClient,
|
||||||
|
|||||||
+32
@@ -5,6 +5,7 @@ import gql from 'graphql-tag';
|
|||||||
import { ExpoGraphqlClient } from '../../../../../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../../../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import { withErrorHandlingAsync } from '../../../../../graphql/client';
|
import { withErrorHandlingAsync } from '../../../../../graphql/client';
|
||||||
import {
|
import {
|
||||||
|
AndroidAppBuildCredentials,
|
||||||
AndroidAppBuildCredentialsFragment,
|
AndroidAppBuildCredentialsFragment,
|
||||||
AndroidAppBuildCredentialsInput,
|
AndroidAppBuildCredentialsInput,
|
||||||
CreateAndroidAppBuildCredentialsMutation,
|
CreateAndroidAppBuildCredentialsMutation,
|
||||||
@@ -55,6 +56,37 @@ export const AndroidAppBuildCredentialsMutation = {
|
|||||||
);
|
);
|
||||||
return data.androidAppBuildCredentials.createAndroidAppBuildCredentials;
|
return data.androidAppBuildCredentials.createAndroidAppBuildCredentials;
|
||||||
},
|
},
|
||||||
|
async setDefaultAndroidAppBuildCredentialsAsync(
|
||||||
|
graphqlClient: ExpoGraphqlClient,
|
||||||
|
androidAppBuildCredentialsId: string
|
||||||
|
): Promise<AndroidAppBuildCredentialsFragment> {
|
||||||
|
const data = await withErrorHandlingAsync(
|
||||||
|
graphqlClient
|
||||||
|
.mutation<AndroidAppBuildCredentials>(
|
||||||
|
gql`
|
||||||
|
mutation AndroidAppBuildCredentialsMutation(
|
||||||
|
$androidAppBuildCredentialsId: ID!
|
||||||
|
$isDefault: Boolean!
|
||||||
|
) {
|
||||||
|
androidAppBuildCredentials {
|
||||||
|
setDefault(id: $androidAppBuildCredentialsId, isDefault: $isDefault) {
|
||||||
|
id
|
||||||
|
...AndroidAppBuildCredentialsFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${print(AndroidAppBuildCredentialsFragmentNode)}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
androidAppBuildCredentialsId,
|
||||||
|
isDefault: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
assert(data, `GraphQL: 'setDefault' not defined in server response ${JSON.stringify(data)}}`);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
async setKeystoreAsync(
|
async setKeystoreAsync(
|
||||||
graphqlClient: ExpoGraphqlClient,
|
graphqlClient: ExpoGraphqlClient,
|
||||||
androidAppBuildCredentialsId: string,
|
androidAppBuildCredentialsId: string,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import chalk from 'chalk';
|
|||||||
import { Analytics } from '../analytics/AnalyticsManager';
|
import { Analytics } from '../analytics/AnalyticsManager';
|
||||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||||
import Log from '../log';
|
import Log from '../log';
|
||||||
import { getExpoConfig } from '../project/expoConfig';
|
import { getPrivateExpoConfig } from '../project/expoConfig';
|
||||||
import { confirmAsync } from '../prompts';
|
import { confirmAsync } from '../prompts';
|
||||||
import { Actor } from '../user/User';
|
import { Actor } from '../user/User';
|
||||||
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
|
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
|
||||||
@@ -56,18 +56,6 @@ export class CredentialsContext {
|
|||||||
this.projectInfo = options.projectInfo;
|
this.projectInfo = options.projectInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getExpoConfigInProject(
|
|
||||||
projectDir: string,
|
|
||||||
{ env }: { env?: Env } = {}
|
|
||||||
): ExpoConfig | null {
|
|
||||||
try {
|
|
||||||
return getExpoConfig(projectDir, { env });
|
|
||||||
} catch {
|
|
||||||
// ignore error, context might be created outside of expo project
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasProjectContext(): boolean {
|
get hasProjectContext(): boolean {
|
||||||
return !!this.projectInfo;
|
return !!this.projectInfo;
|
||||||
}
|
}
|
||||||
@@ -87,7 +75,7 @@ export class CredentialsContext {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// trigger getConfig error
|
// trigger getConfig error
|
||||||
getExpoConfig(this.options.projectDir);
|
getPrivateExpoConfig(this.options.projectDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
async bestEffortAppStoreAuthenticateAsync(): Promise<void> {
|
async bestEffortAppStoreAuthenticateAsync(): Promise<void> {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export async function chooseDevicesAsync(
|
|||||||
const { devices } = await promptAsync({
|
const { devices } = await promptAsync({
|
||||||
type: 'multiselect',
|
type: 'multiselect',
|
||||||
name: 'devices',
|
name: 'devices',
|
||||||
|
selectionFormat: '<num> devices selected',
|
||||||
message: 'Select devices for the ad hoc build:',
|
message: 'Select devices for the ad hoc build:',
|
||||||
hint: '- Space to select. Return to submit',
|
hint: '- Space to select. Return to submit',
|
||||||
choices: allDevices.map(device => ({
|
choices: allDevices.map(device => ({
|
||||||
|
|||||||
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user