mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-15 03:38:08 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
377c7a9ebb | ||
|
|
ef3edcc787 |
No files matched your search
@@ -69,22 +69,16 @@ jobs:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 20
|
||||
- run: brew install python-setuptools
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
- run: yarn install --frozen-lockfile --check-files
|
||||
- run: yarn build
|
||||
- name: Build tarballs
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli
|
||||
run: |
|
||||
yarn pretarball-ci
|
||||
yarn oclif pack:tarballs --no-xz --targets darwin-x64,darwin-arm64
|
||||
yarn oclif pack:tarballs --no-xz --targets darwin-x64
|
||||
- id: x64
|
||||
run: echo "filename=$(ls eas-*-x64.tar.gz)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||
- id: arm64
|
||||
run: echo "filename=$(ls eas-*-arm64.tar.gz)" >> $GITHUB_OUTPUT
|
||||
working-directory: ${{ github.workspace }}/packages/eas-cli/dist
|
||||
- name: Upload darwin-x64 tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -94,15 +88,6 @@ jobs:
|
||||
asset_path: packages/eas-cli/dist/${{ steps.x64.outputs.filename }}
|
||||
asset_name: ${{ steps.x64.outputs.filename }}
|
||||
asset_content_type: application/gzip
|
||||
- name: Upload darwin-arm64 tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||
asset_path: packages/eas-cli/dist/${{ steps.arm64.outputs.filename }}
|
||||
asset_name: ${{ steps.arm64.outputs.filename }}
|
||||
asset_content_type: application/gzip
|
||||
build-windows:
|
||||
name: Build for Windows
|
||||
runs-on: ubuntu-latest
|
||||
@@ -167,13 +152,7 @@ jobs:
|
||||
VERSION=$(cat lerna.json | jq -r .version)
|
||||
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
||||
npm dist-tag add eas-cli@$VERSION latest-eas-build-staging
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||
- name: Add latest-eas-build tag
|
||||
run: |
|
||||
VERSION=$(cat lerna.json | jq -r .version)
|
||||
echo //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN > ~/.npmrc
|
||||
npm dist-tag add eas-cli@$VERSION latest-eas-build
|
||||
echo "Run \"npm dist-tag add eas-cli@$VERSION latest-eas-build\" to promote it to production."
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||
release-changelog:
|
||||
@@ -226,16 +205,3 @@ jobs:
|
||||
SLACK_USERNAME: 'EAS CLI CI'
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_FOOTER: ''
|
||||
open-pr-in-universe:
|
||||
name: Open a PR with `@expo/eas-json` bump in universe
|
||||
needs: publish-to-npm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Open a PR with `@expo/eas-json` version bump in universe
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
repo: expo/universe
|
||||
ref: main
|
||||
workflow: update-eas-json.yml
|
||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['18', '20', '22']
|
||||
node: ['16', '18', '20']
|
||||
include:
|
||||
- node: '20'
|
||||
coverage: true
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
name: Open a PR bumping eas-build modules
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
open-pr:
|
||||
name: Open a PR bumping eas-build modules
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Configure git
|
||||
run: |
|
||||
git config --global user.email "[email protected]"
|
||||
git config --global user.name "Expo Bot"
|
||||
|
||||
- name: Upgrade eas-build modules
|
||||
run: |
|
||||
for DIR in packages/eas-cli packages/eas-json; do
|
||||
pushd $DIR >/dev/null 2>&1
|
||||
# packages are ordered according to output of `lerna list --toposort`
|
||||
# https://github.com/expo/turtle-v2/pull/568
|
||||
for PACKAGE in "@expo/downloader" "@expo/eas-build-job" "@expo/logger" "@expo/template-file" "@expo/steps" "@expo/turtle-spawn" "@expo/build-tools"; do
|
||||
if grep -q $PACKAGE package.json; then
|
||||
echo "[$DIR] yarn --exact add $PACKAGE"
|
||||
yarn add --exact $PACKAGE
|
||||
fi
|
||||
done
|
||||
popd >/dev/null 2>&1
|
||||
done
|
||||
|
||||
- name: Set a branch name
|
||||
id: bump-branch-name
|
||||
run: |
|
||||
HASH="$(echo $RANDOM | md5sum | head -c 10)"
|
||||
BRANCH_NAME="@expo-bot/eas-build-bump-$HASH"
|
||||
|
||||
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create a branch, commit, and push
|
||||
run: |
|
||||
BRANCH_NAME="${{ steps.bump-branch-name.outputs.BRANCH_NAME }}"
|
||||
|
||||
git checkout -b $BRANCH_NAME
|
||||
git add .
|
||||
git commit -m "Upgrade eas-build dependencies"
|
||||
git push origin $BRANCH_NAME
|
||||
|
||||
- name: Open a PR to main
|
||||
uses: repo-sync/pull-request@v2
|
||||
id: open-pr
|
||||
with:
|
||||
source_branch: '${{ steps.bump-branch-name.outputs.BRANCH_NAME }}'
|
||||
destination_branch: 'main'
|
||||
pr_title: 'Upgrade eas-build dependencies'
|
||||
pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.'
|
||||
pr_reviewer: 'szdziedzic,radoslawkrzemien,sjchmiela,khamilowicz'
|
||||
github_token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
|
||||
- uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
script: |
|
||||
github.issues.createComment({
|
||||
issue_number: ${{ steps.open-pr.outputs.pr_number }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "/changelog-entry chore Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies."
|
||||
})
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"eslint.enable": true,
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/bower_components": true,
|
||||
"**/*.code-search": true,
|
||||
"**/src/**/build/**": false,
|
||||
"**/src/build/**": false,
|
||||
}
|
||||
}
|
||||
+3
-385
@@ -8,399 +8,17 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update images list in `eas.schema.json` and warn users when using the deprecated Android images. ([#2450](https://github.com/expo/eas-cli/pull/2450) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [10.2.2](https://github.com/expo/eas-cli/releases/tag/v10.2.2) - 2024-07-31
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Pass correct path to `vcsClient.isFileIgnoredAsync` check for monorepos to validate that custom build config file is not ignored. ([#2470](https://github.com/expo/eas-cli/pull/2470) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Bump asset upload timeout from 90 to 180 seconds. ([#2466](https://github.com/expo/eas-cli/pull/2466) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [10.2.1](https://github.com/expo/eas-cli/releases/tag/v10.2.1) - 2024-07-18
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Indicate if a user is logged in using `EXPO_TOKEN` when running `eas whoami` command. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Throw error when `eas login` command is run with `EXPO_TOKEN` environment variable set. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Check if user is already logged in when running `eas login` command. ([#2461](https://github.com/expo/eas-cli/pull/2461) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [10.2.0](https://github.com/expo/eas-cli/releases/tag/v10.2.0) - 2024-07-15
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Added flag `--emit-metadata` to emit `eas-update-metadata.json` in the bundle folder with detailed information about the generated updates ([#2451](https://github.com/expo/eas-cli/pull/2451) by [@rainst](https://github.com/rainst))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Bump `@expo/apple-utils` version to use the fallback Apple Developer Portal domain on every internal server error. ([#2459](https://github.com/expo/eas-cli/pull/2459) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [10.1.1](https://github.com/expo/eas-cli/releases/tag/v10.1.1) - 2024-07-04
|
||||
|
||||
## [10.1.0](https://github.com/expo/eas-cli/releases/tag/v10.1.0) - 2024-07-02
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Save local fingeprint sources during build. ([#2422](https://github.com/expo/eas-cli/pull/2422) by [@kadikraman](https://github.com/kadikraman))
|
||||
|
||||
## [10.0.3](https://github.com/expo/eas-cli/releases/tag/v10.0.3) - 2024-06-26
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Track usage of `--local` build mode in analytics. ([#2445](https://github.com/expo/eas-cli/pull/2445) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Remove any mentions of deleted Xcode < 15 images. ([#2438](https://github.com/expo/eas-cli/pull/2438) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [10.0.2](https://github.com/expo/eas-cli/releases/tag/v10.0.2) - 2024-06-17
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix parsing of `build.gradle` file by `gradle-to-js` parser by filtering out empty single line comments. ([#2435](https://github.com/expo/eas-cli/pull/2435) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [10.0.1](https://github.com/expo/eas-cli/releases/tag/v10.0.1) - 2024-06-17
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add clarification to private-key flag in command line help. ([#2432](https://github.com/expo/eas-cli/pull/2432) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [10.0.0](https://github.com/expo/eas-cli/releases/tag/v10.0.0) - 2024-06-13
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Drop support for Node 16. ([#2413](https://github.com/expo/eas-cli/pull/2413) by [@byCedric](https://github.com/byCedric))
|
||||
- Update [`eas-build`](https://github.com/expo/eas-build) dependencies to the version requiring Node 18 as minimal Node version. ([#2416](https://github.com/expo/eas-cli/pull/2416) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Resolve correct submit profile configuration for `eas build` command with `--auto-submit-with-profile` flag. ([#2425](https://github.com/expo/eas-cli/pull/2425) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Correctly parse the EXPO_APPLE_PROVIER_ID environment variable. ([#2349](https://github.com/expo/eas-cli/pull/2349) by [@louix](https://github.com/louix))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update lockfile to only include `@types/[email protected]`. ([#2412](https://github.com/expo/eas-cli/pull/2412) by [@byCedric](https://github.com/byCedric))
|
||||
- Update test workflow Node versions to 18, 20, and 22. ([#2413](https://github.com/expo/eas-cli/pull/2413) by [@byCedric](https://github.com/byCedric))
|
||||
|
||||
## [9.2.0](https://github.com/expo/eas-cli/releases/tag/v9.2.0) - 2024-06-06
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `target-profile` and `source-profile` flags to the `eas build:resign` command. ([#2410](https://github.com/expo/eas-cli/pull/2410) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Display build profile in the output of `eas build:list`. ([#2408](https://github.com/expo/eas-cli/pull/2408) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Use the correct app config for no GitHub flow in `init:onboarding`. ([#2397](https://github.com/expo/eas-cli/pull/2397) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Disallow picking expired builds as submit archive source. ([#2406](https://github.com/expo/eas-cli/pull/2406) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Print network error message if present. ([#2407](https://github.com/expo/eas-cli/pull/2407) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Make flags for `eas build:list` command more aligned with flags for rest of the commands. ([#2409](https://github.com/expo/eas-cli/pull/2409) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [9.1.0](https://github.com/expo/eas-cli/releases/tag/v9.1.0) - 2024-05-23
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Make `eas init:onboarding` command public. ([#2399](https://github.com/expo/eas-cli/pull/2399) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [9.0.10](https://github.com/expo/eas-cli/releases/tag/v9.0.10) - 2024-05-22
|
||||
|
||||
## [9.0.9](https://github.com/expo/eas-cli/releases/tag/v9.0.9) - 2024-05-22
|
||||
|
||||
## [9.0.8](https://github.com/expo/eas-cli/releases/tag/v9.0.8) - 2024-05-21
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2387](https://github.com/expo/eas-cli/pull/2387) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Improve displaying of device registration QR code. ([#2391](https://github.com/expo/eas-cli/pull/2391) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [9.0.7](https://github.com/expo/eas-cli/releases/tag/v9.0.7) - 2024-05-17
|
||||
|
||||
## [9.0.6](https://github.com/expo/eas-cli/releases/tag/v9.0.6) - 2024-05-15
|
||||
|
||||
## [9.0.5](https://github.com/expo/eas-cli/releases/tag/v9.0.5) - 2024-05-13
|
||||
|
||||
## [9.0.4](https://github.com/expo/eas-cli/releases/tag/v9.0.4) - 2024-05-13
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add loader and progress information to the `diagnostics` command. ([#2378](https://github.com/expo/eas-cli/pull/2378) by [@simek](https://github.com/simek))
|
||||
|
||||
## [9.0.3](https://github.com/expo/eas-cli/releases/tag/v9.0.3) - 2024-05-10
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2372](https://github.com/expo/eas-cli/pull/2372) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [9.0.2](https://github.com/expo/eas-cli/releases/tag/v9.0.2) - 2024-05-09
|
||||
|
||||
## [9.0.1](https://github.com/expo/eas-cli/releases/tag/v9.0.1) - 2024-05-09
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update image tags in `eas.schema.json`. ([#2363](https://github.com/expo/eas-cli/pull/2363) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [9.0.0](https://github.com/expo/eas-cli/releases/tag/v9.0.0) - 2024-05-08
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Allow modification of provisioning profile in CI, add --freeze-credentials flag. ([#2347](https://github.com/expo/eas-cli/pull/2347) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Pass env from process.env and build profile to expo-updates CLI calls where applicable. ([#2359](https://github.com/expo/eas-cli/pull/2359) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove more classic updates code. ([#2357](https://github.com/expo/eas-cli/pull/2357) by [@wschurman](https://github.com/wschurman))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2360](https://github.com/expo/eas-cli/pull/2360) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Don't pass custom global `"expoCli"` version set in `eas.json` to EAS Build process and warn users when setting it. ([#2361](https://github.com/expo/eas-cli/pull/2361) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [8.0.0](https://github.com/expo/eas-cli/releases/tag/v8.0.0) - 2024-05-01
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Stop creating a channel on `eas update` and `eas update:roll-back-to-embedded` unless the `--channel` flag is specified. ([#2346](https://github.com/expo/eas-cli/pull/2346) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Improve login info message for other login options. ([#2352](https://github.com/expo/eas-cli/pull/2352) by [@wschurman](https://github.com/wschurman))
|
||||
- Show the -s, --sso option in the login command help. ([#2353](https://github.com/expo/eas-cli/pull/2353) by [@lzkb](https://github.com/lzkb))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2351](https://github.com/expo/eas-cli/pull/2351) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Don't prompt users to set push notifications by default if they don't have the `expo-notifications` installed. ([#2343](https://github.com/expo/eas-cli/pull/2343) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.8.5](https://github.com/expo/eas-cli/releases/tag/v7.8.5) - 2024-04-26
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Add explicit workflow arg to expo-update CLI calls. ([#2340](https://github.com/expo/eas-cli/pull/2340) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Improve error message if the server returns `UNAUTHORIZED_ERROR`. ([#2345](https://github.com/expo/eas-cli/pull/2345) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Fill in min expo-updates version for expo-updates CLI. ([#2344](https://github.com/expo/eas-cli/pull/2344) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
## [7.8.4](https://github.com/expo/eas-cli/releases/tag/v7.8.4) - 2024-04-24
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Update the list of available Android images. ([#2337](https://github.com/expo/eas-cli/pull/2337) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Make multi-select for revoking distribution certificates more readable. ([#2342](https://github.com/expo/eas-cli/pull/2342) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Improve error message displayed when EAS CLI version doesn't satisfy minimal version required specified in eas.json. ([#2341](https://github.com/expo/eas-cli/pull/2341) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.8.3](https://github.com/expo/eas-cli/releases/tag/v7.8.3) - 2024-04-23
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Don't prompt to set `android.package` and `ios.bundleIdentifier` values when running in non-interactive mode. ([#2336](https://github.com/expo/eas-cli/pull/2336) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Amend credential removal wording. ([#2334](https://github.com/expo/eas-cli/pull/2334) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [7.8.2](https://github.com/expo/eas-cli/releases/tag/v7.8.2) - 2024-04-15
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix display of errors when expo-updates CLI command fails. ([#2324](https://github.com/expo/eas-cli/pull/2324) by [@wschurman](https://github.com/wschurman))
|
||||
- Move credentials endpoints to paginated counterparts. ([#2327](https://github.com/expo/eas-cli/pull/2327) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add progress bar for fetching paginated datasets. ([#2326](https://github.com/expo/eas-cli/pull/2326) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [7.8.1](https://github.com/expo/eas-cli/releases/tag/v7.8.1) - 2024-04-11
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix command source files URLs in autogenerated `README`. ([#2318](https://github.com/expo/eas-cli/pull/2318) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2316](https://github.com/expo/eas-cli/pull/2316) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Stop querying `Build.resourceClass` field. ([#2320](https://github.com/expo/eas-cli/pull/2320) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.8.0](https://github.com/expo/eas-cli/releases/tag/v7.8.0) - 2024-04-08
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `auto`, `sdk-50` and `sdk-49` image tags. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Add `--build-logger-level` flag to `eas build` command. ([#2313](https://github.com/expo/eas-cli/pull/2313) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Deprecate the `default` image tag. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Deprecate iOS images with Xcode version lower then 15. ([#2298](https://github.com/expo/eas-cli/pull/2298) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.7.0](https://github.com/expo/eas-cli/releases/tag/v7.7.0) - 2024-04-05
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Generate metadata file for project archive ([#2149](https://github.com/expo/eas-cli/pull/2149) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
- Add --verbose-fastlane flag to eas submit command for more robust fastlane pilot logs. ([#2276](https://github.com/expo/eas-cli/pull/2276) by [@khamilowicz](https://github.com/khamilowicz))
|
||||
- Add `eas credentials:configure-build` subcommand. ([#2282](https://github.com/expo/eas-cli/pull/2282) by [@fiberjw](https://github.com/fiberjw))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add info about the Xcode 15.3 image to `eas.schema.json`. ([#2312](https://github.com/expo/eas-cli/pull/2312) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.6.2](https://github.com/expo/eas-cli/releases/tag/v7.6.2) - 2024-03-27
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2301](https://github.com/expo/eas-cli/pull/2301) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2304](https://github.com/expo/eas-cli/pull/2304) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.6.1](https://github.com/expo/eas-cli/releases/tag/v7.6.1) - 2024-03-25
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2291](https://github.com/expo/eas-cli/pull/2291) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Fix asset limit punctuation. ([#2296](https://github.com/expo/eas-cli/pull/2296) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2293](https://github.com/expo/eas-cli/pull/2293) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.6.0](https://github.com/expo/eas-cli/releases/tag/v7.6.0) - 2024-03-18
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Print uncommitted files in non-interactive mode if they fail the execution. ([#2288](https://github.com/expo/eas-cli/pull/2288) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Use a custom build config path with POSIX separator when sending data to the EAS Build server. ([#2285](https://github.com/expo/eas-cli/pull/2285) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Improve resolving vcsClient as a part of the project context. ([#2295](https://github.com/expo/eas-cli/pull/2295) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2277](https://github.com/expo/eas-cli/pull/2277) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2283](https://github.com/expo/eas-cli/pull/2283) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Bump the `@expo/apple-utils` version to switch between the `developer.apple.com` and `developer-mdn.apple.com` domains when one of them doesn't work. ([#2290](https://github.com/expo/eas-cli/pull/2290) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.5.0](https://github.com/expo/eas-cli/releases/tag/v7.5.0) - 2024-03-11
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `--auto-submit` option to `eas build:internal` command. ([#2271](https://github.com/expo/eas-cli/pull/2271) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2274](https://github.com/expo/eas-cli/pull/2274) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.4.0](https://github.com/expo/eas-cli/releases/tag/v7.4.0) - 2024-03-10
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Use new expo-updates configuration:syncnative for versioned native sync. ([#2269](https://github.com/expo/eas-cli/pull/2269) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Fix expo-updates package version detection for canaries. ([#2243](https://github.com/expo/eas-cli/pull/2243) by [@wschurman](https://github.com/wschurman))
|
||||
- Add missing `config` property to `eas.json` schema. ([#2248](https://github.com/expo/eas-cli/pull/2248) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
- Use expo-updates runtime version CLI to generate runtime versions. ([#2251](https://github.com/expo/eas-cli/pull/2251) by [@wschurman](https://github.com/wschurman))
|
||||
- Update @expo/apple-utils to handle changes in API. ([73ba19de6662763cc6bff9fac6b7700ffbd0e88a](https://github.com/expo/eas-cli/commit/73ba19de6662763cc6bff9fac6b7700ffbd0e88a) by [@brentvatne](https://github.com/brentvatne))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2237](https://github.com/expo/eas-cli/pull/2237) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2240](https://github.com/expo/eas-cli/pull/2240) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2253](https://github.com/expo/eas-cli/pull/2253) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Include src/\*\*/build directories in vscode search and replace. ([#2250](https://github.com/expo/eas-cli/pull/2250) by [@wschurman](https://github.com/wschurman))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2259](https://github.com/expo/eas-cli/pull/2259) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.3.0](https://github.com/expo/eas-cli/releases/tag/v7.3.0) - 2024-02-19
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Fix expo-updates fingerprinting during update. ([#2231](https://github.com/expo/eas-cli/pull/2231) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Don't require expo on fresh react-native project. ([#2235](https://github.com/expo/eas-cli/pull/2235) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2229](https://github.com/expo/eas-cli/pull/2229) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2230](https://github.com/expo/eas-cli/pull/2230) by [@expo-bot](https://github.com/expo-bot))
|
||||
- Reword update configuration warning. ([#2234](https://github.com/expo/eas-cli/pull/2234) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [7.2.0](https://github.com/expo/eas-cli/releases/tag/v7.2.0) - 2024-02-11
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Support configuring a Google Service Account Key via eas credentials, for sending Android Notifications via FCM V1. ([#2197](https://github.com/expo/eas-cli/pull/2197) by [@christopherwalter](https://github.com/christopherwalter))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Revert expose expo export dev flag as an option in eas update. ([#2214](https://github.com/expo/eas-cli/pull/2214) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2223](https://github.com/expo/eas-cli/pull/2223) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.1.3](https://github.com/expo/eas-cli/releases/tag/v7.1.3) - 2024-02-07
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove duplicated log message when creating ASC API key. ([#2208](https://github.com/expo/eas-cli/pull/2208) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Add simulator flag to metadata. ([#2073](https://github.com/expo/eas-cli/pull/2073) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Upgrade [`eas-build`](https://github.com/expo/eas-build) dependencies. ([#2220](https://github.com/expo/eas-cli/pull/2220) by [@expo-bot](https://github.com/expo-bot))
|
||||
|
||||
## [7.1.2](https://github.com/expo/eas-cli/releases/tag/v7.1.2) - 2024-01-30
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Add better validation for EAS Submit inputs. ([#2202](https://github.com/expo/eas-cli/pull/2202) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.1.1](https://github.com/expo/eas-cli/releases/tag/v7.1.1) - 2024-01-26
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- Revert incorrect EAS Submit input validation changes. ([#2200](https://github.com/expo/eas-cli/pull/2200) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.1.0](https://github.com/expo/eas-cli/releases/tag/v7.1.0) - 2024-01-26
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Support requireCommit for EAS Update. ([#2196](https://github.com/expo/eas-cli/pull/2196) by [@wschurman](https://github.com/wschurman))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove support for classic updates release channel in 50+. ([#2189](https://github.com/expo/eas-cli/pull/2189) by [@wschurman](https://github.com/wschurman))
|
||||
- Validate EAS Submit inputs better. ([#2198](https://github.com/expo/eas-cli/pull/2198) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [7.0.0](https://github.com/expo/eas-cli/releases/tag/v7.0.0) - 2024-01-19
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
- Stop generating eas-update-metadata.json on publish. ([#2187](https://github.com/expo/eas-cli/pull/2187) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Use branch mapping utility fn. ([#2186](https://github.com/expo/eas-cli/pull/2186) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [6.1.0](https://github.com/expo/eas-cli/releases/tag/v6.1.0) - 2024-01-18
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- Add `build:delete` command. ([#2178](https://github.com/expo/eas-cli/pull/2178) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
- Add filter flags for `platform` and `profile` to `build:cancel` and `build:delete` commands. ([#2178](https://github.com/expo/eas-cli/pull/2178) by [@radoslawkrzemien](https://github.com/radoslawkrzemien))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
- Remove "bare"-specific **eas.json** template. ([#2179](https://github.com/expo/eas-cli/pull/2179) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
- Prompt users if they want to continue if EAS CLI fails to provision the devices. ([#2181](https://github.com/expo/eas-cli/pull/2181) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Update `eas-cli` and `@expo/eas-json` dependencies. ([#2176](https://github.com/expo/eas-cli/pull/2176) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Update `eas.schema.json` to after adding Xcode 15.2 image. ([#2184](https://github.com/expo/eas-cli/pull/2184) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- Upgrade packages from the expo/expo repo. ([#2145](https://github.com/expo/eas-cli/pull/2145) by [@wschurman](https://github.com/wschurman))
|
||||
- Prompt user to map channel on update if unmapped. ([#2185](https://github.com/expo/eas-cli/pull/2185) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
## [6.0.0](https://github.com/expo/eas-cli/releases/tag/v6.0.0) - 2024-01-12
|
||||
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
* @szdziedzic @khamilowicz @sjchmiela
|
||||
* @szdziedzic @khamilowicz
|
||||
|
||||
packages/eas-cli/src/build @szdziedzic @khamilowicz @sjchmiela
|
||||
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz @sjchmiela
|
||||
packages/eas-cli/src/build @szdziedzic @khamilowicz
|
||||
packages/eas-cli/src/commands/build @szdziedzic @khamilowicz
|
||||
|
||||
packages/eas-cli/src/submit @szdziedzic @khamilowicz @sjchmiela
|
||||
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz @sjchmiela
|
||||
packages/eas-cli/src/submit @szdziedzic @khamilowicz
|
||||
packages/eas-cli/src/commands/submit.ts @szdziedzic @khamilowicz
|
||||
|
||||
packages/eas-cli/src/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/commands/metadata @bycedric
|
||||
|
||||
packages/eas-json @szdziedzic @khamilowicz @sjchmiela
|
||||
packages/eas-json @szdziedzic @khamilowicz
|
||||
@@ -12,3 +12,7 @@ The algorithm works as follows:
|
||||
- If there are any entries in the "🛠 Breaking changes" section, bump the MAJOR version.
|
||||
- Otherwise, if there are any entries in the "🎉 New features", bump the MINOR version.
|
||||
- Otherwise, bump the PATCH version.
|
||||
|
||||
## Updating EAS CLI version on EAS Build
|
||||
|
||||
The release process promotes the new version to staging. After verifying it works there, you can promote it to production with `npm dist-tag add eas-cli@{version} latest-eas-build`.
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "10.2.2",
|
||||
"version": "6.0.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
+111
-241
@@ -39,7 +39,7 @@ An example of **eas.json** that enforces `eas-cli` in version `1.0.0` or newer:
|
||||
}
|
||||
```
|
||||
|
||||
Learn more: https://docs.expo.dev/build/eas-json/
|
||||
Learn more: https://docs.expo.dev/build-reference/eas-json/
|
||||
|
||||
# Usage
|
||||
|
||||
@@ -68,7 +68,6 @@ eas --help COMMAND
|
||||
* [`eas build`](#eas-build)
|
||||
* [`eas build:cancel [BUILD_ID]`](#eas-buildcancel-build_id)
|
||||
* [`eas build:configure`](#eas-buildconfigure)
|
||||
* [`eas build:delete [BUILD_ID]`](#eas-builddelete-build_id)
|
||||
* [`eas build:inspect`](#eas-buildinspect)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:resign`](#eas-buildresign)
|
||||
@@ -85,7 +84,6 @@ eas --help COMMAND
|
||||
* [`eas channel:view [NAME]`](#eas-channelview-name)
|
||||
* [`eas config`](#eas-config)
|
||||
* [`eas credentials`](#eas-credentials)
|
||||
* [`eas credentials:configure-build`](#eas-credentialsconfigure-build)
|
||||
* [`eas device:create`](#eas-devicecreate)
|
||||
* [`eas device:delete`](#eas-devicedelete)
|
||||
* [`eas device:list`](#eas-devicelist)
|
||||
@@ -94,17 +92,14 @@ eas --help COMMAND
|
||||
* [`eas diagnostics`](#eas-diagnostics)
|
||||
* [`eas help [COMMAND]`](#eas-help-command)
|
||||
* [`eas init`](#eas-init)
|
||||
* [`eas init:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-initonboarding-target_project_directory)
|
||||
* [`eas login`](#eas-login)
|
||||
* [`eas logout`](#eas-logout)
|
||||
* [`eas metadata:lint`](#eas-metadatalint)
|
||||
* [`eas metadata:pull`](#eas-metadatapull)
|
||||
* [`eas metadata:push`](#eas-metadatapush)
|
||||
* [`eas onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-onboarding-target_project_directory)
|
||||
* [`eas open`](#eas-open)
|
||||
* [`eas project:info`](#eas-projectinfo)
|
||||
* [`eas project:init`](#eas-projectinit)
|
||||
* [`eas project:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-projectonboarding-target_project_directory)
|
||||
* [`eas secret:create`](#eas-secretcreate)
|
||||
* [`eas secret:delete`](#eas-secretdelete)
|
||||
* [`eas secret:list`](#eas-secretlist)
|
||||
@@ -131,10 +126,7 @@ log in with your Expo account
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas account:login [-s]
|
||||
|
||||
FLAGS
|
||||
-s, --sso Login with SSO
|
||||
$ eas account:login
|
||||
|
||||
DESCRIPTION
|
||||
log in with your Expo account
|
||||
@@ -143,7 +135,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -160,7 +152,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -177,7 +169,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -191,7 +183,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/analytics.ts)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -222,7 +214,7 @@ EXAMPLES
|
||||
$ eas autocomplete --refresh-cache
|
||||
```
|
||||
|
||||
_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v2.3.10/packages/eas-cli/src/commands/autocomplete/index.ts)_
|
||||
_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v2.3.10/src/commands/autocomplete/index.ts)_
|
||||
|
||||
## `eas branch:create [NAME]`
|
||||
|
||||
@@ -243,7 +235,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -264,7 +256,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -284,7 +276,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -304,7 +296,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -327,7 +319,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -336,35 +328,28 @@ start a build
|
||||
```
|
||||
USAGE
|
||||
$ eas build [-p android|ios|all] [-e <value>] [--local] [--output <value>] [--wait] [--clear-cache]
|
||||
[--auto-submit | --auto-submit-with-profile <value>] [-m <value>] [--build-logger-level
|
||||
trace|debug|info|warn|error|fatal] [--freeze-credentials] [--json --non-interactive]
|
||||
[--auto-submit | --auto-submit-with-profile <value>] [-m <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.
|
||||
-m, --message=<value> A short message describing the build
|
||||
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined
|
||||
in eas.json.
|
||||
-m, --message=<value> A short message describing the build
|
||||
-p, --platform=(android|ios|all)
|
||||
--auto-submit Submit on build complete using the submit profile with the
|
||||
same name as the build profile
|
||||
--auto-submit-with-profile=PROFILE_NAME Submit on build complete using the submit profile with
|
||||
provided name
|
||||
--build-logger-level=(trace|debug|info|warn|error|fatal) The level of logs to output during the build process.
|
||||
Defaults to "info".
|
||||
--clear-cache Clear cache before the build
|
||||
--freeze-credentials Prevent the build from updating credentials in
|
||||
non-interactive mode
|
||||
--json Enable JSON output, non-JSON messages will be printed to
|
||||
stderr.
|
||||
--local Run build locally [experimental]
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--output=<value> Output path for local build
|
||||
--[no-]wait Wait for build(s) to complete
|
||||
--auto-submit Submit on build complete using the submit profile with the same name as the
|
||||
build profile
|
||||
--auto-submit-with-profile=PROFILE_NAME Submit on build complete using the submit profile with provided name
|
||||
--clear-cache Clear cache before the build
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--local Run build locally [experimental]
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--output=<value> Output path for local build
|
||||
--[no-]wait Wait for build(s) to complete
|
||||
|
||||
DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -372,18 +357,16 @@ cancel a build
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:cancel [BUILD_ID] [--non-interactive] [-p android|ios|all] [-e <value>]
|
||||
$ eas build:cancel [BUILD_ID] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME Filter builds by build profile if build ID is not provided
|
||||
-p, --platform=(android|ios|all) Filter builds by the platform if build ID is not provided
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -400,26 +383,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:delete [BUILD_ID]`
|
||||
|
||||
delete a build
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:delete [BUILD_ID] [--non-interactive] [-p android|ios|all] [-e <value>]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME Filter builds by build profile if build ID is not provided
|
||||
-p, --platform=(android|ios|all) Filter builds by the platform if build ID is not provided
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
|
||||
DESCRIPTION
|
||||
delete a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/delete.ts)_
|
||||
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:inspect`
|
||||
|
||||
@@ -454,7 +418,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -462,27 +426,20 @@ list all builds for your project
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:list [-p android|ios|all] [--status
|
||||
$ eas build:list [--platform all|android|ios] [--status
|
||||
new|in-queue|in-progress|pending-cancel|errored|finished|canceled] [--distribution store|internal|simulator]
|
||||
[--channel <value>] [--app-version <value>] [--app-build-version <value>] [--sdk-version <value>] [--runtime-version
|
||||
<value>] [--app-identifier <value>] [-e <value>] [--git-commit-hash <value>] [--offset <value>] [--limit <value>]
|
||||
[--json --non-interactive] [--simulator]
|
||||
[--channel <value>] [--appVersion <value>] [--appBuildVersion <value>] [--sdkVersion <value>] [--runtimeVersion
|
||||
<value>] [--appIdentifier <value>] [--buildProfile <value>] [--gitCommitHash <value>] [--offset <value>] [--limit
|
||||
<value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --build-profile=<value> Filter only builds created with the
|
||||
specified build profile
|
||||
-p, --platform=(android|ios|all)
|
||||
--app-build-version=<value> Filter only builds created with the
|
||||
specified app build version
|
||||
--app-identifier=<value> Filter only builds created with the
|
||||
specified app identifier
|
||||
--app-version=<value> Filter only builds created with the
|
||||
specified main app version
|
||||
--appBuildVersion=<value>
|
||||
--appIdentifier=<value>
|
||||
--appVersion=<value>
|
||||
--buildProfile=<value>
|
||||
--channel=<value>
|
||||
--distribution=(store|internal|simulator) Filter only builds with the specified
|
||||
distribution type
|
||||
--git-commit-hash=<value> Filter only builds created with the
|
||||
specified git commit hash
|
||||
--distribution=(store|internal|simulator)
|
||||
--gitCommitHash=<value>
|
||||
--json Enable JSON output, non-JSON messages
|
||||
will be printed to stderr.
|
||||
--limit=<value> The number of items to fetch each query.
|
||||
@@ -490,21 +447,16 @@ FLAGS
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--offset=<value> Start queries from specified index. Use
|
||||
for paginating results. Defaults to 0.
|
||||
--runtime-version=<value> Filter only builds created with the
|
||||
specified runtime version
|
||||
--sdk-version=<value> Filter only builds created with the
|
||||
specified Expo SDK version
|
||||
--simulator Filter only iOS simulator builds. Can
|
||||
only be used with --platform flag set to
|
||||
"ios"
|
||||
--status=(new|in-queue|in-progress|pending-cancel|errored|finished|canceled) Filter only builds with the specified
|
||||
status
|
||||
--platform=(all|android|ios)
|
||||
--runtimeVersion=<value>
|
||||
--sdkVersion=<value>
|
||||
--status=(new|in-queue|in-progress|pending-cancel|errored|finished|canceled)
|
||||
|
||||
DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -512,28 +464,25 @@ re-sign a build archive
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas build:resign [-p android|ios] [-e <value>] [--source-profile <value>] [--wait] [--id <value>] [--offset
|
||||
<value>] [--limit <value>] [--json --non-interactive]
|
||||
$ eas build:resign [-p android|ios] [-e <value>] [--wait] [--id <value>] [--offset <value>] [--limit <value>]
|
||||
[--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --target-profile=PROFILE_NAME Name of the target build profile from eas.json. Credentials and environment
|
||||
variables from this profile will be used when re-signing. Defaults to "production"
|
||||
if defined in eas.json.
|
||||
-e, --profile=PROFILE_NAME Name of the build profile from eas.json. Defaults to "production" if defined in
|
||||
eas.json.
|
||||
-p, --platform=(android|ios)
|
||||
--id=<value> ID of the build to re-sign.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--limit=<value> The number of items to fetch each query. Defaults to 50 and is capped at 100.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
|
||||
--source-profile=PROFILE_NAME Name of the source build profile from eas.json. Used to filter builds eligible for
|
||||
re-signing.
|
||||
--[no-]wait Wait for build(s) to complete.
|
||||
--id=<value> ID of the build to re-sign.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--limit=<value> The number of items to fetch each query. Defaults to 50 and is capped at 100.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--offset=<value> Start queries from specified index. Use for paginating results. Defaults to 0.
|
||||
--[no-]wait Wait for build(s) to complete.
|
||||
|
||||
DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -559,7 +508,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -568,7 +517,7 @@ submit app binary to App Store and/or Play Store
|
||||
```
|
||||
USAGE
|
||||
$ eas build:submit [-p android|ios|all] [-e <value>] [--latest | --id <value> | --path <value> | --url <value>]
|
||||
[--verbose] [--wait] [--verbose-fastlane] [--non-interactive]
|
||||
[--verbose] [--wait] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=<value> Name of the submit profile from eas.json. Defaults to "production" if defined in
|
||||
@@ -580,7 +529,6 @@ FLAGS
|
||||
--path=<value> Path to the .apk/.aab/.ipa file
|
||||
--url=<value> App archive url
|
||||
--verbose Always print logs from EAS Submit
|
||||
--verbose-fastlane Enable verbose logging for the submission process
|
||||
--[no-]wait Wait for submission to complete
|
||||
|
||||
DESCRIPTION
|
||||
@@ -609,7 +557,7 @@ DESCRIPTION
|
||||
get the latest version from EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
_See code: [src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/get.ts)_
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
@@ -628,7 +576,7 @@ DESCRIPTION
|
||||
update version of an app
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -647,7 +595,7 @@ DESCRIPTION
|
||||
update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -664,7 +612,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -685,7 +633,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -707,7 +655,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -727,7 +675,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
@@ -752,15 +700,14 @@ FLAGS
|
||||
--action=create
|
||||
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate
|
||||
in expo-updates' configuration. Defaults to a file named "private-key.pem" in
|
||||
the certificate's directory. Only relevant if you are using code signing:
|
||||
https://docs.expo.dev/eas-update/code-signing/
|
||||
the certificate's directory.
|
||||
--runtime-version=<value> Runtime version to target. Use with --action=create
|
||||
|
||||
DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
_See code: [src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -783,7 +730,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -804,7 +751,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/config.ts)_
|
||||
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/config.ts)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -821,25 +768,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/credentials/index.ts)_
|
||||
|
||||
## `eas credentials:configure-build`
|
||||
|
||||
Set up credentials for building your project.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas credentials:configure-build [-p android|ios] [-e <value>]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=PROFILE_NAME The name of the build profile in eas.json.
|
||||
-p, --platform=(android|ios)
|
||||
|
||||
DESCRIPTION
|
||||
Set up credentials for building your project.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/credentials/configure-build.ts)_
|
||||
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/credentials.ts)_
|
||||
|
||||
## `eas device:create`
|
||||
|
||||
@@ -853,7 +782,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -873,7 +802,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -894,7 +823,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:rename`
|
||||
|
||||
@@ -915,7 +844,7 @@ DESCRIPTION
|
||||
rename a registered device
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
_See code: [src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/rename.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -929,7 +858,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -943,7 +872,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/diagnostics.ts)_
|
||||
|
||||
## `eas help [COMMAND]`
|
||||
|
||||
@@ -963,7 +892,7 @@ DESCRIPTION
|
||||
Display help for eas.
|
||||
```
|
||||
|
||||
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.1.23/packages/eas-cli/src/commands/help.ts)_
|
||||
_See code: [@expo/plugin-help](https://github.com/expo/oclif-plugin-help/blob/v5.1.23/src/commands/help.ts)_
|
||||
|
||||
## `eas init`
|
||||
|
||||
@@ -985,32 +914,13 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
## `eas init:onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas init:onboarding [TARGET_PROJECT_DIRECTORY]
|
||||
|
||||
DESCRIPTION
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
ALIASES
|
||||
$ eas init:onboarding
|
||||
$ eas onboarding
|
||||
```
|
||||
|
||||
## `eas login`
|
||||
|
||||
log in with your Expo account
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas login [-s]
|
||||
|
||||
FLAGS
|
||||
-s, --sso Login with SSO
|
||||
$ eas login
|
||||
|
||||
DESCRIPTION
|
||||
log in with your Expo account
|
||||
@@ -1050,7 +960,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -1067,7 +977,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -1084,23 +994,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas onboarding [TARGET_PROJECT_DIRECTORY]
|
||||
|
||||
DESCRIPTION
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
ALIASES
|
||||
$ eas init:onboarding
|
||||
$ eas onboarding
|
||||
```
|
||||
_See code: [src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas open`
|
||||
|
||||
@@ -1114,7 +1008,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/open.ts)_
|
||||
_See code: [src/commands/open.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/open.ts)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1128,7 +1022,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1150,25 +1044,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas project:onboarding [TARGET_PROJECT_DIRECTORY]
|
||||
|
||||
DESCRIPTION
|
||||
continue onboarding process started on the https://expo.new website.
|
||||
|
||||
ALIASES
|
||||
$ eas init:onboarding
|
||||
$ eas onboarding
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/project/init.ts)_
|
||||
|
||||
## `eas secret:create`
|
||||
|
||||
@@ -1191,7 +1067,7 @@ DESCRIPTION
|
||||
create an environment secret on the current project or owner account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/create.ts)_
|
||||
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/create.ts)_
|
||||
|
||||
## `eas secret:delete`
|
||||
|
||||
@@ -1209,7 +1085,7 @@ DESCRIPTION
|
||||
delete an environment secret by ID
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/delete.ts)_
|
||||
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/delete.ts)_
|
||||
|
||||
## `eas secret:list`
|
||||
|
||||
@@ -1223,7 +1099,7 @@ DESCRIPTION
|
||||
list environment secrets available for your current app
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/list.ts)_
|
||||
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/list.ts)_
|
||||
|
||||
## `eas secret:push`
|
||||
|
||||
@@ -1243,7 +1119,7 @@ DESCRIPTION
|
||||
read environment secrets from env file and store on the server
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/secret/push.ts)_
|
||||
_See code: [src/commands/secret/push.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/secret/push.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1252,7 +1128,7 @@ submit app binary to App Store and/or Play Store
|
||||
```
|
||||
USAGE
|
||||
$ eas submit [-p android|ios|all] [-e <value>] [--latest | --id <value> | --path <value> | --url <value>]
|
||||
[--verbose] [--wait] [--verbose-fastlane] [--non-interactive]
|
||||
[--verbose] [--wait] [--non-interactive]
|
||||
|
||||
FLAGS
|
||||
-e, --profile=<value> Name of the submit profile from eas.json. Defaults to "production" if defined in
|
||||
@@ -1264,7 +1140,6 @@ FLAGS
|
||||
--path=<value> Path to the .apk/.aab/.ipa file
|
||||
--url=<value> App archive url
|
||||
--verbose Always print logs from EAS Submit
|
||||
--verbose-fastlane Enable verbose logging for the submission process
|
||||
--[no-]wait Wait for submission to complete
|
||||
|
||||
DESCRIPTION
|
||||
@@ -1274,7 +1149,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/submit.ts)_
|
||||
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/submit.ts)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1283,8 +1158,8 @@ publish an update group
|
||||
```
|
||||
USAGE
|
||||
$ eas update [--branch <value>] [--channel <value>] [-m <value>] [--republish | --input-dir <value> |
|
||||
--skip-bundler] [--group <value> | | ] [--clear-cache] [--emit-metadata] [-p android|ios|all] [--auto]
|
||||
[--private-key-path <value>] [--json --non-interactive]
|
||||
--skip-bundler] [--group <value> | | ] [--clear-cache] [-p android|ios|all] [--dev] [--auto] [--private-key-path
|
||||
<value>] [--json --non-interactive]
|
||||
|
||||
FLAGS
|
||||
-m, --message=<value> A short message describing the update
|
||||
@@ -1293,16 +1168,14 @@ FLAGS
|
||||
--branch=<value> Branch to publish the update group on
|
||||
--channel=<value> Channel that the published update should affect
|
||||
--clear-cache Clear the bundler cache before publishing
|
||||
--emit-metadata Emit "eas-update-metadata.json" in the bundle folder with detailed information about
|
||||
the generated updates
|
||||
--dev Publish a development bundle
|
||||
--group=<value> Update group to republish (deprecated, see republish command)
|
||||
--input-dir=<value> [default: dist] Location of the bundle
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
|
||||
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
|
||||
certificate's directory. Only relevant if you are using code signing:
|
||||
https://docs.expo.dev/eas-update/code-signing/
|
||||
certificate's directory.
|
||||
--republish Republish an update group (deprecated, see republish command)
|
||||
--skip-bundler Skip running Expo CLI to bundle the app before publishing
|
||||
|
||||
@@ -1310,7 +1183,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1328,7 +1201,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1349,7 +1222,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1371,7 +1244,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1392,14 +1265,13 @@ FLAGS
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
|
||||
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
|
||||
certificate's directory. Only relevant if you are using code signing:
|
||||
https://docs.expo.dev/eas-update/code-signing/
|
||||
certificate's directory.
|
||||
|
||||
DESCRIPTION
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:roll-back-to-embedded`
|
||||
|
||||
@@ -1420,14 +1292,13 @@ FLAGS
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
|
||||
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
|
||||
certificate's directory. Only relevant if you are using code signing:
|
||||
https://docs.expo.dev/eas-update/code-signing/
|
||||
certificate's directory.
|
||||
|
||||
DESCRIPTION
|
||||
roll back to the embedded update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
|
||||
_See code: [src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/roll-back-to-embedded.ts)_
|
||||
|
||||
## `eas update:rollback`
|
||||
|
||||
@@ -1440,14 +1311,13 @@ USAGE
|
||||
FLAGS
|
||||
--private-key-path=<value> File containing the PEM-encoded private key corresponding to the certificate in
|
||||
expo-updates' configuration. Defaults to a file named "private-key.pem" in the
|
||||
certificate's directory. Only relevant if you are using code signing:
|
||||
https://docs.expo.dev/eas-update/code-signing/
|
||||
certificate's directory.
|
||||
|
||||
DESCRIPTION
|
||||
roll back to an embedded update or an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/rollback.ts)_
|
||||
_See code: [src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/rollback.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1467,7 +1337,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1488,7 +1358,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1508,7 +1378,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1526,7 +1396,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1548,7 +1418,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1565,7 +1435,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v10.2.2/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v6.0.0/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
|
||||
Generated
+1497
-9626
File diff suppressed because it is too large.
Load diff
@@ -1,22 +1,21 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "10.2.2",
|
||||
"version": "6.0.0",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "1.7.1",
|
||||
"@expo/apple-utils": "1.3.2",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "8.5.4",
|
||||
"@expo/config-plugins": "7.8.4",
|
||||
"@expo/config-types": "50.0.0",
|
||||
"@expo/eas-build-job": "1.0.130",
|
||||
"@expo/eas-json": "10.2.2",
|
||||
"@expo/config": "8.1.2",
|
||||
"@expo/config-plugins": "7.2.4",
|
||||
"@expo/config-types": "49.0.0",
|
||||
"@expo/eas-build-job": "1.0.56",
|
||||
"@expo/eas-json": "6.0.0",
|
||||
"@expo/json-file": "8.2.37",
|
||||
"@expo/logger": "1.0.117",
|
||||
"@expo/multipart-body-parser": "1.1.0",
|
||||
"@expo/osascript": "2.0.33",
|
||||
"@expo/package-manager": "1.1.2",
|
||||
@@ -24,11 +23,11 @@
|
||||
"@expo/plist": "0.0.20",
|
||||
"@expo/plugin-help": "5.1.23",
|
||||
"@expo/plugin-warn-if-update-available": "2.5.1",
|
||||
"@expo/prebuild-config": "6.7.3",
|
||||
"@expo/prebuild-config": "6.2.5",
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.0",
|
||||
"@expo/steps": "1.0.129",
|
||||
"@expo/steps": "1.0.51",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "^1.26.2",
|
||||
"@oclif/plugin-autocomplete": "^2.3.10",
|
||||
@@ -77,7 +76,7 @@
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.5.4",
|
||||
"slash": "3.0.0",
|
||||
"tar": "6.2.1",
|
||||
"tar": "6.2.0",
|
||||
"terminal-link": "2.1.1",
|
||||
"tslib": "2.6.2",
|
||||
"turndown": "7.1.2",
|
||||
@@ -108,9 +107,9 @@
|
||||
"@types/tough-cookie": "4.0.2",
|
||||
"@types/uuid": "9.0.7",
|
||||
"@types/wrap-ansi": "3.0.0",
|
||||
"axios": "0.28.1",
|
||||
"axios": "0.27.2",
|
||||
"eslint-plugin-graphql": "4.0.0",
|
||||
"express": "4.19.2",
|
||||
"express": "4.18.2",
|
||||
"memfs": "3.4.13",
|
||||
"mockdate": "3.0.5",
|
||||
"nock": "13.4.0",
|
||||
@@ -121,7 +120,7 @@
|
||||
"typescript": "5.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"files": [
|
||||
"/bin",
|
||||
|
||||
@@ -3,7 +3,8 @@ const fs = require('fs/promises');
|
||||
(async () => {
|
||||
// Patch `oclif readme` path and link generation
|
||||
let readmeContent = await fs.readFile('README.md', 'utf8');
|
||||
readmeContent = readmeContent.replace(/src\/commands/g, 'packages/eas-cli/src/commands');
|
||||
readmeContent = readmeContent.replace(/\[build\//g, '[src/');
|
||||
readmeContent = readmeContent.replace(/build\/commands/g, 'packages/eas-cli/src/commands');
|
||||
await fs.writeFile('README.md', readmeContent);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@@ -24,7 +24,6 @@ export function getMockAppFragment(): AppFragment {
|
||||
return {
|
||||
id: mockProjectId,
|
||||
slug: 'testapp',
|
||||
name: 'testapp',
|
||||
fullName: '@testuser/testpp',
|
||||
ownerAccount: {
|
||||
id: 'test-account-id',
|
||||
|
||||
@@ -3,7 +3,13 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { selectBuildToCancelAsync } from '../../commands/build/cancel';
|
||||
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildPriority,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
} from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { confirmAsync, selectAsync } from '../../prompts';
|
||||
|
||||
@@ -85,6 +91,6 @@ function createMockBuildFragment({
|
||||
},
|
||||
},
|
||||
status: BuildStatus.InQueue,
|
||||
isForIosSimulator: false,
|
||||
resourceClass: BuildResourceClass.AndroidMedium,
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,13 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { selectBuildToDeleteAsync } from '../../commands/build/delete';
|
||||
import { AppPlatform, BuildFragment, BuildPriority, BuildStatus } from '../../graphql/generated';
|
||||
import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildPriority,
|
||||
BuildResourceClass,
|
||||
BuildStatus,
|
||||
} from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import { confirmAsync, selectAsync } from '../../prompts';
|
||||
|
||||
@@ -85,6 +91,6 @@ function createMockBuildFragment({
|
||||
},
|
||||
},
|
||||
status: BuildStatus.InQueue,
|
||||
isForIosSimulator: false,
|
||||
resourceClass: BuildResourceClass.AndroidMedium,
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import { getAppBuildGradleAsync, resolveConfigValue } from '../../../project/android/gradleUtils';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import { getVcsClient } from '../../../vcs';
|
||||
import {
|
||||
BumpStrategy,
|
||||
bumpVersionAsync,
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
const fsReal = jest.requireActual('fs').promises as typeof fs;
|
||||
jest.mock('fs');
|
||||
|
||||
const vcsClient = resolveVcsClient();
|
||||
const vcsClient = getVcsClient();
|
||||
|
||||
afterAll(async () => {
|
||||
// do not remove the following line
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Android, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Android, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AppVersionSource } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
@@ -99,21 +99,21 @@ export async function prepareAndroidBuildAsync(
|
||||
return await ensureAndroidCredentialsAsync(ctx);
|
||||
},
|
||||
syncProjectConfigurationAsync: async () => {
|
||||
await syncProjectConfigurationAsync({
|
||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
localAutoIncrement:
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
projectId: ctx.projectId,
|
||||
vcsClient: ctx.vcsClient,
|
||||
env: ctx.buildProfile.env,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
ctx: BuildContext<Platform.ANDROID>,
|
||||
jobData: JobData<AndroidCredentials>
|
||||
): Promise<Android.Job> => {
|
||||
): Promise<Job> => {
|
||||
return await prepareJobAsync(ctx, jobData);
|
||||
},
|
||||
sendBuildRequestAsync: async (
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Android } from '@expo/eas-build-job';
|
||||
|
||||
import { AndroidBuildType, AndroidJobInput } from '../../graphql/generated';
|
||||
import {
|
||||
loggerLevelToGraphQLWorkerLoggerLevel,
|
||||
transformBuildMode,
|
||||
transformBuildTrigger,
|
||||
transformProjectArchive,
|
||||
@@ -15,6 +14,7 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||
projectArchive: transformProjectArchive(job.projectArchive),
|
||||
projectRootDirectory: job.projectRootDirectory,
|
||||
releaseChannel: job.releaseChannel,
|
||||
updates: job.updates,
|
||||
secrets: job.secrets,
|
||||
builderEnvironment: job.builderEnvironment,
|
||||
@@ -29,9 +29,6 @@ export function transformJob(job: Android.Job): AndroidJobInput {
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
loggerLevel: job.loggerLevel
|
||||
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,16 @@ import {
|
||||
ArchiveSource,
|
||||
BuildMode,
|
||||
BuildTrigger,
|
||||
Job,
|
||||
Platform,
|
||||
sanitizeBuildJob,
|
||||
sanitizeJob,
|
||||
} from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import path from 'path';
|
||||
import slash from 'slash';
|
||||
|
||||
import { AndroidCredentials } from '../../credentials/android/AndroidCredentialsProvider';
|
||||
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
|
||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
||||
import { getUsername } from '../../project/projectUtils';
|
||||
import { BuildContext } from '../context';
|
||||
|
||||
@@ -28,7 +29,7 @@ const cacheDefaults = {
|
||||
export async function prepareJobAsync(
|
||||
ctx: BuildContext<Platform.ANDROID>,
|
||||
jobData: JobData
|
||||
): Promise<Android.Job> {
|
||||
): Promise<Job> {
|
||||
const username = getUsername(ctx.exp, ctx.user);
|
||||
const buildProfile: BuildProfile<Platform.ANDROID> = ctx.buildProfile;
|
||||
const projectRootDirectory =
|
||||
@@ -53,18 +54,9 @@ export async function prepareJobAsync(
|
||||
}
|
||||
|
||||
const maybeCustomBuildConfigPath = buildProfile.config
|
||||
? getCustomBuildConfigPathForJob(buildProfile.config)
|
||||
? getCustomBuildConfigPath(buildProfile.config)
|
||||
: undefined;
|
||||
|
||||
let buildMode;
|
||||
if (ctx.repack) {
|
||||
buildMode = BuildMode.REPACK;
|
||||
} else if (buildProfile.config) {
|
||||
buildMode = BuildMode.CUSTOM;
|
||||
} else {
|
||||
buildMode = BuildMode.BUILD;
|
||||
}
|
||||
|
||||
const job: Android.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.ANDROID,
|
||||
@@ -77,6 +69,7 @@ export async function prepareJobAsync(
|
||||
bun: buildProfile.bun,
|
||||
yarn: buildProfile.yarn,
|
||||
ndk: buildProfile.ndk,
|
||||
expoCli: buildProfile.expoCli,
|
||||
env: buildProfile.env,
|
||||
},
|
||||
cache: {
|
||||
@@ -87,6 +80,7 @@ export async function prepareJobAsync(
|
||||
secrets: {
|
||||
...buildCredentials,
|
||||
},
|
||||
releaseChannel: buildProfile.releaseChannel,
|
||||
updates: { channel: buildProfile.channel },
|
||||
developmentClient: buildProfile.developmentClient,
|
||||
gradleCommand: buildProfile.gradleCommand,
|
||||
@@ -102,20 +96,14 @@ export async function prepareJobAsync(
|
||||
experimental: {
|
||||
prebuildCommand: buildProfile.prebuildCommand,
|
||||
},
|
||||
mode: buildMode,
|
||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
triggeredBy: BuildTrigger.EAS_CLI,
|
||||
...(maybeCustomBuildConfigPath && {
|
||||
customBuildConfig: {
|
||||
path: maybeCustomBuildConfigPath,
|
||||
},
|
||||
}),
|
||||
...(ctx.repack && {
|
||||
customBuildConfig: {
|
||||
path: '__eas/repack.yml',
|
||||
},
|
||||
}),
|
||||
loggerLevel: ctx.loggerLevel,
|
||||
};
|
||||
|
||||
return sanitizeBuildJob(job) as Android.Job;
|
||||
return sanitizeJob(job);
|
||||
}
|
||||
@@ -1,38 +1,42 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { AndroidConfig } from '@expo/config-plugins';
|
||||
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AndroidVersionAutoIncrement } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import Log from '../../log';
|
||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
localAutoIncrement,
|
||||
vcsClient,
|
||||
env,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||
vcsClient: Client;
|
||||
env: Env | undefined;
|
||||
}): Promise<void> {
|
||||
export async function syncProjectConfigurationAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
exp,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
vcsClient,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
localAutoIncrement?: AndroidVersionAutoIncrement;
|
||||
projectId: string;
|
||||
vcsClient: Client;
|
||||
}
|
||||
): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await cleanUpOldEasBuildGradleScriptAsync(projectDir);
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
await syncUpdatesConfigurationAsync({ projectDir, exp, workflow, env });
|
||||
await syncUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy });
|
||||
} else {
|
||||
|
||||
@@ -232,9 +232,7 @@ export async function resolveRemoteVersionCodeAsync(
|
||||
applicationIdentifier: applicationId,
|
||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion:
|
||||
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
|
||||
undefined,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized versionCode with ${chalk.bold(currentBuildVersion)}.`);
|
||||
} catch (err) {
|
||||
@@ -256,9 +254,7 @@ export async function resolveRemoteVersionCodeAsync(
|
||||
applicationIdentifier: applicationId,
|
||||
storeVersion: localVersions.appVersion ?? exp.version ?? '1.0.0',
|
||||
buildVersion: String(nextBuildVersion),
|
||||
runtimeVersion:
|
||||
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.ANDROID)) ??
|
||||
undefined,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.ANDROID) ?? undefined,
|
||||
});
|
||||
spinner.succeed(
|
||||
`Incremented versionCode from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
ArchiveSource,
|
||||
ArchiveSourceType,
|
||||
BuildJob,
|
||||
FingerprintSourceType,
|
||||
Metadata,
|
||||
Platform,
|
||||
} from '@expo/eas-build-job';
|
||||
import { ArchiveSource, ArchiveSourceType, Job, Metadata, Platform } from '@expo/eas-build-job';
|
||||
import { CredentialsSource } from '@expo/eas-json';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
@@ -13,8 +6,6 @@ import cliProgress from 'cli-progress';
|
||||
import fs from 'fs-extra';
|
||||
import { GraphQLError } from 'graphql/error';
|
||||
import nullthrows from 'nullthrows';
|
||||
import path from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { BuildContext } from './context';
|
||||
import {
|
||||
@@ -35,12 +26,7 @@ import { transformMetadata } from './graphql';
|
||||
import { LocalBuildMode, runLocalBuildAsync } from './local';
|
||||
import { collectMetadataAsync } from './metadata';
|
||||
import { printDeprecationWarnings } from './utils/printBuildInfo';
|
||||
import {
|
||||
LocalFile,
|
||||
makeProjectMetadataFileAsync,
|
||||
makeProjectTarballAsync,
|
||||
reviewAndCommitChangesAsync,
|
||||
} from './utils/repository';
|
||||
import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository';
|
||||
import { BuildEvent } from '../analytics/AnalyticsManager';
|
||||
import { withAnalyticsAsync } from '../analytics/common';
|
||||
import { getExpoWebsiteBaseUrl } from '../api';
|
||||
@@ -63,11 +49,9 @@ import {
|
||||
appPlatformEmojis,
|
||||
requestedPlatformDisplayNames,
|
||||
} from '../platform';
|
||||
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
|
||||
import { uploadFileAtPathToGCSAsync } from '../uploads';
|
||||
import { formatBytes } from '../utils/files';
|
||||
import { printJsonOnlyOutput } from '../utils/json';
|
||||
import { getTmpDirectory } from '../utils/paths';
|
||||
import { createProgressTracker } from '../utils/progress';
|
||||
import { sleepAsync } from '../utils/promise';
|
||||
|
||||
@@ -81,14 +65,14 @@ export interface JobData<Credentials> {
|
||||
projectArchive: ArchiveSource;
|
||||
}
|
||||
|
||||
interface Builder<TPlatform extends Platform, Credentials, TJob extends BuildJob> {
|
||||
interface Builder<TPlatform extends Platform, Credentials, TJob extends Job> {
|
||||
ctx: BuildContext<TPlatform>;
|
||||
|
||||
ensureCredentialsAsync(
|
||||
ctx: BuildContext<TPlatform>
|
||||
): Promise<CredentialsResult<Credentials> | undefined>;
|
||||
syncProjectConfigurationAsync(ctx: BuildContext<TPlatform>): Promise<void>;
|
||||
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<BuildJob>;
|
||||
prepareJobAsync(ctx: BuildContext<TPlatform>, jobData: JobData<Credentials>): Promise<Job>;
|
||||
sendBuildRequestAsync(
|
||||
appId: string,
|
||||
job: TJob,
|
||||
@@ -113,7 +97,7 @@ function resolveBuildParamsInput<T extends Platform>(
|
||||
export async function prepareBuildRequestForPlatformAsync<
|
||||
TPlatform extends Platform,
|
||||
Credentials,
|
||||
TJob extends BuildJob,
|
||||
TJob extends Job,
|
||||
>(builder: Builder<TPlatform, Credentials, TJob>): Promise<BuildRequestSender> {
|
||||
const { ctx } = builder;
|
||||
const credentialsResult = await withAnalyticsAsync(
|
||||
@@ -149,10 +133,9 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
|
||||
let projectArchive: ArchiveSource | undefined;
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
const projectPath = (await makeProjectTarballAsync(ctx.vcsClient)).path;
|
||||
projectArchive = {
|
||||
type: ArchiveSourceType.PATH,
|
||||
path: projectPath,
|
||||
path: (await makeProjectTarballAsync(ctx.vcsClient)).path,
|
||||
};
|
||||
} else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||
projectArchive = {
|
||||
@@ -162,13 +145,12 @@ export async function prepareBuildRequestForPlatformAsync<
|
||||
} else if (!ctx.localBuildOptions.localBuildMode) {
|
||||
projectArchive = {
|
||||
type: ArchiveSourceType.GCS,
|
||||
...(await uploadProjectAsync(ctx)),
|
||||
bucketKey: await uploadProjectAsync(ctx),
|
||||
};
|
||||
}
|
||||
assert(projectArchive);
|
||||
|
||||
const runtimeMetadata = await createAndMaybeUploadFingerprintAsync(ctx);
|
||||
const metadata = await collectMetadataAsync(ctx, runtimeMetadata);
|
||||
const metadata = await collectMetadataAsync(ctx);
|
||||
const buildParams = resolveBuildParamsInput(ctx, metadata);
|
||||
const job = await builder.prepareJobAsync(ctx, {
|
||||
projectArchive,
|
||||
@@ -249,10 +231,7 @@ export function handleBuildRequestError(error: any, platform: Platform): never {
|
||||
|
||||
async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
ctx: BuildContext<TPlatform>
|
||||
): Promise<{
|
||||
bucketKey: string;
|
||||
metadataLocation?: string;
|
||||
}> {
|
||||
): Promise<string> {
|
||||
let projectTarballPath;
|
||||
try {
|
||||
return await withAnalyticsAsync(
|
||||
@@ -281,27 +260,21 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
}
|
||||
|
||||
projectTarballPath = projectTarball.path;
|
||||
const [bucketKey, { metadataLocation }] = await Promise.all([
|
||||
uploadFileAtPathToGCSAsync(
|
||||
ctx.graphqlClient,
|
||||
UploadSessionType.EasBuildGcsProjectSources,
|
||||
projectTarball.path,
|
||||
createProgressTracker({
|
||||
total: projectTarball.size,
|
||||
message: ratio =>
|
||||
`Uploading to EAS Build (${formatBytes(
|
||||
projectTarball.size * ratio
|
||||
)} / ${formatBytes(projectTarball.size)})`,
|
||||
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
|
||||
})
|
||||
),
|
||||
uploadMetadataFileAsync<TPlatform>(projectTarball, ctx),
|
||||
]);
|
||||
|
||||
if (metadataLocation) {
|
||||
return { bucketKey, metadataLocation };
|
||||
}
|
||||
return { bucketKey };
|
||||
const bucketKey = await uploadFileAtPathToGCSAsync(
|
||||
ctx.graphqlClient,
|
||||
UploadSessionType.EasBuildGcsProjectSources,
|
||||
projectTarball.path,
|
||||
createProgressTracker({
|
||||
total: projectTarball.size,
|
||||
message: ratio =>
|
||||
`Uploading to EAS Build (${formatBytes(projectTarball.size * ratio)} / ${formatBytes(
|
||||
projectTarball.size
|
||||
)})`,
|
||||
completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`,
|
||||
})
|
||||
);
|
||||
return bucketKey;
|
||||
},
|
||||
{
|
||||
attemptEvent: BuildEvent.PROJECT_UPLOAD_ATTEMPT,
|
||||
@@ -312,11 +285,9 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
);
|
||||
} 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 {
|
||||
if (projectTarballPath) {
|
||||
@@ -325,41 +296,7 @@ async function uploadProjectAsync<TPlatform extends Platform>(
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadMetadataFileAsync<TPlatform extends Platform>(
|
||||
projectTarball: LocalFile,
|
||||
ctx: BuildContext<TPlatform>
|
||||
): Promise<{ metadataLocation: string | null }> {
|
||||
let projectMetadataFile: LocalFile | null = null;
|
||||
try {
|
||||
projectMetadataFile = await makeProjectMetadataFileAsync(projectTarball.path);
|
||||
|
||||
const metadataLocation = await uploadFileAtPathToGCSAsync(
|
||||
ctx.graphqlClient,
|
||||
UploadSessionType.EasBuildGcsProjectMetadata,
|
||||
projectMetadataFile.path
|
||||
);
|
||||
return { metadataLocation };
|
||||
} catch (err: any) {
|
||||
let errMessage = 'Failed to upload metadata to EAS Build';
|
||||
|
||||
if (err.message) {
|
||||
errMessage += `\n\nReason: ${err.message}`;
|
||||
}
|
||||
|
||||
Log.warn(errMessage);
|
||||
return { metadataLocation: null };
|
||||
} finally {
|
||||
if (projectMetadataFile) {
|
||||
await fs.remove(projectMetadataFile.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendBuildRequestAsync<
|
||||
TPlatform extends Platform,
|
||||
Credentials,
|
||||
TJob extends BuildJob,
|
||||
>(
|
||||
async function sendBuildRequestAsync<TPlatform extends Platform, Credentials, TJob extends Job>(
|
||||
builder: Builder<TPlatform, Credentials, TJob>,
|
||||
job: TJob,
|
||||
metadata: Metadata,
|
||||
@@ -655,77 +592,3 @@ function formatAccountSubscriptionsUrl(accountName: string): string {
|
||||
getExpoWebsiteBaseUrl()
|
||||
).toString();
|
||||
}
|
||||
|
||||
async function createAndMaybeUploadFingerprintAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
): Promise<{
|
||||
runtimeVersion?: string;
|
||||
fingerprintSource?: Metadata['fingerprintSource'];
|
||||
}> {
|
||||
const resolvedRuntimeVersion = await resolveRuntimeVersionAsync({
|
||||
exp: ctx.exp,
|
||||
platform: ctx.platform,
|
||||
workflow: ctx.workflow,
|
||||
projectDir: ctx.projectDir,
|
||||
env: ctx.buildProfile.env,
|
||||
cwd: ctx.projectDir,
|
||||
});
|
||||
|
||||
/**
|
||||
* It's ok for fingerprintSources to be empty
|
||||
* fingerprintSources only exist if the project is using runtimeVersion.policy: fingerprint
|
||||
*/
|
||||
if (!resolvedRuntimeVersion?.fingerprintSources) {
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
await fs.mkdirp(getTmpDirectory());
|
||||
const fingerprintLocation = path.join(getTmpDirectory(), `${uuidv4()}-runtime-fingerprint.json`);
|
||||
|
||||
await fs.writeJSON(fingerprintLocation, {
|
||||
hash: resolvedRuntimeVersion.runtimeVersion,
|
||||
sources: resolvedRuntimeVersion.fingerprintSources,
|
||||
});
|
||||
|
||||
if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
|
||||
fingerprintSource: {
|
||||
type: FingerprintSourceType.PATH,
|
||||
path: fingerprintLocation,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let fingerprintGCSBucketKey = undefined;
|
||||
try {
|
||||
fingerprintGCSBucketKey = await uploadFileAtPathToGCSAsync(
|
||||
ctx.graphqlClient,
|
||||
UploadSessionType.EasUpdateFingerprint,
|
||||
fingerprintLocation
|
||||
);
|
||||
} catch (err: any) {
|
||||
let errMessage = 'Failed to upload fingerprint to EAS';
|
||||
|
||||
if (err.message) {
|
||||
errMessage += `\n\nReason: ${err.message}`;
|
||||
}
|
||||
|
||||
Log.warn(errMessage);
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
|
||||
};
|
||||
} finally {
|
||||
await fs.remove(fingerprintLocation);
|
||||
}
|
||||
|
||||
return {
|
||||
runtimeVersion: resolvedRuntimeVersion?.runtimeVersion ?? undefined,
|
||||
fingerprintSource: {
|
||||
type: FingerprintSourceType.GCS,
|
||||
bucketKey: fingerprintGCSBucketKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -40,7 +40,7 @@ async function configureAsync({
|
||||
nonInteractive,
|
||||
vcsClient,
|
||||
}: ConfigureParams): Promise<void> {
|
||||
await maybeBailOnRepoStatusAsync(vcsClient, nonInteractive);
|
||||
await maybeBailOnRepoStatusAsync(vcsClient);
|
||||
|
||||
await createEasJsonAsync(projectDir, vcsClient);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson } from '@expo/eas-json';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import { NodePackageManager } from '@expo/package-manager';
|
||||
|
||||
import { LocalBuildOptions } from './local';
|
||||
@@ -61,6 +60,4 @@ export interface BuildContext<T extends Platform> {
|
||||
developmentClient: boolean;
|
||||
requiredPackageManager: NodePackageManager['name'] | null;
|
||||
vcsClient: Client;
|
||||
loggerLevel?: LoggerLevel;
|
||||
repack: boolean;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { BuildProfile, EasJson, ResourceClass } from '@expo/eas-json';
|
||||
import JsonFile from '@expo/json-file';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import { resolvePackageManager } from '@expo/package-manager';
|
||||
import getenv from 'getenv';
|
||||
import resolveFrom from 'resolve-from';
|
||||
@@ -10,7 +9,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { createAndroidContextAsync } from './android/build';
|
||||
import { BuildContext, CommonContext } from './context';
|
||||
import { createIosContextAsync } from './ios/build';
|
||||
import { LocalBuildMode, LocalBuildOptions } from './local';
|
||||
import { LocalBuildOptions } from './local';
|
||||
import { resolveBuildResourceClassAsync } from './utils/resourceClass';
|
||||
import { Analytics, AnalyticsEventProperties, BuildEvent } from '../analytics/AnalyticsManager';
|
||||
import { DynamicConfigContextFn } from '../commandUtils/context/DynamicProjectConfigContextField';
|
||||
@@ -40,9 +39,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
vcsClient,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
customBuildConfigMetadata,
|
||||
buildLoggerLevel,
|
||||
freezeCredentials,
|
||||
repack,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
buildProfile: BuildProfile<T>;
|
||||
@@ -61,9 +57,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
vcsClient: Client;
|
||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn;
|
||||
customBuildConfigMetadata?: CustomBuildConfigMetadata;
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||
const projectName = exp.slug;
|
||||
@@ -90,7 +83,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
env: buildProfile.env,
|
||||
easJsonCliConfig,
|
||||
vcsClient,
|
||||
freezeCredentials,
|
||||
});
|
||||
|
||||
const devClientProperties = getDevClientEventProperties({
|
||||
@@ -108,7 +100,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
...devClientProperties,
|
||||
no_wait: noWait,
|
||||
run_from_ci: runFromCI,
|
||||
local: localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN,
|
||||
};
|
||||
analytics.logEvent(BuildEvent.BUILD_COMMAND, analyticsEventProperties);
|
||||
|
||||
@@ -145,8 +136,6 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
customBuildConfigMetadata,
|
||||
developmentClient,
|
||||
requiredPackageManager,
|
||||
loggerLevel: buildLoggerLevel,
|
||||
repack,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
const common = commonContext as CommonContext<Platform.ANDROID>;
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
Metadata,
|
||||
Workflow,
|
||||
} from '@expo/eas-build-job';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import assert from 'assert';
|
||||
|
||||
import {
|
||||
@@ -19,7 +18,6 @@ import {
|
||||
BuildTrigger as GraphQLBuildTrigger,
|
||||
ProjectArchiveSourceInput,
|
||||
ProjectArchiveSourceType,
|
||||
WorkerLoggerLevel,
|
||||
} from '../graphql/generated';
|
||||
|
||||
export function transformProjectArchive(archiveSource: ArchiveSource): ProjectArchiveSourceInput {
|
||||
@@ -27,7 +25,6 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
|
||||
return {
|
||||
type: ProjectArchiveSourceType.Gcs,
|
||||
bucketKey: archiveSource.bucketKey,
|
||||
metadataLocation: archiveSource.metadataLocation,
|
||||
};
|
||||
} else if (archiveSource.type === ArchiveSourceType.URL) {
|
||||
return {
|
||||
@@ -42,7 +39,6 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr
|
||||
export function transformMetadata(metadata: Metadata): BuildMetadataInput {
|
||||
return {
|
||||
...metadata,
|
||||
fingerprintSource: metadata.fingerprintSource as BuildMetadataInput['fingerprintSource'],
|
||||
credentialsSource:
|
||||
metadata.credentialsSource && transformCredentialsSource(metadata.credentialsSource),
|
||||
distribution: metadata.distribution && transformDistribution(metadata.distribution),
|
||||
@@ -95,7 +91,6 @@ const buildModeToGraphQLBuildMode: Record<BuildMode, GraphQLBuildMode> = {
|
||||
[BuildMode.BUILD]: GraphQLBuildMode.Build,
|
||||
[BuildMode.CUSTOM]: GraphQLBuildMode.Custom,
|
||||
[BuildMode.RESIGN]: GraphQLBuildMode.Resign,
|
||||
[BuildMode.REPACK]: GraphQLBuildMode.Repack,
|
||||
};
|
||||
|
||||
export function transformBuildMode(buildMode: BuildMode): GraphQLBuildMode {
|
||||
@@ -112,12 +107,3 @@ export function transformBuildTrigger(buildTrigger: BuildTrigger): GraphQLBuildT
|
||||
}
|
||||
throw new Error('Unknown build trigger');
|
||||
}
|
||||
|
||||
export const loggerLevelToGraphQLWorkerLoggerLevel: Record<LoggerLevel, WorkerLoggerLevel> = {
|
||||
[LoggerLevel.TRACE]: WorkerLoggerLevel.Trace,
|
||||
[LoggerLevel.DEBUG]: WorkerLoggerLevel.Debug,
|
||||
[LoggerLevel.INFO]: WorkerLoggerLevel.Info,
|
||||
[LoggerLevel.WARN]: WorkerLoggerLevel.Warn,
|
||||
[LoggerLevel.ERROR]: WorkerLoggerLevel.Error,
|
||||
[LoggerLevel.FATAL]: WorkerLoggerLevel.Fatal,
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import os from 'os';
|
||||
import type { XCBuildConfiguration } from 'xcode';
|
||||
|
||||
import { readPlistAsync } from '../../../utils/plist';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import { getVcsClient } from '../../../vcs';
|
||||
import {
|
||||
BumpStrategy,
|
||||
bumpVersionAsync,
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
const vcsClient = resolveVcsClient();
|
||||
const vcsClient = getVcsClient();
|
||||
|
||||
const getXCBuildConfigurationFromPbxproj = jest.spyOn(
|
||||
IOSConfig.Target,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Ios, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Ios, Job, Metadata, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { AppVersionSource } from '@expo/eas-json';
|
||||
|
||||
import { ensureIosCredentialsAsync } from './credentials';
|
||||
@@ -80,7 +80,7 @@ export async function prepareIosBuildAsync(
|
||||
return ensureIosCredentialsAsync(ctx, ctx.ios.targets);
|
||||
},
|
||||
syncProjectConfigurationAsync: async () => {
|
||||
await syncProjectConfigurationAsync({
|
||||
await syncProjectConfigurationAsync(ctx.graphqlClient, {
|
||||
projectDir: ctx.projectDir,
|
||||
exp: ctx.exp,
|
||||
targets: ctx.ios.targets,
|
||||
@@ -88,14 +88,14 @@ export async function prepareIosBuildAsync(
|
||||
ctx.easJsonCliConfig?.appVersionSource === AppVersionSource.REMOTE
|
||||
? false
|
||||
: ctx.buildProfile.autoIncrement,
|
||||
projectId: ctx.projectId,
|
||||
vcsClient: ctx.vcsClient,
|
||||
env: ctx.buildProfile.env,
|
||||
});
|
||||
},
|
||||
prepareJobAsync: async (
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData<IosCredentials>
|
||||
): Promise<Ios.Job> => {
|
||||
): Promise<Job> => {
|
||||
return await prepareJobAsync(ctx, {
|
||||
...jobData,
|
||||
buildScheme: ctx.ios.xcodeBuildContext.buildScheme,
|
||||
|
||||
@@ -3,7 +3,6 @@ import nullthrows from 'nullthrows';
|
||||
|
||||
import { IosJobInput, IosJobSecretsInput } from '../../graphql/generated';
|
||||
import {
|
||||
loggerLevelToGraphQLWorkerLoggerLevel,
|
||||
transformBuildMode,
|
||||
transformBuildTrigger,
|
||||
transformProjectArchive,
|
||||
@@ -16,6 +15,7 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
triggeredBy: transformBuildTrigger(job.triggeredBy),
|
||||
projectArchive: transformProjectArchive(job.projectArchive),
|
||||
projectRootDirectory: nullthrows(job.projectRootDirectory),
|
||||
releaseChannel: job.releaseChannel,
|
||||
updates: job.updates,
|
||||
secrets: job.secrets ? transformIosSecrets(job.secrets) : undefined,
|
||||
builderEnvironment: job.builderEnvironment,
|
||||
@@ -31,9 +31,6 @@ export function transformJob(job: Ios.Job): IosJobInput {
|
||||
experimental: job.experimental,
|
||||
mode: transformBuildMode(job.mode),
|
||||
customBuildConfig: job.customBuildConfig,
|
||||
loggerLevel: job.loggerLevel
|
||||
? loggerLevelToGraphQLWorkerLoggerLevel[job.loggerLevel]
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import {
|
||||
BuildMode,
|
||||
BuildTrigger,
|
||||
Ios,
|
||||
Job,
|
||||
Platform,
|
||||
sanitizeBuildJob,
|
||||
sanitizeJob,
|
||||
} from '@expo/eas-build-job';
|
||||
import { BuildProfile } from '@expo/eas-json';
|
||||
import nullthrows from 'nullthrows';
|
||||
@@ -13,7 +14,7 @@ import slash from 'slash';
|
||||
|
||||
import { IosCredentials, TargetCredentials } from '../../credentials/ios/types';
|
||||
import { IosJobSecretsInput } from '../../graphql/generated';
|
||||
import { getCustomBuildConfigPathForJob } from '../../project/customBuildConfig';
|
||||
import { getCustomBuildConfigPath } from '../../project/customBuildConfig';
|
||||
import { getUsername } from '../../project/projectUtils';
|
||||
import { BuildContext } from '../context';
|
||||
|
||||
@@ -31,7 +32,7 @@ const cacheDefaults = {
|
||||
export async function prepareJobAsync(
|
||||
ctx: BuildContext<Platform.IOS>,
|
||||
jobData: JobData
|
||||
): Promise<Ios.Job> {
|
||||
): Promise<Job> {
|
||||
const username = getUsername(ctx.exp, ctx.user);
|
||||
const buildProfile: BuildProfile<Platform.IOS> = ctx.buildProfile;
|
||||
const projectRootDirectory =
|
||||
@@ -45,18 +46,9 @@ export async function prepareJobAsync(
|
||||
}
|
||||
|
||||
const maybeCustomBuildConfigPath = buildProfile.config
|
||||
? getCustomBuildConfigPathForJob(buildProfile.config)
|
||||
? getCustomBuildConfigPath(buildProfile.config)
|
||||
: undefined;
|
||||
|
||||
let buildMode;
|
||||
if (ctx.repack) {
|
||||
buildMode = BuildMode.REPACK;
|
||||
} else if (buildProfile.config) {
|
||||
buildMode = BuildMode.CUSTOM;
|
||||
} else {
|
||||
buildMode = BuildMode.BUILD;
|
||||
}
|
||||
|
||||
const job: Ios.Job = {
|
||||
type: ctx.workflow,
|
||||
platform: Platform.IOS,
|
||||
@@ -71,6 +63,7 @@ export async function prepareJobAsync(
|
||||
bundler: buildProfile.bundler,
|
||||
cocoapods: buildProfile.cocoapods,
|
||||
fastlane: buildProfile.fastlane,
|
||||
expoCli: buildProfile.expoCli,
|
||||
env: buildProfile.env,
|
||||
},
|
||||
cache: {
|
||||
@@ -81,6 +74,7 @@ export async function prepareJobAsync(
|
||||
secrets: {
|
||||
buildCredentials,
|
||||
},
|
||||
releaseChannel: buildProfile.releaseChannel,
|
||||
updates: { channel: buildProfile.channel },
|
||||
developmentClient: buildProfile.developmentClient,
|
||||
simulator: buildProfile.simulator,
|
||||
@@ -97,21 +91,15 @@ export async function prepareJobAsync(
|
||||
experimental: {
|
||||
prebuildCommand: buildProfile.prebuildCommand,
|
||||
},
|
||||
mode: buildMode,
|
||||
mode: buildProfile.config ? BuildMode.CUSTOM : BuildMode.BUILD,
|
||||
triggeredBy: BuildTrigger.EAS_CLI,
|
||||
...(maybeCustomBuildConfigPath && {
|
||||
customBuildConfig: {
|
||||
path: maybeCustomBuildConfigPath,
|
||||
},
|
||||
}),
|
||||
...(ctx.repack && {
|
||||
customBuildConfig: {
|
||||
path: '__eas/repack.yml',
|
||||
},
|
||||
}),
|
||||
loggerLevel: ctx.loggerLevel,
|
||||
};
|
||||
return sanitizeBuildJob(job) as Ios.Job;
|
||||
return sanitizeJob(job);
|
||||
}
|
||||
|
||||
export function prepareCredentialsToResign(credentials: IosCredentials): IosJobSecretsInput {
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { IosVersionAutoIncrement } from '@expo/eas-json';
|
||||
|
||||
import { BumpStrategy, bumpVersionAsync, bumpVersionInAppJsonAsync } from './version';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { Target } from '../../credentials/ios/types';
|
||||
import { isExpoUpdatesInstalled } from '../../project/projectUtils';
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { syncUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
export async function syncProjectConfigurationAsync({
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
localAutoIncrement,
|
||||
vcsClient,
|
||||
env,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
targets: Target[];
|
||||
localAutoIncrement?: IosVersionAutoIncrement;
|
||||
vcsClient: Client;
|
||||
env: Env | undefined;
|
||||
}): Promise<void> {
|
||||
export async function syncProjectConfigurationAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
projectDir,
|
||||
exp,
|
||||
targets,
|
||||
localAutoIncrement,
|
||||
projectId,
|
||||
vcsClient,
|
||||
}: {
|
||||
projectDir: string;
|
||||
exp: ExpoConfig;
|
||||
targets: Target[];
|
||||
localAutoIncrement?: IosVersionAutoIncrement;
|
||||
projectId: string;
|
||||
vcsClient: Client;
|
||||
}
|
||||
): Promise<void> {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
|
||||
const versionBumpStrategy = resolveVersionBumpStrategy(localAutoIncrement ?? false);
|
||||
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
if (isExpoUpdatesInstalled(projectDir)) {
|
||||
await syncUpdatesConfigurationAsync({ vcsClient, projectDir, exp, workflow, env });
|
||||
await syncUpdatesConfigurationAsync(graphqlClient, vcsClient, projectDir, exp, projectId);
|
||||
}
|
||||
await bumpVersionAsync({ projectDir, exp, bumpStrategy: versionBumpStrategy, targets });
|
||||
} else {
|
||||
|
||||
@@ -342,9 +342,7 @@ export async function resolveRemoteBuildNumberAsync(
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
storeVersion: localShortVersion ?? '1.0.0',
|
||||
buildVersion: currentBuildVersion,
|
||||
runtimeVersion:
|
||||
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
|
||||
undefined,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(`Initialized buildNumber with ${chalk.bold(currentBuildVersion)}.`);
|
||||
} catch (err) {
|
||||
@@ -366,9 +364,7 @@ export async function resolveRemoteBuildNumberAsync(
|
||||
applicationIdentifier: applicationTarget.bundleIdentifier,
|
||||
storeVersion: localShortVersion ?? '1.0.0',
|
||||
buildVersion: nextBuildVersion,
|
||||
runtimeVersion:
|
||||
(await Updates.getRuntimeVersionNullableAsync(projectDir, exp, Platform.IOS)) ??
|
||||
undefined,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(exp, Platform.IOS) ?? undefined,
|
||||
});
|
||||
spinner.succeed(
|
||||
`Incremented buildNumber from ${chalk.bold(currentBuildVersion)} to ${chalk.bold(
|
||||
|
||||
@@ -6,7 +6,7 @@ import semver from 'semver';
|
||||
import { ora } from '../ora';
|
||||
|
||||
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.129';
|
||||
const PLUGIN_PACKAGE_VERSION = '1.0.56';
|
||||
|
||||
export enum LocalBuildMode {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FingerprintSource, Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { Updates } from '@expo/config-plugins';
|
||||
import { Metadata, Platform, sanitizeMetadata } from '@expo/eas-build-job';
|
||||
import { IosEnterpriseProvisioning } from '@expo/eas-json';
|
||||
import fs from 'fs-extra';
|
||||
import resolveFrom from 'resolve-from';
|
||||
@@ -7,22 +8,26 @@ import { maybeResolveVersionsAsync as maybeResolveAndroidVersionsAsync } from '.
|
||||
import { BuildContext } from './context';
|
||||
import { maybeResolveVersionsAsync as maybeResolveIosVersionsAsync } from './ios/version';
|
||||
import { LocalBuildMode } from './local';
|
||||
import { BuildDistributionType } from './types';
|
||||
import Log from '../log';
|
||||
import { getUsername, isExpoUpdatesInstalled } from '../project/projectUtils';
|
||||
import { readChannelSafelyAsync as readAndroidChannelSafelyAsync } from '../update/android/UpdatesModule';
|
||||
import { readChannelSafelyAsync as readIosChannelSafelyAsync } from '../update/ios/UpdatesModule';
|
||||
import {
|
||||
readChannelSafelyAsync as readAndroidChannelSafelyAsync,
|
||||
readReleaseChannelSafelyAsync as readAndroidReleaseChannelSafelyAsync,
|
||||
} from '../update/android/UpdatesModule';
|
||||
import {
|
||||
readChannelSafelyAsync as readIosChannelSafelyAsync,
|
||||
readReleaseChannelSafelyAsync as readIosReleaseChannelSafelyAsync,
|
||||
} from '../update/ios/UpdatesModule';
|
||||
import { easCliVersion } from '../utils/easCli';
|
||||
|
||||
export async function collectMetadataAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>,
|
||||
runtimeMetadata: {
|
||||
runtimeVersion?: string | undefined;
|
||||
fingerprintSource?: FingerprintSource | undefined;
|
||||
}
|
||||
ctx: BuildContext<T>
|
||||
): Promise<Metadata> {
|
||||
const channelObject = await resolveChannelAsync(ctx);
|
||||
const distribution = ctx.buildProfile.distribution ?? BuildDistributionType.STORE;
|
||||
const channelOrReleaseChannel = await resolveChannelOrReleaseChannelAsync(ctx);
|
||||
const distribution =
|
||||
('simulator' in ctx.buildProfile && ctx.buildProfile.simulator
|
||||
? 'simulator'
|
||||
: ctx.buildProfile.distribution) ?? 'store';
|
||||
const metadata: Metadata = {
|
||||
trackingContext: ctx.analyticsEventProperties,
|
||||
...(await maybeResolveVersionsAsync(ctx)),
|
||||
@@ -30,10 +35,9 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
workflow: ctx.workflow,
|
||||
credentialsSource: ctx.buildProfile.credentialsSource,
|
||||
sdkVersion: ctx.exp.sdkVersion,
|
||||
runtimeVersion: runtimeMetadata?.runtimeVersion,
|
||||
fingerprintSource: runtimeMetadata?.fingerprintSource,
|
||||
runtimeVersion: Updates.getRuntimeVersionNullable(ctx.exp, ctx.platform) ?? undefined,
|
||||
reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir),
|
||||
...channelObject,
|
||||
...channelOrReleaseChannel,
|
||||
distribution,
|
||||
appName: ctx.exp.name,
|
||||
appIdentifier: resolveAppIdentifier(ctx),
|
||||
@@ -60,8 +64,6 @@ export async function collectMetadataAsync<T extends Platform>(
|
||||
requiredPackageManager: ctx.requiredPackageManager ?? undefined,
|
||||
selectedImage: ctx.buildProfile.image,
|
||||
customNodeVersion: ctx.buildProfile.node,
|
||||
environment: ctx.buildProfile.environment,
|
||||
simulator: 'simulator' in ctx.buildProfile && ctx.buildProfile.simulator,
|
||||
};
|
||||
return sanitizeMetadata(metadata);
|
||||
}
|
||||
@@ -112,21 +114,39 @@ function resolveAppIdentifier<T extends Platform>(ctx: BuildContext<T>): string
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveChannelAsync<T extends Platform>(
|
||||
async function resolveChannelOrReleaseChannelAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
): Promise<{ channel: string } | null> {
|
||||
): Promise<{ channel: string } | { releaseChannel: string } | null> {
|
||||
if (!isExpoUpdatesInstalled(ctx.projectDir)) {
|
||||
return null;
|
||||
}
|
||||
if (ctx.buildProfile.channel) {
|
||||
return { channel: ctx.buildProfile.channel };
|
||||
}
|
||||
if (ctx.buildProfile.releaseChannel) {
|
||||
return { releaseChannel: ctx.buildProfile.releaseChannel };
|
||||
}
|
||||
const channel = await getNativeChannelAsync(ctx);
|
||||
if (channel) {
|
||||
return { channel };
|
||||
}
|
||||
const releaseChannel = await getNativeReleaseChannelAsync(ctx);
|
||||
return { releaseChannel };
|
||||
}
|
||||
|
||||
return null;
|
||||
async function getNativeReleaseChannelAsync<T extends Platform>(
|
||||
ctx: BuildContext<T>
|
||||
): Promise<string> {
|
||||
switch (ctx.platform) {
|
||||
case Platform.ANDROID: {
|
||||
return (await readAndroidReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
|
||||
}
|
||||
case Platform.IOS: {
|
||||
return (await readIosReleaseChannelSafelyAsync(ctx.projectDir)) ?? 'default';
|
||||
}
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
|
||||
async function getNativeChannelAsync<T extends Platform>(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Env, Platform, Workflow } from '@expo/eas-build-job';
|
||||
import { Platform, Workflow } from '@expo/eas-build-job';
|
||||
import {
|
||||
AppVersionSource,
|
||||
BuildProfile,
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
ResourceClass,
|
||||
SubmitProfile,
|
||||
} from '@expo/eas-json';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import assert from 'assert';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
@@ -20,7 +19,7 @@ import { ensureProjectConfiguredAsync } from './configure';
|
||||
import { BuildContext } from './context';
|
||||
import { createBuildContextAsync } from './createContext';
|
||||
import { prepareIosBuildAsync } from './ios/build';
|
||||
import { LocalBuildMode, LocalBuildOptions } from './local';
|
||||
import { LocalBuildOptions } from './local';
|
||||
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from './utils/devClient';
|
||||
import { printBuildResults, printLogsUrls } from './utils/printBuildInfo';
|
||||
import { ensureRepoIsCleanAsync } from './utils/repository';
|
||||
@@ -71,6 +70,7 @@ import {
|
||||
} from '../submit/submit';
|
||||
import { printSubmissionDetailsUrls } from '../submit/utils/urls';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../update/configure';
|
||||
import { validateBuildProfileConfigMatchesProjectConfigAsync } from '../update/utils';
|
||||
import { Actor } from '../user/User';
|
||||
import { downloadAndMaybeExtractAppAsync } from '../utils/download';
|
||||
import { truthy } from '../utils/expodash/filter';
|
||||
@@ -93,9 +93,6 @@ export interface BuildFlags {
|
||||
localBuildOptions: LocalBuildOptions;
|
||||
resourceClass?: ResourceClass;
|
||||
message?: string;
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync(
|
||||
@@ -106,9 +103,7 @@ export async function runBuildAndSubmitAsync(
|
||||
flags: BuildFlags,
|
||||
actor: Actor,
|
||||
getDynamicPrivateProjectConfigAsync: DynamicConfigContextFn
|
||||
): Promise<{
|
||||
buildIds: string[];
|
||||
}> {
|
||||
): Promise<void> {
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
|
||||
|
||||
@@ -137,32 +132,6 @@ export async function runBuildAndSubmitAsync(
|
||||
}. ${learnMore('https://docs.expo.dev/build-reference/variables/')}`
|
||||
);
|
||||
|
||||
for (const buildProfile of buildProfiles) {
|
||||
if (buildProfile.profile.image && ['default', 'stable'].includes(buildProfile.profile.image)) {
|
||||
Log.warn(
|
||||
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${buildProfile.profile.image}". This tag is deprecated and will be removed in the future. Use other images or tags listed here: https://docs.expo.dev/build-reference/infrastructure/`
|
||||
);
|
||||
} else if (
|
||||
buildProfile.profile.image &&
|
||||
[
|
||||
'ubuntu-20.04-jdk-11-ndk-r19c',
|
||||
'ubuntu-20.04-jdk-8-ndk-r19c',
|
||||
'ubuntu-20.04-jdk-11-ndk-r21e',
|
||||
'ubuntu-20.04-jdk-8-ndk-r21e',
|
||||
'ubuntu-22.04-jdk-8-ndk-r21e',
|
||||
'ubuntu-20.04-jdk-11-ndk-r23b',
|
||||
].includes(buildProfile.profile.image)
|
||||
) {
|
||||
Log.warn(
|
||||
`The "image" field in the build profile "${buildProfile.profileName}" is set to "${
|
||||
buildProfile.profile.image
|
||||
}". This image is deprecated and will be removed on September 1st, 2024. ${learnMore(
|
||||
'https://expo.dev/changelog/2024/07-12-eas-build-upcoming-android-images-updates'
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await ensureExpoDevClientInstalledForDevClientBuildsAsync({
|
||||
projectDir,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
@@ -211,25 +180,13 @@ export async function runBuildAndSubmitAsync(
|
||||
buildCtxByPlatform[platform] = buildCtx;
|
||||
}
|
||||
|
||||
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) {
|
||||
return {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
};
|
||||
if (flags.localBuildOptions.localBuildMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) {
|
||||
const startedBuild = await BuildQuery.byIdAsync(
|
||||
graphqlClient,
|
||||
nullthrows(process.env.EAS_BUILD_ID, 'EAS_BUILD_ID is not defined')
|
||||
);
|
||||
startedBuilds.push({ build: startedBuild, buildProfile: buildProfiles[0] });
|
||||
}
|
||||
|
||||
if (!flags.localBuildOptions.localBuildMode) {
|
||||
Log.newLine();
|
||||
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
|
||||
Log.newLine();
|
||||
}
|
||||
Log.newLine();
|
||||
printLogsUrls(startedBuilds.map(startedBuild => startedBuild.build));
|
||||
Log.newLine();
|
||||
|
||||
const submissions: SubmissionFragment[] = [];
|
||||
if (flags.autoSubmit) {
|
||||
@@ -254,7 +211,6 @@ export async function runBuildAndSubmitAsync(
|
||||
buildProfile: startedBuild.buildProfile.profile,
|
||||
submitProfile,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
selectedSubmitProfileName: flags.submitProfile,
|
||||
});
|
||||
startedBuild.build = await BuildQuery.withSubmissionsByIdAsync(
|
||||
graphqlClient,
|
||||
@@ -263,26 +219,16 @@ export async function runBuildAndSubmitAsync(
|
||||
submissions.push(submission);
|
||||
}
|
||||
|
||||
if (!flags.localBuildOptions.localBuildMode) {
|
||||
Log.newLine();
|
||||
printSubmissionDetailsUrls(submissions);
|
||||
Log.newLine();
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.localBuildOptions.localBuildMode) {
|
||||
return {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
};
|
||||
Log.newLine();
|
||||
printSubmissionDetailsUrls(submissions);
|
||||
Log.newLine();
|
||||
}
|
||||
|
||||
if (!flags.wait) {
|
||||
if (flags.json) {
|
||||
printJsonOnlyOutput(startedBuilds.map(buildInfo => buildInfo.build));
|
||||
}
|
||||
return {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const { accountName } = Object.values(buildCtxByPlatform)[0];
|
||||
@@ -323,9 +269,6 @@ export async function runBuildAndSubmitAsync(
|
||||
}
|
||||
exitWithNonZeroCodeIfSomeSubmissionsDidntFinish(completedSubmissions);
|
||||
}
|
||||
return {
|
||||
buildIds: startedBuilds.map(({ build }) => build.id),
|
||||
};
|
||||
}
|
||||
|
||||
async function prepareAndStartBuildAsync({
|
||||
@@ -371,9 +314,6 @@ async function prepareAndStartBuildAsync({
|
||||
vcsClient,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
customBuildConfigMetadata,
|
||||
buildLoggerLevel: flags.buildLoggerLevel,
|
||||
freezeCredentials: flags.freezeCredentials,
|
||||
repack: flags.repack,
|
||||
});
|
||||
|
||||
if (moreBuilds) {
|
||||
@@ -386,8 +326,15 @@ async function prepareAndStartBuildAsync({
|
||||
);
|
||||
}
|
||||
|
||||
await validateBuildProfileConfigMatchesProjectConfigAsync(
|
||||
buildCtx.exp,
|
||||
buildProfile,
|
||||
buildCtx.projectId,
|
||||
flags.nonInteractive
|
||||
);
|
||||
if (buildProfile.profile.channel) {
|
||||
await validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
graphqlClient,
|
||||
exp: buildCtx.exp,
|
||||
projectId: buildCtx.projectId,
|
||||
projectDir,
|
||||
@@ -395,7 +342,6 @@ async function prepareAndStartBuildAsync({
|
||||
sdkVersion: buildCtx.exp.sdkVersion,
|
||||
nonInteractive: flags.nonInteractive,
|
||||
buildProfile,
|
||||
env: buildProfile.profile.env,
|
||||
});
|
||||
if (isUsingEASUpdate(buildCtx.exp, buildCtx.projectId)) {
|
||||
const doesChannelExist = await doesChannelExistAsync(graphqlClient, {
|
||||
@@ -450,7 +396,6 @@ async function prepareAndStartSubmissionAsync({
|
||||
projectDir,
|
||||
buildProfile,
|
||||
submitProfile,
|
||||
selectedSubmitProfileName,
|
||||
nonInteractive,
|
||||
}: {
|
||||
build: BuildFragment;
|
||||
@@ -459,7 +404,6 @@ async function prepareAndStartSubmissionAsync({
|
||||
projectDir: string;
|
||||
buildProfile: BuildProfile;
|
||||
submitProfile: SubmitProfile;
|
||||
selectedSubmitProfileName?: string;
|
||||
nonInteractive: boolean;
|
||||
}): Promise<SubmissionFragment> {
|
||||
const platform = toPlatform(build.platform);
|
||||
@@ -478,8 +422,6 @@ async function prepareAndStartSubmissionAsync({
|
||||
projectId: buildCtx.projectId,
|
||||
exp: buildCtx.exp,
|
||||
vcsClient: buildCtx.vcsClient,
|
||||
isVerboseFastlaneEnabled: false,
|
||||
specifiedProfile: selectedSubmitProfileName,
|
||||
});
|
||||
|
||||
if (moreBuilds) {
|
||||
@@ -537,14 +479,15 @@ async function maybeDownloadAndRunSimulatorBuildsAsync(
|
||||
|
||||
async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
exp,
|
||||
graphqlClient,
|
||||
projectId,
|
||||
projectDir,
|
||||
vcsClient,
|
||||
buildProfile,
|
||||
nonInteractive,
|
||||
sdkVersion,
|
||||
env,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
exp: ExpoConfig;
|
||||
projectId: string;
|
||||
projectDir: string;
|
||||
@@ -552,7 +495,6 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
buildProfile: ProfileData<'build'>;
|
||||
nonInteractive: boolean;
|
||||
sdkVersion?: string;
|
||||
env: Env | undefined;
|
||||
}): Promise<void> {
|
||||
if (isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion)) {
|
||||
return;
|
||||
@@ -574,13 +516,12 @@ async function validateExpoUpdatesInstalledAsProjectDependencyAsync({
|
||||
message: `Would you like to install the "expo-updates" package and configure EAS Update now?`,
|
||||
});
|
||||
if (installExpoUpdates) {
|
||||
await ensureEASUpdateIsConfiguredAsync({
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp,
|
||||
projectId,
|
||||
projectDir,
|
||||
platform: RequestedPlatform.All,
|
||||
vcsClient,
|
||||
env,
|
||||
});
|
||||
Log.withTick('Installed expo-updates and configured EAS Update.');
|
||||
throw new Error('Command must be re-run to pick up new updates configuration.');
|
||||
|
||||
@@ -11,6 +11,5 @@ export enum BuildStatus {
|
||||
export enum BuildDistributionType {
|
||||
STORE = 'store',
|
||||
INTERNAL = 'internal',
|
||||
/** @deprecated Use simulator flag instead */
|
||||
SIMULATOR = 'simulator',
|
||||
}
|
||||
@@ -39,10 +39,6 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Profile',
|
||||
value: build.buildProfile,
|
||||
},
|
||||
{
|
||||
label: 'Message',
|
||||
value: build.message,
|
||||
@@ -55,6 +51,10 @@ export function formatGraphQLBuild(build: BuildFragment): string {
|
||||
label: 'Enterprise Provisioning',
|
||||
value: build.iosEnterpriseProvisioning?.toLowerCase(),
|
||||
},
|
||||
{
|
||||
label: 'Release Channel',
|
||||
value: build.releaseChannel,
|
||||
},
|
||||
{
|
||||
label: 'Channel',
|
||||
value: build.channel,
|
||||
|
||||
@@ -12,10 +12,7 @@ import { getTmpDirectory } from '../../utils/paths';
|
||||
import { endTimer, formatMilliseconds, startTimer } from '../../utils/timer';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
export async function maybeBailOnRepoStatusAsync(
|
||||
vcsClient: Client,
|
||||
nonInteractive: boolean
|
||||
): Promise<void> {
|
||||
export async function maybeBailOnRepoStatusAsync(vcsClient: Client): Promise<void> {
|
||||
if (!(await vcsClient.isCommitRequiredAsync())) {
|
||||
return;
|
||||
}
|
||||
@@ -31,11 +28,6 @@ export async function maybeBailOnRepoStatusAsync(
|
||||
});
|
||||
|
||||
if (!answer) {
|
||||
if (nonInteractive) {
|
||||
Log.log('The following files need to be committed:');
|
||||
await vcsClient.showChangedFilesAsync();
|
||||
}
|
||||
|
||||
throw new Error('Commit all changes. Aborting...');
|
||||
}
|
||||
}
|
||||
@@ -55,9 +47,6 @@ export async function ensureRepoIsCleanAsync(
|
||||
)}.`
|
||||
);
|
||||
if (nonInteractive) {
|
||||
Log.log('The following files need to be committed:');
|
||||
await vcsClient.showChangedFilesAsync();
|
||||
|
||||
throw new Error('Commit all changes. Aborting...');
|
||||
}
|
||||
const answer = await confirmAsync({
|
||||
@@ -94,33 +83,9 @@ export async function commitPromptAsync(
|
||||
});
|
||||
}
|
||||
|
||||
export type LocalFile = {
|
||||
path: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
export async function makeProjectMetadataFileAsync(archivePath: string): Promise<LocalFile> {
|
||||
await fs.mkdirp(getTmpDirectory());
|
||||
const metadataLocation = path.join(getTmpDirectory(), `${uuidv4()}-eas-build-metadata.json`);
|
||||
const archiveContent: string[] = [];
|
||||
|
||||
await tar.list({
|
||||
file: archivePath,
|
||||
onentry: (entry: tar.ReadEntry) => {
|
||||
if (entry.type === 'File' && !entry.path.includes('.git/')) {
|
||||
archiveContent.push(entry.path);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await fs.writeJSON(metadataLocation, {
|
||||
archiveContent,
|
||||
});
|
||||
|
||||
return { path: metadataLocation, size: await fs.stat(metadataLocation).then(stat => stat.size) };
|
||||
}
|
||||
|
||||
export async function makeProjectTarballAsync(vcsClient: Client): Promise<LocalFile> {
|
||||
export async function makeProjectTarballAsync(
|
||||
vcsClient: Client
|
||||
): Promise<{ path: string; size: number }> {
|
||||
const spinner = ora('Compressing project files');
|
||||
|
||||
await fs.mkdirp(getTmpDirectory());
|
||||
|
||||
@@ -13,14 +13,6 @@ import { withErrorHandlingAsync } from '../graphql/client';
|
||||
import {
|
||||
CreateUpdateChannelOnAppMutation,
|
||||
CreateUpdateChannelOnAppMutationVariables,
|
||||
CreateUpdateChannelOnAppWithNoBranchMutation,
|
||||
CreateUpdateChannelOnAppWithNoBranchMutationVariables,
|
||||
DeleteUpdateChannelMutation,
|
||||
DeleteUpdateChannelMutationVariables,
|
||||
DeleteUpdateChannelResult,
|
||||
UpdateChannelBasicInfoFragment,
|
||||
UpdateChannelBranchMappingMutation,
|
||||
UpdateChannelBranchMappingMutationVariables,
|
||||
ViewBranchesOnUpdateChannelQueryVariables,
|
||||
ViewUpdateChannelsOnAppQueryVariables,
|
||||
} from '../graphql/generated';
|
||||
@@ -217,42 +209,6 @@ function renderChannelHeaderContent({
|
||||
Log.log(chalk`{bold Branches pointed at this channel and their most recent update group:}`);
|
||||
}
|
||||
|
||||
export async function createChannelOnAppWithNoBranchAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
appId,
|
||||
channelName,
|
||||
}: {
|
||||
appId: string;
|
||||
channelName: string;
|
||||
}
|
||||
): Promise<CreateUpdateChannelOnAppWithNoBranchMutation> {
|
||||
return await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<
|
||||
CreateUpdateChannelOnAppWithNoBranchMutation,
|
||||
CreateUpdateChannelOnAppWithNoBranchMutationVariables
|
||||
>(
|
||||
gql`
|
||||
mutation CreateUpdateChannelOnAppWithNoBranch($appId: ID!, $name: String!) {
|
||||
updateChannel {
|
||||
createUpdateChannelForApp(appId: $appId, name: $name) {
|
||||
id
|
||||
...UpdateChannelBasicInfoFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateChannelBasicInfoFragmentNode)}
|
||||
`,
|
||||
{
|
||||
appId,
|
||||
name: channelName,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
}
|
||||
|
||||
export async function createChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
@@ -411,57 +367,3 @@ export async function createAndLinkChannelAsync(
|
||||
}
|
||||
return newChannel;
|
||||
}
|
||||
|
||||
export async function updateChannelBranchMappingAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId, branchMapping }: UpdateChannelBranchMappingMutationVariables
|
||||
): Promise<UpdateChannelBasicInfoFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<UpdateChannelBranchMappingMutation, UpdateChannelBranchMappingMutationVariables>(
|
||||
gql`
|
||||
mutation UpdateChannelBranchMapping($channelId: ID!, $branchMapping: String!) {
|
||||
updateChannel {
|
||||
editUpdateChannel(channelId: $channelId, branchMapping: $branchMapping) {
|
||||
id
|
||||
...UpdateChannelBasicInfoFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateChannelBasicInfoFragmentNode)}
|
||||
`,
|
||||
{ channelId, branchMapping }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
const channel = data.updateChannel.editUpdateChannel;
|
||||
if (!channel) {
|
||||
throw new Error(`Could not find a channel with id: ${channelId}`);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
export async function deleteChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId }: DeleteUpdateChannelMutationVariables
|
||||
): Promise<DeleteUpdateChannelResult> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateChannelMutation, DeleteUpdateChannelMutationVariables>(
|
||||
gql`
|
||||
mutation DeleteUpdateChannel($channelId: ID!) {
|
||||
updateChannel {
|
||||
deleteUpdateChannel(channelId: $channelId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
channelId,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return data.updateChannel.deleteUpdateChannel;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Command } from '@oclif/core';
|
||||
import { CombinedError } from '@urql/core';
|
||||
import chalk from 'chalk';
|
||||
import { GraphQLError } from 'graphql/error';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
} from '../analytics/AnalyticsManager';
|
||||
import Log from '../log';
|
||||
import SessionManager from '../user/SessionManager';
|
||||
import { Client } from '../vcs/vcs';
|
||||
|
||||
export type ContextInput<
|
||||
T extends {
|
||||
@@ -141,7 +139,7 @@ export default abstract class EasCommand extends Command {
|
||||
} = object,
|
||||
>(
|
||||
commandClass: { contextDefinition: ContextInput<C> },
|
||||
{ nonInteractive, vcsClientOverride }: { nonInteractive: boolean; vcsClientOverride?: Client }
|
||||
{ nonInteractive }: { nonInteractive: boolean }
|
||||
): Promise<ContextOutput<C>> {
|
||||
const contextDefinition = commandClass.contextDefinition;
|
||||
|
||||
@@ -154,7 +152,6 @@ export default abstract class EasCommand extends Command {
|
||||
nonInteractive,
|
||||
sessionManager: this.sessionManager,
|
||||
analytics: this.analytics,
|
||||
vcsClientOverride,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@@ -201,7 +198,7 @@ export default abstract class EasCommand extends Command {
|
||||
if (err instanceof EasCommandError) {
|
||||
Log.error(err.message);
|
||||
} else if (err instanceof CombinedError && err?.graphQLErrors) {
|
||||
const cleanGQLErrorsMessage = err?.graphQLErrors
|
||||
const cleanMessage = err?.graphQLErrors
|
||||
.map((graphQLError: GraphQLError) => {
|
||||
const messageLine = graphQLError.message.replace('[GraphQL] ', '');
|
||||
const requestIdLine = graphQLError.extensions?.requestId
|
||||
@@ -211,21 +208,12 @@ export default abstract class EasCommand extends Command {
|
||||
const defaultMsg = `${messageLine}${requestIdLine}`;
|
||||
|
||||
if (graphQLError.extensions?.errorCode === 'UNAUTHORIZED_ERROR') {
|
||||
return `${chalk.bold(
|
||||
`You don't have the required permissions to perform this operation.`
|
||||
)}\n\nThis can sometimes happen if you are logged in as incorrect user.\nRun ${chalk.bold(
|
||||
'eas whoami'
|
||||
)} to check the username you are logged in as.\nRun ${chalk.bold(
|
||||
'eas login'
|
||||
)} to change the account.\n\nOriginal error message: ${defaultMsg}`;
|
||||
return `You don't have the required permissions to perform this operation.\n\n${defaultMsg}`;
|
||||
}
|
||||
|
||||
return defaultMsg;
|
||||
})
|
||||
.join('\n');
|
||||
const cleanMessage = err.networkError
|
||||
? `${cleanGQLErrorsMessage}\n${err.networkError.message}`
|
||||
: cleanGQLErrorsMessage;
|
||||
Log.error(cleanMessage);
|
||||
baseMessage = BASE_GRAPHQL_ERROR_MESSAGE;
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Analytics } from '../../analytics/AnalyticsManager';
|
||||
import SessionManager from '../../user/SessionManager';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
export interface ContextOptions {
|
||||
sessionManager: SessionManager;
|
||||
analytics: Analytics;
|
||||
nonInteractive: boolean;
|
||||
vcsClientOverride?: Client;
|
||||
}
|
||||
|
||||
export default abstract class ContextField<T> {
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||
|
||||
import ContextField from './ContextField';
|
||||
import { findProjectDirAndVerifyProjectSetupAsync } from './contextUtils/findProjectDirAndVerifyProjectSetupAsync';
|
||||
import { resolveVcsClient } from '../../vcs';
|
||||
import { getVcsClient } from '../../vcs';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
|
||||
export default class VcsClientContextField extends ContextField<Client> {
|
||||
async getValueAsync({ vcsClientOverride }: { vcsClientOverride?: Client }): Promise<Client> {
|
||||
if (vcsClientOverride) {
|
||||
return vcsClientOverride;
|
||||
}
|
||||
const projectDir = await findProjectDirAndVerifyProjectSetupAsync();
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||
return resolveVcsClient(config?.requireCommit);
|
||||
async getValueAsync(): Promise<Client> {
|
||||
return getVcsClient();
|
||||
}
|
||||
}
|
||||
-7
@@ -55,7 +55,6 @@ describe(getProjectIdAsync, () => {
|
||||
],
|
||||
isExpoAdmin: false,
|
||||
featureGates: {},
|
||||
preferences: {},
|
||||
},
|
||||
authenticationInfo: { accessToken: 'fake', sessionSecret: null },
|
||||
});
|
||||
@@ -80,7 +79,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@notnotbrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'notnotbrent' } as any,
|
||||
});
|
||||
@@ -101,7 +99,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@notnotbrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'notnotbrent' } as any,
|
||||
});
|
||||
@@ -126,7 +123,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@totallybrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'totallybrent' } as any,
|
||||
});
|
||||
@@ -175,7 +171,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@totallybrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'totallybrent' } as any,
|
||||
});
|
||||
@@ -200,7 +195,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@notnotbrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'notnotbrent' } as any,
|
||||
});
|
||||
@@ -270,7 +264,6 @@ describe(getProjectIdAsync, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
fullName: '@notnotbrent/test',
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
ownerAccount: { name: 'notnotbrent' } as any,
|
||||
});
|
||||
|
||||
+7
-7
@@ -8,20 +8,20 @@ import semver from 'semver';
|
||||
|
||||
import { learnMore } from '../../../log';
|
||||
import { easCliVersion } from '../../../utils/easCli';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import { getVcsClient, setVcsClient } from '../../../vcs';
|
||||
import GitClient from '../../../vcs/clients/git';
|
||||
|
||||
async function applyCliConfigAsync(projectDir: string): Promise<void> {
|
||||
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
|
||||
const config = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
|
||||
if (config?.version && !semver.satisfies(easCliVersion, config.version)) {
|
||||
throw new Error(
|
||||
`You are on eas-cli@${easCliVersion} which does not satisfy the CLI version constraint defined in eas.json (${
|
||||
config.version
|
||||
}).\n\nThis error probably means that you need update your eas-cli to a newer version.\nRun ${chalk.bold(
|
||||
'npm install -g eas-cli'
|
||||
)} to update the eas-cli to the latest version.`
|
||||
`You are on eas-cli@${easCliVersion} which does not satisfy the CLI version constraint in eas.json (${config.version})`
|
||||
);
|
||||
}
|
||||
if (config?.requireCommit) {
|
||||
setVcsClient(new GitClient());
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureEasCliIsNotInDependenciesAsync(projectDir: string): Promise<void> {
|
||||
@@ -92,7 +92,7 @@ export async function findProjectRootAsync({
|
||||
} else {
|
||||
let vcsRoot;
|
||||
try {
|
||||
vcsRoot = path.normalize(await resolveVcsClient().getRootPathAsync());
|
||||
vcsRoot = path.normalize(await getVcsClient().getRootPathAsync());
|
||||
} catch {}
|
||||
if (vcsRoot && vcsRoot.startsWith(projectRootDir) && vcsRoot !== projectRootDir) {
|
||||
throw new Error(
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ExpoConfig } from '@expo/config-types';
|
||||
import { Env } from '@expo/eas-build-job';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { ExpoGraphqlClient, createGraphqlClient } from './createGraphqlClient';
|
||||
import { createGraphqlClient } from './createGraphqlClient';
|
||||
import { findProjectRootAsync } from './findProjectDirAndVerifyProjectSetupAsync';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import Log, { learnMore } from '../../../log';
|
||||
@@ -87,23 +87,6 @@ export async function getProjectIdAsync(
|
||||
});
|
||||
const graphqlClient = createGraphqlClient(authenticationInfo);
|
||||
|
||||
const projectId = await validateOrSetProjectIdAsync({ exp, graphqlClient, actor, options });
|
||||
return projectId;
|
||||
}
|
||||
|
||||
export async function validateOrSetProjectIdAsync({
|
||||
exp,
|
||||
graphqlClient,
|
||||
actor,
|
||||
options,
|
||||
cwd,
|
||||
}: {
|
||||
exp: ExpoConfig;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
actor: Actor;
|
||||
options: { env?: Env; nonInteractive: boolean };
|
||||
cwd?: string;
|
||||
}): Promise<string> {
|
||||
const localProjectId = exp.extra?.eas?.projectId;
|
||||
if (localProjectId) {
|
||||
if (typeof localProjectId !== 'string') {
|
||||
@@ -163,9 +146,7 @@ export async function validateOrSetProjectIdAsync({
|
||||
return localProjectId;
|
||||
}
|
||||
|
||||
const projectDir = await findProjectRootAsync({
|
||||
cwd,
|
||||
});
|
||||
const projectDir = await findProjectRootAsync();
|
||||
if (!projectDir) {
|
||||
throw new Error('This command must be run inside a project directory.');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import Log from '../../log';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { getActorDisplayName } from '../../user/User';
|
||||
|
||||
export default class AccountLogin extends EasCommand {
|
||||
static override description = 'log in with your Expo account';
|
||||
@@ -16,11 +13,11 @@ export default class AccountLogin extends EasCommand {
|
||||
description: 'Login with SSO',
|
||||
char: 's',
|
||||
default: false,
|
||||
hidden: true,
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.MaybeLoggedIn,
|
||||
...this.ContextOptions.SessionManagment,
|
||||
};
|
||||
|
||||
@@ -29,28 +26,7 @@ export default class AccountLogin extends EasCommand {
|
||||
flags: { sso },
|
||||
} = await this.parse(AccountLogin);
|
||||
|
||||
const {
|
||||
sessionManager,
|
||||
maybeLoggedIn: { actor },
|
||||
} = await this.getContextAsync(AccountLogin, { nonInteractive: false });
|
||||
|
||||
if (sessionManager.getAccessToken()) {
|
||||
throw new Error(
|
||||
'EXPO_TOKEN is set in your environment, and is being used for all EAS authentication. To use username/password authentication, unset EXPO_TOKEN in your environment and re-run the command.'
|
||||
);
|
||||
}
|
||||
|
||||
if (actor) {
|
||||
Log.warn(`You are already logged in as ${chalk.bold(getActorDisplayName(actor))}.`);
|
||||
|
||||
const shouldContinue = await confirmAsync({
|
||||
message: 'Do you want to continue?',
|
||||
});
|
||||
if (!shouldContinue) {
|
||||
Errors.error('Aborted', { exit: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
const { sessionManager } = await this.getContextAsync(AccountLogin, { nonInteractive: false });
|
||||
await sessionManager.showLoginPromptAsync({ sso });
|
||||
Log.log('Logged in');
|
||||
}
|
||||
|
||||
@@ -12,19 +12,14 @@ export default class AccountView extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.MaybeLoggedIn,
|
||||
...this.ContextOptions.SessionManagment,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
maybeLoggedIn: { actor },
|
||||
sessionManager,
|
||||
} = await this.getContextAsync(AccountView, { nonInteractive: true });
|
||||
if (actor) {
|
||||
const loggedInAs = sessionManager.getAccessToken()
|
||||
? `${getActorDisplayName(actor)} (authenticated using EXPO_TOKEN)`
|
||||
: getActorDisplayName(actor);
|
||||
Log.log(chalk.green(loggedInAs));
|
||||
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
|
||||
|
||||
@@ -11,7 +11,10 @@ import { isExpoUpdatesInstalled, isUsingEASUpdate } from '../../project/projectU
|
||||
import { resolveWorkflowAsync } from '../../project/workflow';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { syncUpdatesConfigurationAsync as syncAndroidUpdatesConfigurationAsync } from '../../update/android/UpdatesModule';
|
||||
import { ensureEASUpdateIsConfiguredInEasJsonAsync } from '../../update/configure';
|
||||
import {
|
||||
ensureEASUpdateIsConfiguredInEasJsonAsync,
|
||||
ensureUseClassicUpdatesIsRemovedAsync,
|
||||
} from '../../update/configure';
|
||||
import { syncUpdatesConfigurationAsync as syncIosUpdatesConfigurationAsync } from '../../update/ios/UpdatesModule';
|
||||
|
||||
export default class BuildConfigure extends EasCommand {
|
||||
@@ -35,6 +38,7 @@ export default class BuildConfigure extends EasCommand {
|
||||
const { flags } = await this.parse(BuildConfigure);
|
||||
const {
|
||||
privateProjectConfig: { exp, projectId, projectDir },
|
||||
loggedIn: { graphqlClient },
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(BuildConfigure, {
|
||||
nonInteractive: false,
|
||||
@@ -66,6 +70,12 @@ export default class BuildConfigure extends EasCommand {
|
||||
vcsClient,
|
||||
});
|
||||
if (didCreateEasJson && isUsingEASUpdate(exp, projectId)) {
|
||||
if (exp.updates?.useClassicUpdates) {
|
||||
// NOTE: this method modifies the Expo config; be sure to use this function's return value
|
||||
// if the config object is used later in the future
|
||||
await ensureUseClassicUpdatesIsRemovedAsync({ exp, projectDir });
|
||||
}
|
||||
|
||||
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
|
||||
}
|
||||
|
||||
@@ -74,20 +84,20 @@ export default class BuildConfigure extends EasCommand {
|
||||
if ([RequestedPlatform.Android, RequestedPlatform.All].includes(platform)) {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.ANDROID, vcsClient);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await syncAndroidUpdatesConfigurationAsync({ projectDir, exp, workflow, env: undefined });
|
||||
await syncAndroidUpdatesConfigurationAsync(graphqlClient, projectDir, exp, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
if ([RequestedPlatform.Ios, RequestedPlatform.All].includes(platform)) {
|
||||
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS, vcsClient);
|
||||
if (workflow === Workflow.GENERIC) {
|
||||
await syncIosUpdatesConfigurationAsync({
|
||||
await syncIosUpdatesConfigurationAsync(
|
||||
graphqlClient,
|
||||
vcsClient,
|
||||
projectDir,
|
||||
exp,
|
||||
workflow,
|
||||
env: undefined,
|
||||
});
|
||||
projectId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
|
||||
import { LoggerLevel } from '@expo/logger';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import figures from 'figures';
|
||||
@@ -34,9 +33,6 @@ interface RawBuildFlags {
|
||||
'auto-submit-with-profile'?: string;
|
||||
'resource-class'?: ResourceClass;
|
||||
message?: string;
|
||||
'build-logger-level'?: LoggerLevel;
|
||||
'freeze-credentials': boolean;
|
||||
repack: boolean;
|
||||
}
|
||||
|
||||
export default class Build extends EasCommand {
|
||||
@@ -102,19 +98,6 @@ export default class Build extends EasCommand {
|
||||
char: 'm',
|
||||
description: 'A short message describing the build',
|
||||
}),
|
||||
'build-logger-level': Flags.enum({
|
||||
description: 'The level of logs to output during the build process. Defaults to "info".',
|
||||
options: Object.values(LoggerLevel),
|
||||
}),
|
||||
'freeze-credentials': Flags.boolean({
|
||||
default: false,
|
||||
description: 'Prevent the build from updating credentials in non-interactive mode',
|
||||
}),
|
||||
repack: Flags.boolean({
|
||||
default: false,
|
||||
hidden: true,
|
||||
description: 'Use the golden dev client build repack flow as it works for onboarding',
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
@@ -227,9 +210,6 @@ export default class Build extends EasCommand {
|
||||
submitProfile: flags['auto-submit-with-profile'] ?? profile,
|
||||
resourceClass: flags['resource-class'],
|
||||
message,
|
||||
buildLoggerLevel: flags['build-logger-level'],
|
||||
freezeCredentials: flags['freeze-credentials'],
|
||||
repack: flags.repack,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,6 @@ export default class BuildInspect extends EasCommand {
|
||||
projectDir,
|
||||
{
|
||||
nonInteractive: false,
|
||||
freezeCredentials: false,
|
||||
wait: true,
|
||||
clearCache: false,
|
||||
json: false,
|
||||
@@ -120,7 +119,6 @@ export default class BuildInspect extends EasCommand {
|
||||
workingdir: tmpWorkingdir,
|
||||
artifactsDir: path.join(tmpWorkingdir, 'artifacts'),
|
||||
},
|
||||
repack: false,
|
||||
},
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync
|
||||
|
||||
@@ -6,6 +6,7 @@ import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
import { setVcsClient } from '../../vcs';
|
||||
import GitNoCommitClient from '../../vcs/clients/gitNoCommit';
|
||||
|
||||
/**
|
||||
@@ -29,17 +30,6 @@ export default class BuildInternal extends EasCommand {
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
'auto-submit': Flags.boolean({
|
||||
default: false,
|
||||
description:
|
||||
'Submit on build complete using the submit profile with the same name as the build profile',
|
||||
exclusive: ['auto-submit-with-profile'],
|
||||
}),
|
||||
'auto-submit-with-profile': Flags.string({
|
||||
description: 'Submit on build complete using the submit profile with provided name',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
exclusive: ['auto-submit'],
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -63,9 +53,10 @@ export default class BuildInternal extends EasCommand {
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(BuildInternal, {
|
||||
nonInteractive: true,
|
||||
vcsClientOverride: new GitNoCommitClient(),
|
||||
});
|
||||
|
||||
setVcsClient(new GitNoCommitClient());
|
||||
|
||||
await handleDeprecatedEasJsonAsync(projectDir, flags.nonInteractive);
|
||||
|
||||
await runBuildAndSubmitAsync(
|
||||
@@ -77,16 +68,13 @@ export default class BuildInternal extends EasCommand {
|
||||
requestedPlatform: flags.platform as RequestedPlatform,
|
||||
profile: flags.profile,
|
||||
nonInteractive: true,
|
||||
freezeCredentials: false,
|
||||
wait: false,
|
||||
clearCache: false,
|
||||
json: true,
|
||||
autoSubmit: flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined,
|
||||
autoSubmit: false,
|
||||
localBuildOptions: {
|
||||
localBuildMode: LocalBuildMode.INTERNAL,
|
||||
},
|
||||
submitProfile: flags['auto-submit-with-profile'] ?? flags.profile,
|
||||
repack: false,
|
||||
},
|
||||
actor,
|
||||
getDynamicPrivateProjectConfigAsync
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
getPaginatedQueryOptions,
|
||||
} from '../../commandUtils/pagination';
|
||||
import { AppPlatform, BuildStatus as GraphQLBuildStatus } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { buildDistributionTypeToGraphQLDistributionType } from '../../utils/buildDistribution';
|
||||
@@ -21,54 +20,37 @@ export default class BuildList extends EasCommand {
|
||||
|
||||
static override flags = {
|
||||
platform: Flags.enum({
|
||||
options: Object.values(RequestedPlatform),
|
||||
char: 'p',
|
||||
options: [RequestedPlatform.All, RequestedPlatform.Android, RequestedPlatform.Ios],
|
||||
}),
|
||||
status: Flags.enum({
|
||||
options: Object.values(BuildStatus),
|
||||
description: 'Filter only builds with the specified status',
|
||||
options: [
|
||||
BuildStatus.NEW,
|
||||
BuildStatus.IN_QUEUE,
|
||||
BuildStatus.IN_PROGRESS,
|
||||
BuildStatus.PENDING_CANCEL,
|
||||
BuildStatus.ERRORED,
|
||||
BuildStatus.FINISHED,
|
||||
BuildStatus.CANCELED,
|
||||
],
|
||||
}),
|
||||
distribution: Flags.enum({
|
||||
options: Object.values(BuildDistributionType),
|
||||
description: 'Filter only builds with the specified distribution type',
|
||||
options: [
|
||||
BuildDistributionType.STORE,
|
||||
BuildDistributionType.INTERNAL,
|
||||
BuildDistributionType.SIMULATOR,
|
||||
],
|
||||
}),
|
||||
channel: Flags.string(),
|
||||
'app-version': Flags.string({
|
||||
aliases: ['appVersion'],
|
||||
description: 'Filter only builds created with the specified main app version',
|
||||
}),
|
||||
'app-build-version': Flags.string({
|
||||
aliases: ['appBuildVersion'],
|
||||
description: 'Filter only builds created with the specified app build version',
|
||||
}),
|
||||
'sdk-version': Flags.string({
|
||||
aliases: ['sdkVersion'],
|
||||
description: 'Filter only builds created with the specified Expo SDK version',
|
||||
}),
|
||||
'runtime-version': Flags.string({
|
||||
aliases: ['runtimeVersion'],
|
||||
description: 'Filter only builds created with the specified runtime version',
|
||||
}),
|
||||
'app-identifier': Flags.string({
|
||||
aliases: ['appIdentifier'],
|
||||
description: 'Filter only builds created with the specified app identifier',
|
||||
}),
|
||||
'build-profile': Flags.string({
|
||||
char: 'e',
|
||||
aliases: ['profile', 'buildProfile'],
|
||||
description: 'Filter only builds created with the specified build profile',
|
||||
}),
|
||||
'git-commit-hash': Flags.string({
|
||||
aliases: ['gitCommitHash'],
|
||||
description: 'Filter only builds created with the specified git commit hash',
|
||||
}),
|
||||
appVersion: Flags.string(),
|
||||
appBuildVersion: Flags.string(),
|
||||
sdkVersion: Flags.string(),
|
||||
runtimeVersion: Flags.string(),
|
||||
appIdentifier: Flags.string(),
|
||||
buildProfile: Flags.string(),
|
||||
gitCommitHash: Flags.string(),
|
||||
...EasPaginatedQueryFlags,
|
||||
limit: getLimitFlagWithCustomValues({ defaultTo: 10, limit: BUILDS_LIMIT }),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
simulator: Flags.boolean({
|
||||
description:
|
||||
'Filter only iOS simulator builds. Can only be used with --platform flag set to "ios"',
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -87,17 +69,6 @@ export default class BuildList extends EasCommand {
|
||||
distribution: buildDistribution,
|
||||
'non-interactive': nonInteractive,
|
||||
} = flags;
|
||||
if (buildDistribution === BuildDistributionType.SIMULATOR) {
|
||||
Log.warn(
|
||||
`Using --distribution flag with "simulator" value is deprecated - use --simulator flag instead`
|
||||
);
|
||||
}
|
||||
if (flags.simulator && requestedPlatform !== RequestedPlatform.Ios) {
|
||||
Log.error(
|
||||
`The --simulator flag is only usable with --platform flag set to "ios", as it is used to filter specifically iOS simulator builds`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
@@ -122,14 +93,13 @@ export default class BuildList extends EasCommand {
|
||||
status: graphqlBuildStatus,
|
||||
distribution: graphqlBuildDistribution,
|
||||
channel: flags.channel,
|
||||
appVersion: flags['app-version'],
|
||||
appBuildVersion: flags['app-build-version'],
|
||||
sdkVersion: flags['sdk-version'],
|
||||
runtimeVersion: flags['runtime-version'],
|
||||
appIdentifier: flags['app-identifier'],
|
||||
buildProfile: flags['build-profile'],
|
||||
gitCommitHash: flags['git-commit-hash'],
|
||||
simulator: flags.simulator,
|
||||
appVersion: flags.appVersion,
|
||||
appBuildVersion: flags.appBuildVersion,
|
||||
sdkVersion: flags.sdkVersion,
|
||||
runtimeVersion: flags.runtimeVersion,
|
||||
appIdentifier: flags.appIdentifier,
|
||||
buildProfile: flags.buildProfile,
|
||||
gitCommitHash: flags.gitCommitHash,
|
||||
},
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
|
||||
@@ -42,8 +42,7 @@ interface BuildResignFlags {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
platform?: Platform;
|
||||
targetProfile?: string;
|
||||
sourceProfile?: string;
|
||||
profile?: string;
|
||||
maybeBuildId?: string;
|
||||
wait: boolean;
|
||||
}
|
||||
@@ -53,8 +52,7 @@ interface RawBuildResignFlags {
|
||||
offset: number | undefined;
|
||||
limit: number | undefined;
|
||||
platform: 'android' | 'ios' | undefined;
|
||||
'target-profile': string | undefined;
|
||||
'source-profile': string | undefined;
|
||||
profile: string | undefined;
|
||||
wait: boolean;
|
||||
id: string | undefined;
|
||||
}
|
||||
@@ -67,16 +65,10 @@ export default class BuildResign extends EasCommand {
|
||||
char: 'p',
|
||||
options: ['android', 'ios'],
|
||||
}),
|
||||
'target-profile': Flags.string({
|
||||
profile: Flags.string({
|
||||
char: 'e',
|
||||
description:
|
||||
'Name of the target build profile from eas.json. Credentials and environment variables from this profile will be used when re-signing. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
aliases: ['profile'],
|
||||
}),
|
||||
'source-profile': Flags.string({
|
||||
description:
|
||||
'Name of the source build profile from eas.json. Used to filter builds eligible for re-signing.',
|
||||
'Name of the build profile from eas.json. Defaults to "production" if defined in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
wait: Flags.boolean({
|
||||
@@ -138,20 +130,12 @@ export default class BuildResign extends EasCommand {
|
||||
const buildProfile = await EasJsonUtils.getBuildProfileAsync(
|
||||
easJsonAccessor,
|
||||
platform,
|
||||
flags.targetProfile ?? 'production'
|
||||
flags.profile ?? 'production'
|
||||
);
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ env: buildProfile.env });
|
||||
const account = await getOwnerAccountForProjectIdAsync(graphqlClient, projectId);
|
||||
const build = await this.ensureBuildSelectedAsync(
|
||||
{
|
||||
graphqlClient,
|
||||
projectId,
|
||||
platform,
|
||||
nonInteractive,
|
||||
limit,
|
||||
offset,
|
||||
buildProfile: flags.sourceProfile,
|
||||
},
|
||||
{ graphqlClient, projectId, platform, nonInteractive, limit, offset },
|
||||
maybeBuild
|
||||
);
|
||||
const credentialsCtx = new CredentialsContext({
|
||||
@@ -244,8 +228,7 @@ export default class BuildResign extends EasCommand {
|
||||
offset: flags.offset,
|
||||
limit: flags.limit,
|
||||
platform: flags.platform as Platform | undefined,
|
||||
sourceProfile: flags['source-profile'],
|
||||
targetProfile: flags['target-profile'],
|
||||
profile: flags.profile,
|
||||
maybeBuildId: flags.id,
|
||||
wait: flags.wait,
|
||||
};
|
||||
@@ -259,7 +242,6 @@ export default class BuildResign extends EasCommand {
|
||||
nonInteractive,
|
||||
limit,
|
||||
offset,
|
||||
buildProfile,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectId: string;
|
||||
@@ -267,7 +249,6 @@ export default class BuildResign extends EasCommand {
|
||||
nonInteractive: boolean;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
buildProfile?: string;
|
||||
},
|
||||
maybeBuild?: BuildFragment
|
||||
): Promise<BuildFragment> {
|
||||
@@ -287,7 +268,6 @@ export default class BuildResign extends EasCommand {
|
||||
distribution: DistributionType.Internal,
|
||||
platform: toAppPlatform(platform),
|
||||
status: BuildStatus.Finished,
|
||||
buildProfile,
|
||||
},
|
||||
});
|
||||
if (!build) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
PaginatedQueryOptions,
|
||||
getPaginatedQueryOptions,
|
||||
} from '../../commandUtils/pagination';
|
||||
import { AppPlatform, BuildFragment, BuildStatus } from '../../graphql/generated';
|
||||
import { AppPlatform, BuildFragment, BuildStatus, DistributionType } from '../../graphql/generated';
|
||||
import { BuildQuery } from '../../graphql/queries/BuildQuery';
|
||||
import Log from '../../log';
|
||||
import { appPlatformDisplayNames } from '../../platform';
|
||||
@@ -200,7 +200,8 @@ async function maybeGetBuildAsync(
|
||||
projectId: string,
|
||||
paginatedQueryOptions: PaginatedQueryOptions
|
||||
): Promise<BuildFragment | null> {
|
||||
const simulator = flags.selectedPlatform === AppPlatform.Ios ? true : undefined;
|
||||
const distributionType =
|
||||
flags.selectedPlatform === AppPlatform.Ios ? DistributionType.Simulator : undefined;
|
||||
|
||||
if (flags.runArchiveFlags.id) {
|
||||
const build = await BuildQuery.byIdAsync(graphqlClient, flags.runArchiveFlags.id);
|
||||
@@ -218,9 +219,9 @@ async function maybeGetBuildAsync(
|
||||
} build to run for ${await getDisplayNameForProjectIdAsync(graphqlClient, projectId)} app`,
|
||||
filter: {
|
||||
platform: flags.selectedPlatform,
|
||||
distribution: distributionType,
|
||||
status: BuildStatus.Finished,
|
||||
buildProfile: flags.profile,
|
||||
simulator,
|
||||
},
|
||||
paginatedQueryOptions,
|
||||
selectPromptDisabledFunction: build => !isRunnableOnSimulatorOrEmulator(build),
|
||||
@@ -234,9 +235,9 @@ async function maybeGetBuildAsync(
|
||||
projectId,
|
||||
filter: {
|
||||
platform: flags.selectedPlatform,
|
||||
distribution: distributionType,
|
||||
status: BuildStatus.Finished,
|
||||
buildProfile: flags.profile,
|
||||
simulator,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -88,7 +88,6 @@ export default class BuildVersionGetView extends EasCommand {
|
||||
buildProfile: profile,
|
||||
platform,
|
||||
vcsClient,
|
||||
nonInteractive: flags['non-interactive'],
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getRuntimeVersionNullableAsync } from '@expo/config-plugins/build/utils/Updates';
|
||||
import { getRuntimeVersionNullable } from '@expo/config-plugins/build/utils/Updates';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJsonAccessor, EasJsonUtils } from '@expo/eas-json';
|
||||
import { Flags } from '@oclif/core';
|
||||
@@ -77,7 +77,6 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
buildProfile: profile,
|
||||
platform,
|
||||
vcsClient,
|
||||
nonInteractive: false,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
@@ -117,8 +116,7 @@ export default class BuildVersionSetView extends EasCommand {
|
||||
applicationIdentifier,
|
||||
storeVersion: exp.version ?? '1.0.0',
|
||||
buildVersion: String(version),
|
||||
runtimeVersion:
|
||||
(await getRuntimeVersionNullableAsync(projectDir, exp, platform)) ?? undefined,
|
||||
runtimeVersion: getRuntimeVersionNullable(exp, platform) ?? undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,6 @@ export default class BuildVersionSyncView extends EasCommand {
|
||||
buildProfile: profileInfo.profile,
|
||||
platform: profileInfo.platform,
|
||||
vcsClient,
|
||||
nonInteractive: false,
|
||||
});
|
||||
const remoteVersions = await AppVersionQuery.latestVersionAsync(
|
||||
graphqlClient,
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { deleteChannelOnAppAsync, selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
DeleteUpdateChannelMutation,
|
||||
DeleteUpdateChannelMutationVariables,
|
||||
DeleteUpdateChannelResult,
|
||||
} from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import { toggleConfirmAsync } from '../../prompts';
|
||||
@@ -86,7 +95,32 @@ export default class ChannelDelete extends EasCommand {
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(deletionResult);
|
||||
} else {
|
||||
Log.withTick(`Deleted channel "${channelName}".`);
|
||||
Log.withTick(`️Deleted channel "${channelName}".`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteChannelOnAppAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId }: DeleteUpdateChannelMutationVariables
|
||||
): Promise<DeleteUpdateChannelResult> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateChannelMutation, DeleteUpdateChannelMutationVariables>(
|
||||
gql`
|
||||
mutation DeleteUpdateChannel($channelId: ID!) {
|
||||
updateChannel {
|
||||
deleteUpdateChannel(channelId: $channelId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
channelId,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return data.updateChannel.deleteUpdateChannel;
|
||||
}
|
||||
@@ -1,21 +1,56 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import { print } from 'graphql';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import {
|
||||
getAlwaysTrueBranchMapping,
|
||||
hasEmptyBranchMap,
|
||||
hasStandardBranchMap,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { selectChannelOnAppAsync, updateChannelBranchMappingAsync } from '../../channel/queries';
|
||||
import { hasEmptyBranchMap, hasStandardBranchMap } from '../../channel/branch-mapping';
|
||||
import { selectChannelOnAppAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
UpdateChannelBasicInfoFragment,
|
||||
UpdateChannelBranchMappingMutation,
|
||||
UpdateChannelBranchMappingMutationVariables,
|
||||
} from '../../graphql/generated';
|
||||
import { BranchQuery } from '../../graphql/queries/BranchQuery';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import { UpdateChannelBasicInfoFragmentNode } from '../../graphql/types/UpdateChannelBasicInfo';
|
||||
import Log from '../../log';
|
||||
import { isRollout } from '../../rollout/branch-mapping';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
|
||||
export async function updateChannelBranchMappingAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{ channelId, branchMapping }: UpdateChannelBranchMappingMutationVariables
|
||||
): Promise<UpdateChannelBasicInfoFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<UpdateChannelBranchMappingMutation, UpdateChannelBranchMappingMutationVariables>(
|
||||
gql`
|
||||
mutation UpdateChannelBranchMapping($channelId: ID!, $branchMapping: String!) {
|
||||
updateChannel {
|
||||
editUpdateChannel(channelId: $channelId, branchMapping: $branchMapping) {
|
||||
id
|
||||
...UpdateChannelBasicInfoFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(UpdateChannelBasicInfoFragmentNode)}
|
||||
`,
|
||||
{ channelId, branchMapping }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
const channel = data.updateChannel.editUpdateChannel;
|
||||
if (!channel) {
|
||||
throw new Error(`Could not find a channel with id: ${channelId}`);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
export default class ChannelEdit extends EasCommand {
|
||||
static override description = 'point a channel at a new branch';
|
||||
|
||||
@@ -89,7 +124,11 @@ export default class ChannelEdit extends EasCommand {
|
||||
|
||||
const channel = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: existingChannel.id,
|
||||
branchMapping: JSON.stringify(getAlwaysTrueBranchMapping(branch.id)),
|
||||
// todo: move branch mapping logic to utility
|
||||
branchMapping: JSON.stringify({
|
||||
data: [{ branchId: branch.id, branchMappingLogic: 'true' }],
|
||||
version: 0,
|
||||
}),
|
||||
});
|
||||
|
||||
if (json) {
|
||||
|
||||
@@ -117,7 +117,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
required: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
@@ -125,7 +125,6 @@ export default class ChannelRollout extends EasCommand {
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.Vcs,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
@@ -133,8 +132,7 @@ export default class ChannelRollout extends EasCommand {
|
||||
const { args, flags } = await this.parse(ChannelRollout);
|
||||
const argsAndFlags = this.sanitizeArgsAndFlags({ ...flags, ...args });
|
||||
const {
|
||||
privateProjectConfig: { exp, projectId, projectDir },
|
||||
vcsClient,
|
||||
privateProjectConfig: { exp, projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(ChannelRollout, {
|
||||
nonInteractive: argsAndFlags.nonInteractive,
|
||||
@@ -143,12 +141,12 @@ export default class ChannelRollout extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const app = { projectId, exp, projectDir };
|
||||
const app = { projectId, exp };
|
||||
const ctx = {
|
||||
projectId,
|
||||
nonInteractive: argsAndFlags.nonInteractive,
|
||||
graphqlClient,
|
||||
app,
|
||||
vcsClient,
|
||||
};
|
||||
if (argsAndFlags.nonInteractive) {
|
||||
await new NonInteractiveRollout(argsAndFlags).runAsync(ctx);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
This set of commands does its best to map appcenter codepush commands to EAS update. Generally it works by mapping codepush deployments as EAS channels. Each release on a deployment corresponds to a branch with one update group. That release can be rolled out against the existing release on the deployment. Release history doesn't work too well, but that's ok I hope.
|
||||
|
||||
deployment add - channel create
|
||||
deployment clear - channel edit, create empty branch, point to blank branch
|
||||
deployment history - branch view of current branch associated with the channel
|
||||
deployment list - channel list
|
||||
deployment remove - channel delete
|
||||
deployment rename - not possible, throw error
|
||||
|
||||
release-react: creates new branch, rolls the channel out to N % on that branch.
|
||||
--use-hermes: not supported
|
||||
--extra-hermes-flag: not supported
|
||||
--extra-bundler-option: not supported
|
||||
--target-binary-version -> not supported, inferred runtime version
|
||||
--output-dir -> same as eas update output dir
|
||||
--sourcemap-output-dir -> not supported
|
||||
--sourcemap-output -> not supported
|
||||
--xcode-target-name -> not supported, use target-binary-version
|
||||
--build-configuration-name -> not supported, use target-binary-version
|
||||
--plist-file-prefix -> not supported
|
||||
--xcode-project-file -> not supported
|
||||
--plist-file -> not supported
|
||||
--pod-file -> not supported
|
||||
--gradle-file -> not supported, use target-binary-version
|
||||
--entry-file -> not supported, same as eas update
|
||||
--development -> not supported, think this is same as eas update
|
||||
--bundle-name -> not supported
|
||||
--rollout -> set N % to N
|
||||
--disable-duplicate-release-error -> no idea
|
||||
--private-key-path -> same as eas update
|
||||
--mandatory -> not supported for now
|
||||
--disabled -> set N % to 0
|
||||
--description -> message
|
||||
--deployment-name -> channel to put the new branch on
|
||||
|
||||
promote: copies latest update on "latest branch" on channel to "latest branch" on other channel. it does this by creating a new branch with the update and pointing the channel to that new branch according to rollout or 100% if no rollout supplied.
|
||||
|
||||
rollback: sets the rollout percentage of branch on channel to 0 (i.e. points to prev branch) and republishes the latest update on the prev branch to itself
|
||||
|
||||
patch: only update the rollout percentage
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush deployment add --help
|
||||
|
||||
Add a new deployment to an app
|
||||
|
||||
Usage: appcenter codepush deployment add [-a|--app <arg>] <new-deployment-name>
|
||||
|
||||
Options:
|
||||
new-deployment-name New CodePush deployment name
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { createChannelOnAppWithNoBranchAsync } from '../../../channel/queries';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import Log from '../../../log';
|
||||
import { getDisplayNameForProjectIdAsync } from '../../../project/projectUtils';
|
||||
import formatFields from '../../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
|
||||
|
||||
export default class CodepushDeploymentAdd extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'create a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the deployment to create',
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { name: deploymentName },
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(CodepushDeploymentAdd);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushDeploymentAdd, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const createChannelResult = await createChannelOnAppWithNoBranchAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
const newChannel = createChannelResult.updateChannel.createUpdateChannelForApp;
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(newChannel);
|
||||
} else {
|
||||
Log.addNewLineIfNone();
|
||||
Log.withTick(
|
||||
`Created a new deployment on project ${chalk.bold(
|
||||
await getDisplayNameForProjectIdAsync(graphqlClient, projectId)
|
||||
)}`
|
||||
);
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Name', value: newChannel.name },
|
||||
{ label: 'ID', value: newChannel.id },
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush deployment clear --help
|
||||
|
||||
Clear the release history associated with a deployment
|
||||
|
||||
Usage: appcenter codepush deployment clear [-a|--app <arg>] <deployment-name>
|
||||
|
||||
Options:
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
deployment-name Specifies CodePush deployment name to be cleared
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { getEmptyBranchMapping } from '../../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../../channel/queries';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../../log';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
|
||||
|
||||
export default class CodepushDeploymentClear extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'clear a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the deployment to clear',
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { name: deploymentName },
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(CodepushDeploymentClear);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushDeploymentClear, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const existingChannel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
const channel = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: existingChannel.id,
|
||||
branchMapping: JSON.stringify(getEmptyBranchMapping()),
|
||||
});
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(channel);
|
||||
} else {
|
||||
Log.withTick(chalk`Deployment {bold ${channel.name}} is now cleared.\n`);
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
chalk`Users with builds on deployment {bold ${channel.name}} will no longer have an available update.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush deployment history --help
|
||||
|
||||
Display the release history for a CodePush deployment
|
||||
|
||||
Usage: appcenter codepush deployment history [-a|--app <arg>] <deployment-name>
|
||||
|
||||
Options:
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
deployment-name Specifies CodePush deployment name to view history
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../../log';
|
||||
import { printRollout } from '../../../rollout/utils';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
|
||||
|
||||
export default class CodepushDeploymentHistory extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'get the release history for a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the deployment',
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { name: deploymentName },
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(CodepushDeploymentHistory);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushDeploymentHistory, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const existingChannel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(existingChannel);
|
||||
} else {
|
||||
Log.withTick(chalk`Deployment {bold ${existingChannel.name}} info:\n`);
|
||||
Log.addNewLineIfNone();
|
||||
printRollout(existingChannel);
|
||||
Log.warn('For formatted output, add the --json flag to your command.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush deployment list --help
|
||||
|
||||
List the deployments associated with an app
|
||||
|
||||
Usage: appcenter codepush deployment list [-s|--skipFetchingDeploymentMetrics] [-k|--displayKeys]
|
||||
[-a|--app <arg>]
|
||||
|
||||
Options:
|
||||
-s|--skipFetchingDeploymentMetrics Specifies whether to fetch deployment metrics
|
||||
-k|--displayKeys Specifies whether to display the deployment keys
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import { CHANNELS_LIMIT, listAndRenderChannelsOnAppAsync } from '../../../channel/queries';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import {
|
||||
EasPaginatedQueryFlags,
|
||||
getLimitFlagWithCustomValues,
|
||||
getPaginatedQueryOptions,
|
||||
} from '../../../commandUtils/pagination';
|
||||
import { enableJsonOutput } from '../../../utils/json';
|
||||
|
||||
export default class CodepushDeploymentList extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'list deployments';
|
||||
|
||||
static override flags = {
|
||||
...EasPaginatedQueryFlags,
|
||||
limit: getLimitFlagWithCustomValues({ defaultTo: 10, limit: CHANNELS_LIMIT }),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(CodepushDeploymentList);
|
||||
const paginatedQueryOptions = getPaginatedQueryOptions(flags);
|
||||
const { json: jsonFlag, 'non-interactive': nonInteractive } = flags;
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushDeploymentList, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
await listAndRenderChannelsOnAppAsync(graphqlClient, {
|
||||
projectId,
|
||||
paginatedQueryOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush deployment remove --help
|
||||
|
||||
Remove CodePush deployment
|
||||
|
||||
Usage: appcenter codepush deployment remove [-a|--app <arg>] <deployment-name>
|
||||
|
||||
Options:
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
deployment-name Specifies CodePush deployment name to be removed
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import { deleteChannelOnAppAsync } from '../../../channel/queries';
|
||||
import EasCommand from '../../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../../log';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../../utils/json';
|
||||
|
||||
export default class CodepushDeploymentRemove extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'Delete a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the deployment to delete',
|
||||
},
|
||||
];
|
||||
static override flags = {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { name: deploymentName },
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
} = await this.parse(CodepushDeploymentRemove);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushDeploymentRemove, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const { id: channelId, name: channelName } = await ChannelQuery.viewUpdateChannelAsync(
|
||||
graphqlClient,
|
||||
{
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
}
|
||||
);
|
||||
|
||||
const deletionResult = await deleteChannelOnAppAsync(graphqlClient, {
|
||||
channelId,
|
||||
});
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(deletionResult);
|
||||
} else {
|
||||
Log.withTick(`Deleted deployment "${channelName}".`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush patch --help
|
||||
|
||||
Update the metadata for an existing CodePush release
|
||||
|
||||
Usage: appcenter codepush patch [-r|--rollout <arg>] [-d|--description <arg>]
|
||||
[-t|--target-binary-version <arg>] [-x|--disabled] [-m|--mandatory]
|
||||
[-l|--existing-release-label <arg>] [-a|--app <arg>] <deployment-name>
|
||||
|
||||
Options:
|
||||
-r|--rollout <arg> Specifies percentage of users this release should be
|
||||
immediately available to. (The specified number must be
|
||||
an integer between 1 and 100)
|
||||
-d|--description <arg> Specifies description of the changes made to the app with
|
||||
this release
|
||||
-t|--target-binary-version <arg> Specifies binary app version(s) that specifies this
|
||||
release is targeting for. (The value must be a semver
|
||||
expression such as 1.1.0, ~1.2.3)
|
||||
-x|--disabled Specifies whether this release should be immediately
|
||||
downloadable. (Putting -x flag means disabled)
|
||||
-m|--mandatory Specifies whether this release should be considered
|
||||
mandatory. (Putting -m flag means mandatory)
|
||||
-l|--existing-release-label <arg> Specifies label of one existing release to update.
|
||||
(Defaults to the latest release within the specified
|
||||
deployment)
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
deployment-name Specifies one existing deployment name.
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import {
|
||||
BranchMapping,
|
||||
getAlwaysTrueBranchMapping,
|
||||
getBranchMapping,
|
||||
isAlwaysTrueBranchMapping,
|
||||
isEmptyBranchMapping,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import {
|
||||
editRolloutBranchMapping,
|
||||
getRolloutInfoFromBranchMapping,
|
||||
isRolloutBranchMapping,
|
||||
} from '../../rollout/branch-mapping';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
export default class CodepushPatch extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'update the metadata for the latest release on a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the codepush deployment to patch the latest release on',
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
rollout: Flags.integer({
|
||||
char: 'r',
|
||||
description: 'Percentage of users this release should be available to',
|
||||
required: false,
|
||||
}),
|
||||
disabled: Flags.boolean({
|
||||
char: 'x',
|
||||
description: 'Specifies whether this release should be immediately downloadable',
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
flags: { rollout, disabled, json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
args: { name: deploymentName },
|
||||
} = await this.parse(CodepushPatch);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushPatch, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
// set channel rollout
|
||||
const rolloutPercent = disabled ? 0 : rollout ?? 100;
|
||||
|
||||
const existingBranchMappingString = channel.branchMapping;
|
||||
let updatedBranchMapping: BranchMapping;
|
||||
if (!existingBranchMappingString) {
|
||||
throw new Error(
|
||||
'No existing rollout on deployment. Publish an release with the `release-react` command.'
|
||||
);
|
||||
} else {
|
||||
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
|
||||
if (isEmptyBranchMapping(existingBranchMapping)) {
|
||||
throw new Error(
|
||||
'No existing rollout on deployment. Publish an release with the `release-react` command.'
|
||||
);
|
||||
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
|
||||
throw new Error(
|
||||
'No existing rollout on deployment. Publish an release with the `release-react` command.'
|
||||
);
|
||||
} else if (isRolloutBranchMapping(existingBranchMapping)) {
|
||||
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
|
||||
const existingRolloutRolloutBranchId = rolloutInfo.rolledOutBranchId;
|
||||
updatedBranchMapping =
|
||||
rolloutPercent === 100
|
||||
? getAlwaysTrueBranchMapping(existingRolloutRolloutBranchId)
|
||||
: editRolloutBranchMapping(existingBranchMapping, rolloutPercent);
|
||||
} else {
|
||||
throw new Error('Unrecognized custom deployment structure');
|
||||
}
|
||||
}
|
||||
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(updatedBranchMapping),
|
||||
});
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush promote --help
|
||||
|
||||
Create a new release for the destination deployment, which includes the exact code and metadata from the latest release of the source deployment
|
||||
|
||||
Usage: appcenter codepush promote -s|--source-deployment-name <arg> -d|--destination-deployment-name <arg>
|
||||
[-t|--target-binary-version <arg>] [-r|--rollout <arg>] [--disable-duplicate-release-error]
|
||||
[-x|--disabled] [-m|--mandatory] [-l|--label <arg>] [--description <arg>] [-a|--app <arg>]
|
||||
|
||||
Options:
|
||||
-s|--source-deployment-name <arg> Specifies source deployment name
|
||||
-d|--destination-deployment-name <arg> Specifies destination deployment name
|
||||
-t|--target-binary-version <arg> Specifies binary app version(s) that specifies this
|
||||
release is targeting for. (The value must be a
|
||||
semver expression such as 1.1.0, ~1.2.3)
|
||||
-r|--rollout <arg> Specifies percentage of users this release should be
|
||||
immediately available to. (The specified number must
|
||||
be an integer between 1 and 100)
|
||||
--disable-duplicate-release-error Specifies that if the update is identical to the
|
||||
latest release on the deployment, the CLI should
|
||||
generate a warning instead of an error
|
||||
-x|--disabled Specifies whether this release should be immediately
|
||||
downloadable. (Putting -x flag means disabled)
|
||||
-m|--mandatory Specifies whether this release should be considered
|
||||
mandatory. (Putting -m flag means mandatory)
|
||||
-l|--label <arg> Allows you to pick the specified label from the
|
||||
source deployment and promote it to the destination
|
||||
deployment
|
||||
--description <arg> Specifies description of the changes made to the app
|
||||
with this release
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import { Flags } from '@oclif/core';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { createUpdateBranchOnAppAsync } from '../../branch/queries';
|
||||
import {
|
||||
BranchMapping,
|
||||
getAlwaysTrueBranchMapping,
|
||||
getBranchMapping,
|
||||
isAlwaysTrueBranchMapping,
|
||||
isEmptyBranchMapping,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import {
|
||||
createRolloutBranchMapping,
|
||||
getRolloutInfoFromBranchMapping,
|
||||
isRolloutBranchMapping,
|
||||
} from '../../rollout/branch-mapping';
|
||||
import { republishAsync } from '../../update/republish';
|
||||
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
export default class CodepushPromote extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description =
|
||||
'republish latest release from source deployment to destination deployment';
|
||||
|
||||
static override flags = {
|
||||
sourceDeploymentName: Flags.string({
|
||||
char: 's',
|
||||
description: 'Source deployment',
|
||||
required: true,
|
||||
}),
|
||||
destinationDeploymentName: Flags.string({
|
||||
char: 'd',
|
||||
description: 'Destination deployment',
|
||||
required: true,
|
||||
}),
|
||||
rollout: Flags.integer({
|
||||
char: 'r',
|
||||
description: 'Percentage of users this release should be available to',
|
||||
required: false,
|
||||
}),
|
||||
disabled: Flags.boolean({
|
||||
char: 'x',
|
||||
description: 'Specifies whether this release should be immediately downloadable',
|
||||
required: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
flags: {
|
||||
sourceDeploymentName,
|
||||
destinationDeploymentName,
|
||||
rollout,
|
||||
disabled,
|
||||
'private-key-path': privateKeyPath,
|
||||
json: jsonFlag,
|
||||
'non-interactive': nonInteractive,
|
||||
},
|
||||
} = await this.parse(CodepushPromote);
|
||||
const {
|
||||
privateProjectConfig: { projectId, exp },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushPromote, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!sourceDeploymentName) {
|
||||
throw new Error('Source deployment name may not be empty.');
|
||||
}
|
||||
|
||||
if (!destinationDeploymentName) {
|
||||
throw new Error('Source deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const [sourceChannel, destinationChannel] = await Promise.all([
|
||||
ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: sourceDeploymentName,
|
||||
}),
|
||||
ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: destinationDeploymentName,
|
||||
}),
|
||||
]);
|
||||
|
||||
const sourceBranchMappingString = sourceChannel.branchMapping;
|
||||
let latestBranchIdOnSourceChannel: string;
|
||||
if (!sourceBranchMappingString) {
|
||||
throw new Error(
|
||||
'No releases on source deployment. Publish an release with the `release-react` command.'
|
||||
);
|
||||
} else {
|
||||
const existingBranchMapping = getBranchMapping(sourceBranchMappingString);
|
||||
if (isEmptyBranchMapping(existingBranchMapping)) {
|
||||
throw new Error(
|
||||
'No releases on source deployment. Publish an update with the `release-react` command.'
|
||||
);
|
||||
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
|
||||
latestBranchIdOnSourceChannel = existingBranchMapping.data[0].branchId;
|
||||
} else if (isRolloutBranchMapping(existingBranchMapping)) {
|
||||
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
|
||||
latestBranchIdOnSourceChannel = rolloutInfo.rolledOutBranchId;
|
||||
} else {
|
||||
throw new Error('Unrecognized custom deployment structure');
|
||||
}
|
||||
}
|
||||
|
||||
const latestBranchOnSourceChannel = nullthrows(
|
||||
sourceChannel.updateBranches.find(b => b.id === latestBranchIdOnSourceChannel)
|
||||
);
|
||||
|
||||
// republish latest updates from source branch to new destination branch
|
||||
const createdBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: latestBranchOnSourceChannel.name, // maybe change name
|
||||
});
|
||||
|
||||
const updateGroupToRepublish = latestBranchOnSourceChannel.updateGroups[0];
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(exp, privateKeyPath ?? undefined);
|
||||
const arbitraryUpdate = updateGroupToRepublish[0];
|
||||
const { message: oldUpdateMessage, group: oldGroupId } = arbitraryUpdate;
|
||||
const newUpdateMessage = `Republish "${oldUpdateMessage!}" - group: ${oldGroupId}`;
|
||||
await republishAsync({
|
||||
graphqlClient,
|
||||
app: {
|
||||
projectId,
|
||||
exp,
|
||||
},
|
||||
updatesToPublish: updateGroupToRepublish.map(update => ({
|
||||
...update,
|
||||
groupId: update.group,
|
||||
branchId: update.branch.id,
|
||||
branchName: update.branch.name,
|
||||
})),
|
||||
codeSigningInfo,
|
||||
targetBranch: { branchId: createdBranch.id, branchName: createdBranch.name },
|
||||
updateMessage: newUpdateMessage,
|
||||
});
|
||||
|
||||
// set destination channel rollout
|
||||
const destinationRolloutPercent = disabled ? 0 : rollout ?? 100;
|
||||
|
||||
const destinationBranchMappingString = destinationChannel.branchMapping;
|
||||
let updatedBranchMapping: BranchMapping;
|
||||
if (!destinationBranchMappingString) {
|
||||
if (destinationRolloutPercent !== 100) {
|
||||
throw new Error('Cannot roll out first release on deployment'); // TODO(wschurman): this needs to be possible
|
||||
}
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else {
|
||||
const existingBranchMapping = getBranchMapping(destinationBranchMappingString);
|
||||
if (isEmptyBranchMapping(existingBranchMapping)) {
|
||||
if (destinationRolloutPercent !== 100) {
|
||||
throw new Error('Cannot roll out first release on deployment'); // TODO(wschurman): this needs to be possible
|
||||
}
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
|
||||
const existingDestinationBranchId = existingBranchMapping.data[0].branchId;
|
||||
updatedBranchMapping =
|
||||
destinationRolloutPercent === 100
|
||||
? getAlwaysTrueBranchMapping(existingDestinationBranchId)
|
||||
: createRolloutBranchMapping({
|
||||
defaultBranchId: existingBranchMapping.data[0].branchId,
|
||||
rolloutBranchId: createdBranch.id,
|
||||
runtimeVersion: arbitraryUpdate.runtimeVersion,
|
||||
percent: destinationRolloutPercent,
|
||||
});
|
||||
} else if (isRolloutBranchMapping(existingBranchMapping)) {
|
||||
if (destinationRolloutPercent !== 100) {
|
||||
throw new Error(
|
||||
'Cannot roll out first release on deployment that already has a roll out in progress'
|
||||
); // TODO(wschurman): handle runtime versions (only check for roll out for runtime version)
|
||||
}
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else {
|
||||
throw new Error('Unrecognized custom deployment structure');
|
||||
}
|
||||
}
|
||||
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: destinationChannel.id,
|
||||
branchMapping: JSON.stringify(updatedBranchMapping),
|
||||
});
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush release-react --help
|
||||
|
||||
Release a React Native update to an app deployment
|
||||
|
||||
Usage: appcenter codepush release-react [--use-hermes] [--extra-hermes-flag <arg>] [--extra-bundler-option <arg>]
|
||||
[-t|--target-binary-version <arg>] [-o|--output-dir <arg>] [--sourcemap-output-dir <arg>]
|
||||
[-s|--sourcemap-output <arg>] [-xt|--xcode-target-name <arg>] [-c|--build-configuration-name <arg>]
|
||||
[--plist-file-prefix <arg>] [-xp|--xcode-project-file <arg>] [-p|--plist-file <arg>] [--pod-file <arg>]
|
||||
[-g|--gradle-file <arg>] [-e|--entry-file <arg>] [--development] [-b|--bundle-name <arg>]
|
||||
[-r|--rollout <arg>] [--disable-duplicate-release-error] [-k|--private-key-path <arg>] [-m|--mandatory]
|
||||
[-x|--disabled] [--description <arg>] [-d|--deployment-name <arg>] [-a|--app <arg>]
|
||||
|
||||
Options:
|
||||
--use-hermes Enable hermes and bypass automatic checks
|
||||
--extra-hermes-flag <arg> Flag that gets passed to Hermes, JavaScript to bytecode compiler. Can
|
||||
be specified multiple times
|
||||
--extra-bundler-option <arg> Option that gets passed to react-native bundler. Can be specified
|
||||
multiple times
|
||||
-t|--target-binary-version <arg> Semver expression that specifies the binary app version(s) this
|
||||
release is targeting (e.g. 1.1.0, ~1.2.3)
|
||||
-o|--output-dir <arg> Path to where the bundle should be written. If omitted, the bundle
|
||||
will not be saved on your machine
|
||||
--sourcemap-output-dir <arg> Path to folder where the sourcemap for the resulting bundle should be
|
||||
written. Name of sourcemap file will be generated automatically. This
|
||||
argument will be ignored if "sourcemap-output" argument is provided.
|
||||
If omitted, a sourcemap will not be generated
|
||||
-s|--sourcemap-output <arg> Path to where the sourcemap for the resulting bundle should be
|
||||
written. If omitted, a sourcemap will not be generated
|
||||
-xt|--xcode-target-name <arg> Name of target (PBXNativeTarget) which specifies the binary version
|
||||
you want to target this release at (iOS only)
|
||||
-c|--build-configuration-name <arg> Name of build configuration which specifies the binary version you
|
||||
want to target this release at. For example, "Debug" or "Release" (iOS
|
||||
only)
|
||||
--plist-file-prefix <arg> Prefix to append to the file name when attempting to find your app's
|
||||
Info.plist file (iOS only)
|
||||
-xp|--xcode-project-file <arg> Path to the Xcode project or project.pbxproj file
|
||||
-p|--plist-file <arg> Path to the plist file which specifies the binary version you want to
|
||||
target this release at (iOS only)
|
||||
--pod-file <arg> Path to the cocopods config file (iOS only)
|
||||
-g|--gradle-file <arg> Path to the gradle file which specifies the binary version you want to
|
||||
target this release at (android only)
|
||||
-e|--entry-file <arg> Path to the app's entry JavaScript file. If omitted,
|
||||
"index.<platform>.js" and then "index.js" will be used (if they exist)
|
||||
--development Specifies whether to generate a dev or release build
|
||||
-b|--bundle-name <arg> Name of the generated JS bundle file. If unspecified, the standard
|
||||
bundle name will be used, depending on the specified platform:
|
||||
"main.jsbundle" (iOS), "index.android.bundle" (Android) or
|
||||
"index.windows.bundle" (Windows)
|
||||
-r|--rollout <arg> Percentage of users this release should be available to
|
||||
--disable-duplicate-release-error When this flag is set, releasing a package that is identical to the
|
||||
latest release will produce a warning instead of an error
|
||||
-k|--private-key-path <arg> Specifies the location of a RSA private key to sign the release
|
||||
with.NOTICE: use it for react native applications only, client SDK on
|
||||
other platforms will be ignoring signature verification for now!
|
||||
-m|--mandatory Specifies whether this release should be considered mandatory
|
||||
-x|--disabled Specifies whether this release should be immediately downloadable
|
||||
--description <arg> Description of the changes made to the app in this release
|
||||
-d|--deployment-name <arg> Deployment to release the update to
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import { createUpdateBranchOnAppAsync } from '../../branch/queries';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import {
|
||||
BranchMapping,
|
||||
getAlwaysTrueBranchMapping,
|
||||
getBranchMapping,
|
||||
isAlwaysTrueBranchMapping,
|
||||
isEmptyBranchMapping,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { StatuspageServiceName } from '../../graphql/generated';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
buildAndUploadAssetsAsync,
|
||||
getRuntimeVersionObjectAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
publishUpdateGroupsAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
|
||||
import { createRolloutBranchMapping, isRolloutBranchMapping } from '../../rollout/branch-mapping';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
|
||||
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
|
||||
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
|
||||
export default class CodepushReleaseReact extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'Release an update to a deployment';
|
||||
|
||||
static override flags = {
|
||||
rollout: Flags.integer({
|
||||
char: 'r',
|
||||
description: 'Percentage of users this release should be available to',
|
||||
required: false,
|
||||
}),
|
||||
description: Flags.string({
|
||||
description: 'Description of the changes made to the app in this release',
|
||||
required: true,
|
||||
}),
|
||||
deploymentName: Flags.string({
|
||||
char: 'd',
|
||||
description: 'Deployment to release the update to',
|
||||
required: true,
|
||||
}),
|
||||
disabled: Flags.boolean({
|
||||
char: 'x',
|
||||
description: 'Specifies whether this release should be immediately downloadable',
|
||||
required: false,
|
||||
}),
|
||||
'clear-cache': Flags.boolean({
|
||||
description: `Clear the bundler cache before publishing`,
|
||||
default: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.Vcs,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
flags: {
|
||||
rollout,
|
||||
description,
|
||||
deploymentName,
|
||||
disabled,
|
||||
'clear-cache': clearCache,
|
||||
'private-key-path': privateKeyPath,
|
||||
json: jsonFlag,
|
||||
'non-interactive': nonInteractive,
|
||||
},
|
||||
} = await this.parse(CodepushReleaseReact);
|
||||
const {
|
||||
getDynamicPublicProjectConfigAsync,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
loggedIn: { graphqlClient },
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(CodepushReleaseReact, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
await ensureRepoIsCleanAsync(vcsClient, nonInteractive);
|
||||
|
||||
const {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
projectId,
|
||||
projectDir,
|
||||
} = await getDynamicPublicProjectConfigAsync();
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync({
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
platform: RequestedPlatform.All,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
env: undefined,
|
||||
});
|
||||
|
||||
const { exp } = await getDynamicPublicProjectConfigAsync();
|
||||
const { exp: expPrivate } = await getDynamicPrivateProjectConfigAsync();
|
||||
const codeSigningInfo = await getCodeSigningInfoAsync(expPrivate, privateKeyPath);
|
||||
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
const createdBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
name: description, // TODO(wschurman): make this name something else maybe, maybe something with date or something
|
||||
});
|
||||
|
||||
const {
|
||||
realizedPlatforms,
|
||||
unsortedUpdateInfoGroups,
|
||||
uploadedAssetCount,
|
||||
assetLimitPerUpdateGroup,
|
||||
} = await buildAndUploadAssetsAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
skipBundler: false,
|
||||
projectDir,
|
||||
inputDir: 'dist',
|
||||
exp,
|
||||
platformFlag: 'all',
|
||||
clearCache,
|
||||
});
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync({
|
||||
exp,
|
||||
platforms: realizedPlatforms,
|
||||
projectDir,
|
||||
workflows: {
|
||||
...workflows,
|
||||
web: Workflow.UNKNOWN,
|
||||
},
|
||||
env: undefined,
|
||||
});
|
||||
|
||||
const runtimeVersionsArr = Array.from(new Set(runtimeVersions.map(r => r.runtimeVersion)));
|
||||
if (runtimeVersionsArr.length > 1) {
|
||||
throw new Error(
|
||||
'Using codepush requires the runtime version to be the same for all platforms'
|
||||
);
|
||||
}
|
||||
|
||||
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
||||
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
||||
|
||||
const { newUpdates } = await publishUpdateGroupsAsync({
|
||||
graphqlClient,
|
||||
branchId: createdBranch.id,
|
||||
runtimeVersions,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
unsortedUpdateInfoGroups,
|
||||
updateMessage: description,
|
||||
codeSigningInfo,
|
||||
});
|
||||
|
||||
// set channel rollout
|
||||
const rolloutPercent = disabled ? 0 : rollout ?? 100;
|
||||
const existingBranchMappingString = channel.branchMapping;
|
||||
let updatedBranchMapping: BranchMapping;
|
||||
if (!existingBranchMappingString) {
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else {
|
||||
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
|
||||
if (isEmptyBranchMapping(existingBranchMapping)) {
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
|
||||
updatedBranchMapping =
|
||||
rolloutPercent === 100
|
||||
? getAlwaysTrueBranchMapping(createdBranch.id)
|
||||
: createRolloutBranchMapping({
|
||||
defaultBranchId: existingBranchMapping.data[0].branchId,
|
||||
rolloutBranchId: createdBranch.id,
|
||||
runtimeVersion: runtimeVersionsArr[0],
|
||||
percent: rolloutPercent,
|
||||
});
|
||||
} else if (isRolloutBranchMapping(existingBranchMapping)) {
|
||||
if (rolloutPercent !== 100) {
|
||||
throw new Error('Cannot start a rollout on a deployment when one is already in progress');
|
||||
}
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(createdBranch.id);
|
||||
} else {
|
||||
throw new Error('Unrecognized custom deployment structure');
|
||||
}
|
||||
}
|
||||
|
||||
await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(updatedBranchMapping),
|
||||
});
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
|
||||
} else {
|
||||
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
|
||||
);
|
||||
}
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
for (const runtime of uniqBy(runtimeVersions, version => version.runtimeVersion)) {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime.runtimeVersion
|
||||
);
|
||||
if (newUpdatesForRuntimeVersion.length === 0) {
|
||||
throw new Error(
|
||||
`Publish response is missing updates with runtime ${runtime.runtimeVersion}.`
|
||||
);
|
||||
}
|
||||
const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
|
||||
const newAndroidUpdate = newUpdatesForRuntimeVersion.find(
|
||||
update => update.platform === 'android'
|
||||
);
|
||||
const newIosUpdate = newUpdatesForRuntimeVersion.find(update => update.platform === 'ios');
|
||||
const updateGroupId = newUpdatesForRuntimeVersion[0].group;
|
||||
|
||||
const projectName = exp.slug;
|
||||
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
|
||||
const updateGroupUrl = getUpdateGroupUrl(accountName, projectName, updateGroupId);
|
||||
const updateGroupLink = link(updateGroupUrl, { dim: false });
|
||||
|
||||
Log.log(
|
||||
formatFields([
|
||||
{ label: 'Branch', value: createdBranch.name },
|
||||
{ label: 'Runtime version', value: runtime.runtimeVersion },
|
||||
{ label: 'Platform', value: platforms.join(', ') },
|
||||
{ label: 'Update group ID', value: updateGroupId },
|
||||
...(newAndroidUpdate
|
||||
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
|
||||
: []),
|
||||
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
|
||||
{ label: 'Message', value: description ?? '' },
|
||||
...(gitCommitHash
|
||||
? [
|
||||
{
|
||||
label: 'Commit',
|
||||
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ label: 'EAS Dashboard', value: updateGroupLink },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
if (
|
||||
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
|
||||
) {
|
||||
Log.warn(
|
||||
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
|
||||
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
|
||||
learnMoreMessage: 'Consider optimizing your usage of assets',
|
||||
dim: false,
|
||||
})}.`
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* $ appcenter codepush rollback --help
|
||||
|
||||
Rollback a deployment to a previous release
|
||||
|
||||
Usage: appcenter codepush rollback [--target-release <arg>] [-a|--app <arg>] <deployment-name>
|
||||
|
||||
Options:
|
||||
--target-release <arg> Specifies the release label to be rolled back
|
||||
-a|--app <arg> Specify app in the <ownerName>/<appName> format
|
||||
deployment-name Specifies deployment name to be rolled back
|
||||
|
||||
Common Options (works on all commands):
|
||||
--disable-telemetry Disable telemetry for this command
|
||||
-v|--version Display appcenter version
|
||||
--quiet Auto-confirm any prompts without waiting for input
|
||||
-h|--help Display help for current command
|
||||
--env <arg> Environment when using API token
|
||||
--token <arg> API token
|
||||
--output <arg> Output format: json
|
||||
--debug Display extra output for debugging
|
||||
*/
|
||||
|
||||
import {
|
||||
BranchMapping,
|
||||
getAlwaysTrueBranchMapping,
|
||||
getBranchMapping,
|
||||
isAlwaysTrueBranchMapping,
|
||||
isEmptyBranchMapping,
|
||||
} from '../../channel/branch-mapping';
|
||||
import { updateChannelBranchMappingAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { ChannelQuery } from '../../graphql/queries/ChannelQuery';
|
||||
import Log from '../../log';
|
||||
import {
|
||||
getRolloutInfoFromBranchMapping,
|
||||
isRolloutBranchMapping,
|
||||
} from '../../rollout/branch-mapping';
|
||||
import { enableJsonOutput } from '../../utils/json';
|
||||
|
||||
export default class CodepushRollback extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description = 'roll back a rollout on a deployment';
|
||||
|
||||
static override args = [
|
||||
{
|
||||
name: 'name',
|
||||
required: true,
|
||||
description: 'Name of the codepush deployment to roll back the current rollout on',
|
||||
},
|
||||
];
|
||||
|
||||
static override flags = {
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
flags: { json: jsonFlag, 'non-interactive': nonInteractive },
|
||||
args: { name: deploymentName },
|
||||
} = await this.parse(CodepushRollback);
|
||||
const {
|
||||
privateProjectConfig: { projectId },
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(CodepushRollback, {
|
||||
nonInteractive,
|
||||
});
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
if (!deploymentName) {
|
||||
throw new Error('Deployment name may not be empty.');
|
||||
}
|
||||
|
||||
const channel = await ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
channelName: deploymentName,
|
||||
});
|
||||
|
||||
// set channel rollout
|
||||
|
||||
const existingBranchMappingString = channel.branchMapping;
|
||||
let updatedBranchMapping: BranchMapping;
|
||||
if (!existingBranchMappingString) {
|
||||
throw new Error('No existing rollout on deployment.');
|
||||
} else {
|
||||
const existingBranchMapping = getBranchMapping(existingBranchMappingString);
|
||||
if (isEmptyBranchMapping(existingBranchMapping)) {
|
||||
throw new Error('No existing rollout on deployment.');
|
||||
} else if (isAlwaysTrueBranchMapping(existingBranchMapping)) {
|
||||
throw new Error('No existing rollout on deployment.');
|
||||
} else if (isRolloutBranchMapping(existingBranchMapping)) {
|
||||
const rolloutInfo = getRolloutInfoFromBranchMapping(existingBranchMapping);
|
||||
const existingRolloutDefaultBranchId = rolloutInfo.defaultBranchId;
|
||||
updatedBranchMapping = getAlwaysTrueBranchMapping(existingRolloutDefaultBranchId);
|
||||
} else {
|
||||
throw new Error('Unrecognized custom deployment structure');
|
||||
}
|
||||
}
|
||||
|
||||
const newChannelInfo = await updateChannelBranchMappingAsync(graphqlClient, {
|
||||
channelId: channel.id,
|
||||
branchMapping: JSON.stringify(updatedBranchMapping),
|
||||
});
|
||||
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(`✅ Successfuly updated rollout on ${newChannelInfo.name}`);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { SelectPlatform } from '../../credentials/manager/SelectPlatform';
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import { SelectPlatform } from '../credentials/manager/SelectPlatform';
|
||||
|
||||
export default class Credentials extends EasCommand {
|
||||
static override description = 'manage credentials';
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { Flags } from '@oclif/core';
|
||||
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { SelectBuildProfileFromEasJson } from '../../credentials/manager/SelectBuildProfileFromEasJson';
|
||||
import { SetUpBuildCredentialsCommandAction } from '../../credentials/manager/SetUpBuildCredentialsCommandAction';
|
||||
import { selectPlatformAsync } from '../../platform';
|
||||
|
||||
export default class InitializeBuildCredentials extends EasCommand {
|
||||
static override description = 'Set up credentials for building your project.';
|
||||
|
||||
static override flags = {
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: [Platform.ANDROID, Platform.IOS],
|
||||
}),
|
||||
profile: Flags.string({
|
||||
char: 'e',
|
||||
description: 'The name of the build profile in eas.json.',
|
||||
helpValue: 'PROFILE_NAME',
|
||||
}),
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.DynamicProjectConfig,
|
||||
...this.ContextOptions.Analytics,
|
||||
...this.ContextOptions.Vcs,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(InitializeBuildCredentials);
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
privateProjectConfig,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
analytics,
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(InitializeBuildCredentials, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
const platform = await selectPlatformAsync(flags.platform);
|
||||
|
||||
const buildProfile =
|
||||
flags.profile ??
|
||||
(await new SelectBuildProfileFromEasJson(
|
||||
privateProjectConfig.projectDir,
|
||||
Platform.IOS
|
||||
).getProfileNameFromEasConfigAsync());
|
||||
|
||||
await new SetUpBuildCredentialsCommandAction(
|
||||
actor,
|
||||
graphqlClient,
|
||||
vcsClient,
|
||||
analytics,
|
||||
privateProjectConfig ?? null,
|
||||
getDynamicPrivateProjectConfigAsync,
|
||||
platform,
|
||||
buildProfile,
|
||||
process.cwd()
|
||||
).runAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import chalk from 'chalk';
|
||||
import envinfo from 'envinfo';
|
||||
|
||||
import EasCommand from '../commandUtils/EasCommand';
|
||||
import Log from '../log';
|
||||
import { ora } from '../ora';
|
||||
import { resolveWorkflowAsync } from '../project/workflow';
|
||||
import { easCliVersion } from '../utils/easCli';
|
||||
import { Client } from '../vcs/vcs';
|
||||
@@ -22,7 +20,6 @@ export default class Diagnostics extends EasCommand {
|
||||
nonInteractive: true,
|
||||
});
|
||||
|
||||
const spinner = ora().start(`Gathering diagnostic information...`);
|
||||
const info = await envinfo.run(
|
||||
{
|
||||
System: ['OS', 'Shell'],
|
||||
@@ -43,10 +40,9 @@ export default class Diagnostics extends EasCommand {
|
||||
npmGlobalPackages: ['eas-cli', 'expo-cli'],
|
||||
},
|
||||
{
|
||||
title: chalk.bold(`EAS CLI ${easCliVersion} environment info`),
|
||||
title: `EAS CLI ${easCliVersion} environment info`,
|
||||
}
|
||||
);
|
||||
spinner.succeed('All needed information gathered!');
|
||||
|
||||
Log.log(info.trimEnd());
|
||||
await this.printWorkflowAsync(projectDir, vcsClient);
|
||||
@@ -60,7 +56,7 @@ export default class Diagnostics extends EasCommand {
|
||||
if (androidWorkflow === iosWorkflow) {
|
||||
Log.log(` Project workflow: ${androidWorkflow}`);
|
||||
} else {
|
||||
Log.log(` Project workflow:`);
|
||||
Log.log(` Project Workflow:`);
|
||||
Log.log(` Android: ${androidWorkflow}`);
|
||||
Log.log(` iOS: ${iosWorkflow}`);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,6 @@ describe(ProjectInit.name, () => {
|
||||
it('is no-op if already configured for id', async () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
name: 'testing-123',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
@@ -122,7 +121,6 @@ describe(ProjectInit.name, () => {
|
||||
it('prompts to overwrite when different', async () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
name: 'testing-123',
|
||||
slug: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
@@ -137,7 +135,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -155,7 +152,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -190,7 +186,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -203,7 +198,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -229,7 +223,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -254,7 +247,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -281,7 +273,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -297,7 +288,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -312,7 +302,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -340,7 +329,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -367,7 +355,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -397,7 +384,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -412,7 +398,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-124',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester2/testing-124',
|
||||
ownerAccount: jester2.accounts[0],
|
||||
});
|
||||
@@ -452,7 +437,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -480,7 +464,6 @@ describe(ProjectInit.name, () => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '0129',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConfigError, getProjectConfigDescription } from '@expo/config';
|
||||
import { getProjectConfigDescription } from '@expo/config';
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
@@ -60,19 +60,7 @@ export default class ProjectInit extends EasCommand {
|
||||
projectDir: string,
|
||||
modifications: Partial<ExpoConfig>
|
||||
): Promise<void> {
|
||||
let result;
|
||||
try {
|
||||
result = await createOrModifyExpoConfigAsync(projectDir, modifications);
|
||||
} catch (error) {
|
||||
if (error instanceof ConfigError && error.code === 'MODULE_NOT_FOUND') {
|
||||
Log.warn(
|
||||
'Cannot determine which native SDK version your project uses because the module `expo` is not installed.'
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const result = await createOrModifyExpoConfigAsync(projectDir, modifications);
|
||||
switch (result.type) {
|
||||
case 'success':
|
||||
break;
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
import { ExpoConfig } from '@expo/config-types';
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import { EasJson } from '@expo/eas-json';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { getEASUpdateURL, getExpoWebsiteBaseUrl } from '../../api';
|
||||
import { runBuildAndSubmitAsync } from '../../build/runBuildAndSubmit';
|
||||
import { reviewAndCommitChangesAsync } from '../../build/utils/repository';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { DynamicConfigContextFn } from '../../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { validateOrSetProjectIdAsync } from '../../commandUtils/context/contextUtils/getProjectIdAsync';
|
||||
import { CredentialsContextProjectInfo } from '../../credentials/context';
|
||||
import { SetUpBuildCredentialsCommandAction } from '../../credentials/manager/SetUpBuildCredentialsCommandAction';
|
||||
import {
|
||||
AppFragment,
|
||||
AppPlatform,
|
||||
OnboardingDeviceType,
|
||||
OnboardingEnvironment,
|
||||
} from '../../graphql/generated';
|
||||
import { UserPreferencesMutation } from '../../graphql/mutations/UserPreferencesMutation';
|
||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import {
|
||||
canAccessRepositoryUsingSshAsync,
|
||||
runGitCloneAsync,
|
||||
runGitPushAsync,
|
||||
} from '../../onboarding/git';
|
||||
import { installDependenciesAsync } from '../../onboarding/installDependencies';
|
||||
import { runCommandAsync } from '../../onboarding/runCommand';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { ExpoConfigOptions, getPrivateExpoConfig } from '../../project/expoConfig';
|
||||
import { promptAsync } from '../../prompts';
|
||||
import { Actor } from '../../user/User';
|
||||
import { easCliVersion } from '../../utils/easCli';
|
||||
import GitClient from '../../vcs/clients/git';
|
||||
|
||||
export default class Onboarding extends EasCommand {
|
||||
static override aliases = ['init:onboarding', 'onboarding'];
|
||||
|
||||
static override description =
|
||||
'continue onboarding process started on the https://expo.new website.';
|
||||
|
||||
static override flags = {};
|
||||
|
||||
static override args = [{ name: 'TARGET_PROJECT_DIRECTORY' }];
|
||||
|
||||
static override contextDefinition = {
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.Analytics,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const {
|
||||
args: { TARGET_PROJECT_DIRECTORY: targetProjectDirInput },
|
||||
} = await this.parse(Onboarding);
|
||||
|
||||
const {
|
||||
loggedIn: { actor, graphqlClient },
|
||||
analytics,
|
||||
} = await this.getContextAsync(Onboarding, {
|
||||
nonInteractive: false,
|
||||
});
|
||||
|
||||
if (actor.__typename === 'Robot') {
|
||||
throw new Error(
|
||||
'This command is not available for robot users. Make sure you are not using a robot token and try again.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!actor.preferences.onboarding) {
|
||||
throw new Error(
|
||||
'This command can only be run as part of the onboarding process started on the Expo website. Visit https://expo.new to start a new project.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!actor.preferences.onboarding.platform) {
|
||||
throw new Error(
|
||||
'This command can only be run as part of the onboarding process started on the Expo website. It seems like you started an onboarding process, but we are missing some information needed to be filled in before running the eas init:onboarding command (selected platform). Continue the onboarding process on the Expo website.'
|
||||
);
|
||||
}
|
||||
if (!actor.preferences.onboarding.environment) {
|
||||
throw new Error(
|
||||
'This command can only be run as part of the onboarding process started on the Expo website. It seems like you started an onboarding process, but we are missing some information needed to be filled in before running the eas init:onboarding command (selected environment). Continue the onboarding process on the Expo website.'
|
||||
);
|
||||
}
|
||||
if (!actor.preferences.onboarding.deviceType) {
|
||||
throw new Error(
|
||||
'This command can only be run as part of the onboarding process started on the Expo website. It seems like you started an onboarding process, but we are missing some information needed to be filled in before running the eas init:onboarding command (selected device type). Continue the onboarding process on the Expo website.'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
new Date(actor.preferences.onboarding.lastUsed) < new Date(Date.now() - 1000 * 60 * 60 * 24)
|
||||
) {
|
||||
Log.warn(
|
||||
'It seems like you started an onboarding process, but it has been a while since you last used it. If you want to start a new onboarding process, visit https://expo.new.'
|
||||
);
|
||||
Log.log();
|
||||
}
|
||||
|
||||
const platform =
|
||||
actor.preferences.onboarding.platform === AppPlatform.Android
|
||||
? Platform.ANDROID
|
||||
: Platform.IOS;
|
||||
|
||||
const app = await AppQuery.byIdAsync(graphqlClient, actor.preferences.onboarding.appId);
|
||||
|
||||
const githubUsername = app.githubRepository
|
||||
? app.githubRepository.metadata.githubRepoOwnerName
|
||||
: 'expo';
|
||||
const githubRepositoryName = app.githubRepository
|
||||
? app.githubRepository.metadata.githubRepoName
|
||||
: 'expo-template-default';
|
||||
|
||||
Log.log(`👋 Welcome to Expo, ${actor.username}!`);
|
||||
Log.log();
|
||||
Log.log('✨ We will continue your onboarding process in EAS CLI');
|
||||
Log.log();
|
||||
Log.log(
|
||||
`🚚 Let's start by cloning ${
|
||||
app.githubRepository
|
||||
? `your project (${githubUsername}/${githubRepositoryName})`
|
||||
: `default Expo template project (${githubUsername}/${githubRepositoryName})`
|
||||
} from GitHub and installing dependencies.`
|
||||
);
|
||||
Log.log();
|
||||
let initialTargetProjectDirectory: string;
|
||||
if (targetProjectDirInput) {
|
||||
initialTargetProjectDirectory = targetProjectDirInput;
|
||||
Log.log(`📂 Cloning the project to ${initialTargetProjectDirectory}`);
|
||||
} else {
|
||||
const { selectedTargetProjectDirectory } = await promptAsync({
|
||||
type: 'text',
|
||||
name: 'selectedTargetProjectDirectory',
|
||||
message: app.githubRepository
|
||||
? '📂 Where would you like to clone the project to?'
|
||||
: '📂 Where would you like to create your new project directory?',
|
||||
initial: app.githubRepository
|
||||
? path.join(process.cwd(), githubRepositoryName)
|
||||
: path.join(process.cwd(), `${actor.username}-first-project`),
|
||||
});
|
||||
initialTargetProjectDirectory = selectedTargetProjectDirectory;
|
||||
}
|
||||
Log.log();
|
||||
|
||||
const cloneMethod = (await canAccessRepositoryUsingSshAsync({
|
||||
githubUsername,
|
||||
githubRepositoryName,
|
||||
}))
|
||||
? 'ssh'
|
||||
: 'https';
|
||||
Log.log(chalk.dim(`We detected that ${cloneMethod} is your preffered git clone method`));
|
||||
Log.log();
|
||||
|
||||
const { targetProjectDir: finalTargetProjectDirectory } = await runGitCloneAsync({
|
||||
githubUsername,
|
||||
githubRepositoryName,
|
||||
targetProjectDir: initialTargetProjectDirectory,
|
||||
cloneMethod,
|
||||
});
|
||||
|
||||
const vcsClient = new GitClient(finalTargetProjectDirectory);
|
||||
if (!app.githubRepository) {
|
||||
await fs.remove(path.join(finalTargetProjectDirectory, '.git'));
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'git',
|
||||
args: ['init'],
|
||||
});
|
||||
Log.log();
|
||||
await configureProjectFromBareDefaultExpoTemplateAsync({
|
||||
app,
|
||||
vcsClient,
|
||||
targetDir: finalTargetProjectDirectory,
|
||||
});
|
||||
}
|
||||
|
||||
await installDependenciesAsync({
|
||||
projectDir: finalTargetProjectDirectory,
|
||||
});
|
||||
const exp = await getPrivateExpoConfigWithProjectIdAsync({
|
||||
projectDir: finalTargetProjectDirectory,
|
||||
graphqlClient,
|
||||
actor,
|
||||
});
|
||||
const getDynamicProjectConfigFn = getDynamicPrivateProjectConfigGetter({
|
||||
projectDir: finalTargetProjectDirectory,
|
||||
graphqlClient,
|
||||
actor,
|
||||
});
|
||||
|
||||
if (!app.githubRepository) {
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'npx',
|
||||
args: ['expo', 'install', 'expo-updates'],
|
||||
});
|
||||
Log.log();
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'npx',
|
||||
args: ['expo', 'install', 'expo-insights'],
|
||||
});
|
||||
Log.log();
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'npx',
|
||||
args: ['expo', 'install', 'expo-dev-client'],
|
||||
});
|
||||
Log.log();
|
||||
}
|
||||
await vcsClient.trackFileAsync('package-lock.json');
|
||||
|
||||
const shouldSetupCredentials =
|
||||
((platform === Platform.IOS &&
|
||||
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Device) ||
|
||||
platform === Platform.ANDROID) &&
|
||||
actor.preferences.onboarding.environment === OnboardingEnvironment.DevBuild;
|
||||
if (shouldSetupCredentials) {
|
||||
Log.log('🔑 Now we need to set up build credentials for your project:');
|
||||
await new SetUpBuildCredentialsCommandAction(
|
||||
actor,
|
||||
graphqlClient,
|
||||
vcsClient,
|
||||
analytics,
|
||||
exp,
|
||||
getDynamicProjectConfigFn,
|
||||
platform,
|
||||
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Simulator
|
||||
? 'development-simulator'
|
||||
: 'development',
|
||||
finalTargetProjectDirectory
|
||||
).runAsync();
|
||||
}
|
||||
|
||||
if (app.githubRepository && (await vcsClient.hasUncommittedChangesAsync())) {
|
||||
Log.log(
|
||||
'📦 We will now commit the changes made by the configuration process and push them to GitHub:'
|
||||
);
|
||||
Log.log();
|
||||
Log.log('🔍 Checking for changes in the repository...');
|
||||
await vcsClient.showChangedFilesAsync();
|
||||
await reviewAndCommitChangesAsync(
|
||||
vcsClient,
|
||||
`[eas-onboarding] Install dependencies${
|
||||
shouldSetupCredentials ? ' and set up build credentials' : ''
|
||||
}`,
|
||||
{ nonInteractive: false }
|
||||
);
|
||||
Log.log('📤 Pushing changes to GitHub...');
|
||||
await runGitPushAsync({
|
||||
targetProjectDir: finalTargetProjectDirectory,
|
||||
});
|
||||
} else if (!app.githubRepository) {
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'git',
|
||||
args: ['add', '.'],
|
||||
});
|
||||
Log.log();
|
||||
await runCommandAsync({
|
||||
cwd: finalTargetProjectDirectory,
|
||||
command: 'git',
|
||||
args: ['commit', '-m', 'Initial commit'],
|
||||
});
|
||||
Log.log();
|
||||
}
|
||||
|
||||
Log.log();
|
||||
Log.log('🎉 We finished configuring your project.');
|
||||
Log.log();
|
||||
if (
|
||||
app.githubRepository ||
|
||||
actor.preferences.onboarding.environment === OnboardingEnvironment.ExpoGo
|
||||
) {
|
||||
Log.log('🚀 You can now go back to the website to continue:');
|
||||
const url = new URL(
|
||||
`/onboarding/develop/set-up-project-on-your-machine?project=${app.slug}&accountId=${app.ownerAccount.id}`,
|
||||
getExpoWebsiteBaseUrl()
|
||||
).toString();
|
||||
Log.log(`👉 ${link(url)}`);
|
||||
} else {
|
||||
Log.log('🚀 Now we are going to trigger your first build');
|
||||
Log.log();
|
||||
const { buildIds } = await runBuildAndSubmitAsync(
|
||||
graphqlClient,
|
||||
analytics,
|
||||
vcsClient,
|
||||
finalTargetProjectDirectory,
|
||||
{
|
||||
nonInteractive: true,
|
||||
requestedPlatform:
|
||||
platform === Platform.ANDROID ? RequestedPlatform.Android : RequestedPlatform.Ios,
|
||||
profile:
|
||||
actor.preferences.onboarding.deviceType === OnboardingDeviceType.Simulator
|
||||
? 'development-simulator'
|
||||
: 'development',
|
||||
wait: false,
|
||||
clearCache: false,
|
||||
json: false,
|
||||
autoSubmit: false,
|
||||
localBuildOptions: {},
|
||||
freezeCredentials: false,
|
||||
repack: true,
|
||||
},
|
||||
actor,
|
||||
getDynamicProjectConfigFn
|
||||
);
|
||||
const buildId = buildIds[0];
|
||||
Log.log();
|
||||
Log.log('🚀 You can now go back to the website to continue:');
|
||||
const url = new URL(
|
||||
`/onboarding/develop/set-up-project-on-your-machine?project=${app.slug}&accountId=${app.ownerAccount.id}&buildId=${buildId}`,
|
||||
getExpoWebsiteBaseUrl()
|
||||
).toString();
|
||||
Log.log(`👉 ${link(url)}`);
|
||||
}
|
||||
|
||||
const { __typename, ...previousPreferences } = actor.preferences.onboarding;
|
||||
await UserPreferencesMutation.markCliDoneInOnboardingUserPreferencesAsync(graphqlClient, {
|
||||
...previousPreferences,
|
||||
appId: app.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// we can't get this automated by using command context because when we run a command the project directory doesn't exist yet
|
||||
async function getPrivateExpoConfigWithProjectIdAsync({
|
||||
projectDir,
|
||||
graphqlClient,
|
||||
actor,
|
||||
options,
|
||||
}: {
|
||||
projectDir: string;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
actor: Actor;
|
||||
options?: ExpoConfigOptions;
|
||||
}): Promise<CredentialsContextProjectInfo> {
|
||||
const expBefore = getPrivateExpoConfig(projectDir, options);
|
||||
const projectId = await validateOrSetProjectIdAsync({
|
||||
exp: expBefore,
|
||||
graphqlClient,
|
||||
actor,
|
||||
options: {
|
||||
nonInteractive: false,
|
||||
},
|
||||
cwd: projectDir,
|
||||
});
|
||||
const exp = getPrivateExpoConfig(projectDir, options);
|
||||
return {
|
||||
exp,
|
||||
projectId,
|
||||
};
|
||||
}
|
||||
|
||||
// we can't get this automated by using command context because when we run a command the project directory doesn't exist yet
|
||||
function getDynamicPrivateProjectConfigGetter({
|
||||
projectDir,
|
||||
graphqlClient,
|
||||
actor,
|
||||
}: {
|
||||
projectDir: string;
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
actor: Actor;
|
||||
}): DynamicConfigContextFn {
|
||||
return async (options?: ExpoConfigOptions) => {
|
||||
return {
|
||||
...(await getPrivateExpoConfigWithProjectIdAsync({
|
||||
projectDir,
|
||||
graphqlClient,
|
||||
actor,
|
||||
options,
|
||||
})),
|
||||
projectDir,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
async function configureProjectFromBareDefaultExpoTemplateAsync({
|
||||
app,
|
||||
vcsClient,
|
||||
targetDir,
|
||||
}: {
|
||||
app: AppFragment;
|
||||
vcsClient: GitClient;
|
||||
targetDir: string;
|
||||
}): Promise<void> {
|
||||
// Android package name requires each component to start with a lowercase letter.
|
||||
const isUsernameValidSegment = /^[^a-z]/.test(app.ownerAccount.name);
|
||||
const userPrefix = isUsernameValidSegment ? 'user' : '';
|
||||
const isSlugValidSegment = /^[^a-z]/.test(app.slug);
|
||||
const slugPrefix = isSlugValidSegment ? 'app' : '';
|
||||
|
||||
const bundleIdentifier = `com.${userPrefix}${stripInvalidCharactersForBundleIdentifier(
|
||||
app.ownerAccount.name
|
||||
)}.${slugPrefix}${stripInvalidCharactersForBundleIdentifier(app.slug)}`;
|
||||
const updateUrl = getEASUpdateURL(app.id);
|
||||
|
||||
const easBuildGitHubConfig = {
|
||||
android: {
|
||||
image: 'latest',
|
||||
},
|
||||
ios: {
|
||||
image: 'latest',
|
||||
},
|
||||
};
|
||||
|
||||
const easJson: EasJson = {
|
||||
cli: {
|
||||
version: `>= ${easCliVersion}`,
|
||||
},
|
||||
build: {
|
||||
development: {
|
||||
developmentClient: true,
|
||||
distribution: 'internal',
|
||||
...easBuildGitHubConfig,
|
||||
},
|
||||
'development-simulator': {
|
||||
extends: 'development',
|
||||
ios: {
|
||||
simulator: true,
|
||||
},
|
||||
},
|
||||
preview: {
|
||||
distribution: 'internal',
|
||||
channel: 'main',
|
||||
...easBuildGitHubConfig,
|
||||
},
|
||||
production: {
|
||||
channel: 'production',
|
||||
...easBuildGitHubConfig,
|
||||
},
|
||||
},
|
||||
submit: {
|
||||
production: {},
|
||||
},
|
||||
};
|
||||
|
||||
const easJsonPath = path.join(targetDir, 'eas.json');
|
||||
await fs.writeFile(easJsonPath, `${JSON.stringify(easJson, null, 2)}\n`);
|
||||
await vcsClient.trackFileAsync(easJsonPath);
|
||||
Log.log(
|
||||
`✅ Generated ${chalk.bold('eas.json')}. ${learnMore(
|
||||
'https://docs.expo.dev/build-reference/eas-json/'
|
||||
)}`
|
||||
);
|
||||
Log.log();
|
||||
|
||||
const baseExpoConfig = JSON.parse(await fs.readFile(path.join(targetDir, 'app.json'), 'utf8'))
|
||||
.expo as ExpoConfig;
|
||||
|
||||
const expoConfig: ExpoConfig = {
|
||||
...baseExpoConfig,
|
||||
name: app.name ?? app.slug,
|
||||
slug: app.slug,
|
||||
extra: {
|
||||
eas: {
|
||||
projectId: app.id,
|
||||
},
|
||||
},
|
||||
owner: app.ownerAccount.name,
|
||||
updates: {
|
||||
url: updateUrl,
|
||||
},
|
||||
runtimeVersion: {
|
||||
policy: 'appVersion',
|
||||
},
|
||||
ios: {
|
||||
...baseExpoConfig.ios,
|
||||
bundleIdentifier,
|
||||
},
|
||||
android: {
|
||||
...baseExpoConfig.android,
|
||||
package: bundleIdentifier,
|
||||
},
|
||||
};
|
||||
|
||||
const appJsonPath = path.join(targetDir, 'app.json');
|
||||
await fs.writeFile(appJsonPath, `${JSON.stringify({ expo: expoConfig }, null, 2)}\n`);
|
||||
await vcsClient.trackFileAsync(appJsonPath);
|
||||
Log.log(
|
||||
`✅ Generated ${chalk.bold('app.json')}. ${learnMore(
|
||||
'https://docs.expo.dev/versions/latest/config/app/'
|
||||
)}`
|
||||
);
|
||||
Log.log();
|
||||
}
|
||||
|
||||
function stripInvalidCharactersForBundleIdentifier(string: string): string {
|
||||
return string.replaceAll(/[^A-Za-z0-9]/g, '');
|
||||
}
|
||||
@@ -33,7 +33,6 @@ interface RawCommandFlags {
|
||||
verbose: boolean;
|
||||
wait: boolean;
|
||||
'non-interactive': boolean;
|
||||
'verbose-fastlane': boolean;
|
||||
}
|
||||
|
||||
interface CommandFlags {
|
||||
@@ -43,7 +42,6 @@ interface CommandFlags {
|
||||
verbose: boolean;
|
||||
wait: boolean;
|
||||
nonInteractive: boolean;
|
||||
isVerboseFastlaneEnabled: boolean;
|
||||
}
|
||||
|
||||
export default class Submit extends EasCommand {
|
||||
@@ -85,10 +83,6 @@ export default class Submit extends EasCommand {
|
||||
default: true,
|
||||
allowNo: true,
|
||||
}),
|
||||
'verbose-fastlane': Flags.boolean({
|
||||
default: false,
|
||||
description: 'Enable verbose logging for the submission process',
|
||||
}),
|
||||
'non-interactive': Flags.boolean({
|
||||
default: false,
|
||||
description: 'Run command in non-interactive mode',
|
||||
@@ -138,7 +132,6 @@ export default class Submit extends EasCommand {
|
||||
profile: submissionProfile.profile,
|
||||
archiveFlags: flagsWithPlatform.archiveFlags,
|
||||
nonInteractive: flagsWithPlatform.nonInteractive,
|
||||
isVerboseFastlaneEnabled: flagsWithPlatform.isVerboseFastlaneEnabled,
|
||||
actor,
|
||||
graphqlClient,
|
||||
analytics,
|
||||
@@ -182,7 +175,6 @@ export default class Submit extends EasCommand {
|
||||
wait,
|
||||
profile,
|
||||
'non-interactive': nonInteractive,
|
||||
'verbose-fastlane': isVerboseFastlaneEnabled,
|
||||
...archiveFlags
|
||||
} = flags;
|
||||
|
||||
@@ -203,7 +195,6 @@ export default class Submit extends EasCommand {
|
||||
wait,
|
||||
profile,
|
||||
nonInteractive,
|
||||
isVerboseFastlaneEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ExpoConfig } from '@expo/config';
|
||||
import { instance, mock } from 'ts-mockito';
|
||||
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../../update/configure';
|
||||
import { Client } from '../../../vcs/vcs';
|
||||
|
||||
describe(ensureEASUpdateIsConfiguredAsync, () => {
|
||||
it('errors with "useClassicUpdates" set and no app.json', async () => {
|
||||
const graphqlClient = instance(mock<ExpoGraphqlClient>({}));
|
||||
const vcsClient = instance(mock<Client>({}));
|
||||
const exp: ExpoConfig = {
|
||||
name: 'test',
|
||||
slug: 'test',
|
||||
updates: { useClassicUpdates: true },
|
||||
};
|
||||
|
||||
await expect(async () => {
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp,
|
||||
projectId: 'test',
|
||||
projectDir: '/tmp/test',
|
||||
platform: null,
|
||||
vcsClient,
|
||||
});
|
||||
}).rejects.toThrow(
|
||||
`Your app config sets "updates.useClassicUpdates" but EAS Update does not support classic updates. Remove "useClassicUpdates" from your app config and run this command again.`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
DynamicPublicProjectConfigContextField,
|
||||
} from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||
import VcsClientContextField from '../../../commandUtils/context/VcsClientContextField';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
||||
@@ -22,7 +21,6 @@ import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { collectAssetsAsync, uploadAssetsAsync } from '../../../project/publish';
|
||||
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
|
||||
const projectRoot = '/test-project';
|
||||
const commandOptions = { root: projectRoot } as any;
|
||||
@@ -267,14 +265,10 @@ function mockTestProject({
|
||||
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
|
||||
graphqlClient,
|
||||
});
|
||||
jest
|
||||
.spyOn(VcsClientContextField.prototype, 'getValueAsync')
|
||||
.mockResolvedValue(resolveVcsClient());
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
@@ -330,7 +324,7 @@ function mockTestExport({
|
||||
uniqueUploadedAssetPaths: [],
|
||||
});
|
||||
|
||||
jest.mocked(Updates.getRuntimeVersionAsync).mockResolvedValue(runtimeVersion);
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
return { inputDir: exportDir, platforms, runtimeVersion };
|
||||
}
|
||||
@@ -367,7 +367,6 @@ function mockTestProject({
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
DynamicPublicProjectConfigContextField,
|
||||
} from '../../../commandUtils/context/DynamicProjectConfigContextField';
|
||||
import LoggedInContextField from '../../../commandUtils/context/LoggedInContextField';
|
||||
import VcsClientContextField from '../../../commandUtils/context/VcsClientContextField';
|
||||
import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import FeatureGateEnvOverrides from '../../../commandUtils/gating/FeatureGateEnvOverrides';
|
||||
import FeatureGating from '../../../commandUtils/gating/FeatureGating';
|
||||
@@ -20,7 +19,6 @@ import { UpdateFragment } from '../../../graphql/generated';
|
||||
import { PublishMutation } from '../../../graphql/mutations/PublishMutation';
|
||||
import { AppQuery } from '../../../graphql/queries/AppQuery';
|
||||
import { getBranchNameFromChannelNameAsync } from '../../../update/getBranchNameFromChannelNameAsync';
|
||||
import { resolveVcsClient } from '../../../vcs';
|
||||
import UpdateRollBackToEmbedded from '../roll-back-to-embedded';
|
||||
|
||||
const projectRoot = '/test-project';
|
||||
@@ -86,7 +84,7 @@ describe(UpdateRollBackToEmbedded.name, () => {
|
||||
mockTestProject();
|
||||
const platforms = ['android', 'ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersionAsync).mockResolvedValue(runtimeVersion);
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
branchId: 'branch123',
|
||||
@@ -108,7 +106,7 @@ describe(UpdateRollBackToEmbedded.name, () => {
|
||||
const { projectId } = mockTestProject();
|
||||
const platforms = ['android', 'ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersionAsync).mockResolvedValue(runtimeVersion);
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
jest.mocked(getBranchNameFromChannelNameAsync).mockResolvedValue('branchFromChannel');
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
@@ -156,7 +154,7 @@ describe(UpdateRollBackToEmbedded.name, () => {
|
||||
|
||||
const platforms = ['ios'];
|
||||
const runtimeVersion = 'exposdk:47.0.0';
|
||||
jest.mocked(Updates.getRuntimeVersionAsync).mockResolvedValue(runtimeVersion);
|
||||
jest.mocked(Updates.getRuntimeVersion).mockReturnValue(runtimeVersion);
|
||||
|
||||
// Mock an existing branch, so we don't create a new one
|
||||
jest.mocked(ensureBranchExistsAsync).mockResolvedValue({
|
||||
@@ -252,14 +250,10 @@ function mockTestProject({
|
||||
featureGating: new FeatureGating({}, new FeatureGateEnvOverrides()),
|
||||
graphqlClient,
|
||||
});
|
||||
jest
|
||||
.spyOn(VcsClientContextField.prototype, 'getValueAsync')
|
||||
.mockResolvedValue(resolveVcsClient());
|
||||
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue({
|
||||
id: '1234',
|
||||
slug: 'testing-123',
|
||||
name: 'testing-123',
|
||||
fullName: '@jester/testing-123',
|
||||
ownerAccount: jester.accounts[0],
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ export default class UpdateConfigure extends EasCommand {
|
||||
const platform = flags.platform as RequestedPlatform;
|
||||
const {
|
||||
privateProjectConfig: { projectId, exp, projectDir },
|
||||
loggedIn: { graphqlClient },
|
||||
vcsClient,
|
||||
} = await this.getContextAsync(UpdateConfigure, {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
@@ -46,13 +47,12 @@ export default class UpdateConfigure extends EasCommand {
|
||||
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync({
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp,
|
||||
projectId,
|
||||
projectDir,
|
||||
platform,
|
||||
vcsClient,
|
||||
env: undefined,
|
||||
});
|
||||
|
||||
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
|
||||
|
||||
@@ -1,33 +1,54 @@
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { Platform as PublishPlatform } from '@expo/config';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import { ensureChannelExistsAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import { StatuspageServiceName } from '../../graphql/generated';
|
||||
import fetch from '../../fetch';
|
||||
import {
|
||||
PublishUpdateGroupInput,
|
||||
StatuspageServiceName,
|
||||
UpdateInfoGroup,
|
||||
UpdatePublishMutation,
|
||||
} from '../../graphql/generated';
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
ExpoCLIExportPlatformFlag,
|
||||
buildAndUploadAssetsAsync,
|
||||
RawAsset,
|
||||
buildBundlesAsync,
|
||||
buildUnsortedUpdateInfoGroupAsync,
|
||||
collectAssetsAsync,
|
||||
defaultPublishPlatforms,
|
||||
filterExportedPlatformsByFlag,
|
||||
generateEasMetadataAsync,
|
||||
getBranchNameForCommandAsync,
|
||||
getRequestedPlatform,
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions,
|
||||
getRuntimeVersionObjectAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
isUploadedAssetCountAboveWarningThreshold,
|
||||
publishUpdateGroupsAsync,
|
||||
platformDisplayNames,
|
||||
resolveInputDirectoryAsync,
|
||||
uploadAssetsAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
|
||||
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
|
||||
import { getCodeSigningInfoAsync } from '../../utils/code-signing';
|
||||
import {
|
||||
checkManifestBodyAgainstUpdateInfoGroup,
|
||||
getCodeSigningInfoAsync,
|
||||
getManifestBodyAsync,
|
||||
signBody,
|
||||
} from '../../utils/code-signing';
|
||||
import areSetsEqual from '../../utils/expodash/areSetsEqual';
|
||||
import uniqBy from '../../utils/expodash/uniqBy';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
@@ -35,6 +56,7 @@ import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
|
||||
|
||||
type RawUpdateFlags = {
|
||||
auto: boolean;
|
||||
dev: boolean;
|
||||
branch?: string;
|
||||
channel?: string;
|
||||
message?: string;
|
||||
@@ -44,7 +66,6 @@ type RawUpdateFlags = {
|
||||
'clear-cache': boolean;
|
||||
'private-key-path'?: string;
|
||||
'non-interactive': boolean;
|
||||
'emit-metadata': boolean;
|
||||
json: boolean;
|
||||
/** @deprecated see UpdateRepublish command */
|
||||
group?: string;
|
||||
@@ -54,6 +75,7 @@ type RawUpdateFlags = {
|
||||
|
||||
type UpdateFlags = {
|
||||
auto: boolean;
|
||||
dev: boolean;
|
||||
platform: ExpoCLIExportPlatformFlag;
|
||||
branchName?: string;
|
||||
channelName?: string;
|
||||
@@ -64,7 +86,6 @@ type UpdateFlags = {
|
||||
privateKeyPath?: string;
|
||||
json: boolean;
|
||||
nonInteractive: boolean;
|
||||
emitMetadata: boolean;
|
||||
};
|
||||
|
||||
export default class UpdatePublish extends EasCommand {
|
||||
@@ -105,10 +126,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
description: `Clear the bundler cache before publishing`,
|
||||
default: false,
|
||||
}),
|
||||
'emit-metadata': Flags.boolean({
|
||||
description: `Emit "eas-update-metadata.json" in the bundle folder with detailed information about the generated updates`,
|
||||
default: false,
|
||||
}),
|
||||
platform: Flags.enum({
|
||||
char: 'p',
|
||||
options: [
|
||||
@@ -119,13 +136,17 @@ export default class UpdatePublish extends EasCommand {
|
||||
default: 'all',
|
||||
required: false,
|
||||
}),
|
||||
dev: Flags.boolean({
|
||||
description: 'Publish a development bundle',
|
||||
default: false,
|
||||
}),
|
||||
auto: Flags.boolean({
|
||||
description:
|
||||
'Use the current git branch and commit message for the EAS branch and update message',
|
||||
default: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
@@ -144,6 +165,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
auto: autoFlag,
|
||||
platform: platformFlag,
|
||||
channelName: channelNameArg,
|
||||
dev,
|
||||
updateMessage: updateMessageArg,
|
||||
inputDir,
|
||||
skipBundler,
|
||||
@@ -152,7 +174,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
json: jsonFlag,
|
||||
nonInteractive,
|
||||
branchName: branchNameArg,
|
||||
emitMetadata,
|
||||
} = this.sanitizeFlags(rawFlags);
|
||||
|
||||
const {
|
||||
@@ -168,9 +189,6 @@ export default class UpdatePublish extends EasCommand {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
await vcsClient.ensureRepoExistsAsync();
|
||||
await ensureRepoIsCleanAsync(vcsClient, nonInteractive);
|
||||
|
||||
const {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
projectId,
|
||||
@@ -179,13 +197,12 @@ export default class UpdatePublish extends EasCommand {
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync({
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
platform: getRequestedPlatform(platformFlag),
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
env: undefined,
|
||||
});
|
||||
|
||||
const { exp } = await getDynamicPublicProjectConfigAsync();
|
||||
@@ -210,56 +227,249 @@ export default class UpdatePublish extends EasCommand {
|
||||
jsonFlag,
|
||||
});
|
||||
|
||||
const {
|
||||
distRoot,
|
||||
realizedPlatforms,
|
||||
unsortedUpdateInfoGroups,
|
||||
uploadedAssetCount,
|
||||
assetLimitPerUpdateGroup,
|
||||
} = await buildAndUploadAssetsAsync({
|
||||
graphqlClient,
|
||||
projectId,
|
||||
skipBundler,
|
||||
projectDir,
|
||||
inputDir,
|
||||
exp,
|
||||
platformFlag,
|
||||
clearCache,
|
||||
});
|
||||
// build bundle and upload assets for a new publish
|
||||
if (!skipBundler) {
|
||||
const bundleSpinner = ora().start('Exporting...');
|
||||
try {
|
||||
await buildBundlesAsync({ projectDir, inputDir, dev, exp, platformFlag, clearCache });
|
||||
bundleSpinner.succeed('Exported bundle(s)');
|
||||
} catch (e) {
|
||||
bundleSpinner.fail('Export failed');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
// After possibly bundling, assert that the input directory can be found.
|
||||
const distRoot = await resolveInputDirectoryAsync(inputDir, { skipBundler });
|
||||
|
||||
const assetSpinner = ora().start('Uploading...');
|
||||
let unsortedUpdateInfoGroups: UpdateInfoGroup = {};
|
||||
let uploadedAssetCount = 0;
|
||||
let assetLimitPerUpdateGroup = 0;
|
||||
let realizedPlatforms: PublishPlatform[] = [];
|
||||
|
||||
try {
|
||||
const collectedAssets = await collectAssetsAsync(distRoot);
|
||||
const assets = filterExportedPlatformsByFlag(collectedAssets, platformFlag);
|
||||
realizedPlatforms = Object.keys(assets) as PublishPlatform[];
|
||||
|
||||
// Timeout mechanism:
|
||||
// - Start with NO_ACTIVITY_TIMEOUT. 90 seconds is chosen because the cloud function that processes
|
||||
// uploaded assets has a timeout of 60 seconds and uploading can take some time on a slow connection.
|
||||
// - Each time one or more assets reports as ready, reset the timeout.
|
||||
// - Each time an asset upload begins, reset the timeout. This includes retries.
|
||||
// - Start upload. Internally, uploadAssetsAsync uploads them all first and then checks for successful
|
||||
// processing every (5 + n) seconds with a linear backoff of n + 1 second.
|
||||
// - At the same time as upload is started, start timeout checker which checks every 1 second to see
|
||||
// if timeout has been reached. When timeout expires, send a cancellation signal to currently running
|
||||
// upload function call to instruct it to stop uploading or checking for successful processing.
|
||||
const NO_ACTIVITY_TIMEOUT = 90 * 1000; // 90 seconds
|
||||
let lastUploadedStorageKeys = new Set<string>();
|
||||
let lastAssetUploadResults: {
|
||||
asset: RawAsset & { storageKey: string };
|
||||
finished: boolean;
|
||||
}[] = [];
|
||||
let timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT;
|
||||
const cancelationToken = { isCanceledOrFinished: false };
|
||||
|
||||
const uploadResults = await Promise.race([
|
||||
uploadAssetsAsync(
|
||||
graphqlClient,
|
||||
assets,
|
||||
projectId,
|
||||
cancelationToken,
|
||||
assetUploadResults => {
|
||||
const currentUploadedStorageKeys = new Set(
|
||||
assetUploadResults.filter(r => r.finished).map(r => r.asset.storageKey)
|
||||
);
|
||||
if (!areSetsEqual(currentUploadedStorageKeys, lastUploadedStorageKeys)) {
|
||||
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
|
||||
lastUploadedStorageKeys = currentUploadedStorageKeys;
|
||||
lastAssetUploadResults = assetUploadResults;
|
||||
}
|
||||
|
||||
const totalAssets = assetUploadResults.length;
|
||||
const missingAssetCount = assetUploadResults.filter(a => !a.finished).length;
|
||||
assetSpinner.text = `Uploading (${totalAssets - missingAssetCount}/${totalAssets})`;
|
||||
},
|
||||
() => {
|
||||
// when an upload is retried, reset the timeout as we know this will now need more time
|
||||
timeAtWhichToTimeout = Date.now() + NO_ACTIVITY_TIMEOUT; // reset timeout to NO_ACTIVITY_TIMEOUT
|
||||
}
|
||||
),
|
||||
(async () => {
|
||||
while (Date.now() < timeAtWhichToTimeout) {
|
||||
if (cancelationToken.isCanceledOrFinished) {
|
||||
break;
|
||||
}
|
||||
await new Promise(res => setTimeout(res, 1000)); // wait 1 second
|
||||
}
|
||||
cancelationToken.isCanceledOrFinished = true;
|
||||
const timedOutAssets = lastAssetUploadResults
|
||||
.filter(r => !r.finished)
|
||||
.map(r => `\n- ${r.asset.originalPath ?? r.asset.path}`);
|
||||
throw new Error(`Asset processing timed out for assets: ${timedOutAssets}`);
|
||||
})(),
|
||||
]);
|
||||
|
||||
uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
|
||||
assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
|
||||
unsortedUpdateInfoGroups = await buildUnsortedUpdateInfoGroupAsync(assets, exp);
|
||||
|
||||
// NOTE(cedric): we assume that bundles are always uploaded, and always are part of
|
||||
// `uploadedAssetCount`, perferably we don't assume. For that, we need to refactor the
|
||||
// `uploadAssetsAsync` and be able to determine asset type from the uploaded assets.
|
||||
const uploadedBundleCount = uploadResults.launchAssetCount;
|
||||
const uploadedNormalAssetCount = Math.max(0, uploadedAssetCount - uploadedBundleCount);
|
||||
const reusedNormalAssetCount = uploadResults.uniqueAssetCount - uploadedNormalAssetCount;
|
||||
|
||||
assetSpinner.stop();
|
||||
Log.withTick(
|
||||
`Uploaded ${uploadedBundleCount} app ${uploadedBundleCount === 1 ? 'bundle' : 'bundles'}`
|
||||
);
|
||||
if (uploadedNormalAssetCount === 0) {
|
||||
Log.withTick(`Uploading assets skipped - no new assets found`);
|
||||
} else {
|
||||
let message = `Uploaded ${uploadedNormalAssetCount} ${
|
||||
uploadedNormalAssetCount === 1 ? 'asset' : 'assets'
|
||||
}`;
|
||||
if (reusedNormalAssetCount > 0) {
|
||||
message += ` (reused ${reusedNormalAssetCount} ${
|
||||
reusedNormalAssetCount === 1 ? 'asset' : 'assets'
|
||||
})`;
|
||||
}
|
||||
Log.withTick(message);
|
||||
}
|
||||
for (const uploadedAssetPath of uploadResults.uniqueUploadedAssetPaths) {
|
||||
Log.debug(chalk.dim(`- ${uploadedAssetPath}`));
|
||||
}
|
||||
|
||||
const platformString = (Object.keys(assets) as PublishPlatform[])
|
||||
.map(platform => {
|
||||
const collectedAssetForPlatform = nullthrows(assets[platform]);
|
||||
const totalAssetsForPlatform = collectedAssetForPlatform.assets.length + 1; // launch asset
|
||||
const assetString = totalAssetsForPlatform === 1 ? 'asset' : 'assets';
|
||||
return `${totalAssetsForPlatform} ${platformDisplayNames[platform]} ${assetString}`;
|
||||
})
|
||||
.join(', ');
|
||||
Log.withInfo(
|
||||
`${platformString} (maximum: ${assetLimitPerUpdateGroup} total per update). ${learnMore(
|
||||
'https://expo.fyi/eas-update-asset-limits',
|
||||
{ learnMoreMessage: 'Learn more about asset limits.' }
|
||||
)}`
|
||||
);
|
||||
} catch (e) {
|
||||
assetSpinner.fail('Failed to upload');
|
||||
throw e;
|
||||
}
|
||||
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync(
|
||||
exp,
|
||||
realizedPlatforms,
|
||||
projectDir,
|
||||
vcsClient
|
||||
);
|
||||
const runtimeToPlatformMapping =
|
||||
getRuntimeToPlatformMappingFromRuntimeVersions(runtimeVersions);
|
||||
|
||||
const { branchId, createdBranch } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchName,
|
||||
});
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync({
|
||||
exp,
|
||||
platforms: realizedPlatforms,
|
||||
projectDir,
|
||||
workflows: {
|
||||
...workflows,
|
||||
web: Workflow.UNKNOWN,
|
||||
},
|
||||
env: undefined,
|
||||
});
|
||||
if (createdBranch) {
|
||||
await ensureChannelExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchId,
|
||||
channelName: branchName,
|
||||
});
|
||||
Log.withTick(
|
||||
`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`
|
||||
);
|
||||
}
|
||||
|
||||
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
||||
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
||||
|
||||
const { newUpdates } = await publishUpdateGroupsAsync({
|
||||
graphqlClient,
|
||||
branchId,
|
||||
runtimeVersions,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
unsortedUpdateInfoGroups,
|
||||
updateMessage,
|
||||
codeSigningInfo,
|
||||
});
|
||||
// Sort the updates into different groups based on their platform specific runtime versions
|
||||
const updateGroups: PublishUpdateGroupInput[] = runtimeToPlatformMapping.map(
|
||||
({ runtimeVersion, platforms }) => {
|
||||
const localUpdateInfoGroup = Object.fromEntries(
|
||||
platforms.map(platform => [
|
||||
platform,
|
||||
unsortedUpdateInfoGroups[platform as keyof UpdateInfoGroup],
|
||||
])
|
||||
);
|
||||
|
||||
if (!skipBundler && emitMetadata) {
|
||||
Log.log('Generating eas-update-metadata.json');
|
||||
return {
|
||||
branchId,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
isGitWorkingTreeDirty,
|
||||
awaitingCodeSigningInfo: !!codeSigningInfo,
|
||||
};
|
||||
}
|
||||
);
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
try {
|
||||
newUpdates = await PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
|
||||
|
||||
if (codeSigningInfo) {
|
||||
Log.log('🔒 Signing updates');
|
||||
|
||||
const updatesTemp = [...newUpdates];
|
||||
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
|
||||
const newUpdates = updatesTemp.splice(
|
||||
0,
|
||||
Object.keys(nullthrows(updateGroup.updateInfoGroup)).length
|
||||
);
|
||||
return {
|
||||
updateGroup,
|
||||
newUpdates,
|
||||
};
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
updateGroupsAndTheirUpdates.map(async ({ updateGroup, newUpdates }) => {
|
||||
await Promise.all(
|
||||
newUpdates.map(async newUpdate => {
|
||||
const response = await fetch(newUpdate.manifestPermalink, {
|
||||
method: 'GET',
|
||||
headers: { accept: 'multipart/mixed' },
|
||||
});
|
||||
const manifestBody = nullthrows(await getManifestBodyAsync(response));
|
||||
|
||||
checkManifestBodyAgainstUpdateInfoGroup(
|
||||
manifestBody,
|
||||
nullthrows(
|
||||
nullthrows(updateGroup.updateInfoGroup)[
|
||||
newUpdate.platform as keyof UpdateInfoGroup
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
const manifestSignature = signBody(manifestBody, codeSigningInfo);
|
||||
|
||||
await PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
||||
alg: codeSigningInfo.codeSigningMetadata.alg,
|
||||
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
||||
sig: manifestSignature,
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
publishSpinner.succeed('Published!');
|
||||
} catch (e) {
|
||||
publishSpinner.fail('Failed to publish updates');
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (!skipBundler) {
|
||||
await generateEasMetadataAsync(distRoot, getUpdateJsonInfosForUpdates(newUpdates));
|
||||
}
|
||||
if (jsonFlag) {
|
||||
@@ -314,7 +524,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ label: 'EAS Dashboard', value: updateGroupLink },
|
||||
{ label: 'Website link', value: updateGroupLink },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
@@ -337,7 +547,7 @@ export default class UpdatePublish extends EasCommand {
|
||||
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
|
||||
const nonInteractive = flags['non-interactive'] ?? false;
|
||||
|
||||
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
|
||||
const { auto, branch: branchName, channel: channelName, dev, message: updateMessage } = flags;
|
||||
if (nonInteractive && !auto && !(updateMessage && (branchName || channelName))) {
|
||||
Errors.error(
|
||||
'--branch and --message, or --channel and --message are required when updating in non-interactive mode unless --auto is specified',
|
||||
@@ -362,28 +572,18 @@ export default class UpdatePublish extends EasCommand {
|
||||
Errors.error('--group and --republish flags are deprecated', { exit: 1 });
|
||||
}
|
||||
|
||||
const skipBundler = flags['skip-bundler'] ?? false;
|
||||
let emitMetadata = flags['emit-metadata'] ?? false;
|
||||
|
||||
if (skipBundler && emitMetadata) {
|
||||
emitMetadata = false;
|
||||
Log.warn(
|
||||
'ignoring flag --emit-metadata as metadata cannot be generated when skipping bundle generation'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
auto,
|
||||
branchName,
|
||||
channelName,
|
||||
dev,
|
||||
updateMessage,
|
||||
inputDir: flags['input-dir'],
|
||||
skipBundler,
|
||||
skipBundler: flags['skip-bundler'],
|
||||
clearCache: flags['clear-cache'],
|
||||
platform: flags.platform as RequestedPlatform,
|
||||
privateKeyPath: flags['private-key-path'],
|
||||
nonInteractive,
|
||||
emitMetadata,
|
||||
json: flags.json ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export default class UpdateRepublish extends EasCommand {
|
||||
required: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
@@ -293,7 +293,12 @@ async function askUpdatesFromChannelNameAsync(
|
||||
nonInteractive,
|
||||
}: { projectId: string; channelName: string; json: boolean; nonInteractive: boolean }
|
||||
): Promise<UpdateToRepublish[]> {
|
||||
const branchName = await getBranchNameFromChannelNameAsync(graphqlClient, projectId, channelName);
|
||||
const branchName = await getBranchNameFromChannelNameAsync(
|
||||
graphqlClient,
|
||||
projectId,
|
||||
channelName,
|
||||
{ json, nonInteractive, offset: 0 }
|
||||
);
|
||||
|
||||
return await askUpdatesFromBranchNameAsync(graphqlClient, {
|
||||
projectId,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Platform as PublishPlatform } from '@expo/config';
|
||||
import { Workflow } from '@expo/eas-build-job';
|
||||
import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { getUpdateGroupUrl } from '../../build/utils/url';
|
||||
import { ensureChannelExistsAsync } from '../../channel/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
@@ -32,7 +33,6 @@ import {
|
||||
getRuntimeVersionObjectAsync,
|
||||
getUpdateMessageForCommandAsync,
|
||||
} from '../../project/publish';
|
||||
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
|
||||
import { ensureEASUpdateIsConfiguredAsync } from '../../update/configure';
|
||||
import { getUpdateJsonInfosForUpdates } from '../../update/utils';
|
||||
import {
|
||||
@@ -101,7 +101,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
default: false,
|
||||
}),
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
@@ -148,13 +148,12 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
|
||||
await maybeWarnAboutEasOutagesAsync(graphqlClient, [StatuspageServiceName.EasUpdate]);
|
||||
|
||||
await ensureEASUpdateIsConfiguredAsync({
|
||||
await ensureEASUpdateIsConfiguredAsync(graphqlClient, {
|
||||
exp: expPossiblyWithoutEasUpdateConfigured,
|
||||
platform: getRequestedPlatform(platformFlag),
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
env: undefined,
|
||||
});
|
||||
|
||||
// check that the expo-updates package version supports roll back to embedded
|
||||
@@ -185,25 +184,29 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
const realizedPlatforms: PublishPlatform[] =
|
||||
platformFlag === 'all' ? defaultPublishPlatforms : [platformFlag];
|
||||
|
||||
const { branchId } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
const { branchId, createdBranch } = await ensureBranchExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchName,
|
||||
});
|
||||
if (createdBranch) {
|
||||
await ensureChannelExistsAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
branchId,
|
||||
channelName: branchName,
|
||||
});
|
||||
}
|
||||
|
||||
Log.withTick(`Channel: ${chalk.bold(branchName)} pointed at branch: ${chalk.bold(branchName)}`);
|
||||
|
||||
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
||||
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
||||
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync({
|
||||
const runtimeVersions = await getRuntimeVersionObjectAsync(
|
||||
exp,
|
||||
platforms: realizedPlatforms,
|
||||
realizedPlatforms,
|
||||
projectDir,
|
||||
workflows: {
|
||||
...workflows,
|
||||
web: Workflow.UNKNOWN,
|
||||
},
|
||||
env: undefined,
|
||||
});
|
||||
vcsClient
|
||||
);
|
||||
|
||||
let newUpdates: UpdatePublishMutation['updateBranch']['publishUpdateGroups'];
|
||||
const publishSpinner = ora('Publishing...').start();
|
||||
@@ -276,7 +279,7 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ label: 'EAS Dashboard', value: updateGroupLink },
|
||||
{ label: 'Website link', value: updateGroupLink },
|
||||
])
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class UpdateRollback extends EasCommand {
|
||||
|
||||
static override flags = {
|
||||
'private-key-path': Flags.string({
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
|
||||
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -55,7 +55,6 @@ export const testPKCS12EmptyPasswordKeystore: Keystore = {
|
||||
export const testAppFragment: AppFragment = {
|
||||
id: 'test-app-id',
|
||||
fullName: '@testuser/testapp',
|
||||
name: 'testapp',
|
||||
slug: 'testapp',
|
||||
ownerAccount: {
|
||||
id: 'test-account-id',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppStoreApi from '../ios/appstore/AppStoreApi';
|
||||
import { AuthCtx, AuthenticationMode } from '../ios/appstore/authenticateTypes';
|
||||
import { AuthCtx } from '../ios/appstore/authenticateTypes';
|
||||
|
||||
export const testAuthCtx: AuthCtx = {
|
||||
appleId: 'test-apple-id',
|
||||
@@ -9,7 +9,6 @@ export const testAuthCtx: AuthCtx = {
|
||||
|
||||
export function getAppstoreMock(): AppStoreApi {
|
||||
return {
|
||||
defaultAuthenticationMode: AuthenticationMode.USER,
|
||||
ensureAuthenticatedAsync: jest.fn(),
|
||||
ensureBundleIdExistsAsync: jest.fn(),
|
||||
listDistributionCertificatesAsync: jest.fn(),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import assert from 'node:assert';
|
||||
|
||||
import { AppFragment, Role } from '../../graphql/generated';
|
||||
import { Actor } from '../../user/User';
|
||||
|
||||
@@ -26,7 +24,6 @@ export const jester: Actor = {
|
||||
],
|
||||
isExpoAdmin: false,
|
||||
featureGates: {},
|
||||
preferences: {},
|
||||
};
|
||||
|
||||
export const jester2 = {
|
||||
@@ -44,7 +41,6 @@ export const jester2 = {
|
||||
featureGates: {},
|
||||
};
|
||||
|
||||
assert(jester.__typename === 'User');
|
||||
export const testUsername = jester.username;
|
||||
export const testSlug = 'testApp';
|
||||
export const testProjectId = '7ef93448-3bc7-4b57-be32-99326dcf24f0';
|
||||
@@ -80,7 +76,6 @@ export const testAppJsonWithDifferentOwner = {
|
||||
export const testAppQueryByIdResponse: AppFragment = {
|
||||
id: testProjectId,
|
||||
slug: testSlug,
|
||||
name: 'testing 123',
|
||||
fullName: testExperienceName,
|
||||
ownerAccount: jester.accounts[0],
|
||||
};
|
||||
@@ -55,7 +55,6 @@ export const testAppleAppIdentifierFragment: AppleAppIdentifierFragment = {
|
||||
export const testAppFragment: AppFragment = {
|
||||
id: 'test-app-id',
|
||||
fullName: '@testuser/testapp',
|
||||
name: 'testapp',
|
||||
slug: 'testapp',
|
||||
ownerAccount: {
|
||||
id: 'test-account-id',
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import Log from '../../../log';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
|
||||
export class AssignGoogleServiceAccountKeyForSubmissions {
|
||||
export class AssignGoogleServiceAccountKey {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
public async runAsync(
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
import {
|
||||
CommonAndroidAppCredentialsFragment,
|
||||
GoogleServiceAccountKeyFragment,
|
||||
} from '../../../graphql/generated';
|
||||
import Log from '../../../log';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
|
||||
export class AssignGoogleServiceAccountKeyForFcmV1 {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
public async runAsync(
|
||||
ctx: CredentialsContext,
|
||||
googleServiceAccountKey: GoogleServiceAccountKeyFragment
|
||||
): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
const appCredentials =
|
||||
await ctx.android.createOrGetExistingAndroidAppCredentialsWithBuildCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
const updatedAppCredentials = await ctx.android.updateAndroidAppCredentialsAsync(
|
||||
ctx.graphqlClient,
|
||||
appCredentials,
|
||||
{
|
||||
googleServiceAccountKeyForFcmV1Id: googleServiceAccountKey.id,
|
||||
}
|
||||
);
|
||||
Log.succeed(
|
||||
`Google Service Account Key assigned to ${this.app.androidApplicationIdentifier} for FCM V1`
|
||||
);
|
||||
return updatedAppCredentials;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export class CreateGoogleServiceAccountKey {
|
||||
|
||||
Log.log(
|
||||
`${chalk.bold(
|
||||
'A Google Service Account JSON key is required for uploading your app to Google Play Store, and for sending Android Notifications via FCM V1.'
|
||||
'A Google Service Account JSON key is required to upload your app to Google Play Store'
|
||||
)}.\n` +
|
||||
`If you're not sure what this is or how to create one, ${learnMore(
|
||||
'https://expo.fyi/creating-google-service-account',
|
||||
|
||||
+3
-6
@@ -1,6 +1,6 @@
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { AssignGoogleServiceAccountKeyForSubmissions } from './AssignGoogleServiceAccountKeyForSubmissions';
|
||||
import { AssignGoogleServiceAccountKey } from './AssignGoogleServiceAccountKey';
|
||||
import { CreateGoogleServiceAccountKey } from './CreateGoogleServiceAccountKey';
|
||||
import { UseExistingGoogleServiceAccountKey } from './UseExistingGoogleServiceAccountKey';
|
||||
import {
|
||||
@@ -13,7 +13,7 @@ import { CredentialsContext } from '../../context';
|
||||
import { MissingCredentialsNonInteractiveError } from '../../errors';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
|
||||
export class SetUpGoogleServiceAccountKeyForSubmissions {
|
||||
export class SetUpGoogleServiceAccountKey {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
public async runAsync(ctx: CredentialsContext): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
@@ -46,10 +46,7 @@ export class SetUpGoogleServiceAccountKeyForSubmissions {
|
||||
} else {
|
||||
googleServiceAccountKey = await this.createOrUseExistingKeyAsync(ctx);
|
||||
}
|
||||
return await new AssignGoogleServiceAccountKeyForSubmissions(this.app).runAsync(
|
||||
ctx,
|
||||
googleServiceAccountKey
|
||||
);
|
||||
return await new AssignGoogleServiceAccountKey(this.app).runAsync(ctx, googleServiceAccountKey);
|
||||
}
|
||||
|
||||
private async isGoogleServiceAccountKeySetupAsync(ctx: CredentialsContext): Promise<boolean> {
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { AssignGoogleServiceAccountKeyForFcmV1 } from './AssignGoogleServiceAccountKeyForFcmV1';
|
||||
import { CreateGoogleServiceAccountKey } from './CreateGoogleServiceAccountKey';
|
||||
import { UseExistingGoogleServiceAccountKey } from './UseExistingGoogleServiceAccountKey';
|
||||
import {
|
||||
CommonAndroidAppCredentialsFragment,
|
||||
GoogleServiceAccountKeyFragment,
|
||||
} from '../../../graphql/generated';
|
||||
import Log from '../../../log';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { MissingCredentialsNonInteractiveError } from '../../errors';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
|
||||
export class SetUpGoogleServiceAccountKeyForFcmV1 {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
public async runAsync(ctx: CredentialsContext): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
const isKeySetup = await this.isGoogleServiceAccountKeySetupAsync(ctx);
|
||||
if (isKeySetup) {
|
||||
Log.succeed('Google Service Account Key for FCM V1 already set up.');
|
||||
return nullthrows(
|
||||
await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
),
|
||||
'androidAppCredentials cannot be null if google service account key is already set up'
|
||||
);
|
||||
}
|
||||
if (ctx.nonInteractive) {
|
||||
throw new MissingCredentialsNonInteractiveError(
|
||||
'Google Service Account Keys cannot be set up in --non-interactive mode.'
|
||||
);
|
||||
}
|
||||
|
||||
const keysForAccount = await ctx.android.getGoogleServiceAccountKeysForAccountAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app.account
|
||||
);
|
||||
let googleServiceAccountKey = null;
|
||||
if (keysForAccount.length === 0) {
|
||||
googleServiceAccountKey = await new CreateGoogleServiceAccountKey(this.app.account).runAsync(
|
||||
ctx
|
||||
);
|
||||
} else {
|
||||
googleServiceAccountKey = await this.createOrUseExistingKeyAsync(ctx);
|
||||
}
|
||||
return await new AssignGoogleServiceAccountKeyForFcmV1(this.app).runAsync(
|
||||
ctx,
|
||||
googleServiceAccountKey
|
||||
);
|
||||
}
|
||||
|
||||
private async isGoogleServiceAccountKeySetupAsync(ctx: CredentialsContext): Promise<boolean> {
|
||||
const appCredentials = await ctx.android.getAndroidAppCredentialsWithCommonFieldsAsync(
|
||||
ctx.graphqlClient,
|
||||
this.app
|
||||
);
|
||||
return !!appCredentials?.googleServiceAccountKeyForFcmV1;
|
||||
}
|
||||
|
||||
private async createOrUseExistingKeyAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<GoogleServiceAccountKeyFragment> {
|
||||
const { action } = await promptAsync({
|
||||
type: 'select',
|
||||
name: 'action',
|
||||
message: 'Select the Google Service Account Key to use for FCM V1:',
|
||||
choices: [
|
||||
{
|
||||
title: '[Choose an existing key]',
|
||||
value: 'CHOOSE_EXISTING',
|
||||
},
|
||||
{ title: '[Upload a new service account key]', value: 'GENERATE' },
|
||||
],
|
||||
});
|
||||
|
||||
if (action === 'GENERATE') {
|
||||
return await new CreateGoogleServiceAccountKey(this.app.account).runAsync(ctx);
|
||||
}
|
||||
return (
|
||||
(await new UseExistingGoogleServiceAccountKey(this.app.account).runAsync(ctx)) ??
|
||||
(await this.createOrUseExistingKeyAsync(ctx))
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -2,12 +2,12 @@ import { AppQuery } from '../../../../graphql/queries/AppQuery';
|
||||
import { testGoogleServiceAccountKeyFragment } from '../../../__tests__/fixtures-android';
|
||||
import { testAppQueryByIdResponse } from '../../../__tests__/fixtures-constants';
|
||||
import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { AssignGoogleServiceAccountKeyForSubmissions } from '../AssignGoogleServiceAccountKeyForSubmissions';
|
||||
import { AssignGoogleServiceAccountKey } from '../AssignGoogleServiceAccountKey';
|
||||
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
|
||||
|
||||
jest.mock('../../../../graphql/queries/AppQuery');
|
||||
|
||||
describe(AssignGoogleServiceAccountKeyForSubmissions, () => {
|
||||
describe(AssignGoogleServiceAccountKey, () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue(testAppQueryByIdResponse);
|
||||
});
|
||||
@@ -16,9 +16,7 @@ describe(AssignGoogleServiceAccountKeyForSubmissions, () => {
|
||||
nonInteractive: false,
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKeyForSubmissions(
|
||||
appLookupParams
|
||||
);
|
||||
const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKey(appLookupParams);
|
||||
await assignGoogleServiceAccountKeyAction.runAsync(ctx, testGoogleServiceAccountKeyFragment);
|
||||
|
||||
// expect app credentials to be fetched/created, then updated
|
||||
@@ -32,9 +30,7 @@ describe(AssignGoogleServiceAccountKeyForSubmissions, () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKeyForSubmissions(
|
||||
appLookupParams
|
||||
);
|
||||
const assignGoogleServiceAccountKeyAction = new AssignGoogleServiceAccountKey(appLookupParams);
|
||||
|
||||
// dont fail if users are running in non-interactive mode
|
||||
await expect(
|
||||
+5
-11
@@ -11,7 +11,7 @@ import { testAppQueryByIdResponse } from '../../../__tests__/fixtures-constants'
|
||||
import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { MissingCredentialsNonInteractiveError } from '../../../errors';
|
||||
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
|
||||
import { SetUpGoogleServiceAccountKeyForSubmissions } from '../SetUpGoogleServiceAccountKeyForSubmissions';
|
||||
import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('fs');
|
||||
@@ -24,7 +24,7 @@ beforeEach(() => {
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
describe(SetUpGoogleServiceAccountKeyForSubmissions, () => {
|
||||
describe(SetUpGoogleServiceAccountKey, () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(AppQuery.byIdAsync).mockResolvedValue(testAppQueryByIdResponse);
|
||||
});
|
||||
@@ -39,9 +39,7 @@ describe(SetUpGoogleServiceAccountKeyForSubmissions, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions(
|
||||
appLookupParams
|
||||
);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
await setupGoogleServiceAccountKeyAction.runAsync(ctx);
|
||||
|
||||
expect(ctx.android.createGoogleServiceAccountKeyAsync).not.toHaveBeenCalled();
|
||||
@@ -64,9 +62,7 @@ describe(SetUpGoogleServiceAccountKeyForSubmissions, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions(
|
||||
appLookupParams
|
||||
);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
await setupGoogleServiceAccountKeyAction.runAsync(ctx);
|
||||
|
||||
expect(ctx.android.createGoogleServiceAccountKeyAsync).toHaveBeenCalledTimes(1);
|
||||
@@ -77,9 +73,7 @@ describe(SetUpGoogleServiceAccountKeyForSubmissions, () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKeyForSubmissions(
|
||||
appLookupParams
|
||||
);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
await expect(setupGoogleServiceAccountKeyAction.runAsync(ctx)).rejects.toThrowError(
|
||||
MissingCredentialsNonInteractiveError
|
||||
);
|
||||
|
||||
@@ -106,11 +106,9 @@ export async function updateAndroidAppCredentialsAsync(
|
||||
{
|
||||
androidFcmId,
|
||||
googleServiceAccountKeyForSubmissionsId,
|
||||
googleServiceAccountKeyForFcmV1Id,
|
||||
}: {
|
||||
androidFcmId?: string;
|
||||
googleServiceAccountKeyForSubmissionsId?: string;
|
||||
googleServiceAccountKeyForFcmV1Id?: string;
|
||||
}
|
||||
): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
let updatedAppCredentials = appCredentials;
|
||||
@@ -129,14 +127,6 @@ export async function updateAndroidAppCredentialsAsync(
|
||||
googleServiceAccountKeyForSubmissionsId
|
||||
);
|
||||
}
|
||||
if (googleServiceAccountKeyForFcmV1Id) {
|
||||
updatedAppCredentials =
|
||||
await AndroidAppCredentialsMutation.setGoogleServiceAccountKeyForFcmV1Async(
|
||||
graphqlClient,
|
||||
appCredentials.id,
|
||||
googleServiceAccountKeyForFcmV1Id
|
||||
);
|
||||
}
|
||||
return updatedAppCredentials;
|
||||
}
|
||||
|
||||
|
||||
-39
@@ -8,7 +8,6 @@ import {
|
||||
CommonAndroidAppCredentialsFragment,
|
||||
CreateAndroidAppCredentialsMutation,
|
||||
SetFcmMutation,
|
||||
SetGoogleServiceAccountKeyForFcmV1Mutation,
|
||||
SetGoogleServiceAccountKeyForSubmissionsMutation,
|
||||
} from '../../../../../graphql/generated';
|
||||
import { CommonAndroidAppCredentialsFragmentNode } from '../../../../../graphql/types/credentials/AndroidAppCredentials';
|
||||
@@ -125,42 +124,4 @@ export const AndroidAppCredentialsMutation = {
|
||||
);
|
||||
return data.androidAppCredentials.setGoogleServiceAccountKeyForSubmissions;
|
||||
},
|
||||
async setGoogleServiceAccountKeyForFcmV1Async(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
androidAppCredentialsId: string,
|
||||
googleServiceAccountKeyId: string
|
||||
): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<SetGoogleServiceAccountKeyForFcmV1Mutation>(
|
||||
gql`
|
||||
mutation SetGoogleServiceAccountKeyForFcmV1Mutation(
|
||||
$androidAppCredentialsId: ID!
|
||||
$googleServiceAccountKeyId: ID!
|
||||
) {
|
||||
androidAppCredentials {
|
||||
setGoogleServiceAccountKeyForFcmV1(
|
||||
id: $androidAppCredentialsId
|
||||
googleServiceAccountKeyId: $googleServiceAccountKeyId
|
||||
) {
|
||||
id
|
||||
...CommonAndroidAppCredentialsFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
${print(CommonAndroidAppCredentialsFragmentNode)}
|
||||
`,
|
||||
{
|
||||
androidAppCredentialsId,
|
||||
googleServiceAccountKeyId,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
assert(
|
||||
data.androidAppCredentials.setGoogleServiceAccountKeyForFcmV1,
|
||||
'GraphQL: `setGoogleServiceAccountKeyForFcmV1` not defined in server response'
|
||||
);
|
||||
return data.androidAppCredentials.setGoogleServiceAccountKeyForFcmV1;
|
||||
},
|
||||
};
|
||||
+7
-60
@@ -4,76 +4,27 @@ import gql from 'graphql-tag';
|
||||
import { ExpoGraphqlClient } from '../../../../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { withErrorHandlingAsync } from '../../../../../graphql/client';
|
||||
import {
|
||||
GoogleServiceAccountKeyByAccountQuery,
|
||||
GoogleServiceAccountKeyFragment,
|
||||
GoogleServiceAccountKeysPaginatedByAccountQuery,
|
||||
} from '../../../../../graphql/generated';
|
||||
import { GoogleServiceAccountKeyFragmentNode } from '../../../../../graphql/types/credentials/GoogleServiceAccountKey';
|
||||
import { Connection, QueryParams, fetchEntireDatasetAsync } from '../../../../../utils/relay';
|
||||
|
||||
export const GoogleServiceAccountKeyQuery = {
|
||||
async getAllForAccountAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
accountName: string
|
||||
): Promise<GoogleServiceAccountKeyFragment[]> {
|
||||
const paginatedGetterAsync = async (
|
||||
relayArgs: QueryParams
|
||||
): Promise<Connection<GoogleServiceAccountKeyFragment>> => {
|
||||
return await GoogleServiceAccountKeyQuery.getAllForAccountPaginatedAsync(
|
||||
graphqlClient,
|
||||
accountName,
|
||||
relayArgs
|
||||
);
|
||||
};
|
||||
return await fetchEntireDatasetAsync({
|
||||
paginatedGetterAsync,
|
||||
progressBarLabel: 'fetching Google Service Account Keys...',
|
||||
});
|
||||
},
|
||||
async getAllForAccountPaginatedAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
accountName: string,
|
||||
{
|
||||
after,
|
||||
first,
|
||||
before,
|
||||
last,
|
||||
}: { after?: string; first?: number; before?: string; last?: number }
|
||||
): Promise<
|
||||
GoogleServiceAccountKeysPaginatedByAccountQuery['account']['byName']['googleServiceAccountKeysPaginated']
|
||||
> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.query<GoogleServiceAccountKeysPaginatedByAccountQuery>(
|
||||
.query<GoogleServiceAccountKeyByAccountQuery>(
|
||||
gql`
|
||||
query GoogleServiceAccountKeysPaginatedByAccountQuery(
|
||||
$accountName: String!
|
||||
$after: String
|
||||
$first: Int
|
||||
$before: String
|
||||
$last: Int
|
||||
) {
|
||||
query GoogleServiceAccountKeyByAccountQuery($accountName: String!) {
|
||||
account {
|
||||
byName(accountName: $accountName) {
|
||||
id
|
||||
googleServiceAccountKeysPaginated(
|
||||
after: $after
|
||||
first: $first
|
||||
before: $before
|
||||
last: $last
|
||||
) {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
...GoogleServiceAccountKeyFragment
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
googleServiceAccountKeys {
|
||||
id
|
||||
...GoogleServiceAccountKeyFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,10 +33,6 @@ export const GoogleServiceAccountKeyQuery = {
|
||||
`,
|
||||
{
|
||||
accountName,
|
||||
after,
|
||||
first,
|
||||
before,
|
||||
last,
|
||||
},
|
||||
{
|
||||
additionalTypenames: ['GoogleServiceAccountKey'],
|
||||
@@ -93,6 +40,6 @@ export const GoogleServiceAccountKeyQuery = {
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return data.account.byName.googleServiceAccountKeysPaginated;
|
||||
return data.account.byName.googleServiceAccountKeys;
|
||||
},
|
||||
};
|
||||
Loaded 100 of 214 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user