Compare commits

..
Author SHA1 Message Date
Quinlan Jung d8d7ac68da [eas-cli] amend fingerprint compare url 2025-02-26 20:24:55 -08:00
81 changed files with 522 additions and 5152 deletions

No files matched your search

+1 -5
View File
@@ -3,7 +3,7 @@ commentConfiguration:
epilogue: 'Generated by [CodeMention](https://github.com/expo/expo/actions/workflows/codemention.yaml)' epilogue: 'Generated by [CodeMention](https://github.com/expo/expo/actions/workflows/codemention.yaml)'
rules: rules:
- patterns: ['**/*'] - patterns: ['**/*']
mentions: ['khamilowicz', 'sjchmiela'] mentions: ['szdziedzic', 'khamilowicz', 'sjchmiela', 'radoslawkrzemien']
- patterns: ['packages/eas-cli/src/update/**'] - patterns: ['packages/eas-cli/src/update/**']
mentions: ['wschurman', 'quinlanj'] mentions: ['wschurman', 'quinlanj']
- patterns: ['packages/eas-cli/src/commands/update/**'] - patterns: ['packages/eas-cli/src/commands/update/**']
@@ -18,7 +18,3 @@ rules:
mentions: ['bycedric'] mentions: ['bycedric']
- patterns: ['packages/eas-cli/src/commands/metadata/**'] - patterns: ['packages/eas-cli/src/commands/metadata/**']
mentions: ['bycedric'] mentions: ['bycedric']
- patterns: ['packages/eas-cli/src/worker/**']
mentions: ['kitten', 'kadikraman']
- patterns: ['packages/eas-cli/src/commands/deploy/**']
mentions: ['kitten', 'kadikraman']
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Check if CHANGELOG.md was updated - name: Check if CHANGELOG.md was updated
id: changelog-updated id: changelog-updated
uses: tj-actions/changed-files@b1ba699b304f2083b602164e06a89b868c84f076 uses: tj-actions/changed-files@v41
with: with:
files: CHANGELOG.md files: CHANGELOG.md
- name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent - name: Fail if CHANGELOG.md was not updated and the "no changelog" label is absent
@@ -66,7 +66,7 @@ jobs:
destination_branch: 'main' destination_branch: 'main'
pr_title: 'Upgrade eas-build dependencies' pr_title: 'Upgrade eas-build dependencies'
pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.' pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.'
pr_reviewer: 'radoslawkrzemien,sjchmiela,khamilowicz' pr_reviewer: 'szdziedzic,radoslawkrzemien,sjchmiela,khamilowicz'
github_token: ${{ secrets.EXPO_BOT_PAT }} github_token: ${{ secrets.EXPO_BOT_PAT }}
- uses: actions/github-script@v3 - uses: actions/github-script@v3
-118
View File
@@ -8,131 +8,13 @@ This is the log of notable changes to EAS CLI and related packages.
### 🎉 New features ### 🎉 New features
-- Add `--verbose-logs` flag for `build` command ([#3000](https://github.com/expo/eas-cli/pull/3000) by [@khamilowicz](https://github.com/khamilowicz))
- Add `eas-build-cache-provider` package. ([#3002](https://github.com/expo/eas-cli/pull/3002) by [@gabrieldonadel](https://github.com/gabrieldonadel))
### 🐛 Bug fixes ### 🐛 Bug fixes
- Add missing sourcemap normalization to EAS Hosting worker deployment uploads. ([#3004](https://github.com/expo/eas-cli/pull/3004) by [@kitten](https://github.com/kitten))
### 🧹 Chores
## [16.3.3](https://github.com/expo/eas-cli/releases/tag/v16.3.3) - 2025-04-24
### 🐛 Bug fixes
- Add `sdk-52` alias to the list or supported android images. ([#2989](https://github.com/expo/eas-cli/pull/2989) by [@kadikraman](https://github.com/kadikraman))
- Detect when expo metro config is used. ([#2996](https://github.com/expo/eas-cli/pull/2996) by [@kadikraman](https://github.com/kadikraman))
### 🧹 Chores
- upload assetmap.json when publishing update. ([#2994](https://github.com/expo/eas-cli/pull/2994) by [@quinlanj](https://github.com/quinlanj))
- Add error messages for CDN-level upload errors. ([#2998](https://github.com/expo/eas-cli/pull/2998) by [@kitten](https://github.com/kitten))
## [16.3.2](https://github.com/expo/eas-cli/releases/tag/v16.3.2) - 2025-04-17
### 🧹 Chores
- Update upload command to display build info. ([#2990](https://github.com/expo/eas-cli/pull/2990) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- change launch asset file extension. ([#2991](https://github.com/expo/eas-cli/pull/2991) by [@quinlanj](https://github.com/quinlanj))
## [16.3.1](https://github.com/expo/eas-cli/releases/tag/v16.3.1) - 2025-04-11
### 🧹 Chores
- Add `--no-dev-client` flag for `build:download` command. ([#2985](https://github.com/expo/eas-cli/pull/2985) by [@gabrieldonadel](https://github.com/gabrieldonadel))
## [16.3.0](https://github.com/expo/eas-cli/releases/tag/v16.3.0) - 2025-04-09
### 🎉 New features
- Add `eas upload` command. ([#2932](https://github.com/expo/eas-cli/pull/2932), [#2981](https://github.com/expo/eas-cli/pull/2981), [#2983](https://github.com/expo/eas-cli/pull/2983) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Add `eas build:download` command. ([#2982](https://github.com/expo/eas-cli/pull/2982) by [@gabrieldonadel](https://github.com/gabrieldonadel))
## [16.2.2](https://github.com/expo/eas-cli/releases/tag/v16.2.2) - 2025-04-08
## [16.2.1](https://github.com/expo/eas-cli/releases/tag/v16.2.1) - 2025-04-04
### 🧹 Chores
- Bump `@expo/apple-utils` to use async JWT API. ([#2973](https://github.com/expo/eas-cli/pull/2973) by [@EvanBacon](https://github.com/EvanBacon))
## [16.2.0](https://github.com/expo/eas-cli/releases/tag/v16.2.0) - 2025-04-03
### 🎉 New features
- Add environment flag to `eas fingerprint:compare`. ([#2954](https://github.com/expo/eas-cli/pull/2954) by [@quinlanj](https://github.com/quinlanj))
- Add build-profile flag to `eas fingerprint:generate`. ([#2966](https://github.com/expo/eas-cli/pull/2966) by [@quinlanj](https://github.com/quinlanj))
### 🧹 Chores
- Remove hidden flag from `eas fingerprint:generate`. ([#2965](https://github.com/expo/eas-cli/pull/2965) by [@quinlanj](https://github.com/quinlanj))
- Refactor `eas update` command to improve code readability. ([#2976](https://github.com/expo/eas-cli/pull/2976) by [@quinlanj](https://github.com/quinlanj))
- `eas update`: add warning if no build exists with fingerprint. ([#2977](https://github.com/expo/eas-cli/pull/2977) by [@quinlanj](https://github.com/quinlanj))
## [16.1.0](https://github.com/expo/eas-cli/releases/tag/v16.1.0) - 2025-03-26
### 🎉 New features
- Add environment flag to `eas fingerprint:generate`. ([#2951](https://github.com/expo/eas-cli/pull/2951) by [@quinlanj](https://github.com/quinlanj))
- Add `corepack` field to `eas.json`. ([#2964](https://github.com/expo/eas-cli/pull/2964) by [@szdziedzic](https://github.com/szdziedzic))
## [16.0.1](https://github.com/expo/eas-cli/releases/tag/v16.0.1) - 2025-03-20
### 🐛 Bug fixes
- Make `eas update:configure` re-apply configuration from app.json /app.config.js when run multiple times. ([#2957](https://github.com/expo/eas-cli/pull/2957) by [@brentvatne](https://github.com/brentvatne))
## [16.0.0](https://github.com/expo/eas-cli/releases/tag/v16.0.0) - 2025-03-19
### 🛠 Breaking changes
- Add support for `.easignore` when `requireCommit` is set to `true`. ([#2942](https://github.com/expo/eas-cli/pull/2942) by [@sjchmiela](https://github.com/sjchmiela))
- Up to 15.0.0, if `requireCommit` was `true`, `.easignore` was silently ignored.
- Versions 15.0.0-15.0.13 started using `.easignore` to skip files from being bundled into a tarball when `requireCommit` was `true`. This was an unexpected change in behavior.
- To clear this up, versions 15.0.13-15.0.15 were erroring if `.easignore` was present when `requireCommit` was `true`.
- `[email protected]` formalizes the 15.0.0-15.0.13 behavior by adhering to `.easignore` even when `requireCommit` is set to `true`.
- If you know what you're doing and you want to suppress a warning printed, you can do so by setting `EAS_SUPPRESS_REQUIRE_COMMIT_EASIGNORE_WARNING` environment variable to `true`.
### 🎉 New features
- Add requestId to ApiV2Error. ([#2941](https://github.com/expo/eas-cli/pull/2941) by [@wschurman](https://github.com/wschurman))
- Release `eas fingerprint:generate` in hidden mode ([#2937](https://github.com/expo/eas-cli/pull/2937) by [@quinlanj](https://github.com/quinlanj))
### 🐛 Bug fixes
- Use correct logic to determine whether artifacts have expired in `eas build:run` command. ([#2931](https://github.com/expo/eas-cli/pull/2931) by [@szdziedzic](https://github.com/szdziedzic))
## [15.0.15](https://github.com/expo/eas-cli/releases/tag/v15.0.15) - 2025-03-12
### 🐛 Bug fixes
- Pass through the updates version to `setUpdatesConfigAsync`, which expects it in order to determine which field values to use. ([#2934](https://github.com/expo/eas-cli/pull/2934) by [@brentvatne](https://github.com/brentvatne)).
## [15.0.14](https://github.com/expo/eas-cli/releases/tag/v15.0.14) - 2025-03-06
### 🐛 Bug fixes
- In `EAS_NO_VCS=1`, use Git for repository root when `EAS_PROJECT_ROOT` is not set. ([#2901](https://github.com/expo/eas-cli/pull/2901) by [@sjchmiela](https://github.com/sjchmiela))
### 🧹 Chores
- Suggest using `eas build:dev` for matching configurations. ([#2929](https://github.com/expo/eas-cli/pull/2929) by [@szdziedzic](https://github.com/szdziedzic))
## [15.0.13](https://github.com/expo/eas-cli/releases/tag/v15.0.13) - 2025-03-04
### 🐛 Bug fixes
- Fixed `.git` being always unexpectedly removed if you had `requireCommit: true` and `.easignore` present. ([#2925](https://github.com/expo/eas-cli/pull/2925) by [@sjchmiela](https://github.com/sjchmiela))
### 🧹 Chores ### 🧹 Chores
- Fix `eas fingerprint:compare` URL generation and pretty prints. ([#2909](https://github.com/expo/eas-cli/pull/2909) by [@quinlanj](https://github.com/quinlanj)) - Fix `eas fingerprint:compare` URL generation and pretty prints. ([#2909](https://github.com/expo/eas-cli/pull/2909) by [@quinlanj](https://github.com/quinlanj))
- fix formatFields to handle empty array. ([#2914](https://github.com/expo/eas-cli/pull/2914) by [@quinlanj](https://github.com/quinlanj)) - fix formatFields to handle empty array. ([#2914](https://github.com/expo/eas-cli/pull/2914) by [@quinlanj](https://github.com/quinlanj))
- fix git diff header in `fingerprint:compare`. ([#2915](https://github.com/expo/eas-cli/pull/2915) by [@quinlanj](https://github.com/quinlanj)) - fix git diff header in `fingerprint:compare`. ([#2915](https://github.com/expo/eas-cli/pull/2915) by [@quinlanj](https://github.com/quinlanj))
- amend fingerprint compare url. ([#2923](https://github.com/expo/eas-cli/pull/2923) by [@quinlanj](https://github.com/quinlanj))
- improve error message for `applicationIdSuffix`. ([#2924](https://github.com/expo/eas-cli/pull/2924) by [@kadikraman](https://github.com/kadikraman))
## [15.0.12](https://github.com/expo/eas-cli/releases/tag/v15.0.12) - 2025-02-22 ## [15.0.12](https://github.com/expo/eas-cli/releases/tag/v15.0.12) - 2025-02-22
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"npmClient": "yarn", "npmClient": "yarn",
"version": "16.3.3", "version": "15.0.12",
"$schema": "node_modules/lerna/schemas/lerna-schema.json" "$schema": "node_modules/lerna/schemas/lerna-schema.json"
} }
@@ -1,3 +0,0 @@
# eas-build-cache-provider
A build cache provider plugin for the Expo CLI
@@ -1,58 +0,0 @@
{
"name": "eas-build-cache-provider",
"description": "A build cache provider plugin for the Expo CLI",
"version": "0.1.0",
"author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/config": "10.0.6",
"@babel/code-frame": "7.23.5",
"@expo/spawn-async": "^1.7.2",
"chalk": "4.1.2",
"figures": "3.2.0",
"fs-extra": "11.2.0",
"getenv": "1.0.0",
"log-symbols": "4.1.0",
"semver": "7.5.2",
"terminal-link": "2.1.1",
"tslib": "2.4.1"
},
"devDependencies": {
"@tsconfig/node18": "18.2.4",
"@types/babel__code-frame": "7.0.3",
"@types/fs-extra": "11.0.4",
"@types/getenv": "^1.0.0",
"memfs": "3.4.13",
"rimraf": "3.0.2",
"typescript": "5.3.3"
},
"engines": {
"node": ">=18.0.0"
},
"homepage": "https://github.com/expo/eas-cli",
"license": "MIT",
"main": "build/index.js",
"types": "build/index.d.ts",
"repository": "expo/eas-cli",
"scripts": {
"build": "tsc --project tsconfig.build.json",
"build-allow-unused": "tsc --project tsconfig.allowUnused.json",
"watch": "yarn build --watch --preserveWatchOutput",
"watch-allow-unused": "yarn build-allow-unused --watch --preserveWatchOutput",
"typecheck": "tsc",
"prepack": "yarn rebuild",
"rebuild": "rimraf build && yarn build",
"test": "jest",
"clean": "rimraf build node_modules yarn-error.log"
},
"files": [
"/build"
],
"publishConfig": {
"access": "public"
},
"volta": {
"node": "20.11.0",
"yarn": "1.22.21"
}
}
@@ -1,19 +0,0 @@
module.exports = {
extends: ['universe/shared/typescript-analysis'],
overrides: [
{
files: ['*.ts', '*.d.ts'],
parserOptions: {
project: './packages/eas-build-cache-provider/tsconfig.json',
},
rules: {
'@typescript-eslint/explicit-function-return-type': [
'warn',
{
allowExpressions: true,
},
],
},
},
],
};
@@ -1,41 +0,0 @@
import { getPackageJson } from '@expo/config';
import { SpawnResult } from '@expo/spawn-async';
// import from '@expo/config';
type RunOptions = any;
export function isSpawnResultError(obj: any): obj is Error & SpawnResult {
return (
obj &&
'message' in obj &&
obj.status !== undefined &&
obj.stdout !== undefined &&
obj.stderr !== undefined
);
}
export function isDevClientBuild({
runOptions,
projectRoot,
}: {
runOptions: RunOptions;
projectRoot: string;
}): boolean {
if (!hasDirectDevClientDependency(projectRoot)) {
return false;
}
if ('variant' in runOptions && runOptions.variant !== undefined) {
return runOptions.variant === 'debug';
}
if ('configuration' in runOptions && runOptions.configuration !== undefined) {
return runOptions.configuration === 'Debug';
}
return true;
}
export function hasDirectDevClientDependency(projectRoot: string): boolean {
const { dependencies = {}, devDependencies = {} } = getPackageJson(projectRoot);
return !!dependencies['expo-dev-client'] || !!devDependencies['expo-dev-client'];
}
@@ -1,147 +0,0 @@
import { Platform } from '@expo/config';
import spawnAsync from '@expo/spawn-async';
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { isDevClientBuild, isSpawnResultError } from './helpers';
import Log from './log';
// import from '@expo/config';
type CalculateFingerprintHashProps = any;
type RemoteBuildCachePlugin = any;
type RunOptions = any;
async function resolveRemoteBuildCacheAsync({
projectRoot,
platform,
fingerprintHash,
runOptions,
}: {
projectRoot: string;
platform: Platform;
fingerprintHash: string;
runOptions: RunOptions;
}): Promise<string | null> {
const easJsonPath = path.join(projectRoot, 'eas.json');
if (!(await fs.exists(easJsonPath))) {
Log.debug('eas.json not found, skip checking for remote builds');
return null;
}
Log.log(
chalk`{whiteBright \u203A} {bold Searching builds with matching fingerprint on EAS servers}`
);
try {
const results = await spawnAsync(
'npx',
[
'eas-cli',
'build:download',
`--platform=${platform}`,
`--fingerprint=${fingerprintHash}`,
'--non-interactive',
isDevClientBuild({ runOptions, projectRoot }) ? '--dev-client' : '--no-dev-client',
'--json',
],
{
cwd: projectRoot,
}
);
Log.log(chalk`{whiteBright \u203A} {bold Successfully downloaded cached build}`);
// {
// "path": "/var/folders/03/lppcpcnn61q3mz5ckzmzd8w80000gn/T/eas-cli-nodejs/eas-build-run-cache/c0f9ba9c-0cf1-4c5c-8566-b28b7971050f_22f1bbfa-1c09-4b67-9e4a-721906546b58.app"
// }
const json = JSON.parse(results.stdout.trim());
return json?.path;
} catch (error) {
Log.debug('eas-cli error:', error);
// @TODO(2025-04-11): remove this in a future release
if (isSpawnResultError(error) && error.stderr.includes('command build:download not found')) {
Log.warn(
`To take advantage of remote build cache, upgrade your eas-cli installation to latest.`
);
}
return null;
}
}
async function uploadEASRemoteBuildCacheAsync({
projectRoot,
platform,
fingerprintHash,
buildPath,
}: {
projectRoot: string;
runOptions: RunOptions;
platform: Platform;
fingerprintHash: string;
buildPath?: string;
}): Promise<string | null> {
const easJsonPath = path.join(projectRoot, 'eas.json');
if (!(await fs.exists(easJsonPath))) {
Log.debug('eas.json not found, skip uploading builds');
return null;
}
try {
Log.log(chalk`{whiteBright \u203A} {bold Uploading build to remote cache}`);
const results = await spawnAsync(
'npx',
[
'eas-cli',
'upload',
`--platform=${platform}`,
`--fingerprint=${fingerprintHash}`,
buildPath ? `--build-path=${buildPath}` : '',
'--non-interactive',
'--json',
],
{
cwd: projectRoot,
}
);
// {
// "url": "/var/folders/03/lppcpcnn61q3mz5ckzmzd8w80000gn/T/eas-cli-nodejs/eas-build-run-cache/c0f9ba9c-0cf1-4c5c-8566-b28b7971050f_22f1bbfa-1c09-4b67-9e4a-721906546b58.app"
// }
const json = JSON.parse(results.stdout.trim());
Log.log(chalk`{whiteBright \u203A} {bold Build successfully uploaded: ${json?.url}}`);
return json?.url;
} catch (error) {
Log.debug('eas-cli error:', error);
}
return null;
}
async function calculateEASFingerprintHashAsync({
projectRoot,
platform,
}: CalculateFingerprintHashProps): Promise<string | null> {
// prefer using `eas fingerprint:generate` because it automatically upload sources
try {
const results = await spawnAsync(
'npx',
['eas-cli', 'fingerprint:generate', `--platform=${platform}`, '--json', '--non-interactive'],
{
cwd: projectRoot,
}
);
// {
// "hash": "203f960b965e154b77dc31c6c42e5582e8d77196"
// }
const json = JSON.parse(results.stdout.trim());
return json?.hash;
} catch (error) {
Log.debug('eas-cli error:', error);
}
return null;
}
const EASRemoteBuildCacheProvider: RemoteBuildCachePlugin = {
resolveRemoteBuildCache: resolveRemoteBuildCacheAsync,
uploadRemoteBuildCache: uploadEASRemoteBuildCacheAsync,
calculateFingerprintHash: calculateEASFingerprintHashAsync,
};
export default EASRemoteBuildCacheProvider;
@@ -1,121 +0,0 @@
import chalk from 'chalk';
import figures from 'figures';
import { boolish } from 'getenv';
import logSymbols from 'log-symbols';
import terminalLink from 'terminal-link';
type Color = (...text: string[]) => string;
export default class Log {
public static readonly isDebug = boolish('EXPO_DEBUG', false);
public static log(...args: any[]): void {
Log.consoleLog(...args);
}
public static newLine(): void {
Log.consoleLog();
}
public static addNewLineIfNone(): void {
if (!Log.isLastLineNewLine) {
Log.newLine();
}
}
public static error(...args: any[]): void {
Log.consoleLog(...Log.withTextColor(args, chalk.red));
}
public static warn(...args: any[]): void {
Log.consoleLog(...Log.withTextColor(args, chalk.yellow));
}
public static debug(...args: any[]): void {
if (Log.isDebug) {
Log.consoleLog(...args);
}
}
public static gray(...args: any[]): void {
Log.consoleLog(...Log.withTextColor(args, chalk.gray));
}
public static warnDeprecatedFlag(flag: string, message: string): void {
Log.warn(`› ${chalk.bold('--' + flag)} flag is deprecated. ${message}`);
}
public static fail(message: string): void {
Log.log(`${chalk.red(logSymbols.error)} ${message}`);
}
public static succeed(message: string): void {
Log.log(`${chalk.green(logSymbols.success)} ${message}`);
}
public static withTick(...args: any[]): void {
Log.consoleLog(chalk.green(figures.tick), ...args);
}
public static withInfo(...args: any[]): void {
Log.consoleLog(chalk.green(figures.info), ...args);
}
private static consoleLog(...args: any[]): void {
Log.updateIsLastLineNewLine(args);
// eslint-disable-next-line no-console
console.log(...args);
}
private static withTextColor(args: any[], chalkColor: Color): string[] {
return args.map(arg => chalkColor(arg));
}
private static isLastLineNewLine = false;
private static updateIsLastLineNewLine(args: any[]): void {
if (args.length === 0) {
Log.isLastLineNewLine = true;
} else {
const lastArg = args[args.length - 1];
if (typeof lastArg === 'string' && (lastArg === '' || lastArg.match(/[\r\n]$/))) {
Log.isLastLineNewLine = true;
} else {
Log.isLastLineNewLine = false;
}
}
}
}
/**
* Prints a link for given URL, using text if provided, otherwise text is just the URL.
* Format links as dim (unless disabled) and with an underline.
*
* @example https://expo.dev
*/
export function link(
url: string,
{ text = url, fallback, dim = true }: { text?: string; dim?: boolean; fallback?: string } = {}
): string {
// Links can be disabled via env variables https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
const output = terminalLink(text, url, {
fallback: () =>
fallback ?? (text === url ? chalk.underline(url) : `${text}: ${chalk.underline(url)}`),
});
return dim ? chalk.dim(output) : output;
}
/**
* Provide a consistent "Learn more" link experience.
* Format links as dim (unless disabled) with an underline.
*
* @example Learn more: https://expo.dev
*/
export function learnMore(
url: string,
{
learnMoreMessage: maybeLearnMoreMessage,
dim = true,
}: { learnMoreMessage?: string; dim?: boolean } = {}
): string {
return link(url, { text: maybeLearnMoreMessage ?? 'Learn more', dim });
}
@@ -1,4 +0,0 @@
{
"extends": "./tsconfig",
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
}
@@ -1,18 +0,0 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"compilerOptions": {
"declaration": true,
"importHelpers": true,
"module": "commonjs",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "build",
"rootDir": "src",
"typeRoots": ["../../node_modules/@types", "../../ts-declarations", "./node_modules/@types"]
},
"include": ["src/**/*"]
}
+85 -143
View File
@@ -70,7 +70,6 @@ eas --help COMMAND
* [`eas build:configure`](#eas-buildconfigure) * [`eas build:configure`](#eas-buildconfigure)
* [`eas build:delete [BUILD_ID]`](#eas-builddelete-build_id) * [`eas build:delete [BUILD_ID]`](#eas-builddelete-build_id)
* [`eas build:dev`](#eas-builddev) * [`eas build:dev`](#eas-builddev)
* [`eas build:download`](#eas-builddownload)
* [`eas build:inspect`](#eas-buildinspect) * [`eas build:inspect`](#eas-buildinspect)
* [`eas build:list`](#eas-buildlist) * [`eas build:list`](#eas-buildlist)
* [`eas build:resign`](#eas-buildresign) * [`eas build:resign`](#eas-buildresign)
@@ -108,7 +107,6 @@ eas --help COMMAND
* [`eas env:push [ENVIRONMENT]`](#eas-envpush-environment) * [`eas env:push [ENVIRONMENT]`](#eas-envpush-environment)
* [`eas env:update [ENVIRONMENT]`](#eas-envupdate-environment) * [`eas env:update [ENVIRONMENT]`](#eas-envupdate-environment)
* [`eas fingerprint:compare [HASH1] [HASH2]`](#eas-fingerprintcompare-hash1-hash2) * [`eas fingerprint:compare [HASH1] [HASH2]`](#eas-fingerprintcompare-hash1-hash2)
* [`eas fingerprint:generate`](#eas-fingerprintgenerate)
* [`eas help [COMMAND]`](#eas-help-command) * [`eas help [COMMAND]`](#eas-help-command)
* [`eas init`](#eas-init) * [`eas init`](#eas-init)
* [`eas init:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-initonboarding-target_project_directory) * [`eas init:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-initonboarding-target_project_directory)
@@ -162,7 +160,7 @@ ALIASES
$ eas login $ eas login
``` ```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/login.ts)_ _See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout` ## `eas account:logout`
@@ -179,7 +177,7 @@ ALIASES
$ eas logout $ eas logout
``` ```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/logout.ts)_ _See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view` ## `eas account:view`
@@ -196,7 +194,7 @@ ALIASES
$ eas whoami $ eas whoami
``` ```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/view.ts)_ _See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]` ## `eas analytics [STATUS]`
@@ -210,7 +208,7 @@ DESCRIPTION
display or change analytics settings display or change analytics settings
``` ```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/analytics.ts)_ _See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]` ## `eas autocomplete [SHELL]`
@@ -262,7 +260,7 @@ DESCRIPTION
create a branch create a branch
``` ```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/create.ts)_ _See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]` ## `eas branch:delete [NAME]`
@@ -283,7 +281,7 @@ DESCRIPTION
delete a branch delete a branch
``` ```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/delete.ts)_ _See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list` ## `eas branch:list`
@@ -303,7 +301,7 @@ DESCRIPTION
list all branches list all branches
``` ```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/list.ts)_ _See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename` ## `eas branch:rename`
@@ -323,7 +321,7 @@ DESCRIPTION
rename a branch rename a branch
``` ```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/rename.ts)_ _See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]` ## `eas branch:view [NAME]`
@@ -346,7 +344,7 @@ DESCRIPTION
view a branch view a branch
``` ```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/view.ts)_ _See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build` ## `eas build`
@@ -383,7 +381,7 @@ DESCRIPTION
start a build start a build
``` ```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/index.ts)_ _See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]` ## `eas build:cancel [BUILD_ID]`
@@ -402,7 +400,7 @@ DESCRIPTION
cancel a build cancel a build
``` ```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/cancel.ts)_ _See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure` ## `eas build:configure`
@@ -419,7 +417,7 @@ DESCRIPTION
configure the project to support EAS Build configure the project to support EAS Build
``` ```
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/configure.ts)_ _See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]` ## `eas build:delete [BUILD_ID]`
@@ -438,7 +436,7 @@ DESCRIPTION
delete a build delete a build
``` ```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/delete.ts)_ _See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:dev` ## `eas build:dev`
@@ -458,28 +456,7 @@ DESCRIPTION
run dev client simulator/emulator build with matching fingerprint or create a new one run dev client simulator/emulator build with matching fingerprint or create a new one
``` ```
_See code: [packages/eas-cli/src/commands/build/dev.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/dev.ts)_ _See code: [packages/eas-cli/src/commands/build/dev.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/dev.ts)_
## `eas build:download`
download simulator/emulator builds for a given fingerprint hash
```
USAGE
$ eas build:download --fingerprint <value> [-p ios|android] [--dev-client] [--json --non-interactive]
FLAGS
-p, --platform=(ios|android)
--[no-]dev-client Filter only dev-client builds.
--fingerprint=<value> (required) Fingerprint hash of the build to download
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
DESCRIPTION
download simulator/emulator builds for a given fingerprint hash
```
_See code: [packages/eas-cli/src/commands/build/download.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/download.ts)_
## `eas build:inspect` ## `eas build:inspect`
@@ -514,7 +491,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting inspect the state of the project at specific build stages, useful for troubleshooting
``` ```
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/inspect.ts)_ _See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list` ## `eas build:list`
@@ -566,7 +543,7 @@ DESCRIPTION
list all builds for your project list all builds for your project
``` ```
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/list.ts)_ _See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign` ## `eas build:resign`
@@ -595,7 +572,7 @@ DESCRIPTION
re-sign a build archive re-sign a build archive
``` ```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/resign.ts)_ _See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run` ## `eas build:run`
@@ -621,7 +598,7 @@ DESCRIPTION
run simulator/emulator builds from eas-cli run simulator/emulator builds from eas-cli
``` ```
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/run.ts)_ _See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit` ## `eas build:submit`
@@ -671,7 +648,7 @@ DESCRIPTION
get the latest version from EAS servers 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/v16.3.3/packages/eas-cli/src/commands/build/version/get.ts)_ _See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set` ## `eas build:version:set`
@@ -690,7 +667,7 @@ DESCRIPTION
update version of an app update version of an app
``` ```
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/version/set.ts)_ _See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync` ## `eas build:version:sync`
@@ -709,7 +686,7 @@ DESCRIPTION
update a version in native code with a value stored on EAS servers update a version in native code with a value stored on EAS servers
``` ```
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/version/sync.ts)_ _See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]` ## `eas build:view [BUILD_ID]`
@@ -726,7 +703,7 @@ DESCRIPTION
view a build for your project view a build for your project
``` ```
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/view.ts)_ _See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]` ## `eas channel:create [NAME]`
@@ -747,7 +724,7 @@ DESCRIPTION
create a channel create a channel
``` ```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/create.ts)_ _See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]` ## `eas channel:edit [NAME]`
@@ -769,7 +746,7 @@ DESCRIPTION
point a channel at a new branch point a channel at a new branch
``` ```
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/edit.ts)_ _See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list` ## `eas channel:list`
@@ -789,7 +766,7 @@ DESCRIPTION
list all channels list all channels
``` ```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/list.ts)_ _See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:pause [NAME]` ## `eas channel:pause [NAME]`
@@ -811,7 +788,7 @@ DESCRIPTION
pause a channel to stop it from sending updates pause a channel to stop it from sending updates
``` ```
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/pause.ts)_ _See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/pause.ts)_
## `eas channel:resume [NAME]` ## `eas channel:resume [NAME]`
@@ -833,7 +810,7 @@ DESCRIPTION
resume a channel to start sending updates resume a channel to start sending updates
``` ```
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/resume.ts)_ _See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/resume.ts)_
## `eas channel:rollout [CHANNEL]` ## `eas channel:rollout [CHANNEL]`
@@ -866,7 +843,7 @@ DESCRIPTION
Roll a new branch out on a channel incrementally. 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/v16.3.3/packages/eas-cli/src/commands/channel/rollout.ts)_ _See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]` ## `eas channel:view [NAME]`
@@ -889,7 +866,7 @@ DESCRIPTION
view a channel view a channel
``` ```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/view.ts)_ _See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config` ## `eas config`
@@ -910,7 +887,7 @@ DESCRIPTION
display project configuration (app.json + eas.json) display project configuration (app.json + eas.json)
``` ```
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/config.ts)_ _See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/config.ts)_
## `eas credentials` ## `eas credentials`
@@ -927,7 +904,7 @@ DESCRIPTION
manage credentials manage credentials
``` ```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/credentials/index.ts)_ _See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build` ## `eas credentials:configure-build`
@@ -945,11 +922,11 @@ DESCRIPTION
Set up credentials for building your project. 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/v16.3.3/packages/eas-cli/src/commands/credentials/configure-build.ts)_ _See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas deploy [options]` ## `eas deploy [options]`
deploy your Expo Router web build and API Routes Deploy your Expo Router web build and API Routes.
``` ```
USAGE USAGE
@@ -967,13 +944,13 @@ FLAGS
--prod Create a new production deployment. --prod Create a new production deployment.
DESCRIPTION DESCRIPTION
deploy your Expo Router web build and API Routes Deploy your Expo Router web build and API Routes.
ALIASES ALIASES
$ eas worker:deploy $ eas worker:deploy
``` ```
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/deploy/index.ts)_ _See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/deploy/index.ts)_
## `eas deploy:alias` ## `eas deploy:alias`
@@ -998,7 +975,7 @@ ALIASES
$ eas deploy:promote $ eas deploy:promote
``` ```
_See code: [packages/eas-cli/src/commands/deploy/alias.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/deploy/alias.ts)_ _See code: [packages/eas-cli/src/commands/deploy/alias.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/deploy/alias.ts)_
## `eas deploy:promote` ## `eas deploy:promote`
@@ -1035,7 +1012,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution 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/v16.3.3/packages/eas-cli/src/commands/device/create.ts)_ _See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete` ## `eas device:delete`
@@ -1055,7 +1032,7 @@ DESCRIPTION
remove a registered device from your account remove a registered device from your account
``` ```
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/delete.ts)_ _See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list` ## `eas device:list`
@@ -1076,7 +1053,7 @@ DESCRIPTION
list all registered devices for your account list all registered devices for your account
``` ```
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/list.ts)_ _See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename` ## `eas device:rename`
@@ -1097,7 +1074,7 @@ DESCRIPTION
rename a registered device rename a registered device
``` ```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/rename.ts)_ _See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]` ## `eas device:view [UDID]`
@@ -1111,7 +1088,7 @@ DESCRIPTION
view a device for your project view a device for your project
``` ```
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/view.ts)_ _See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics` ## `eas diagnostics`
@@ -1125,7 +1102,7 @@ DESCRIPTION
display environment info display environment info
``` ```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/diagnostics.ts)_ _See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas env:create [ENVIRONMENT]` ## `eas env:create [ENVIRONMENT]`
@@ -1154,7 +1131,7 @@ DESCRIPTION
create an environment variable for the current project or account create an environment variable for the current project or account
``` ```
_See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/create.ts)_ _See code: [packages/eas-cli/src/commands/env/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/create.ts)_
## `eas env:delete [ENVIRONMENT]` ## `eas env:delete [ENVIRONMENT]`
@@ -1178,7 +1155,7 @@ DESCRIPTION
delete an environment variable for the current project or account delete an environment variable for the current project or account
``` ```
_See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/delete.ts)_ _See code: [packages/eas-cli/src/commands/env/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/delete.ts)_
## `eas env:exec ENVIRONMENT BASH_COMMAND` ## `eas env:exec ENVIRONMENT BASH_COMMAND`
@@ -1199,7 +1176,7 @@ DESCRIPTION
execute a command with environment variables from the selected environment execute a command with environment variables from the selected environment
``` ```
_See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/exec.ts)_ _See code: [packages/eas-cli/src/commands/env/exec.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/exec.ts)_
## `eas env:get [ENVIRONMENT]` ## `eas env:get [ENVIRONMENT]`
@@ -1224,7 +1201,7 @@ DESCRIPTION
view an environment variable for the current project or account view an environment variable for the current project or account
``` ```
_See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/get.ts)_ _See code: [packages/eas-cli/src/commands/env/get.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/get.ts)_
## `eas env:list [ENVIRONMENT]` ## `eas env:list [ENVIRONMENT]`
@@ -1249,7 +1226,7 @@ DESCRIPTION
list environment variables for the current project or account list environment variables for the current project or account
``` ```
_See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/list.ts)_ _See code: [packages/eas-cli/src/commands/env/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/list.ts)_
## `eas env:pull [ENVIRONMENT]` ## `eas env:pull [ENVIRONMENT]`
@@ -1272,7 +1249,7 @@ DESCRIPTION
pull environment variables for the selected environment to .env file pull environment variables for the selected environment to .env file
``` ```
_See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/pull.ts)_ _See code: [packages/eas-cli/src/commands/env/pull.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/pull.ts)_
## `eas env:push [ENVIRONMENT]` ## `eas env:push [ENVIRONMENT]`
@@ -1293,7 +1270,7 @@ DESCRIPTION
push environment variables from .env file to the selected environment push environment variables from .env file to the selected environment
``` ```
_See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/push.ts)_ _See code: [packages/eas-cli/src/commands/env/push.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/push.ts)_
## `eas env:update [ENVIRONMENT]` ## `eas env:update [ENVIRONMENT]`
@@ -1324,7 +1301,7 @@ DESCRIPTION
update an environment variable on the current project or account update an environment variable on the current project or account
``` ```
_See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/env/update.ts)_ _See code: [packages/eas-cli/src/commands/env/update.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/env/update.ts)_
## `eas fingerprint:compare [HASH1] [HASH2]` ## `eas fingerprint:compare [HASH1] [HASH2]`
@@ -1332,21 +1309,19 @@ compare fingerprints of the current project, builds, and updates
``` ```
USAGE USAGE
$ eas fingerprint:compare [HASH1] [HASH2] [--build-id <value>] [--update-id <value>] [--open] [--environment $ eas fingerprint:compare [HASH1] [HASH2] [--build-id <value>] [--update-id <value>] [--open] [--json
development|preview|production] [--json --non-interactive] --non-interactive]
ARGUMENTS ARGUMENTS
HASH1 If provided alone, HASH1 is compared against the current project's fingerprint. HASH1 If provided alone, HASH1 is compared against the current project's fingerprint.
HASH2 If two hashes are provided, HASH1 is compared against HASH2. HASH2 If two hashes are provided, HASH1 is compared against HASH2.
FLAGS FLAGS
--build-id=<value>... Compare the fingerprint with the build with the specified ID --build-id=<value>... Compare the fingerprint with the build with the specified ID
--environment=(development|preview|production) If generating a fingerprint from the local directory, use the --json Enable JSON output, non-JSON messages will be printed to stderr.
specified environment. --non-interactive Run the command in non-interactive mode.
--json Enable JSON output, non-JSON messages will be printed to stderr. --open Open the fingerprint comparison in the browser
--non-interactive Run the command in non-interactive mode. --update-id=<value>... Compare the fingerprint with the update with the specified ID
--open Open the fingerprint comparison in the browser
--update-id=<value>... Compare the fingerprint with the update with the specified ID
DESCRIPTION DESCRIPTION
compare fingerprints of the current project, builds, and updates compare fingerprints of the current project, builds, and updates
@@ -1360,8 +1335,6 @@ EXAMPLES
$ eas fingerprint:compare --build-id <BUILD-ID> # Compare fingerprint from build against local directory $ eas fingerprint:compare --build-id <BUILD-ID> # Compare fingerprint from build against local directory
$ eas fingerprint:compare --build-id <BUILD-ID> --environment production # Compare fingerprint from build against local directory with the "production" environment
$ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2> # Compare fingerprint from a build against another build $ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2> # Compare fingerprint from a build against another build
$ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID> # Compare fingerprint from build against fingerprint from update $ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID> # Compare fingerprint from build against fingerprint from update
@@ -1369,38 +1342,7 @@ EXAMPLES
$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> # Compare fingerprint from update against provided fingerprint $ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> # Compare fingerprint from update against provided fingerprint
``` ```
_See code: [packages/eas-cli/src/commands/fingerprint/compare.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/fingerprint/compare.ts)_ _See code: [packages/eas-cli/src/commands/fingerprint/compare.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/fingerprint/compare.ts)_
## `eas fingerprint:generate`
generate fingerprints from the current project
```
USAGE
$ eas fingerprint:generate [-p android|ios] [--environment development|preview|production | -e <value>] [--json
--non-interactive]
FLAGS
-e, --build-profile=<value> Name of the build profile from eas.json.
-p, --platform=(android|ios)
--environment=(development|preview|production) Environment variable's environment
--json Enable JSON output, non-JSON messages will be printed to stderr.
--non-interactive Run the command in non-interactive mode.
DESCRIPTION
generate fingerprints from the current project
EXAMPLES
$ eas fingerprint:generate # Generate fingerprint in interactive mode
$ eas fingerprint:generate --build-profile preview # Generate a fingerprint using the "preview" build profile
$ eas fingerprint:generate --environment preview # Generate a fingerprint using the "preview" environment
$ eas fingerprint:generate --json --non-interactive --platform android # Output fingerprint json to stdout
```
_See code: [packages/eas-cli/src/commands/fingerprint/generate.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/fingerprint/generate.ts)_
## `eas help [COMMAND]` ## `eas help [COMMAND]`
@@ -1508,7 +1450,7 @@ DESCRIPTION
validate the local store configuration validate the local store configuration
``` ```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/metadata/lint.ts)_ _See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull` ## `eas metadata:pull`
@@ -1525,7 +1467,7 @@ DESCRIPTION
generate the local store configuration from the app stores 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/v16.3.3/packages/eas-cli/src/commands/metadata/pull.ts)_ _See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push` ## `eas metadata:push`
@@ -1542,7 +1484,7 @@ DESCRIPTION
sync the local store configuration to the app stores 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/v16.3.3/packages/eas-cli/src/commands/metadata/push.ts)_ _See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas onboarding [TARGET_PROJECT_DIRECTORY]` ## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1572,7 +1514,7 @@ DESCRIPTION
open the project page in a web browser open the project page in a web browser
``` ```
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/open.ts)_ _See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/open.ts)_
## `eas project:info` ## `eas project:info`
@@ -1586,7 +1528,7 @@ DESCRIPTION
information about the current project information about the current project
``` ```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/info.ts)_ _See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init` ## `eas project:init`
@@ -1609,7 +1551,7 @@ ALIASES
$ eas init $ eas init
``` ```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/init.ts)_ _See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]` ## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1627,7 +1569,7 @@ ALIASES
$ eas onboarding $ eas onboarding
``` ```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/onboarding.ts)_ _See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas submit` ## `eas submit`
@@ -1658,7 +1600,7 @@ ALIASES
$ eas build:submit $ eas build:submit
``` ```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/submit.ts)_ _See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/submit.ts)_
## `eas update` ## `eas update`
@@ -1700,7 +1642,7 @@ DESCRIPTION
publish an update group publish an update group
``` ```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/index.ts)_ _See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure` ## `eas update:configure`
@@ -1720,7 +1662,7 @@ DESCRIPTION
configure the project to support EAS Update configure the project to support EAS Update
``` ```
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/configure.ts)_ _See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID` ## `eas update:delete GROUPID`
@@ -1741,7 +1683,7 @@ DESCRIPTION
delete all the updates in an update group 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/v16.3.3/packages/eas-cli/src/commands/update/delete.ts)_ _See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:edit [GROUPID]` ## `eas update:edit [GROUPID]`
@@ -1765,7 +1707,7 @@ DESCRIPTION
edit all the updates in an update group edit all the updates in an update group
``` ```
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/edit.ts)_ _See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/edit.ts)_
## `eas update:list` ## `eas update:list`
@@ -1787,7 +1729,7 @@ DESCRIPTION
view the recent updates view the recent updates
``` ```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/list.ts)_ _See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish` ## `eas update:republish`
@@ -1819,7 +1761,7 @@ DESCRIPTION
roll back to an existing update roll back to an existing update
``` ```
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/republish.ts)_ _See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:roll-back-to-embedded` ## `eas update:roll-back-to-embedded`
@@ -1847,7 +1789,7 @@ DESCRIPTION
roll back to the embedded update 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/v16.3.3/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_ _See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback` ## `eas update:rollback`
@@ -1867,7 +1809,7 @@ DESCRIPTION
roll back to an embedded update or an existing update 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/v16.3.3/packages/eas-cli/src/commands/update/rollback.ts)_ _See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID` ## `eas update:view GROUPID`
@@ -1887,7 +1829,7 @@ DESCRIPTION
update group details update group details
``` ```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/view.ts)_ _See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create` ## `eas webhook:create`
@@ -1908,7 +1850,7 @@ DESCRIPTION
create a webhook create a webhook
``` ```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/create.ts)_ _See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]` ## `eas webhook:delete [ID]`
@@ -1928,7 +1870,7 @@ DESCRIPTION
delete a webhook delete a webhook
``` ```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/delete.ts)_ _See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list` ## `eas webhook:list`
@@ -1946,7 +1888,7 @@ DESCRIPTION
list webhooks list webhooks
``` ```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/list.ts)_ _See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update` ## `eas webhook:update`
@@ -1968,7 +1910,7 @@ DESCRIPTION
update a webhook update a webhook
``` ```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/update.ts)_ _See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID` ## `eas webhook:view ID`
@@ -1985,7 +1927,7 @@ DESCRIPTION
view a webhook view a webhook
``` ```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/view.ts)_ _See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami` ## `eas whoami`
@@ -2027,7 +1969,7 @@ ALIASES
## `eas deploy [options]` ## `eas deploy [options]`
deploy your Expo Router web build and API Routes Deploy your Expo Router web build and API Routes.
``` ```
USAGE USAGE
@@ -2045,7 +1987,7 @@ FLAGS
--prod Create a new production deployment. --prod Create a new production deployment.
DESCRIPTION DESCRIPTION
deploy your Expo Router web build and API Routes Deploy your Expo Router web build and API Routes.
ALIASES ALIASES
$ eas worker:deploy $ eas worker:deploy
@@ -2069,7 +2011,7 @@ DESCRIPTION
create a new workflow configuration YAML file create a new workflow configuration YAML file
``` ```
_See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/workflow/create.ts)_ _See code: [packages/eas-cli/src/commands/workflow/create.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/workflow/create.ts)_
## `eas workflow:run [FILE]` ## `eas workflow:run [FILE]`
@@ -2089,7 +2031,7 @@ DESCRIPTION
Run an EAS workflow Run an EAS workflow
``` ```
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/workflow/run.ts)_ _See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/workflow/run.ts)_
## `eas workflow:validate PATH` ## `eas workflow:validate PATH`
@@ -2109,5 +2051,5 @@ DESCRIPTION
validate a workflow configuration yaml file validate a workflow configuration yaml file
``` ```
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/workflow/validate.ts)_ _See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v15.0.12/packages/eas-cli/src/commands/workflow/validate.ts)_
<!-- commandsstop --> <!-- commandsstop -->
+20 -1842
View File
File diff suppressed because it is too large. Load diff
+5 -7
View File
@@ -1,19 +1,19 @@
{ {
"name": "eas-cli", "name": "eas-cli",
"description": "EAS command line tool", "description": "EAS command line tool",
"version": "16.3.3", "version": "15.0.12",
"author": "Expo <[email protected]>", "author": "Expo <[email protected]>",
"bin": { "bin": {
"eas": "./bin/run" "eas": "./bin/run"
}, },
"bugs": "https://github.com/expo/eas-cli/issues", "bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": { "dependencies": {
"@expo/apple-utils": "2.1.12", "@expo/apple-utils": "2.1.11",
"@expo/code-signing-certificates": "0.0.5", "@expo/code-signing-certificates": "0.0.5",
"@expo/config": "10.0.6", "@expo/config": "10.0.6",
"@expo/config-plugins": "9.0.12", "@expo/config-plugins": "9.0.12",
"@expo/eas-build-job": "1.0.173", "@expo/eas-build-job": "1.0.165",
"@expo/eas-json": "16.3.3", "@expo/eas-json": "15.0.0",
"@expo/env": "^1.0.0", "@expo/env": "^1.0.0",
"@expo/json-file": "8.3.3", "@expo/json-file": "8.3.3",
"@expo/logger": "1.0.117", "@expo/logger": "1.0.117",
@@ -28,7 +28,7 @@
"@expo/results": "1.0.0", "@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1", "@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.7.2", "@expo/spawn-async": "1.7.2",
"@expo/steps": "1.0.173", "@expo/steps": "1.0.166",
"@expo/timeago.js": "1.0.0", "@expo/timeago.js": "1.0.0",
"@oclif/core": "^1.26.2", "@oclif/core": "^1.26.2",
"@oclif/plugin-autocomplete": "^2.3.10", "@oclif/plugin-autocomplete": "^2.3.10",
@@ -68,7 +68,6 @@
"nanoid": "3.3.8", "nanoid": "3.3.8",
"node-fetch": "2.6.7", "node-fetch": "2.6.7",
"node-forge": "1.3.1", "node-forge": "1.3.1",
"node-stream-zip": "1.15.0",
"nullthrows": "1.1.1", "nullthrows": "1.1.1",
"ora": "5.1.0", "ora": "5.1.0",
"pkg-dir": "4.2.0", "pkg-dir": "4.2.0",
@@ -79,7 +78,6 @@
"qrcode-terminal": "0.12.0", "qrcode-terminal": "0.12.0",
"resolve-from": "5.0.0", "resolve-from": "5.0.0",
"semver": "7.5.4", "semver": "7.5.4",
"set-interval-async": "3.0.3",
"slash": "3.0.0", "slash": "3.0.0",
"tar": "6.2.1", "tar": "6.2.1",
"tar-stream": "3.1.7", "tar-stream": "3.1.7",
-3
View File
@@ -6,7 +6,6 @@ export class ApiV2Error extends Error {
readonly expoApiV2ErrorDetails?: JSONValue; readonly expoApiV2ErrorDetails?: JSONValue;
readonly expoApiV2ErrorServerStack?: string; readonly expoApiV2ErrorServerStack?: string;
readonly expoApiV2ErrorMetadata?: object; readonly expoApiV2ErrorMetadata?: object;
readonly expoApiV2ErrorRequestId?: string;
constructor(response: { constructor(response: {
message: string; message: string;
@@ -14,13 +13,11 @@ export class ApiV2Error extends Error {
stack?: string; stack?: string;
details?: JSONValue; details?: JSONValue;
metadata?: object; metadata?: object;
requestId?: string;
}) { }) {
super(response.message); super(response.message);
this.expoApiV2ErrorCode = response.code; this.expoApiV2ErrorCode = response.code;
this.expoApiV2ErrorDetails = response.details; this.expoApiV2ErrorDetails = response.details;
this.expoApiV2ErrorServerStack = response.stack; this.expoApiV2ErrorServerStack = response.stack;
this.expoApiV2ErrorMetadata = response.metadata; this.expoApiV2ErrorMetadata = response.metadata;
this.expoApiV2ErrorRequestId = response.requestId;
} }
} }
@@ -16,7 +16,6 @@ describe(ApiV2Client, () => {
stack: 'line 1: hello', stack: 'line 1: hello',
details: { who: 'world' }, details: { who: 'world' },
metadata: { an: 'object' }, metadata: { an: 'object' },
requestId: 'request-id',
}, },
], ],
}); });
@@ -38,7 +37,6 @@ describe(ApiV2Client, () => {
expect(error.expoApiV2ErrorDetails).toEqual({ who: 'world' }); expect(error.expoApiV2ErrorDetails).toEqual({ who: 'world' });
expect(error.expoApiV2ErrorMetadata).toEqual({ an: 'object' }); expect(error.expoApiV2ErrorMetadata).toEqual({ an: 'object' });
expect(error.expoApiV2ErrorServerStack).toEqual('line 1: hello'); expect(error.expoApiV2ErrorServerStack).toEqual('line 1: hello');
expect(error.expoApiV2ErrorRequestId).toEqual('request-id');
}); });
it('does not convert non-APIv2 error to ApiV2Error', async () => { it('does not convert non-APIv2 error to ApiV2Error', async () => {
@@ -73,15 +73,11 @@ export async function prepareJobAsync(
builderEnvironment: { builderEnvironment: {
image: buildProfile.image, image: buildProfile.image,
node: buildProfile.node, node: buildProfile.node,
corepack: buildProfile.corepack,
pnpm: buildProfile.pnpm, pnpm: buildProfile.pnpm,
bun: buildProfile.bun, bun: buildProfile.bun,
yarn: buildProfile.yarn, yarn: buildProfile.yarn,
ndk: buildProfile.ndk, ndk: buildProfile.ndk,
env: { env: buildProfile.env,
...buildProfile.env,
...(ctx.isVerboseLoggingEnabled ? { EAS_VERBOSE: '1' } : {}),
},
}, },
cache: { cache: {
...cacheDefaults, ...cacheDefaults,
+1 -1
View File
@@ -46,7 +46,6 @@ import { withAnalyticsAsync } from '../analytics/common';
import { getExpoWebsiteBaseUrl } from '../api'; import { getExpoWebsiteBaseUrl } from '../api';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EasCommandError } from '../commandUtils/errors'; import { EasCommandError } from '../commandUtils/errors';
import { createFingerprintAsync } from '../fingerprint/cli';
import { import {
AppPlatform, AppPlatform,
BuildFragment, BuildFragment,
@@ -69,6 +68,7 @@ import { maybeUploadFingerprintAsync } from '../project/maybeUploadFingerprintAs
import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync'; import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync';
import { uploadFileAtPathToGCSAsync } from '../uploads'; import { uploadFileAtPathToGCSAsync } from '../uploads';
import { formatBytes } from '../utils/files'; import { formatBytes } from '../utils/files';
import { createFingerprintAsync } from '../utils/fingerprintCli';
import { printJsonOnlyOutput } from '../utils/json'; import { printJsonOnlyOutput } from '../utils/json';
import { createProgressTracker } from '../utils/progress'; import { createProgressTracker } from '../utils/progress';
import { sleepAsync } from '../utils/promise'; import { sleepAsync } from '../utils/promise';
-1
View File
@@ -63,6 +63,5 @@ export interface BuildContext<T extends Platform> {
vcsClient: Client; vcsClient: Client;
loggerLevel?: LoggerLevel; loggerLevel?: LoggerLevel;
repack: boolean; repack: boolean;
isVerboseLoggingEnabled: boolean;
env: Record<string, string>; env: Record<string, string>;
} }
@@ -43,7 +43,6 @@ export async function createBuildContextAsync<T extends Platform>({
buildLoggerLevel, buildLoggerLevel,
freezeCredentials, freezeCredentials,
repack, repack,
isVerboseLoggingEnabled,
env, env,
}: { }: {
buildProfileName: string; buildProfileName: string;
@@ -66,7 +65,6 @@ export async function createBuildContextAsync<T extends Platform>({
buildLoggerLevel?: LoggerLevel; buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean; freezeCredentials: boolean;
repack: boolean; repack: boolean;
isVerboseLoggingEnabled: boolean;
env: Record<string, string>; env: Record<string, string>;
}): Promise<BuildContext<T>> { }): Promise<BuildContext<T>> {
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({ const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
@@ -153,7 +151,6 @@ export async function createBuildContextAsync<T extends Platform>({
requiredPackageManager, requiredPackageManager,
loggerLevel: buildLoggerLevel, loggerLevel: buildLoggerLevel,
repack, repack,
isVerboseLoggingEnabled,
env, env,
}; };
if (platform === Platform.ANDROID) { if (platform === Platform.ANDROID) {
+1 -5
View File
@@ -65,17 +65,13 @@ export async function prepareJobAsync(
builderEnvironment: { builderEnvironment: {
image: buildProfile.image, image: buildProfile.image,
node: buildProfile.node, node: buildProfile.node,
corepack: buildProfile.corepack,
pnpm: buildProfile.pnpm, pnpm: buildProfile.pnpm,
bun: buildProfile.bun, bun: buildProfile.bun,
yarn: buildProfile.yarn, yarn: buildProfile.yarn,
bundler: buildProfile.bundler, bundler: buildProfile.bundler,
cocoapods: buildProfile.cocoapods, cocoapods: buildProfile.cocoapods,
fastlane: buildProfile.fastlane, fastlane: buildProfile.fastlane,
env: { env: buildProfile.env,
...buildProfile.env,
...(ctx.isVerboseLoggingEnabled ? { EAS_VERBOSE: '1' } : {}),
},
}, },
cache: { cache: {
...cacheDefaults, ...cacheDefaults,
+1 -1
View File
@@ -7,7 +7,7 @@ import Log from '../log';
import { ora } from '../ora'; import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin'; const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '1.0.171'; const PLUGIN_PACKAGE_VERSION = '1.0.168';
export enum LocalBuildMode { export enum LocalBuildMode {
/** /**
@@ -98,7 +98,6 @@ export interface BuildFlags {
buildLoggerLevel?: LoggerLevel; buildLoggerLevel?: LoggerLevel;
freezeCredentials: boolean; freezeCredentials: boolean;
repack: boolean; repack: boolean;
isVerboseLoggingEnabled?: boolean;
} }
export async function runBuildAndSubmitAsync({ export async function runBuildAndSubmitAsync({
@@ -123,7 +122,6 @@ export async function runBuildAndSubmitAsync({
envOverride?: Env; envOverride?: Env;
}): Promise<{ }): Promise<{
buildIds: string[]; buildIds: string[];
buildProfiles?: ProfileData<'build'>[];
}> { }> {
await vcsClient.ensureRepoExistsAsync(); await vcsClient.ensureRepoExistsAsync();
await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive); await ensureRepoIsCleanAsync(vcsClient, flags.nonInteractive);
@@ -350,7 +348,6 @@ export async function runBuildAndSubmitAsync({
} }
return { return {
buildIds: startedBuilds.map(({ build }) => build.id), buildIds: startedBuilds.map(({ build }) => build.id),
buildProfiles,
}; };
} }
@@ -402,7 +399,6 @@ async function prepareAndStartBuildAsync({
buildLoggerLevel: flags.buildLoggerLevel ?? (Log.isDebug ? LoggerLevel.DEBUG : undefined), buildLoggerLevel: flags.buildLoggerLevel ?? (Log.isDebug ? LoggerLevel.DEBUG : undefined),
freezeCredentials: flags.freezeCredentials, freezeCredentials: flags.freezeCredentials,
repack: flags.repack, repack: flags.repack,
isVerboseLoggingEnabled: flags.isVerboseLoggingEnabled ?? false,
env, env,
}); });
-9
View File
@@ -72,12 +72,3 @@ export function getProjectGitHubSettingsUrl(accountName: string, projectName: st
getExpoWebsiteBaseUrl() getExpoWebsiteBaseUrl()
).toString(); ).toString();
} }
export function getHostingDeploymentsUrl(accountName: string, projectName: string): string {
return new URL(
`/accounts/${encodeURIComponent(accountName)}/projects/${encodeURIComponent(
projectName
)}/hosting/deployments`,
getExpoWebsiteBaseUrl()
).toString();
}
@@ -27,7 +27,7 @@ import {
createAnalyticsAsync, createAnalyticsAsync,
} from '../analytics/AnalyticsManager'; } from '../analytics/AnalyticsManager';
import { EnvironmentVariableEnvironment } from '../graphql/generated'; import { EnvironmentVariableEnvironment } from '../graphql/generated';
import Log, { link } from '../log'; import Log from '../log';
import SessionManager from '../user/SessionManager'; import SessionManager from '../user/SessionManager';
import { Client } from '../vcs/vcs'; import { Client } from '../vcs/vcs';
@@ -264,16 +264,6 @@ export default abstract class EasCommand extends Command {
const defaultMsg = `${messageLine}${requestIdLine}`; const defaultMsg = `${messageLine}${requestIdLine}`;
if (graphQLError.extensions?.errorCode === 'UNAUTHORIZED_ERROR') { if (graphQLError.extensions?.errorCode === 'UNAUTHORIZED_ERROR') {
if (defaultMsg.includes('ScopedAccountActorViewerContext') && process.env.EAS_BUILD) {
// We're in EAS, authenticated with a scoped account actor access token.
// We may have not added the scoped actor privacy rule to the right place yet.
return `${chalk.bold(
`You don't have the required permissions to perform this operation.`
)}\n\nWe are in the process of migrating EAS to a more granular permissioning system. If you believe what you're doing is a legitimate operation you should be able to perform, report this to us at ${link(
'https://expo.dev/contact'
)}\n\nOriginal error message: ${defaultMsg}`;
}
return `${chalk.bold( return `${chalk.bold(
`You don't have the required permissions to perform this operation.` `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( )}\n\nThis can sometimes happen if you are logged in as incorrect user.\nRun ${chalk.bold(
@@ -43,7 +43,6 @@ export async function fetchBuildsAsync({
platform?: RequestedPlatform; platform?: RequestedPlatform;
profile?: string; profile?: string;
hasFingerprint?: boolean; hasFingerprint?: boolean;
fingerprintHash?: string;
}; };
}): Promise<BuildFragment[]> { }): Promise<BuildFragment[]> {
let builds: BuildFragment[]; let builds: BuildFragment[];
@@ -57,9 +56,6 @@ export async function fetchBuildsAsync({
if (filters?.hasFingerprint) { if (filters?.hasFingerprint) {
queryFilters['hasFingerprint'] = filters.hasFingerprint; queryFilters['hasFingerprint'] = filters.hasFingerprint;
} }
if (filters?.fingerprintHash) {
queryFilters['fingerprintHash'] = filters.fingerprintHash;
}
if (!filters?.statuses) { if (!filters?.statuses) {
builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, { builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId, appId: projectId,
@@ -5,7 +5,7 @@ import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync'; import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
import { getPublicExpoConfigAsync } from '../../project/expoConfig'; import { getPublicExpoConfigAsync } from '../../project/expoConfig';
export type GetServerSideEnvironmentVariablesFn = ( type GetServerSideEnvironmentVariablesFn = (
maybeEnv?: Record<string, string> maybeEnv?: Record<string, string>
) => Promise<Record<string, string>>; ) => Promise<Record<string, string>>;
+1 -1
View File
@@ -13,7 +13,6 @@ import { downloadAndRunAsync, runBuildAndSubmitAsync } from '../../build/runBuil
import { ensureRepoIsCleanAsync } from '../../build/utils/repository'; import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
import EasCommand from '../../commandUtils/EasCommand'; import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { createFingerprintAsync } from '../../fingerprint/cli';
import { BuildFragment, BuildStatus, DistributionType } from '../../graphql/generated'; import { BuildFragment, BuildStatus, DistributionType } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery'; import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { toAppPlatform } from '../../graphql/types/AppPlatform'; import { toAppPlatform } from '../../graphql/types/AppPlatform';
@@ -22,6 +21,7 @@ import { RequestedPlatform } from '../../platform';
import { resolveWorkflowAsync } from '../../project/workflow'; import { resolveWorkflowAsync } from '../../project/workflow';
import { confirmAsync, promptAsync } from '../../prompts'; import { confirmAsync, promptAsync } from '../../prompts';
import { expoCommandAsync } from '../../utils/expoCli'; import { expoCommandAsync } from '../../utils/expoCli';
import { createFingerprintAsync } from '../../utils/fingerprintCli';
import { ProfileData, getProfilesAsync } from '../../utils/profiles'; import { ProfileData, getProfilesAsync } from '../../utils/profiles';
import { Client } from '../../vcs/vcs'; import { Client } from '../../vcs/vcs';
@@ -1,166 +0,0 @@
import { Platform } from '@expo/eas-build-job';
import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk';
import { pathExists } from 'fs-extra';
import EasCommand from '../../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { AppPlatform, BuildFragment, BuildStatus, DistributionType } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
import Log from '../../log';
import { promptAsync } from '../../prompts';
import { getEasBuildRunCachedAppPath } from '../../run/run';
import { downloadAndMaybeExtractAppAsync } from '../../utils/download';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
export default class Download extends EasCommand {
static override description = 'download simulator/emulator builds for a given fingerprint hash';
static override flags = {
fingerprint: Flags.string({
description: 'Fingerprint hash of the build to download',
required: true,
}),
platform: Flags.enum<Platform.IOS | Platform.ANDROID>({
char: 'p',
options: [Platform.IOS, Platform.ANDROID],
}),
'dev-client': Flags.boolean({
description: 'Filter only dev-client builds.',
allowNo: true,
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.LoggedIn,
...this.ContextOptions.ProjectId,
};
async runAsync(): Promise<void> {
const {
flags: {
json: jsonFlag,
platform,
fingerprint,
'dev-client': developmentClient,
'non-interactive': nonInteractive,
},
} = await this.parse(Download);
const {
loggedIn: { graphqlClient },
projectId,
} = await this.getContextAsync(Download, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const selectedPlatform = await resolvePlatformAsync({ nonInteractive, platform });
const build = await this.getBuildAsync({
graphqlClient,
projectId,
platform: selectedPlatform,
fingerprintHash: fingerprint,
developmentClient,
});
const buildArtifactPath = await this.getPathToBuildArtifactAsync(build, selectedPlatform);
if (jsonFlag) {
const jsonResults = { path: buildArtifactPath };
printJsonOnlyOutput(jsonResults);
} else {
Log.log(`Build downloaded to ${chalk.bold(buildArtifactPath)}`);
}
}
private async getBuildAsync({
graphqlClient,
projectId,
platform,
fingerprintHash,
developmentClient,
}: {
graphqlClient: ExpoGraphqlClient;
projectId: string;
platform: AppPlatform;
fingerprintHash: string;
developmentClient: boolean;
}): Promise<BuildFragment> {
const builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
appId: projectId,
filter: {
platform,
fingerprintHash,
status: BuildStatus.Finished,
simulator: platform === AppPlatform.Ios ? true : undefined,
distribution: platform === AppPlatform.Android ? DistributionType.Internal : undefined,
developmentClient,
},
offset: 0,
limit: 1,
});
if (builds.length === 0) {
throw new Errors.CLIError(
`No builds available for ${platform} with fingerprint hash ${fingerprintHash}`
);
}
Log.succeed(`🎯 Found successful build with matching fingerprint on EAS servers.`);
return builds[0];
}
async getPathToBuildArtifactAsync(build: BuildFragment, platform: AppPlatform): Promise<string> {
const cachedBuildArtifactPath = getEasBuildRunCachedAppPath(
build.project.id,
build.id,
platform
);
if (await pathExists(cachedBuildArtifactPath)) {
Log.newLine();
Log.log(`Using cached build...`);
return cachedBuildArtifactPath;
}
if (!build.artifacts?.applicationArchiveUrl) {
throw new Error('Build does not have an application archive url');
}
return await downloadAndMaybeExtractAppAsync(
build.artifacts.applicationArchiveUrl,
platform,
cachedBuildArtifactPath
);
}
}
async function resolvePlatformAsync({
nonInteractive,
platform,
}: {
nonInteractive: boolean;
platform?: Platform;
}): Promise<AppPlatform> {
if (nonInteractive && !platform) {
throw new Error('Platform must be provided in non-interactive mode');
}
if (platform) {
return toAppPlatform(platform);
}
const { selectedPlatform } = await promptAsync({
type: 'select',
message: 'Select platform',
name: 'selectedPlatform',
choices: [
{ title: 'Android', value: AppPlatform.Android },
{ title: 'iOS', value: AppPlatform.Ios },
],
});
return selectedPlatform;
}
+2 -59
View File
@@ -1,5 +1,5 @@
import { Platform } from '@expo/eas-build-job'; import { Platform } from '@expo/eas-build-job';
import { BuildProfile, EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json'; import { EasJsonAccessor, EasJsonUtils, ResourceClass } from '@expo/eas-json';
import { LoggerLevel } from '@expo/logger'; import { LoggerLevel } from '@expo/logger';
import { Errors, Flags } from '@oclif/core'; import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk'; import chalk from 'chalk';
@@ -17,7 +17,6 @@ import { RequestedPlatform, selectRequestedPlatformAsync } from '../../platform'
import { selectAsync } from '../../prompts'; import { selectAsync } from '../../prompts';
import uniq from '../../utils/expodash/uniq'; import uniq from '../../utils/expodash/uniq';
import { enableJsonOutput } from '../../utils/json'; import { enableJsonOutput } from '../../utils/json';
import { ProfileData } from '../../utils/profiles';
import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService'; import { maybeWarnAboutEasOutagesAsync } from '../../utils/statuspageService';
interface RawBuildFlags { interface RawBuildFlags {
@@ -37,7 +36,6 @@ interface RawBuildFlags {
message?: string; message?: string;
'build-logger-level'?: LoggerLevel; 'build-logger-level'?: LoggerLevel;
'freeze-credentials': boolean; 'freeze-credentials': boolean;
'verbose-logs'?: boolean;
repack: boolean; repack: boolean;
} }
@@ -119,10 +117,6 @@ export default class Build extends EasCommand {
hidden: true, hidden: true,
description: 'Use the golden dev client build repack flow as it works for onboarding', description: 'Use the golden dev client build repack flow as it works for onboarding',
}), }),
'verbose-logs': Flags.boolean({
default: false,
description: 'Use verbose logs for the build process',
}),
...EasNonInteractiveAndJsonFlags, ...EasNonInteractiveAndJsonFlags,
}; };
@@ -166,7 +160,7 @@ export default class Build extends EasCommand {
const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags); const flagsWithPlatform = await this.ensurePlatformSelectedAsync(flags);
const { buildProfiles } = await runBuildAndSubmitAsync({ await runBuildAndSubmitAsync({
graphqlClient, graphqlClient,
analytics, analytics,
vcsClient, vcsClient,
@@ -175,11 +169,6 @@ export default class Build extends EasCommand {
actor, actor,
getDynamicPrivateProjectConfigAsync, getDynamicPrivateProjectConfigAsync,
}); });
this.maybeSuggestUsingEasBuildDev({
buildProfiles,
nonInteractive: flags.nonInteractive,
});
} }
private sanitizeFlags( private sanitizeFlags(
@@ -243,7 +232,6 @@ export default class Build extends EasCommand {
message, message,
buildLoggerLevel: flags['build-logger-level'], buildLoggerLevel: flags['build-logger-level'],
freezeCredentials: flags['freeze-credentials'], freezeCredentials: flags['freeze-credentials'],
isVerboseLoggingEnabled: flags['verbose-logs'],
repack: flags.repack, repack: flags.repack,
}; };
} }
@@ -280,51 +268,6 @@ export default class Build extends EasCommand {
requestedPlatform, requestedPlatform,
}; };
} }
private maybeSuggestUsingEasBuildDev({
buildProfiles,
nonInteractive,
}: {
buildProfiles: ProfileData<'build'>[] | undefined;
nonInteractive: boolean;
}): void {
// suggest using eas build:dev if the build configuration results in simulator/emulator dev client build
if (
!nonInteractive &&
!process.env.CI &&
buildProfiles?.some(({ profile, platform }) => {
if (profile.developmentClient !== true) {
return false;
}
if (profile.distribution !== 'internal') {
return false;
}
if (platform === Platform.IOS) {
const iosProfile = profile as BuildProfile<Platform.IOS>;
if (iosProfile.simulator !== true && iosProfile.withoutCredentials !== true) {
return false;
}
} else {
const androidProfile = profile as BuildProfile<Platform.ANDROID>;
if (
androidProfile.distribution !== 'internal' &&
androidProfile.withoutCredentials !== true
) {
return false;
}
}
return true;
})
) {
Log.newLine();
Log.log(
`🔎 TIP: You are using a build configuration that could benefit from using ${chalk.bold(
'eas build:dev'
)} command. Run it to install and run cached development build, or create a new one if a compatible build doesn't exist yet.`
);
}
}
} }
export async function handleDeprecatedEasJsonAsync( export async function handleDeprecatedEasJsonAsync(
+3 -35
View File
@@ -1,17 +1,14 @@
import { format as formatTimeAgo } from '@expo/timeago.js'; import { format as formatTimeAgo } from '@expo/timeago.js';
import { Flags } from '@oclif/core'; import { Flags } from '@oclif/core';
import { CombinedError } from '@urql/core';
import chalk from 'chalk'; import chalk from 'chalk';
import fs from 'node:fs'; import fs from 'node:fs';
import * as path from 'node:path'; import * as path from 'node:path';
import { getHostingDeploymentsUrl } from '../../build/utils/url';
import EasCommand from '../../commandUtils/EasCommand'; import EasCommand from '../../commandUtils/EasCommand';
import { EASEnvironmentFlag, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags'; import { EASEnvironmentFlag, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { EnvironmentVariableEnvironment } from '../../graphql/generated'; import { EnvironmentVariableEnvironment } from '../../graphql/generated';
import Log, { link } from '../../log'; import Log from '../../log';
import { ora } from '../../ora'; import { ora } from '../../ora';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json'; import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { createProgressTracker } from '../../utils/progress'; import { createProgressTracker } from '../../utils/progress';
import * as WorkerAssets from '../../worker/assets'; import * as WorkerAssets from '../../worker/assets';
@@ -68,7 +65,7 @@ interface DeployInProgressParams {
} }
export default class WorkerDeploy extends EasCommand { export default class WorkerDeploy extends EasCommand {
static override description = 'deploy your Expo Router web build and API Routes'; static override description = 'Deploy your Expo Router web build and API Routes.';
static override aliases = ['worker:deploy']; static override aliases = ['worker:deploy'];
static override usage = [chalk`deploy {dim [options]}`, `deploy --prod`]; static override usage = [chalk`deploy {dim [options]}`, `deploy --prod`];
static override state = 'preview'; static override state = 'preview';
@@ -123,10 +120,7 @@ export default class WorkerDeploy extends EasCommand {
} = await this.getContextAsync(WorkerDeploy, { ...flags, withServerSideEnvironment: null }); } = await this.getContextAsync(WorkerDeploy, { ...flags, withServerSideEnvironment: null });
const projectDist = await resolveExportedProjectAsync(flags, projectDir); const projectDist = await resolveExportedProjectAsync(flags, projectDir);
const { projectId, exp } = await getDynamicPrivateProjectConfigAsync(); const { projectId } = await getDynamicPrivateProjectConfigAsync();
const projectName = exp.slug;
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
logExportedProjectInfo(projectDist); logExportedProjectInfo(projectDist);
@@ -305,32 +299,6 @@ export default class WorkerDeploy extends EasCommand {
progress.succeed('Created deployment'); progress.succeed('Created deployment');
} catch (error: any) { } catch (error: any) {
progress.fail('Failed to create deployment'); progress.fail('Failed to create deployment');
if (
flags.isProduction &&
error instanceof CombinedError &&
error.graphQLErrors.some(err => {
return (
err.extensions?.errorCode === 'UNAUTHORIZED_ERROR' &&
err.message.includes('AppDevDomainNameEntity') &&
err.message.includes('CREATE')
);
})
) {
throw new Error(
`You have specified the new deployment should be a production deployment, but a production domain has not been set up yet for this app.\n\nRun ${chalk.bold(
'eas deploy --prod'
)} as an app admin on your machine or promote an existing deployment to production on the ${link(
getHostingDeploymentsUrl(accountName, projectName),
{
dim: false,
text: 'Hosting Dashboard',
fallback: `Hosting Dashboard (${getHostingDeploymentsUrl(accountName, projectName)})`,
}
)} to set up a production domain and then try again.`
);
}
throw error; throw error;
} }
@@ -1,3 +1,4 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { Flags } from '@oclif/core'; import { Flags } from '@oclif/core';
import openBrowserAsync from 'better-opn'; import openBrowserAsync from 'better-opn';
import chalk from 'chalk'; import chalk from 'chalk';
@@ -6,27 +7,16 @@ import { getExpoWebsiteBaseUrl } from '../../api';
import { selectBranchOnAppAsync } from '../../branch/queries'; import { selectBranchOnAppAsync } from '../../branch/queries';
import EasCommand from '../../commandUtils/EasCommand'; import EasCommand from '../../commandUtils/EasCommand';
import { fetchBuildsAsync, formatBuild } from '../../commandUtils/builds'; import { fetchBuildsAsync, formatBuild } from '../../commandUtils/builds';
import { GetServerSideEnvironmentVariablesFn } from '../../commandUtils/context/ServerSideEnvironmentVariablesContextField';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient'; import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
EasEnvironmentFlagParameters,
EasNonInteractiveAndJsonFlags,
} from '../../commandUtils/flags';
import { diffFingerprint } from '../../fingerprint/cli';
import { abridgedDiff } from '../../fingerprint/diff';
import { Fingerprint, FingerprintDiffItem } from '../../fingerprint/types';
import {
getFingerprintInfoFromLocalProjectForPlatformsAsync,
stringToAppPlatform,
} from '../../fingerprint/utils';
import { import {
AppPlatform, AppPlatform,
BuildFragment, BuildFragment,
BuildStatus, BuildStatus,
EnvironmentVariableEnvironment,
FingerprintFragment, FingerprintFragment,
UpdateFragment, UpdateFragment,
} from '../../graphql/generated'; } from '../../graphql/generated';
import { FingerprintMutation } from '../../graphql/mutations/FingerprintMutation';
import { AppQuery } from '../../graphql/queries/AppQuery'; import { AppQuery } from '../../graphql/queries/AppQuery';
import { BuildQuery } from '../../graphql/queries/BuildQuery'; import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { FingerprintQuery } from '../../graphql/queries/FingerprintQuery'; import { FingerprintQuery } from '../../graphql/queries/FingerprintQuery';
@@ -34,9 +24,14 @@ import { UpdateQuery } from '../../graphql/queries/UpdateQuery';
import Log, { learnMore } from '../../log'; import Log, { learnMore } from '../../log';
import { ora } from '../../ora'; import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform'; import { RequestedPlatform } from '../../platform';
import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync';
import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils'; import { getDisplayNameForProjectIdAsync } from '../../project/projectUtils';
import { resolveWorkflowPerPlatformAsync } from '../../project/workflow';
import { promptAsync, selectAsync } from '../../prompts'; import { promptAsync, selectAsync } from '../../prompts';
import { selectUpdateGroupOnBranchAsync } from '../../update/queries'; import { selectUpdateGroupOnBranchAsync } from '../../update/queries';
import { Fingerprint, FingerprintDiffItem } from '../../utils/fingerprint';
import { createFingerprintAsync, diffFingerprint } from '../../utils/fingerprintCli';
import { abridgedDiff } from '../../utils/fingerprintDiff';
import formatFields, { FormatFieldsItem } from '../../utils/formatFields'; import formatFields, { FormatFieldsItem } from '../../utils/formatFields';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json'; import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { Client } from '../../vcs/vcs'; import { Client } from '../../vcs/vcs';
@@ -63,7 +58,6 @@ export default class FingerprintCompare extends EasCommand {
'$ eas fingerprint:compare <FINGERPRINT-HASH> \t # Compare fingerprint against local directory', '$ eas fingerprint:compare <FINGERPRINT-HASH> \t # Compare fingerprint against local directory',
'$ eas fingerprint:compare <FINGERPRINT-HASH-1> <FINGERPRINT-HASH-2> \t # Compare provided fingerprints', '$ eas fingerprint:compare <FINGERPRINT-HASH-1> <FINGERPRINT-HASH-2> \t # Compare provided fingerprints',
'$ eas fingerprint:compare --build-id <BUILD-ID> \t # Compare fingerprint from build against local directory', '$ eas fingerprint:compare --build-id <BUILD-ID> \t # Compare fingerprint from build against local directory',
'$ eas fingerprint:compare --build-id <BUILD-ID> --environment production \t # Compare fingerprint from build against local directory with the "production" environment',
'$ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2>\t # Compare fingerprint from a build against another build', '$ eas fingerprint:compare --build-id <BUILD-ID-1> --build-id <BUILD-ID-2>\t # Compare fingerprint from a build against another build',
'$ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID>\t # Compare fingerprint from build against fingerprint from update', '$ eas fingerprint:compare --build-id <BUILD-ID> --update-id <UPDATE-ID>\t # Compare fingerprint from build against fingerprint from update',
'$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> \t # Compare fingerprint from update against provided fingerprint', '$ eas fingerprint:compare <FINGERPRINT-HASH> --update-id <UPDATE-ID> \t # Compare fingerprint from update against provided fingerprint',
@@ -97,11 +91,6 @@ export default class FingerprintCompare extends EasCommand {
open: Flags.boolean({ open: Flags.boolean({
description: 'Open the fingerprint comparison in the browser', description: 'Open the fingerprint comparison in the browser',
}), }),
environment: Flags.enum<EnvironmentVariableEnvironment>({
...EasEnvironmentFlagParameters,
description:
'If generating a fingerprint from the local directory, use the specified environment.',
}),
...EasNonInteractiveAndJsonFlags, ...EasNonInteractiveAndJsonFlags,
}; };
@@ -110,7 +99,6 @@ export default class FingerprintCompare extends EasCommand {
...this.ContextOptions.ProjectConfig, ...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn, ...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs, ...this.ContextOptions.Vcs,
...this.ContextOptions.ServerSideEnvironmentVariables,
}; };
async runAsync(): Promise<void> { async runAsync(): Promise<void> {
@@ -122,7 +110,6 @@ export default class FingerprintCompare extends EasCommand {
'build-id': buildIds, 'build-id': buildIds,
'update-id': updateIds, 'update-id': updateIds,
open, open,
environment,
} = flags; } = flags;
const [buildId1, buildId2] = buildIds ?? []; const [buildId1, buildId2] = buildIds ?? [];
const [updateId1, updateId2] = updateIds ?? []; const [updateId1, updateId2] = updateIds ?? [];
@@ -132,10 +119,9 @@ export default class FingerprintCompare extends EasCommand {
privateProjectConfig: { projectDir }, privateProjectConfig: { projectDir },
loggedIn: { graphqlClient }, loggedIn: { graphqlClient },
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync,
} = await this.getContextAsync(FingerprintCompare, { } = await this.getContextAsync(FingerprintCompare, {
nonInteractive, nonInteractive,
withServerSideEnvironment: environment ?? null, withServerSideEnvironment: null,
}); });
if (json) { if (json) {
enableJsonOutput(); enableJsonOutput();
@@ -146,7 +132,6 @@ export default class FingerprintCompare extends EasCommand {
projectDir, projectDir,
projectId, projectId,
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync,
{ {
nonInteractive, nonInteractive,
buildId: buildId1, buildId: buildId1,
@@ -163,7 +148,6 @@ export default class FingerprintCompare extends EasCommand {
projectDir, projectDir,
projectId, projectId,
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync,
{ {
nonInteractive, nonInteractive,
buildId: buildId2, buildId: buildId2,
@@ -171,7 +155,6 @@ export default class FingerprintCompare extends EasCommand {
hash: hash2, hash: hash2,
useProjectFingerprint: useProjectFingerprint:
isFirstFingerprintSpecifiedByFlagOrArg && !isSecondFingerprintSpecifiedByFlagOrArg, isFirstFingerprintSpecifiedByFlagOrArg && !isSecondFingerprintSpecifiedByFlagOrArg,
environmentForProjectFingerprint: environment,
}, },
firstFingerprintInfo firstFingerprintInfo
); );
@@ -277,20 +260,17 @@ async function getFingerprintInfoAsync(
projectDir: string, projectDir: string,
projectId: string, projectId: string,
vcsClient: Client, vcsClient: Client,
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn,
{ {
buildId, buildId,
updateId, updateId,
hash, hash,
useProjectFingerprint, useProjectFingerprint,
environmentForProjectFingerprint,
nonInteractive, nonInteractive,
}: { }: {
buildId?: string; buildId?: string;
updateId?: string; updateId?: string;
hash: string; hash: string;
useProjectFingerprint?: boolean; useProjectFingerprint?: boolean;
environmentForProjectFingerprint?: string;
nonInteractive: boolean; nonInteractive: boolean;
}, },
firstFingerprintInfo?: { firstFingerprintInfo?: {
@@ -316,15 +296,13 @@ async function getFingerprintInfoAsync(
'First fingerprint must be provided in order to compare against the project.' 'First fingerprint must be provided in order to compare against the project.'
); );
} }
return await getFingerprintInfoFromLocalProjectAsync({ return await getFingerprintInfoFromLocalProjectAsync(
graphqlClient, graphqlClient,
projectDir, projectDir,
projectId, projectId,
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync, firstFingerprintInfo
firstFingerprintInfo, );
environment: environmentForProjectFingerprint,
});
} }
if (nonInteractive) { if (nonInteractive) {
@@ -332,38 +310,26 @@ async function getFingerprintInfoAsync(
'Insufficent arguments provided for fingerprint comparison in non-interactive mode' 'Insufficent arguments provided for fingerprint comparison in non-interactive mode'
); );
} }
return await getFingerprintInfoInteractiveAsync({ return await getFingerprintInfoInteractiveAsync(
graphqlClient, graphqlClient,
projectDir, projectDir,
projectId, projectId,
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync, firstFingerprintInfo
firstFingerprintInfo, );
environmentForProjectFingerprint,
});
} }
async function getFingerprintInfoInteractiveAsync({ async function getFingerprintInfoInteractiveAsync(
graphqlClient, graphqlClient: ExpoGraphqlClient,
projectDir, projectDir: string,
projectId, projectId: string,
vcsClient, vcsClient: Client,
getServerSideEnvironmentVariablesAsync,
firstFingerprintInfo,
environmentForProjectFingerprint,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
vcsClient: Client;
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn;
firstFingerprintInfo?: { firstFingerprintInfo?: {
fingerprint: Fingerprint; fingerprint: Fingerprint;
platforms?: AppPlatform[]; platforms?: AppPlatform[];
origin: FingerprintOrigin; origin: FingerprintOrigin;
}; }
environmentForProjectFingerprint?: string; ): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
}): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
const prompt = firstFingerprintInfo const prompt = firstFingerprintInfo
? 'Select the second fingerprint to compare against' ? 'Select the second fingerprint to compare against'
: 'Select a reference fingerprint for comparison'; : 'Select a reference fingerprint for comparison';
@@ -381,15 +347,13 @@ async function getFingerprintInfoInteractiveAsync({
'First fingerprint must be provided in order to compare against the project.' 'First fingerprint must be provided in order to compare against the project.'
); );
} }
return await getFingerprintInfoFromLocalProjectAsync({ return await getFingerprintInfoFromLocalProjectAsync(
graphqlClient, graphqlClient,
projectDir, projectDir,
projectId, projectId,
vcsClient, vcsClient,
getServerSideEnvironmentVariablesAsync, firstFingerprintInfo
firstFingerprintInfo, );
environment: environmentForProjectFingerprint,
});
} else if (originType === FingerprintOriginType.Build) { } else if (originType === FingerprintOriginType.Build) {
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId); const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
const buildId = await selectBuildToCompareAsync(graphqlClient, projectId, displayName, { const buildId = await selectBuildToCompareAsync(graphqlClient, projectId, displayName, {
@@ -443,49 +407,54 @@ async function getFingerprintInfoInteractiveAsync({
} }
} }
async function getFingerprintInfoFromLocalProjectAsync({ async function getFingerprintInfoFromLocalProjectAsync(
graphqlClient, graphqlClient: ExpoGraphqlClient,
projectDir, projectDir: string,
projectId, projectId: string,
vcsClient, vcsClient: Client,
getServerSideEnvironmentVariablesAsync,
firstFingerprintInfo,
environment,
}: {
graphqlClient: ExpoGraphqlClient;
projectDir: string;
projectId: string;
vcsClient: Client;
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn;
firstFingerprintInfo: { firstFingerprintInfo: {
fingerprint: Fingerprint; fingerprint: Fingerprint;
platforms?: AppPlatform[]; platforms?: AppPlatform[];
origin: FingerprintOrigin; origin: FingerprintOrigin;
}; }
environment?: string; ): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
}): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
const firstFingerprintPlatforms = firstFingerprintInfo.platforms; const firstFingerprintPlatforms = firstFingerprintInfo.platforms;
if (!firstFingerprintPlatforms || firstFingerprintPlatforms.length === 0) { if (!firstFingerprintPlatforms) {
throw new Error( throw new Error(
`Cannot compare the local directory against the provided fingerprint hash "${firstFingerprintInfo.fingerprint.hash}" because the associated platform could not be determined. Ensure the fingerprint is linked to a build or update to identify the platform.` `Cannot compare the local directory against the provided fingerprint hash "${firstFingerprintInfo.fingerprint.hash}" because the associated platform could not be determined. Ensure the fingerprint is linked to a build or update to identify the platform.`
); );
} }
if (environment) { const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
Log.log(`🔧 Using environment: ${environment}`); const optionsFromWorkflow = getFingerprintOptionsFromWorkflow(
}
const env = environment
? { ...(await getServerSideEnvironmentVariablesAsync()), EXPO_NO_DOTENV: '1' }
: undefined;
const fingerprint = await getFingerprintInfoFromLocalProjectForPlatformsAsync(
graphqlClient,
projectDir,
projectId,
vcsClient,
firstFingerprintPlatforms, firstFingerprintPlatforms,
{ env } workflows
); );
return { fingerprint, origin: { type: FingerprintOriginType.Project } };
const projectFingerprint = await createFingerprintAsync(projectDir, {
...optionsFromWorkflow,
platforms: firstFingerprintPlatforms.map(appPlatformToString),
debug: true,
env: undefined,
});
if (!projectFingerprint) {
throw new Error('Project fingerprints can only be computed for projects with SDK 52 or higher');
}
const uploadedFingerprint = await maybeUploadFingerprintAsync({
hash: projectFingerprint.hash,
fingerprint: {
fingerprintSources: projectFingerprint.sources,
isDebugFingerprintSource: Log.isDebug,
},
graphqlClient,
});
await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, {
hash: uploadedFingerprint.hash,
source: uploadedFingerprint.fingerprintSource,
});
return { fingerprint: projectFingerprint, origin: { type: FingerprintOriginType.Project } };
} }
async function getFingerprintFromUpdateFragmentAsync( async function getFingerprintFromUpdateFragmentAsync(
@@ -632,6 +601,37 @@ async function getFingerprintFromFingerprintFragmentAsync(
return (await fingerprintResponse.json()) as Fingerprint; return (await fingerprintResponse.json()) as Fingerprint;
} }
function getFingerprintOptionsFromWorkflow(
platforms: AppPlatform[],
workflowsByPlatform: Record<Platform, Workflow>
): { workflow?: Workflow; ignorePaths?: string[] } {
if (platforms.length === 0) {
throw new Error('Could not determine platform from fingerprint sources');
}
// Single platform case
if (platforms.length === 1) {
const platform = platforms[0];
return { workflow: workflowsByPlatform[appPlatformToPlatform(platform)] };
}
// Multiple platforms case
const workflows = platforms.map(platform => workflowsByPlatform[appPlatformToPlatform(platform)]);
// If all workflows are the same, return the common workflow
const [firstWorkflow, ...restWorkflows] = workflows;
if (restWorkflows.every(workflow => workflow === firstWorkflow)) {
return { workflow: firstWorkflow };
}
// Generate ignorePaths for mixed workflows
const ignorePaths = platforms
.filter(platform => workflowsByPlatform[appPlatformToPlatform(platform)] === Workflow.MANAGED)
.map(platform => `${appPlatformToString(platform)}/**/*`);
return { ignorePaths };
}
function printContentDiff(diff: FingerprintDiffItem): void { function printContentDiff(diff: FingerprintDiffItem): void {
if (diff.op === 'added') { if (diff.op === 'added') {
const sourceType = diff.addedSource.type; const sourceType = diff.addedSource.type;
@@ -810,7 +810,40 @@ function isJSON(str: string): boolean {
} }
} }
async function selectBuildToCompareAsync( function appPlatformToPlatform(platform: AppPlatform): Platform {
switch (platform) {
case AppPlatform.Android:
return Platform.ANDROID;
case AppPlatform.Ios:
return Platform.IOS;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
function appPlatformToString(platform: AppPlatform): string {
switch (platform) {
case AppPlatform.Android:
return 'android';
case AppPlatform.Ios:
return 'ios';
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
function stringToAppPlatform(platform: string): AppPlatform {
switch (platform) {
case 'android':
return AppPlatform.Android;
case 'ios':
return AppPlatform.Ios;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
export async function selectBuildToCompareAsync(
graphqlClient: ExpoGraphqlClient, graphqlClient: ExpoGraphqlClient,
projectId: string, projectId: string,
projectDisplayName: string, projectDisplayName: string,
@@ -1,164 +0,0 @@
import { Env } from '@expo/eas-build-job';
import { EasJsonAccessor } from '@expo/eas-json';
import { Flags } from '@oclif/core';
import { getExpoWebsiteBaseUrl } from '../../api';
import { evaluateConfigWithEnvVarsAsync } from '../../build/evaluateConfigWithEnvVarsAsync';
import EasCommand from '../../commandUtils/EasCommand';
import {
EASEnvironmentFlag,
EasEnvironmentFlagParameters,
EasNonInteractiveAndJsonFlags,
} from '../../commandUtils/flags';
import {
appPlatformToPlatform,
getFingerprintInfoFromLocalProjectForPlatformsAsync,
stringToAppPlatform,
} from '../../fingerprint/utils';
import { AppPlatform, EnvironmentVariableEnvironment } from '../../graphql/generated';
import { AppQuery } from '../../graphql/queries/AppQuery';
import Log, { link } from '../../log';
import { promptAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { getProfilesAsync } from '../../utils/profiles';
export default class FingerprintGenerate extends EasCommand {
static override description = 'generate fingerprints from the current project';
static override strict = false;
static override examples = [
'$ eas fingerprint:generate \t # Generate fingerprint in interactive mode',
'$ eas fingerprint:generate --build-profile preview \t # Generate a fingerprint using the "preview" build profile',
'$ eas fingerprint:generate --environment preview \t # Generate a fingerprint using the "preview" environment',
'$ eas fingerprint:generate --json --non-interactive --platform android \t # Output fingerprint json to stdout',
];
static override flags = {
platform: Flags.enum({
char: 'p',
options: ['android', 'ios'],
}),
...EASEnvironmentFlag,
environment: Flags.enum<EnvironmentVariableEnvironment>({
...EasEnvironmentFlagParameters,
exclusive: ['build-profile'],
}),
'build-profile': Flags.string({
char: 'e',
description: 'Name of the build profile from eas.json.',
exclusive: ['environment'],
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.ProjectConfig,
...this.ContextOptions.LoggedIn,
...this.ContextOptions.Vcs,
...this.ContextOptions.ServerSideEnvironmentVariables,
...this.ContextOptions.DynamicProjectConfig,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(FingerprintGenerate);
const {
json,
'non-interactive': nonInteractive,
platform: platformStringFlag,
environment,
'build-profile': buildProfileName,
} = flags;
const {
projectId,
privateProjectConfig: { projectDir },
loggedIn: { graphqlClient },
vcsClient,
getServerSideEnvironmentVariablesAsync,
getDynamicPrivateProjectConfigAsync,
} = await this.getContextAsync(FingerprintGenerate, {
nonInteractive,
withServerSideEnvironment: environment ?? null,
});
if (json) {
enableJsonOutput();
}
let platform: AppPlatform;
if (platformStringFlag) {
platform = stringToAppPlatform(platformStringFlag);
} else {
if (nonInteractive) {
throw new Error('Platform must be specified in non-interactive mode with the --p flag');
}
platform = await selectRequestedPlatformAsync();
}
let env: Env | undefined = undefined;
if (environment) {
Log.log(`🔧 Using environment: ${environment}`);
env = { ...(await getServerSideEnvironmentVariablesAsync()), EXPO_NO_DOTENV: '1' };
} else if (buildProfileName) {
Log.log(`🔧 Using build profile: ${buildProfileName}`);
const easJsonAccessor = EasJsonAccessor.fromProjectPath(projectDir);
const buildProfile = (
await getProfilesAsync({
type: 'build',
easJsonAccessor,
platforms: [appPlatformToPlatform(platform)],
profileName: buildProfileName ?? undefined,
projectDir,
})
)[0];
if (!buildProfile) {
throw new Error(`Build profile ${buildProfile} not found for platform: ${platform}`);
}
const configResult = await evaluateConfigWithEnvVarsAsync({
buildProfile: buildProfile.profile,
buildProfileName: buildProfile.profileName,
graphqlClient,
getProjectConfig: getDynamicPrivateProjectConfigAsync,
opts: { env: buildProfile.profile.env },
});
env = configResult.env;
}
const fingerprint = await getFingerprintInfoFromLocalProjectForPlatformsAsync(
graphqlClient,
projectDir,
projectId,
vcsClient,
[platform],
{ env }
);
if (json) {
printJsonOnlyOutput(fingerprint);
return;
}
Log.log(`✅ Fingerprint generated: ${fingerprint.hash}`);
const project = await AppQuery.byIdAsync(graphqlClient, projectId);
const fingerprintUrl = new URL(
`/accounts/${project.ownerAccount.name}/projects/${project.slug}/fingerprints/${fingerprint.hash}`,
getExpoWebsiteBaseUrl()
);
Log.log(`🔍 View the fingerprint at ${link(fingerprintUrl.toString())}`);
Log.log(`💡 If you want to see the entire fingerprint output, pass in the --json flag.`);
}
}
async function selectRequestedPlatformAsync(): Promise<AppPlatform> {
const { requestedPlatform } = await promptAsync({
type: 'select',
message: 'Select platform',
name: 'requestedPlatform',
choices: [
{ title: 'Android', value: AppPlatform.Android },
{ title: 'iOS', value: AppPlatform.Ios },
],
});
return requestedPlatform;
}
@@ -54,7 +54,6 @@ export default class UpdateConfigure extends EasCommand {
platform: flags['platform'], platform: flags['platform'],
vcsClient, vcsClient,
env: undefined, env: undefined,
forceNativeConfigSync: true,
}); });
await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir); await ensureEASUpdateIsConfiguredInEasJsonAsync(projectDir);
+10 -18
View File
@@ -6,43 +6,37 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags'; import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
import { withErrorHandlingAsync } from '../../graphql/client'; import { withErrorHandlingAsync } from '../../graphql/client';
import { import {
BackgroundJobReceiptDataFragment, DeleteUpdateGroupMutation,
ScheduleUpdateGroupDeletionMutation, UpdateMutationDeleteUpdateGroupArgs,
ScheduleUpdateGroupDeletionMutationVariables,
} from '../../graphql/generated'; } from '../../graphql/generated';
import { BackgroundJobReceiptNode } from '../../graphql/types/BackgroundJobReceipt';
import Log from '../../log'; import Log from '../../log';
import { confirmAsync } from '../../prompts'; import { confirmAsync } from '../../prompts';
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json'; import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
import { pollForBackgroundJobReceiptAsync } from '../../utils/pollForBackgroundJobReceiptAsync';
async function scheduleUpdateGroupDeletionAsync( async function deleteUpdateGroupAsync(
graphqlClient: ExpoGraphqlClient, graphqlClient: ExpoGraphqlClient,
{ {
group, group,
}: { }: {
group: string; group: string;
} }
): Promise<BackgroundJobReceiptDataFragment> { ): Promise<DeleteUpdateGroupMutation> {
const result = await withErrorHandlingAsync( return await withErrorHandlingAsync(
graphqlClient graphqlClient
.mutation<ScheduleUpdateGroupDeletionMutation, ScheduleUpdateGroupDeletionMutationVariables>( .mutation<DeleteUpdateGroupMutation, UpdateMutationDeleteUpdateGroupArgs>(
gql` gql`
mutation ScheduleUpdateGroupDeletion($group: ID!) { mutation DeleteUpdateGroup($group: ID!) {
update { update {
scheduleUpdateGroupDeletion(group: $group) { deleteUpdateGroup(group: $group) {
id group
...BackgroundJobReceiptData
} }
} }
} }
${BackgroundJobReceiptNode}
`, `,
{ group } { group }
) )
.toPromise() .toPromise()
); );
return result.update.scheduleUpdateGroupDeletion;
} }
export default class UpdateDelete extends EasCommand { export default class UpdateDelete extends EasCommand {
@@ -96,9 +90,7 @@ export default class UpdateDelete extends EasCommand {
} }
} }
const receipt = await scheduleUpdateGroupDeletionAsync(graphqlClient, { group }); await deleteUpdateGroupAsync(graphqlClient, { group });
const successfulReceipt = await pollForBackgroundJobReceiptAsync(graphqlClient, receipt);
Log.debug('Deletion result', { successfulReceipt });
if (jsonFlag) { if (jsonFlag) {
printJsonOnlyOutput({ group }); printJsonOnlyOutput({ group });
+81 -144
View File
@@ -3,7 +3,6 @@ import { Errors, Flags } from '@oclif/core';
import chalk from 'chalk'; import chalk from 'chalk';
import nullthrows from 'nullthrows'; import nullthrows from 'nullthrows';
import { getExpoWebsiteBaseUrl } from '../../api';
import { ensureBranchExistsAsync } from '../../branch/queries'; import { ensureBranchExistsAsync } from '../../branch/queries';
import { transformFingerprintSource } from '../../build/graphql'; import { transformFingerprintSource } from '../../build/graphql';
import { ensureRepoIsCleanAsync } from '../../build/utils/repository'; import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
@@ -13,9 +12,8 @@ import { EasNonInteractiveAndJsonFlags, EasUpdateEnvironmentFlag } from '../../c
import { getPaginatedQueryOptions } from '../../commandUtils/pagination'; import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
import fetch from '../../fetch'; import fetch from '../../fetch';
import { import {
AssetMapSourceInput,
EnvironmentVariableEnvironment, EnvironmentVariableEnvironment,
FingerprintInfoGroup as GraphqlFingerprintInfoGroup, FingerprintInfoGroup,
PublishUpdateGroupInput, PublishUpdateGroupInput,
StatuspageServiceName, StatuspageServiceName,
UpdateInfoGroup, UpdateInfoGroup,
@@ -23,11 +21,9 @@ import {
UpdateRolloutInfoGroup, UpdateRolloutInfoGroup,
} from '../../graphql/generated'; } from '../../graphql/generated';
import { PublishMutation } from '../../graphql/mutations/PublishMutation'; import { PublishMutation } from '../../graphql/mutations/PublishMutation';
import { AppQuery } from '../../graphql/queries/AppQuery';
import Log, { learnMore, link } from '../../log'; import Log, { learnMore, link } from '../../log';
import { ora } from '../../ora'; import { ora } from '../../ora';
import { RequestedPlatform } from '../../platform'; import { RequestedPlatform } from '../../platform';
import { maybeUploadAssetMapAsync } from '../../project/maybeUploadAssetMapAsync';
import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync'; import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync';
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils'; import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
import { import {
@@ -37,7 +33,6 @@ import {
buildUnsortedUpdateInfoGroupAsync, buildUnsortedUpdateInfoGroupAsync,
collectAssetsAsync, collectAssetsAsync,
filterCollectedAssetsByRequestedPlatforms, filterCollectedAssetsByRequestedPlatforms,
findCompatibleBuildsAsync,
generateEasMetadataAsync, generateEasMetadataAsync,
getBranchNameForCommandAsync, getBranchNameForCommandAsync,
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects, getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects,
@@ -273,14 +268,9 @@ export default class UpdatePublish extends EasCommand {
let uploadedAssetCount = 0; let uploadedAssetCount = 0;
let assetLimitPerUpdateGroup = 0; let assetLimitPerUpdateGroup = 0;
let realizedPlatforms: UpdatePublishPlatform[] = []; let realizedPlatforms: UpdatePublishPlatform[] = [];
let assetMapSource: AssetMapSourceInput | null = null;
try { try {
const [collectedAssets, maybeAssetMapSource] = await Promise.all([ const collectedAssets = await collectAssetsAsync(distRoot);
collectAssetsAsync(distRoot),
maybeUploadAssetMapAsync(distRoot, graphqlClient),
]);
assetMapSource = maybeAssetMapSource;
const assets = filterCollectedAssetsByRequestedPlatforms(collectedAssets, requestedPlatform); const assets = filterCollectedAssetsByRequestedPlatforms(collectedAssets, requestedPlatform);
realizedPlatforms = Object.keys(assets) as UpdatePublishPlatform[]; realizedPlatforms = Object.keys(assets) as UpdatePublishPlatform[];
@@ -488,19 +478,14 @@ export default class UpdatePublish extends EasCommand {
}, },
}; };
}, },
{} as GraphqlFingerprintInfoGroup {} as FingerprintInfoGroup
); );
const assetMapGroup = assetMapSource
? Object.fromEntries(platforms.map(platform => [platform, assetMapSource]))
: null;
return { return {
branchId: branch.id, branchId: branch.id,
updateInfoGroup: localUpdateInfoGroup, updateInfoGroup: localUpdateInfoGroup,
rolloutInfoGroup: localRolloutInfoGroup, rolloutInfoGroup: localRolloutInfoGroup,
fingerprintInfoGroup: transformedFingerprintInfoGroup, fingerprintInfoGroup: transformedFingerprintInfoGroup,
assetMapGroup,
runtimeVersion, runtimeVersion,
message: updateMessage, message: updateMessage,
gitCommitHash, gitCommitHash,
@@ -574,143 +559,95 @@ export default class UpdatePublish extends EasCommand {
} }
if (jsonFlag) { if (jsonFlag) {
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates)); printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
return; } else {
} if (new Set(newUpdates.map(update => update.group)).size > 1) {
Log.addNewLineIfNone();
if (new Set(newUpdates.map(update => update.group)).size > 1) { Log.log(
Log.addNewLineIfNone(); '👉 Since multiple runtime versions are defined, multiple update groups have been published.'
Log.log(
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
);
}
Log.addNewLineIfNone();
const runtimeToCompatibleBuilds = await Promise.all(
runtimeToPlatformsAndFingerprintInfoAndFingerprintSourceMapping.map(obj =>
findCompatibleBuildsAsync(graphqlClient, projectId, obj)
)
);
for (const runtime of uniqBy(
runtimeToPlatformsAndFingerprintInfoMapping,
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: branchName },
{ 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 }] : []),
...(newAndroidUpdate?.rolloutControlUpdate
? [
{
label: 'Android Rollout',
value: `${newAndroidUpdate.rolloutPercentage}% (Base update ID: ${newAndroidUpdate.rolloutControlUpdate.id})`,
},
]
: []),
...(newIosUpdate?.rolloutControlUpdate
? [
{
label: 'iOS Rollout',
value: `${newIosUpdate.rolloutPercentage}% (Base update ID: ${newIosUpdate.rolloutControlUpdate.id})`,
},
]
: []),
{ label: 'Message', value: updateMessage ?? '' },
...(gitCommitHash
? [
{
label: 'Commit',
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
},
]
: []),
{ label: 'EAS Dashboard', value: updateGroupLink },
])
);
Log.addNewLineIfNone(); Log.addNewLineIfNone();
if (isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)) {
Log.warn( for (const runtime of uniqBy(
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` + runtimeToPlatformsAndFingerprintInfoMapping,
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', { version => version.runtimeVersion
learnMoreMessage: 'Consider optimizing your usage of assets', )) {
dim: false, 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: branchName },
{ 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 }] : []),
...(newAndroidUpdate?.rolloutControlUpdate
? [
{
label: 'Android Rollout',
value: `${newAndroidUpdate.rolloutPercentage}% (Base update ID: ${newAndroidUpdate.rolloutControlUpdate.id})`,
},
]
: []),
...(newIosUpdate?.rolloutControlUpdate
? [
{
label: 'iOS Rollout',
value: `${newIosUpdate.rolloutPercentage}% (Base update ID: ${newIosUpdate.rolloutControlUpdate.id})`,
},
]
: []),
{ label: 'Message', value: updateMessage ?? '' },
...(gitCommitHash
? [
{
label: 'Commit',
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
},
]
: []),
{ label: 'EAS Dashboard', value: updateGroupLink },
])
); );
Log.addNewLineIfNone(); Log.addNewLineIfNone();
} if (
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
const fingerprintsWithoutCompatibleBuilds = runtimeToCompatibleBuilds.find( ) {
({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion Log.warn(
)?.fingerprintInfoGroupWithCompatibleBuilds; `This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
if (fingerprintsWithoutCompatibleBuilds) { `${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
const missingBuilds = Object.entries(fingerprintsWithoutCompatibleBuilds).filter( learnMoreMessage: 'Consider optimizing your usage of assets',
([_platform, fingerprintInfo]) => !fingerprintInfo.build dim: false,
); })}.`
if (missingBuilds.length > 0) { );
const project = await AppQuery.byIdAsync(graphqlClient, projectId); Log.addNewLineIfNone();
Log.warn('No compatible builds found for the following fingerprints:');
for (const [platform, fingerprintInfo] of missingBuilds) {
const fingerprintUrl = new URL(
`/accounts/${project.ownerAccount.name}/projects/${project.slug}/fingerprints/${fingerprintInfo.fingerprintHash}`,
getExpoWebsiteBaseUrl()
);
Log.warn(
formatFields(
[
{
label: `${this.prettyPlatform(platform)} fingerprint`,
value: fingerprintInfo.fingerprintHash,
},
{ label: 'URL', value: fingerprintUrl.toString() },
],
{
labelFormat: label => ` ${chalk.dim(label)}:`,
}
)
);
Log.addNewLineIfNone();
}
} }
} }
} }
} }
private prettyPlatform(updatePlatform: string): string {
switch (updatePlatform) {
case 'android':
return 'Android';
case 'ios':
return 'iOS';
default:
return updatePlatform;
}
}
private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags { private sanitizeFlags(flags: RawUpdateFlags): UpdateFlags {
const nonInteractive = flags['non-interactive'] ?? false; const nonInteractive = flags['non-interactive'] ?? false;
-386
View File
@@ -1,386 +0,0 @@
import { IOSConfig } from '@expo/config-plugins';
import { Platform } from '@expo/eas-build-job';
import { Flags } from '@oclif/core';
import fg from 'fast-glob';
import fs from 'fs-extra';
import StreamZip from 'node-stream-zip';
import path from 'path';
import tar from 'tar';
import { v4 as uuidv4 } from 'uuid';
import { getBuildLogsUrl } from '../build/utils/url';
import EasCommand from '../commandUtils/EasCommand';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { EasNonInteractiveAndJsonFlags } from '../commandUtils/flags';
import {
DistributionType,
LocalBuildArchiveSourceType,
UploadSessionType,
} from '../graphql/generated';
import { FingerprintMutation } from '../graphql/mutations/FingerprintMutation';
import { LocalBuildMutation } from '../graphql/mutations/LocalBuildMutation';
import { toAppPlatform } from '../graphql/types/AppPlatform';
import Log from '../log';
import { promptAsync } from '../prompts';
import * as xcode from '../run/ios/xcode';
import { uploadFileAtPathToGCSAsync } from '../uploads';
import { fromNow } from '../utils/date';
import { enableJsonOutput, printJsonOnlyOutput } from '../utils/json';
import { getTmpDirectory } from '../utils/paths';
import { createProgressTracker } from '../utils/progress';
export default class BuildUpload extends EasCommand {
static override description = 'upload a local build and generate a sharable link';
static override hidden = true;
static override flags = {
platform: Flags.enum<Platform.IOS | Platform.ANDROID>({
char: 'p',
options: [Platform.IOS, Platform.ANDROID],
}),
'build-path': Flags.string({
description: 'Path for the local build',
}),
fingerprint: Flags.string({
description: 'Fingerprint hash of the local build',
}),
...EasNonInteractiveAndJsonFlags,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(BuildUpload);
const {
'build-path': buildPath,
fingerprint: manualFingerprintHash,
json: jsonFlag,
'non-interactive': nonInteractive,
} = flags;
const {
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(BuildUpload, {
nonInteractive,
});
if (jsonFlag) {
enableJsonOutput();
}
const platform = await this.selectPlatformAsync({ platform: flags.platform, nonInteractive });
const localBuildPath = await resolveLocalBuildPathAsync({
platform,
inputBuildPath: buildPath,
nonInteractive,
});
const {
fingerprintHash: buildFingerprintHash,
developmentClient,
simulator,
} = await extractAppMetadataAsync(localBuildPath, platform);
let fingerprint = manualFingerprintHash ?? buildFingerprintHash;
if (fingerprint) {
if (
manualFingerprintHash &&
buildFingerprintHash &&
manualFingerprintHash !== buildFingerprintHash &&
!nonInteractive
) {
const selectedAnswer = await promptAsync({
name: 'fingerprint',
message: `The provided fingerprint hash ${manualFingerprintHash} does not match the fingerprint hash of the build ${buildFingerprintHash}. Which fingerprint do you want to use?`,
type: 'select',
choices: [
{ title: manualFingerprintHash, value: manualFingerprintHash },
{ title: buildFingerprintHash, value: buildFingerprintHash },
],
});
fingerprint = String(selectedAnswer.fingerprint);
}
await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, {
hash: fingerprint,
});
}
Log.log(`Using build ${localBuildPath}`);
Log.log(`Fingerprint hash: ${fingerprint ?? 'Unknown'}`);
Log.log('Uploading your app archive to EAS');
const bucketKey = await uploadAppArchiveAsync(graphqlClient, localBuildPath);
const build = await LocalBuildMutation.createLocalBuildAsync(
graphqlClient,
projectId,
{ platform: toAppPlatform(platform), simulator },
{ type: LocalBuildArchiveSourceType.Gcs, bucketKey },
{ distribution: DistributionType.Internal, fingerprintHash: fingerprint, developmentClient }
);
if (jsonFlag) {
printJsonOnlyOutput({ url: getBuildLogsUrl(build) });
return;
}
Log.withTick(`Shareable link to the build: ${getBuildLogsUrl(build)}`);
}
private async selectPlatformAsync({
nonInteractive,
platform,
}: {
nonInteractive: boolean;
platform?: Platform;
}): Promise<Platform> {
if (nonInteractive && !platform) {
throw new Error('Platform must be provided in non-interactive mode');
}
if (platform) {
return platform;
}
const { resolvedPlatform } = await promptAsync({
type: 'select',
message: 'Select platform',
name: 'resolvedPlatform',
choices: [
{ title: 'Android', value: Platform.ANDROID },
{ title: 'iOS', value: Platform.IOS },
],
});
return resolvedPlatform;
}
}
async function resolveLocalBuildPathAsync({
platform,
inputBuildPath,
nonInteractive,
}: {
platform: Platform;
inputBuildPath?: string;
nonInteractive: boolean;
}): Promise<string> {
const rootDir = process.cwd();
let applicationArchivePatternOrPath: string[] = [];
if (inputBuildPath) {
applicationArchivePatternOrPath.push(inputBuildPath);
} else if (platform === Platform.ANDROID) {
applicationArchivePatternOrPath.push('android/app/build/outputs/**/*.{apk,aab}');
} else {
const xcworkspacePath = await xcode.resolveXcodeProjectAsync(rootDir);
const schemes = IOSConfig.BuildScheme.getRunnableSchemesFromXcodeproj(rootDir);
if (xcworkspacePath && schemes.length > 0) {
for (const scheme of schemes) {
const buildSettings = await xcode.getXcodeBuildSettingsAsync(xcworkspacePath, scheme.name);
applicationArchivePatternOrPath = applicationArchivePatternOrPath.concat(
buildSettings.map(({ buildSettings }) => `${buildSettings.BUILD_DIR}/**/*.app`)
);
}
}
}
let applicationArchives = await findArtifactsAsync({
rootDir,
patternOrPathArray: applicationArchivePatternOrPath,
});
if (applicationArchives.length === 0 && !nonInteractive && !inputBuildPath) {
Log.warn(`No application archives found at ${applicationArchivePatternOrPath}.`);
const { path } = await promptAsync({
type: 'text',
name: 'path',
message: 'Provide a path to the application archive:',
validate: value => (value ? true : 'Path may not be empty.'),
});
applicationArchives = await findArtifactsAsync({
rootDir,
patternOrPathArray: [path],
});
}
if (applicationArchives.length === 1) {
return applicationArchives[0];
}
if (applicationArchives.length > 1) {
// sort by modification time
const applicationArchivesInfo = await Promise.all(
applicationArchives.map(async archivePath => ({
path: archivePath,
stat: await fs.stat(archivePath),
}))
);
applicationArchivesInfo.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs);
if (nonInteractive) {
return applicationArchivesInfo[0].path;
}
const { selectedPath } = await promptAsync({
type: 'select',
name: 'selectedPath',
message: 'Found multiple application archives. Select one:',
choices: applicationArchivesInfo.map(archive => {
return {
title: `${
archive.path.startsWith(rootDir) ? path.relative(rootDir, archive.path) : archive.path
} (${fromNow(archive.stat.mtime)} ago)`,
value: archive.path,
};
}),
});
return selectedPath;
}
throw new Error(`Found no application archives at ${inputBuildPath}.`);
}
async function findArtifactsAsync({
rootDir,
patternOrPathArray,
}: {
rootDir: string;
patternOrPathArray: string[];
}): Promise<string[]> {
const files = new Set<string>();
for (const patternOrPath of patternOrPathArray) {
if (path.isAbsolute(patternOrPath) && (await fs.pathExists(patternOrPath))) {
files.add(patternOrPath);
} else {
const filesFound = await fg(patternOrPath, {
cwd: rootDir,
onlyFiles: false,
});
filesFound.forEach(file => files.add(file));
}
}
return [...files].map(filePath => {
// User may provide an absolute path as input in which case
// fg will return an absolute path.
if (path.isAbsolute(filePath)) {
return filePath;
}
// User may also provide a relative path in which case
// fg will return a path relative to rootDir.
return path.join(rootDir, filePath);
});
}
async function uploadAppArchiveAsync(
graphqlClient: ExpoGraphqlClient,
originalPath: string
): Promise<string> {
let filePath = originalPath;
if ((await fs.stat(filePath)).isDirectory()) {
await fs.mkdirp(getTmpDirectory());
const tarPath = path.join(getTmpDirectory(), `${uuidv4()}.tar.gz`);
const parentPath = path.dirname(originalPath);
const folderName = path.basename(originalPath);
await tar.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
filePath = tarPath;
}
const fileSize = (await fs.stat(filePath)).size;
const bucketKey = await uploadFileAtPathToGCSAsync(
graphqlClient,
UploadSessionType.EasShareGcsAppArchive,
filePath,
createProgressTracker({
total: fileSize,
message: 'Uploading to EAS',
completedMessage: 'Uploaded to EAS',
})
);
return bucketKey;
}
type AppMetadata = {
fingerprintHash?: string;
developmentClient: boolean;
simulator: boolean;
};
async function extractAppMetadataAsync(
buildPath: string,
platform: Platform
): Promise<AppMetadata> {
let developmentClient = false;
let fingerprintHash: string | undefined;
const simulator = platform === Platform.IOS;
const basePath = platform === Platform.ANDROID ? 'assets/' : buildPath;
const fingerprintFilePath =
platform === Platform.ANDROID ? 'fingerprint' : 'EXUpdates.bundle/fingerprint';
const devMenuBundlePath =
platform === Platform.ANDROID ? 'EXDevMenuApp.android.js' : 'EXDevMenu.bundle/';
const buildExtension = path.extname(buildPath);
if (['.apk', '.aab'].includes(buildExtension)) {
const zip = new StreamZip.async({ file: buildPath });
try {
developmentClient = Boolean(await zip.entry(path.join(basePath, devMenuBundlePath)));
if (await zip.entry(path.join(basePath, fingerprintFilePath))) {
fingerprintHash = (await zip.entryData(path.join(basePath, fingerprintFilePath))).toString(
'utf-8'
);
}
} catch (err) {
Log.error(`Error reading ${buildExtension}: ${err}`);
} finally {
await zip.close();
}
} else if (buildExtension === '.app') {
developmentClient = await fs.exists(path.join(basePath, devMenuBundlePath));
if (await fs.exists(path.join(basePath, fingerprintFilePath))) {
fingerprintHash = await fs.readFile(path.join(basePath, fingerprintFilePath), 'utf8');
}
} else {
// Use tar to list files in the archive
try {
let fingerprintHashPromise: Promise<string> | undefined;
await tar.list({
file: buildPath,
// eslint-disable-next-line async-protect/async-suffix
onentry: entry => {
if (entry.path.endsWith(devMenuBundlePath)) {
developmentClient = true;
}
if (entry.path.endsWith(fingerprintFilePath)) {
fingerprintHashPromise = new Promise<string>(async (resolve, reject) => {
try {
let content = '';
for await (const chunk of entry) {
content += chunk.toString('utf8');
}
resolve(content);
} catch (error) {
reject(error);
}
});
}
},
});
if (fingerprintHashPromise !== undefined) {
fingerprintHash = await fingerprintHashPromise;
}
} catch (err) {
Log.error(`Error reading ${buildExtension}: ${err}`);
}
}
return {
developmentClient,
fingerprintHash,
simulator,
};
}
@@ -65,7 +65,7 @@ async function ensureInternalGroupAsync({
{ {
shouldRetry(error) { shouldRetry(error) {
if (isAppleError(error)) { if (isAppleError(error)) {
spinner.text = `TestFlight still preparing, retrying in 10 seconds...`; spinner.text = `TestFlight not ready, retrying in 25 seconds...`;
return error.data.errors.some( return error.data.errors.some(
error => error.code === 'ENTITY_ERROR.RELATIONSHIP.INVALID' error => error.code === 'ENTITY_ERROR.RELATIONSHIP.INVALID'
@@ -199,10 +199,9 @@ async function pollRetryAsync<T>(
fn: () => Promise<T>, fn: () => Promise<T>,
{ {
shouldRetry, shouldRetry,
retries = 15, retries = 10,
// 25 seconds was the minium interval I calculated when measuring against 5 second intervals. // 25 seconds was the minium interval I calculated when measuring against 5 second intervals.
// Switching to 10 seconds to account for days where Apple APIs are faster. interval = 25000,
interval = 10000,
}: { shouldRetry?: (error: Error) => boolean; retries?: number; interval?: number } = {} }: { shouldRetry?: (error: Error) => boolean; retries?: number; interval?: number } = {}
): Promise<T> { ): Promise<T> {
let lastError: Error | null = null; let lastError: Error | null = null;
-112
View File
@@ -1,112 +0,0 @@
import { Platform, Workflow } from '@expo/eas-build-job';
import { createFingerprintAsync } from './cli';
import { Fingerprint } from './types';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { AppPlatform } from '../graphql/generated';
import { FingerprintMutation } from '../graphql/mutations/FingerprintMutation';
import Log from '../log';
import { maybeUploadFingerprintAsync } from '../project/maybeUploadFingerprintAsync';
import { resolveWorkflowPerPlatformAsync } from '../project/workflow';
import { Client } from '../vcs/vcs';
export async function getFingerprintInfoFromLocalProjectForPlatformsAsync(
graphqlClient: ExpoGraphqlClient,
projectDir: string,
projectId: string,
vcsClient: Client,
platforms: AppPlatform[],
{ env }: { env?: Record<string, string> } = {}
): Promise<Fingerprint> {
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
const optionsFromWorkflow = getFingerprintOptionsFromWorkflow(platforms, workflows);
const projectFingerprint = await createFingerprintAsync(projectDir, {
...optionsFromWorkflow,
platforms: platforms.map(appPlatformToString),
debug: true,
env,
});
if (!projectFingerprint) {
throw new Error('Project fingerprints can only be computed for projects with SDK 52 or higher');
}
const uploadedFingerprint = await maybeUploadFingerprintAsync({
hash: projectFingerprint.hash,
fingerprint: {
fingerprintSources: projectFingerprint.sources,
isDebugFingerprintSource: Log.isDebug,
},
graphqlClient,
});
await FingerprintMutation.createFingerprintAsync(graphqlClient, projectId, {
hash: uploadedFingerprint.hash,
source: uploadedFingerprint.fingerprintSource,
});
return projectFingerprint;
}
function getFingerprintOptionsFromWorkflow(
platforms: AppPlatform[],
workflowsByPlatform: Record<Platform, Workflow>
): { workflow?: Workflow; ignorePaths?: string[] } {
if (platforms.length === 0) {
throw new Error('Could not determine platform from fingerprint sources');
}
// Single platform case
if (platforms.length === 1) {
const platform = platforms[0];
return { workflow: workflowsByPlatform[appPlatformToPlatform(platform)] };
}
// Multiple platforms case
const workflows = platforms.map(platform => workflowsByPlatform[appPlatformToPlatform(platform)]);
// If all workflows are the same, return the common workflow
const [firstWorkflow, ...restWorkflows] = workflows;
if (restWorkflows.every(workflow => workflow === firstWorkflow)) {
return { workflow: firstWorkflow };
}
// Generate ignorePaths for mixed workflows
const ignorePaths = platforms
.filter(platform => workflowsByPlatform[appPlatformToPlatform(platform)] === Workflow.MANAGED)
.map(platform => `${appPlatformToString(platform)}/**/*`);
return { ignorePaths };
}
export function appPlatformToPlatform(platform: AppPlatform): Platform {
switch (platform) {
case AppPlatform.Android:
return Platform.ANDROID;
case AppPlatform.Ios:
return Platform.IOS;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
export function appPlatformToString(platform: AppPlatform): string {
switch (platform) {
case AppPlatform.Android:
return 'android';
case AppPlatform.Ios:
return 'ios';
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
export function stringToAppPlatform(platform: string): AppPlatform {
switch (platform) {
case 'android':
return AppPlatform.Android;
case 'ios':
return AppPlatform.Ios;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
+9 -269
View File
@@ -131,7 +131,6 @@ export type Account = {
googleServiceAccountKeys: Array<GoogleServiceAccountKey>; googleServiceAccountKeys: Array<GoogleServiceAccountKey>;
/** Android credentials for account */ /** Android credentials for account */
googleServiceAccountKeysPaginated: AccountGoogleServiceAccountKeysConnection; googleServiceAccountKeysPaginated: AccountGoogleServiceAccountKeysConnection;
hasBuilds: Scalars['Boolean']['output'];
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
isCurrent: Scalars['Boolean']['output']; isCurrent: Scalars['Boolean']['output'];
isDisabled: Scalars['Boolean']['output']; isDisabled: Scalars['Boolean']['output'];
@@ -148,12 +147,10 @@ export type Account = {
owner?: Maybe<User>; owner?: Maybe<User>;
/** Owning UserActor of this account if personal account */ /** Owning UserActor of this account if personal account */
ownerUserActor?: Maybe<UserActor>; ownerUserActor?: Maybe<UserActor>;
pendingSentryInstallation?: Maybe<PendingSentryInstallation>;
profileImageUrl: Scalars['String']['output']; profileImageUrl: Scalars['String']['output'];
pushSecurityEnabled: Scalars['Boolean']['output']; pushSecurityEnabled: Scalars['Boolean']['output'];
/** @deprecated Legacy access tokens are deprecated */ /** @deprecated Legacy access tokens are deprecated */
requiresAccessTokenForPushSecurity: Scalars['Boolean']['output']; requiresAccessTokenForPushSecurity: Scalars['Boolean']['output'];
sentryInstallation?: Maybe<SentryInstallation>;
/** Snacks associated with this account */ /** Snacks associated with this account */
snacks: Array<Snack>; snacks: Array<Snack>;
/** SSO configuration for this account */ /** SSO configuration for this account */
@@ -1025,7 +1022,6 @@ export enum AndroidBuildType {
export type AndroidBuilderEnvironmentInput = { export type AndroidBuilderEnvironmentInput = {
bun?: InputMaybe<Scalars['String']['input']>; bun?: InputMaybe<Scalars['String']['input']>;
corepack?: InputMaybe<Scalars['Boolean']['input']>;
env?: InputMaybe<Scalars['JSONObject']['input']>; env?: InputMaybe<Scalars['JSONObject']['input']>;
expoCli?: InputMaybe<Scalars['String']['input']>; expoCli?: InputMaybe<Scalars['String']['input']>;
image?: InputMaybe<Scalars['String']['input']>; image?: InputMaybe<Scalars['String']['input']>;
@@ -1057,12 +1053,9 @@ export type AndroidFcmInput = {
export type AndroidFcmMutation = { export type AndroidFcmMutation = {
__typename?: 'AndroidFcmMutation'; __typename?: 'AndroidFcmMutation';
/** /** Create an FCM credential */
* Create an FCM V0/Legacy credential
* @deprecated FCM Legacy credentials are no longer supported by Google. Use createFcmV1Credential instead.
*/
createAndroidFcm: AndroidFcm; createAndroidFcm: AndroidFcm;
/** Delete an FCM V0/Legacy credential */ /** Delete an FCM credential */
deleteAndroidFcm: DeleteAndroidFcmResult; deleteAndroidFcm: DeleteAndroidFcmResult;
}; };
@@ -1325,7 +1318,6 @@ export type App = Project & {
privacy: Scalars['String']['output']; privacy: Scalars['String']['output'];
/** @deprecated No longer supported */ /** @deprecated No longer supported */
privacySetting: AppPrivacy; privacySetting: AppPrivacy;
profileImageUrl?: Maybe<Scalars['String']['output']>;
/** /**
* Whether there have been any classic update publishes * Whether there have been any classic update publishes
* @deprecated Classic updates have been deprecated. * @deprecated Classic updates have been deprecated.
@@ -1350,7 +1342,6 @@ export type App = Project & {
* @deprecated Classic updates have been deprecated. * @deprecated Classic updates have been deprecated.
*/ */
sdkVersion: Scalars['String']['output']; sdkVersion: Scalars['String']['output'];
sentryProject?: Maybe<SentryProject>;
slug: Scalars['String']['output']; slug: Scalars['String']['output'];
/** EAS Submissions associated with this app */ /** EAS Submissions associated with this app */
submissions: Array<Submission>; submissions: Array<Submission>;
@@ -1534,7 +1525,6 @@ export type AppLikedByArgs = {
export type AppRuntimesArgs = { export type AppRuntimesArgs = {
after?: InputMaybe<Scalars['String']['input']>; after?: InputMaybe<Scalars['String']['input']>;
before?: InputMaybe<Scalars['String']['input']>; before?: InputMaybe<Scalars['String']['input']>;
filter?: InputMaybe<RuntimeFilterInput>;
first?: InputMaybe<Scalars['Int']['input']>; first?: InputMaybe<Scalars['Int']['input']>;
last?: InputMaybe<Scalars['Int']['input']>; last?: InputMaybe<Scalars['Int']['input']>;
}; };
@@ -2550,21 +2540,6 @@ export type AscApiKeyInput = {
keyP8: Scalars['String']['input']; keyP8: Scalars['String']['input'];
}; };
export type AssetMapGroup = {
android?: InputMaybe<AssetMapSourceInput>;
ios?: InputMaybe<AssetMapSourceInput>;
web?: InputMaybe<AssetMapSourceInput>;
};
export type AssetMapSourceInput = {
bucketKey?: InputMaybe<Scalars['String']['input']>;
type?: InputMaybe<AssetMapSourceType>;
};
export enum AssetMapSourceType {
Gcs = 'GCS'
}
export type AssetMetadataResult = { export type AssetMetadataResult = {
__typename?: 'AssetMetadataResult'; __typename?: 'AssetMetadataResult';
status: AssetMetadataStatus; status: AssetMetadataStatus;
@@ -2683,11 +2658,6 @@ export enum AuthProviderIdentifier {
StubIdp = 'STUB_IDP' StubIdp = 'STUB_IDP'
} }
export type AverageMetrics = {
__typename?: 'AverageMetrics';
averageDownloadSize: Scalars['Int']['output'];
};
export type BackgroundJobReceipt = { export type BackgroundJobReceipt = {
__typename?: 'BackgroundJobReceipt'; __typename?: 'BackgroundJobReceipt';
account: Account; account: Account;
@@ -2779,7 +2749,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
/** @deprecated Use 'updateChannel' field instead. */ /** @deprecated Use 'updateChannel' field instead. */
channel?: Maybe<Scalars['String']['output']>; channel?: Maybe<Scalars['String']['output']>;
childBuild?: Maybe<Build>; childBuild?: Maybe<Build>;
cliVersion?: Maybe<Scalars['String']['output']>;
completedAt?: Maybe<Scalars['DateTime']['output']>; completedAt?: Maybe<Scalars['DateTime']['output']>;
createdAt: Scalars['DateTime']['output']; createdAt: Scalars['DateTime']['output'];
customNodeVersion?: Maybe<Scalars['String']['output']>; customNodeVersion?: Maybe<Scalars['String']['output']>;
@@ -2795,7 +2764,6 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
gitCommitHash?: Maybe<Scalars['String']['output']>; gitCommitHash?: Maybe<Scalars['String']['output']>;
gitCommitMessage?: Maybe<Scalars['String']['output']>; gitCommitMessage?: Maybe<Scalars['String']['output']>;
gitRef?: Maybe<Scalars['String']['output']>; gitRef?: Maybe<Scalars['String']['output']>;
/** @deprecated Use 'githubRepository' field instead */
githubRepositoryOwnerAndName?: Maybe<Scalars['String']['output']>; githubRepositoryOwnerAndName?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
/** Queue position is 1-indexed */ /** Queue position is 1-indexed */
@@ -3052,7 +3020,6 @@ export type BuildMetrics = {
export enum BuildMode { export enum BuildMode {
Build = 'BUILD', Build = 'BUILD',
Custom = 'CUSTOM', Custom = 'CUSTOM',
Local = 'LOCAL',
Repack = 'REPACK', Repack = 'REPACK',
Resign = 'RESIGN' Resign = 'RESIGN'
} }
@@ -3070,8 +3037,6 @@ export type BuildMutation = {
createAndroidBuild: CreateBuildResult; createAndroidBuild: CreateBuildResult;
/** Create an iOS build */ /** Create an iOS build */
createIosBuild: CreateBuildResult; createIosBuild: CreateBuildResult;
/** Create a local build */
createLocalBuild: CreateBuildResult;
/** Delete an EAS Build build */ /** Delete an EAS Build build */
deleteBuild: Build; deleteBuild: Build;
/** Retry an Android EAS Build */ /** Retry an Android EAS Build */
@@ -3109,14 +3074,6 @@ export type BuildMutationCreateIosBuildArgs = {
}; };
export type BuildMutationCreateLocalBuildArgs = {
appId: Scalars['ID']['input'];
artifactSource: LocalBuildArchiveSourceInput;
job: LocalBuildJobInput;
metadata?: InputMaybe<BuildMetadataInput>;
};
export type BuildMutationDeleteBuildArgs = { export type BuildMutationDeleteBuildArgs = {
buildId: Scalars['ID']['input']; buildId: Scalars['ID']['input'];
}; };
@@ -3216,7 +3173,6 @@ export enum BuildPriority {
export type BuildPublicData = { export type BuildPublicData = {
__typename?: 'BuildPublicData'; __typename?: 'BuildPublicData';
artifacts: PublicArtifacts; artifacts: PublicArtifacts;
buildMode?: Maybe<BuildMode>;
distribution?: Maybe<DistributionType>; distribution?: Maybe<DistributionType>;
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
isForIosSimulator: Scalars['Boolean']['output']; isForIosSimulator: Scalars['Boolean']['output'];
@@ -3424,11 +3380,6 @@ export type CreateAccessTokenResponse = {
token: Scalars['String']['output']; token: Scalars['String']['output'];
}; };
export type CreateAndConfigureRepositoryInput = {
appId: Scalars['ID']['input'];
installationIdentifier: Scalars['Int']['input'];
};
export type CreateAndroidSubmissionInput = { export type CreateAndroidSubmissionInput = {
appId: Scalars['ID']['input']; appId: Scalars['ID']['input'];
archiveSource?: InputMaybe<SubmissionArchiveSourceInput>; archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
@@ -3521,12 +3472,6 @@ export type CreateIosSubmissionInput = {
submittedBuildId?: InputMaybe<Scalars['ID']['input']>; submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
}; };
export type CreateSentryProjectInput = {
appId: Scalars['ID']['input'];
sentryProjectId: Scalars['String']['input'];
sentryProjectSlug: Scalars['String']['input'];
};
export type CreateSharedEnvironmentVariableInput = { export type CreateSharedEnvironmentVariableInput = {
environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>; environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
fileName?: InputMaybe<Scalars['String']['input']>; fileName?: InputMaybe<Scalars['String']['input']>;
@@ -3746,11 +3691,6 @@ export type DeleteSsoUserResult = {
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
}; };
export type DeleteSentryProjectResult = {
__typename?: 'DeleteSentryProjectResult';
id: Scalars['ID']['output'];
};
export type DeleteUpdateBranchResult = { export type DeleteUpdateBranchResult = {
__typename?: 'DeleteUpdateBranchResult'; __typename?: 'DeleteUpdateBranchResult';
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
@@ -4061,8 +4001,6 @@ export enum EntityTypeName {
CustomerEntity = 'CustomerEntity', CustomerEntity = 'CustomerEntity',
GoogleServiceAccountKeyEntity = 'GoogleServiceAccountKeyEntity', GoogleServiceAccountKeyEntity = 'GoogleServiceAccountKeyEntity',
IosAppCredentialsEntity = 'IosAppCredentialsEntity', IosAppCredentialsEntity = 'IosAppCredentialsEntity',
LogRocketOrganizationEntity = 'LogRocketOrganizationEntity',
LogRocketProjectEntity = 'LogRocketProjectEntity',
UserInvitationEntity = 'UserInvitationEntity', UserInvitationEntity = 'UserInvitationEntity',
UserPermissionEntity = 'UserPermissionEntity', UserPermissionEntity = 'UserPermissionEntity',
WorkerCustomDomainEntity = 'WorkerCustomDomainEntity', WorkerCustomDomainEntity = 'WorkerCustomDomainEntity',
@@ -4450,13 +4388,6 @@ export type GenerateLogRocketReplayTokenResult = {
replayToken: Scalars['String']['output']; replayToken: Scalars['String']['output'];
}; };
export type GenerateSentryTokenResult = {
__typename?: 'GenerateSentryTokenResult';
installationId: Scalars['ID']['output'];
orgSlug: Scalars['String']['output'];
token: Scalars['String']['output'];
};
export type GetSignedAssetUploadSpecificationsResult = { export type GetSignedAssetUploadSpecificationsResult = {
__typename?: 'GetSignedAssetUploadSpecificationsResult'; __typename?: 'GetSignedAssetUploadSpecificationsResult';
specifications: Array<Scalars['String']['output']>; specifications: Array<Scalars['String']['output']>;
@@ -4712,7 +4643,6 @@ export type GitHubRepositoryMutation = {
__typename?: 'GitHubRepositoryMutation'; __typename?: 'GitHubRepositoryMutation';
/** Configure EAS by pushing a commit to the default branch which updates or creates app.json, eas.json, and installs necessary dependencies. */ /** Configure EAS by pushing a commit to the default branch which updates or creates app.json, eas.json, and installs necessary dependencies. */
configureEAS: BackgroundJobReceipt; configureEAS: BackgroundJobReceipt;
createAndConfigureRepository: BackgroundJobReceipt;
/** Create a GitHub repository for an App */ /** Create a GitHub repository for an App */
createGitHubRepository: GitHubRepository; createGitHubRepository: GitHubRepository;
/** Delete a GitHub repository by ID */ /** Delete a GitHub repository by ID */
@@ -4725,11 +4655,6 @@ export type GitHubRepositoryMutationConfigureEasArgs = {
}; };
export type GitHubRepositoryMutationCreateAndConfigureRepositoryArgs = {
input: CreateAndConfigureRepositoryInput;
};
export type GitHubRepositoryMutationCreateGitHubRepositoryArgs = { export type GitHubRepositoryMutationCreateGitHubRepositoryArgs = {
githubRepositoryData: CreateGitHubRepositoryInput; githubRepositoryData: CreateGitHubRepositoryInput;
}; };
@@ -5114,7 +5039,6 @@ export type IosBuilderEnvironmentInput = {
bun?: InputMaybe<Scalars['String']['input']>; bun?: InputMaybe<Scalars['String']['input']>;
bundler?: InputMaybe<Scalars['String']['input']>; bundler?: InputMaybe<Scalars['String']['input']>;
cocoapods?: InputMaybe<Scalars['String']['input']>; cocoapods?: InputMaybe<Scalars['String']['input']>;
corepack?: InputMaybe<Scalars['Boolean']['input']>;
env?: InputMaybe<Scalars['JSONObject']['input']>; env?: InputMaybe<Scalars['JSONObject']['input']>;
expoCli?: InputMaybe<Scalars['String']['input']>; expoCli?: InputMaybe<Scalars['String']['input']>;
fastlane?: InputMaybe<Scalars['String']['input']>; fastlane?: InputMaybe<Scalars['String']['input']>;
@@ -5237,7 +5161,6 @@ export type IosSubmissionConfigInput = {
ascApiKey?: InputMaybe<AscApiKeyInput>; ascApiKey?: InputMaybe<AscApiKeyInput>;
ascApiKeyId?: InputMaybe<Scalars['String']['input']>; ascApiKeyId?: InputMaybe<Scalars['String']['input']>;
ascAppIdentifier: Scalars['String']['input']; ascAppIdentifier: Scalars['String']['input'];
groups?: InputMaybe<Array<Scalars['String']['input']>>;
isVerboseFastlaneEnabled?: InputMaybe<Scalars['Boolean']['input']>; isVerboseFastlaneEnabled?: InputMaybe<Scalars['Boolean']['input']>;
}; };
@@ -5364,34 +5287,12 @@ export type LinkLogRocketOrganizationToExpoAccountInput = {
state: Scalars['String']['input']; state: Scalars['String']['input'];
}; };
export type LinkSentryInstallationToExpoAccountInput = {
accountId: Scalars['ID']['input'];
code: Scalars['String']['input'];
installationId: Scalars['ID']['input'];
sentryOrgSlug: Scalars['String']['input'];
};
export type LinkSharedEnvironmentVariableInput = { export type LinkSharedEnvironmentVariableInput = {
appId: Scalars['ID']['input']; appId: Scalars['ID']['input'];
environment?: InputMaybe<EnvironmentVariableEnvironment>; environment?: InputMaybe<EnvironmentVariableEnvironment>;
environmentVariableId: Scalars['ID']['input']; environmentVariableId: Scalars['ID']['input'];
}; };
export type LocalBuildArchiveSourceInput = {
bucketKey: Scalars['String']['input'];
type: LocalBuildArchiveSourceType;
};
export enum LocalBuildArchiveSourceType {
Gcs = 'GCS'
}
export type LocalBuildJobInput = {
developmentClient?: InputMaybe<Scalars['Boolean']['input']>;
platform: AppPlatform;
simulator?: InputMaybe<Scalars['Boolean']['input']>;
};
export type LogNameTypeMapping = { export type LogNameTypeMapping = {
__typename?: 'LogNameTypeMapping'; __typename?: 'LogNameTypeMapping';
publicName: Scalars['String']['output']; publicName: Scalars['String']['output'];
@@ -5794,15 +5695,6 @@ export type PaymentDetails = {
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
}; };
export type PendingSentryInstallation = {
__typename?: 'PendingSentryInstallation';
account: Account;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
installationId: Scalars['String']['output'];
orgSlug: Scalars['String']['output'];
};
export enum Permission { export enum Permission {
Admin = 'ADMIN', Admin = 'ADMIN',
Own = 'OWN', Own = 'OWN',
@@ -5838,6 +5730,7 @@ export enum ProjectArchiveSourceType {
Gcs = 'GCS', Gcs = 'GCS',
Git = 'GIT', Git = 'GIT',
None = 'NONE', None = 'NONE',
S3 = 'S3',
Url = 'URL' Url = 'URL'
} }
@@ -5868,7 +5761,6 @@ export type PublicArtifacts = {
}; };
export type PublishUpdateGroupInput = { export type PublishUpdateGroupInput = {
assetMapGroup?: InputMaybe<AssetMapGroup>;
awaitingCodeSigningInfo?: InputMaybe<Scalars['Boolean']['input']>; awaitingCodeSigningInfo?: InputMaybe<Scalars['Boolean']['input']>;
branchId: Scalars['String']['input']; branchId: Scalars['String']['input'];
environment?: InputMaybe<EnvironmentVariableEnvironment>; environment?: InputMaybe<EnvironmentVariableEnvironment>;
@@ -5905,7 +5797,6 @@ export type RequestsFilters = {
method?: InputMaybe<Array<RequestMethod>>; method?: InputMaybe<Array<RequestMethod>>;
os?: InputMaybe<Array<UserAgentOs>>; os?: InputMaybe<Array<UserAgentOs>>;
pathname?: InputMaybe<Scalars['String']['input']>; pathname?: InputMaybe<Scalars['String']['input']>;
platform?: InputMaybe<Array<UserAgentPlatform>>;
requestId?: InputMaybe<Array<Scalars['WorkerDeploymentRequestID']['input']>>; requestId?: InputMaybe<Array<Scalars['WorkerDeploymentRequestID']['input']>>;
responseType?: InputMaybe<Array<ResponseType>>; responseType?: InputMaybe<Array<ResponseType>>;
status?: InputMaybe<Array<Scalars['Int']['input']>>; status?: InputMaybe<Array<Scalars['Int']['input']>>;
@@ -6054,7 +5945,7 @@ export type RootMutation = {
androidAppBuildCredentials: AndroidAppBuildCredentialsMutation; androidAppBuildCredentials: AndroidAppBuildCredentialsMutation;
/** Mutations that modify the credentials for an Android app */ /** Mutations that modify the credentials for an Android app */
androidAppCredentials: AndroidAppCredentialsMutation; androidAppCredentials: AndroidAppCredentialsMutation;
/** Mutations that modify an FCM V0/Legacy credential */ /** Mutations that modify an FCM credential */
androidFcm: AndroidFcmMutation; androidFcm: AndroidFcmMutation;
/** Mutations that modify a Keystore */ /** Mutations that modify a Keystore */
androidKeystore: AndroidKeystoreMutation; androidKeystore: AndroidKeystoreMutation;
@@ -6130,10 +6021,6 @@ export type RootMutation = {
notificationSubscription: NotificationSubscriptionMutation; notificationSubscription: NotificationSubscriptionMutation;
/** Mutations that create, update, and delete Robots */ /** Mutations that create, update, and delete Robots */
robot: RobotMutation; robot: RobotMutation;
/** Mutations for Sentry installations */
sentryInstallation: SentryInstallationMutation;
/** Mutations for Sentry projects */
sentryProject: SentryProjectMutation;
/** Mutations that modify an EAS Submit submission */ /** Mutations that modify an EAS Submit submission */
submission: SubmissionMutation; submission: SubmissionMutation;
update: UpdateMutation; update: UpdateMutation;
@@ -6324,7 +6211,6 @@ export type Runtime = {
fingerprint?: Maybe<Fingerprint>; fingerprint?: Maybe<Fingerprint>;
firstBuildCreatedAt?: Maybe<Scalars['DateTime']['output']>; firstBuildCreatedAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
isFingerprint: Scalars['Boolean']['output'];
updatedAt: Scalars['DateTime']['output']; updatedAt: Scalars['DateTime']['output'];
updates: AppUpdatesConnection; updates: AppUpdatesConnection;
version: Scalars['String']['output']; version: Scalars['String']['output'];
@@ -6378,7 +6264,6 @@ export type RuntimeEdge = {
export type RuntimeFilterInput = { export type RuntimeFilterInput = {
/** Only return runtimes shared with this branch */ /** Only return runtimes shared with this branch */
branchId?: InputMaybe<Scalars['String']['input']>; branchId?: InputMaybe<Scalars['String']['input']>;
runtimeVersions?: InputMaybe<Array<Scalars['String']['input']>>;
}; };
export type RuntimeQuery = { export type RuntimeQuery = {
@@ -6443,7 +6328,6 @@ export type SsoUser = Actor & UserActor & {
preferences: UserPreferences; preferences: UserPreferences;
/** Associated accounts */ /** Associated accounts */
primaryAccount: Account; primaryAccount: Account;
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
profilePhoto: Scalars['String']['output']; profilePhoto: Scalars['String']['output'];
/** Snacks associated with this account */ /** Snacks associated with this account */
snacks: Array<Snack>; snacks: Array<Snack>;
@@ -6539,69 +6423,6 @@ export type SecondFactorRegenerateBackupCodesResult = {
plaintextBackupCodes: Array<Scalars['String']['output']>; plaintextBackupCodes: Array<Scalars['String']['output']>;
}; };
export type SentryInstallation = {
__typename?: 'SentryInstallation';
account: Account;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
installationId: Scalars['String']['output'];
orgSlug: Scalars['String']['output'];
};
export type SentryInstallationMutation = {
__typename?: 'SentryInstallationMutation';
/** Confirm a pending Sentry installation */
confirmPendingSentryInstallation: SentryInstallation;
/** Generate a Sentry token for an installation */
generateSentryToken: GenerateSentryTokenResult;
/** Link a Sentry installation to an Expo account */
linkSentryInstallationToExpoAccount: PendingSentryInstallation;
};
export type SentryInstallationMutationConfirmPendingSentryInstallationArgs = {
installationId: Scalars['ID']['input'];
};
export type SentryInstallationMutationGenerateSentryTokenArgs = {
accountId: Scalars['ID']['input'];
};
export type SentryInstallationMutationLinkSentryInstallationToExpoAccountArgs = {
input: LinkSentryInstallationToExpoAccountInput;
};
export type SentryProject = {
__typename?: 'SentryProject';
app: App;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
sentryInstallationId: Scalars['ID']['output'];
sentryProjectId: Scalars['String']['output'];
sentryProjectSlug: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type SentryProjectMutation = {
__typename?: 'SentryProjectMutation';
/** Create a Sentry project */
createSentryProject: SentryProject;
/** Delete a Sentry project by ID */
deleteSentryProject: DeleteSentryProjectResult;
};
export type SentryProjectMutationCreateSentryProjectArgs = {
input: CreateSentryProjectInput;
};
export type SentryProjectMutationDeleteSentryProjectArgs = {
sentryProjectId: Scalars['ID']['input'];
};
export type Snack = Project & { export type Snack = Project & {
__typename?: 'Snack'; __typename?: 'Snack';
/** Description of the Snack */ /** Description of the Snack */
@@ -6985,7 +6806,6 @@ export type Update = ActivityTimelineProjectActivity & {
activityTimestamp: Scalars['DateTime']['output']; activityTimestamp: Scalars['DateTime']['output'];
actor?: Maybe<Actor>; actor?: Maybe<Actor>;
app: App; app: App;
assetMapUrl?: Maybe<Scalars['String']['output']>;
awaitingCodeSigningInfo: Scalars['Boolean']['output']; awaitingCodeSigningInfo: Scalars['Boolean']['output'];
branch: UpdateBranch; branch: UpdateBranch;
branchId: Scalars['ID']['output']; branchId: Scalars['ID']['output'];
@@ -7227,7 +7047,6 @@ export type UpdateInfoGroup = {
export type UpdateInsights = { export type UpdateInsights = {
__typename?: 'UpdateInsights'; __typename?: 'UpdateInsights';
averageMetrics: AverageMetrics;
cumulativeMetrics: CumulativeMetrics; cumulativeMetrics: CumulativeMetrics;
id: Scalars['ID']['output']; id: Scalars['ID']['output'];
totalUniqueUsers: Scalars['Int']['output']; totalUniqueUsers: Scalars['Int']['output'];
@@ -7245,13 +7064,8 @@ export type UpdateInsightsTotalUniqueUsersArgs = {
export type UpdateMutation = { export type UpdateMutation = {
__typename?: 'UpdateMutation'; __typename?: 'UpdateMutation';
/** /** Delete an EAS update group */
* Delete an EAS update group
* @deprecated Use scheduleUpdateGroupDeletion instead
*/
deleteUpdateGroup: DeleteUpdateGroupResult; deleteUpdateGroup: DeleteUpdateGroupResult;
/** Delete an EAS update group in the background */
scheduleUpdateGroupDeletion: BackgroundJobReceipt;
/** Set code signing info for an update */ /** Set code signing info for an update */
setCodeSigningInfo: Update; setCodeSigningInfo: Update;
/** Set rollout percentage for an update */ /** Set rollout percentage for an update */
@@ -7264,11 +7078,6 @@ export type UpdateMutationDeleteUpdateGroupArgs = {
}; };
export type UpdateMutationScheduleUpdateGroupDeletionArgs = {
group: Scalars['ID']['input'];
};
export type UpdateMutationSetCodeSigningInfoArgs = { export type UpdateMutationSetCodeSigningInfoArgs = {
codeSigningInfo: CodeSigningInfoInput; codeSigningInfo: CodeSigningInfoInput;
updateId: Scalars['ID']['input']; updateId: Scalars['ID']['input'];
@@ -7345,7 +7154,6 @@ export type UploadSessionCreateAppScopedUploadSessionArgs = {
export type UploadSessionCreateUploadSessionArgs = { export type UploadSessionCreateUploadSessionArgs = {
filename?: InputMaybe<Scalars['String']['input']>;
type: UploadSessionType; type: UploadSessionType;
}; };
@@ -7354,11 +7162,9 @@ export enum UploadSessionType {
EasBuildGcsProjectSources = 'EAS_BUILD_GCS_PROJECT_SOURCES', EasBuildGcsProjectSources = 'EAS_BUILD_GCS_PROJECT_SOURCES',
/** @deprecated Use EAS_BUILD_GCS_PROJECT_SOURCES instead. */ /** @deprecated Use EAS_BUILD_GCS_PROJECT_SOURCES instead. */
EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES', EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES',
EasShareGcsAppArchive = 'EAS_SHARE_GCS_APP_ARCHIVE',
/** @deprecated Use EAS_SUBMIT_GCS_APP_ARCHIVE instead. */ /** @deprecated Use EAS_SUBMIT_GCS_APP_ARCHIVE instead. */
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE', EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE',
EasSubmitGcsAppArchive = 'EAS_SUBMIT_GCS_APP_ARCHIVE', EasSubmitGcsAppArchive = 'EAS_SUBMIT_GCS_APP_ARCHIVE',
EasUpdateAssetsMetadata = 'EAS_UPDATE_ASSETS_METADATA',
EasUpdateFingerprint = 'EAS_UPDATE_FINGERPRINT' EasUpdateFingerprint = 'EAS_UPDATE_FINGERPRINT'
} }
@@ -7446,7 +7252,6 @@ export type User = Actor & UserActor & {
preferences: UserPreferences; preferences: UserPreferences;
/** Associated accounts */ /** Associated accounts */
primaryAccount: Account; primaryAccount: Account;
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
profilePhoto: Scalars['String']['output']; profilePhoto: Scalars['String']['output'];
/** Get all certified second factor authentication methods */ /** Get all certified second factor authentication methods */
secondFactorDevices: Array<UserSecondFactorDevice>; secondFactorDevices: Array<UserSecondFactorDevice>;
@@ -7552,7 +7357,6 @@ export type UserActor = {
preferences: UserPreferences; preferences: UserPreferences;
/** Associated accounts */ /** Associated accounts */
primaryAccount: Account; primaryAccount: Account;
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
profilePhoto: Scalars['String']['output']; profilePhoto: Scalars['String']['output'];
/** Snacks associated with this user's personal account */ /** Snacks associated with this user's personal account */
snacks: Array<Snack>; snacks: Array<Snack>;
@@ -7688,13 +7492,6 @@ export enum UserAgentOs {
Windows = 'WINDOWS' Windows = 'WINDOWS'
} }
export enum UserAgentPlatform {
Android = 'ANDROID',
Apple = 'APPLE',
Unknown = 'UNKNOWN',
Web = 'WEB'
}
export type UserAppPinMutation = { export type UserAppPinMutation = {
__typename?: 'UserAppPinMutation'; __typename?: 'UserAppPinMutation';
pinApp: Scalars['ID']['output']; pinApp: Scalars['ID']['output'];
@@ -7722,9 +7519,7 @@ export type UserAuditLog = {
targetEntityMutationType: TargetEntityMutationType; targetEntityMutationType: TargetEntityMutationType;
targetEntityTypeName: UserEntityTypeName; targetEntityTypeName: UserEntityTypeName;
targetEntityTypePublicName: Scalars['String']['output']; targetEntityTypePublicName: Scalars['String']['output'];
/** @deprecated Use userActor instead */
user: User; user: User;
userActor: UserActor;
websiteMessage: Scalars['String']['output']; websiteMessage: Scalars['String']['output'];
}; };
@@ -8313,7 +8108,6 @@ export type WorkerDeploymentRequestNode = {
method: Scalars['String']['output']; method: Scalars['String']['output'];
os?: Maybe<UserAgentOs>; os?: Maybe<UserAgentOs>;
pathname: Scalars['String']['output']; pathname: Scalars['String']['output'];
platform: UserAgentPlatform;
region?: Maybe<Scalars['String']['output']>; region?: Maybe<Scalars['String']['output']>;
requestId: Scalars['WorkerDeploymentRequestID']['output']; requestId: Scalars['WorkerDeploymentRequestID']['output'];
requestTimestamp: Scalars['DateTime']['output']; requestTimestamp: Scalars['DateTime']['output'];
@@ -8332,8 +8126,6 @@ export type WorkerDeploymentRequests = {
byCountry: Array<WorkerDeploymentRequestsCountryEdge>; byCountry: Array<WorkerDeploymentRequestsCountryEdge>;
byMethod: Array<WorkerDeploymentRequestsMethodEdge>; byMethod: Array<WorkerDeploymentRequestsMethodEdge>;
byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>; byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>;
byPathname: Array<WorkerDeploymentRequestsPathnameEdge>;
byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>;
byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>; byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>;
byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>; byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>;
interval: Scalars['Int']['output']; interval: Scalars['Int']['output'];
@@ -8380,18 +8172,6 @@ export type WorkerDeploymentRequestsByOsArgs = {
}; };
export type WorkerDeploymentRequestsByPathnameArgs = {
limit?: InputMaybe<Scalars['Int']['input']>;
orderBy?: InputMaybe<RequestsOrderBy>;
};
export type WorkerDeploymentRequestsByPlatformArgs = {
limit?: InputMaybe<Scalars['Int']['input']>;
orderBy?: InputMaybe<RequestsOrderBy>;
};
export type WorkerDeploymentRequestsByResponseTypeArgs = { export type WorkerDeploymentRequestsByResponseTypeArgs = {
limit?: InputMaybe<Scalars['Int']['input']>; limit?: InputMaybe<Scalars['Int']['input']>;
orderBy?: InputMaybe<RequestsOrderBy>; orderBy?: InputMaybe<RequestsOrderBy>;
@@ -8460,7 +8240,7 @@ export type WorkerDeploymentRequestsCacheStatusEdge = {
export type WorkerDeploymentRequestsContinentEdge = { export type WorkerDeploymentRequestsContinentEdge = {
__typename?: 'WorkerDeploymentRequestsContinentEdge'; __typename?: 'WorkerDeploymentRequestsContinentEdge';
continent?: Maybe<ContinentCode>; continent: ContinentCode;
node: WorkerDeploymentRequestsAggregationNode; node: WorkerDeploymentRequestsAggregationNode;
}; };
@@ -8482,18 +8262,6 @@ export type WorkerDeploymentRequestsOperatingSystemEdge = {
os?: Maybe<UserAgentOs>; os?: Maybe<UserAgentOs>;
}; };
export type WorkerDeploymentRequestsPathnameEdge = {
__typename?: 'WorkerDeploymentRequestsPathnameEdge';
node: WorkerDeploymentRequestsAggregationNode;
pathname: Scalars['String']['output'];
};
export type WorkerDeploymentRequestsPlatformEdge = {
__typename?: 'WorkerDeploymentRequestsPlatformEdge';
node: WorkerDeploymentRequestsAggregationNode;
platform: UserAgentPlatform;
};
export type WorkerDeploymentRequestsResponseTypeEdge = { export type WorkerDeploymentRequestsResponseTypeEdge = {
__typename?: 'WorkerDeploymentRequestsResponseTypeEdge'; __typename?: 'WorkerDeploymentRequestsResponseTypeEdge';
node: WorkerDeploymentRequestsAggregationNode; node: WorkerDeploymentRequestsAggregationNode;
@@ -8514,8 +8282,6 @@ export type WorkerDeploymentRequestsTimeseriesEdge = {
byCountry: Array<WorkerDeploymentRequestsCountryEdge>; byCountry: Array<WorkerDeploymentRequestsCountryEdge>;
byMethod: Array<WorkerDeploymentRequestsMethodEdge>; byMethod: Array<WorkerDeploymentRequestsMethodEdge>;
byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>; byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>;
byPathname: Array<WorkerDeploymentRequestsPathnameEdge>;
byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>;
byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>; byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>;
byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>; byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>;
node?: Maybe<WorkerDeploymentRequestsAggregationNode>; node?: Maybe<WorkerDeploymentRequestsAggregationNode>;
@@ -8639,11 +8405,9 @@ export enum WorkflowJobType {
Build = 'BUILD', Build = 'BUILD',
Custom = 'CUSTOM', Custom = 'CUSTOM',
Deploy = 'DEPLOY', Deploy = 'DEPLOY',
Fingerprint = 'FINGERPRINT',
GetBuild = 'GET_BUILD', GetBuild = 'GET_BUILD',
MaestroTest = 'MAESTRO_TEST', MaestroTest = 'MAESTRO_TEST',
RequireApproval = 'REQUIRE_APPROVAL', RequireApproval = 'REQUIRE_APPROVAL',
Slack = 'SLACK',
Submission = 'SUBMISSION', Submission = 'SUBMISSION',
Update = 'UPDATE' Update = 'UPDATE'
} }
@@ -8739,8 +8503,6 @@ export type WorkflowRun = ActivityTimelineProjectActivity & {
sourceExpiresAt?: Maybe<Scalars['DateTime']['output']>; sourceExpiresAt?: Maybe<Scalars['DateTime']['output']>;
status: WorkflowRunStatus; status: WorkflowRunStatus;
triggerEventType: WorkflowRunTriggerEventType; triggerEventType: WorkflowRunTriggerEventType;
triggeringLabelName?: Maybe<Scalars['String']['output']>;
triggeringSchedule?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output']; updatedAt: Scalars['DateTime']['output'];
workflow: Workflow; workflow: Workflow;
workflowRevision?: Maybe<WorkflowRevision>; workflowRevision?: Maybe<WorkflowRevision>;
@@ -8808,13 +8570,11 @@ export enum WorkflowRunStatus {
} }
export enum WorkflowRunTriggerEventType { export enum WorkflowRunTriggerEventType {
GithubPullRequestLabeled = 'GITHUB_PULL_REQUEST_LABELED',
GithubPullRequestOpened = 'GITHUB_PULL_REQUEST_OPENED', GithubPullRequestOpened = 'GITHUB_PULL_REQUEST_OPENED',
GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED', GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED',
GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE', GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE',
GithubPush = 'GITHUB_PUSH', GithubPush = 'GITHUB_PUSH',
Manual = 'MANUAL', Manual = 'MANUAL'
Schedule = 'SCHEDULE'
} }
export type WorkflowRunsConnection = { export type WorkflowRunsConnection = {
@@ -8932,12 +8692,12 @@ export type AppInfoQueryVariables = Exact<{
export type AppInfoQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, fullName: string } } }; export type AppInfoQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, fullName: string } } };
export type ScheduleUpdateGroupDeletionMutationVariables = Exact<{ export type DeleteUpdateGroupMutationVariables = Exact<{
group: Scalars['ID']['input']; group: Scalars['ID']['input'];
}>; }>;
export type ScheduleUpdateGroupDeletionMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', scheduleUpdateGroupDeletion: { __typename?: 'BackgroundJobReceipt', id: string, state: BackgroundJobState, tries: number, willRetry: boolean, resultId?: string | null, resultType: BackgroundJobResultType, resultData?: any | null, errorCode?: string | null, errorMessage?: string | null, createdAt: any, updatedAt: any } } }; export type DeleteUpdateGroupMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', deleteUpdateGroup: { __typename?: 'DeleteUpdateGroupResult', group: string } } };
export type CreateAndroidAppBuildCredentialsMutationVariables = Exact<{ export type CreateAndroidAppBuildCredentialsMutationVariables = Exact<{
androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput; androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput;
@@ -9492,16 +9252,6 @@ export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]
export type CreateKeystoreGenerationUrlMutation = { __typename?: 'RootMutation', keystoreGenerationUrl: { __typename?: 'KeystoreGenerationUrlMutation', createKeystoreGenerationUrl: { __typename?: 'KeystoreGenerationUrl', id: string, url: string } } }; export type CreateKeystoreGenerationUrlMutation = { __typename?: 'RootMutation', keystoreGenerationUrl: { __typename?: 'KeystoreGenerationUrlMutation', createKeystoreGenerationUrl: { __typename?: 'KeystoreGenerationUrl', id: string, url: string } } };
export type CreateLocalBuildMutationVariables = Exact<{
appId: Scalars['ID']['input'];
jobInput: LocalBuildJobInput;
artifactSource: LocalBuildArchiveSourceInput;
metadata?: InputMaybe<BuildMetadataInput>;
}>;
export type CreateLocalBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', createLocalBuild: { __typename?: 'CreateBuildResult', build: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null } } } };
export type GetSignedUploadMutationVariables = Exact<{ export type GetSignedUploadMutationVariables = Exact<{
contentTypes: Array<Scalars['String']['input']> | Scalars['String']['input']; contentTypes: Array<Scalars['String']['input']> | Scalars['String']['input'];
}>; }>;
@@ -9554,7 +9304,6 @@ export type CreateIosSubmissionMutation = { __typename?: 'RootMutation', submiss
export type CreateUploadSessionMutationVariables = Exact<{ export type CreateUploadSessionMutationVariables = Exact<{
type: UploadSessionType; type: UploadSessionType;
filename?: InputMaybe<Scalars['String']['input']>;
}>; }>;
@@ -9645,13 +9394,6 @@ export type LatestAppVersionQueryVariables = Exact<{
export type LatestAppVersionQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, latestAppVersionByPlatformAndApplicationIdentifier?: { __typename?: 'AppVersion', id: string, storeVersion: string, buildVersion: string } | null } } }; export type LatestAppVersionQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, latestAppVersionByPlatformAndApplicationIdentifier?: { __typename?: 'AppVersion', id: string, storeVersion: string, buildVersion: string } | null } } };
export type BackgroundJobReceiptByIdQueryVariables = Exact<{
id: Scalars['ID']['input'];
}>;
export type BackgroundJobReceiptByIdQuery = { __typename?: 'RootQuery', backgroundJobReceipt: { __typename?: 'BackgroundJobReceiptQuery', byId: { __typename?: 'BackgroundJobReceipt', id: string, state: BackgroundJobState, tries: number, willRetry: boolean, resultId?: string | null, resultType: BackgroundJobResultType, resultData?: any | null, errorCode?: string | null, errorMessage?: string | null, createdAt: any, updatedAt: any } } };
export type ViewBranchQueryVariables = Exact<{ export type ViewBranchQueryVariables = Exact<{
appId: Scalars['String']['input']; appId: Scalars['String']['input'];
name: Scalars['String']['input']; name: Scalars['String']['input'];
@@ -9945,8 +9687,6 @@ export type AccountFragment = { __typename?: 'Account', id: string, name: string
export type AppFragment = { __typename?: 'App', id: string, name: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> }, githubRepository?: { __typename?: 'GitHubRepository', id: string, metadata: { __typename?: 'GitHubRepositoryMetadata', githubRepoOwnerName: string, githubRepoName: string } } | null }; export type AppFragment = { __typename?: 'App', id: string, name: string, fullName: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string, ownerUserActor?: { __typename?: 'SSOUser', id: string, username: string } | { __typename?: 'User', id: string, username: string } | null, users: Array<{ __typename?: 'UserPermission', role: Role, actor: { __typename?: 'Robot', id: string } | { __typename?: 'SSOUser', id: string } | { __typename?: 'User', id: string } }> }, githubRepository?: { __typename?: 'GitHubRepository', id: string, metadata: { __typename?: 'GitHubRepositoryMetadata', githubRepoOwnerName: string, githubRepoName: string } } | null };
export type BackgroundJobReceiptDataFragment = { __typename?: 'BackgroundJobReceipt', id: string, state: BackgroundJobState, tries: number, willRetry: boolean, resultId?: string | null, resultType: BackgroundJobResultType, resultData?: any | null, errorCode?: string | null, errorMessage?: string | null, createdAt: any, updatedAt: any };
export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null }; export type BuildFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null };
export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null }; export type BuildWithSubmissionsFragment = { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, channel?: string | null, distribution?: DistributionType | null, iosEnterpriseProvisioning?: BuildIosEnterpriseProvisioning | null, buildProfile?: string | null, sdkVersion?: string | null, appVersion?: string | null, appBuildVersion?: string | null, runtimeVersion?: string | null, gitCommitHash?: string | null, gitCommitMessage?: string | null, initialQueuePosition?: number | null, queuePosition?: number | null, estimatedWaitTimeLeftSeconds?: number | null, priority: BuildPriority, createdAt: any, updatedAt: any, message?: string | null, completedAt?: any | null, expirationDate?: any | null, isForIosSimulator: boolean, submissions: Array<{ __typename?: 'Submission', id: string, status: SubmissionStatus, platform: AppPlatform, logFiles: Array<string>, app: { __typename?: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, androidConfig?: { __typename?: 'AndroidSubmissionConfig', applicationIdentifier?: string | null, track: SubmissionAndroidTrack, releaseStatus?: SubmissionAndroidReleaseStatus | null, rollout?: number | null } | null, iosConfig?: { __typename?: 'IosSubmissionConfig', ascAppIdentifier: string, appleIdUsername?: string | null } | null, error?: { __typename?: 'SubmissionError', errorCode?: string | null, message?: string | null } | null }>, error?: { __typename?: 'BuildError', errorCode: string, message: string, docsUrl?: string | null } | null, artifacts?: { __typename?: 'BuildArtifacts', buildUrl?: string | null, xcodeBuildLogsUrl?: string | null, applicationArchiveUrl?: string | null, buildArtifactsUrl?: string | null } | null, initiatingActor?: { __typename: 'Robot', id: string, displayName: string } | { __typename: 'SSOUser', id: string, displayName: string } | { __typename: 'User', id: string, displayName: string } | null, project: { __typename: 'App', id: string, name: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } } | { __typename: 'Snack', id: string, name: string, slug: string }, metrics?: { __typename?: 'BuildMetrics', buildWaitTime?: number | null, buildQueueTime?: number | null, buildDuration?: number | null } | null };
@@ -1,55 +0,0 @@
import { print } from 'graphql';
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
BuildFragment,
BuildMetadataInput,
CreateLocalBuildMutation,
LocalBuildArchiveSourceInput,
LocalBuildJobInput,
} from '../generated';
import { BuildFragmentNode } from '../types/Build';
export const LocalBuildMutation = {
async createLocalBuildAsync(
graphqlClient: ExpoGraphqlClient,
appId: string,
job: LocalBuildJobInput,
artifactSource: LocalBuildArchiveSourceInput,
metadata: BuildMetadataInput
): Promise<BuildFragment> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<CreateLocalBuildMutation>(
gql`
mutation createLocalBuildMutation(
$appId: ID!
$jobInput: LocalBuildJobInput!
$artifactSource: LocalBuildArchiveSourceInput!
$metadata: BuildMetadataInput
) {
build {
createLocalBuild(
appId: $appId
job: $jobInput
artifactSource: $artifactSource
metadata: $metadata
) {
build {
id
...BuildFragment
}
}
}
}
${print(BuildFragmentNode)}
`,
{ appId, jobInput: job, artifactSource, metadata }
)
.toPromise()
);
return data.build.createLocalBuild.build;
},
};
@@ -20,22 +20,20 @@ export interface SignedUrl {
export const UploadSessionMutation = { export const UploadSessionMutation = {
async createUploadSessionAsync( async createUploadSessionAsync(
graphqlClient: ExpoGraphqlClient, graphqlClient: ExpoGraphqlClient,
type: UploadSessionType, type: UploadSessionType
filename?: string
): Promise<SignedUrl> { ): Promise<SignedUrl> {
const data = await withErrorHandlingAsync( const data = await withErrorHandlingAsync(
graphqlClient graphqlClient
.mutation<CreateUploadSessionMutation, CreateUploadSessionMutationVariables>( .mutation<CreateUploadSessionMutation, CreateUploadSessionMutationVariables>(
gql` gql`
mutation CreateUploadSessionMutation($type: UploadSessionType!, $filename: String) { mutation CreateUploadSessionMutation($type: UploadSessionType!) {
uploadSession { uploadSession {
createUploadSession(type: $type, filename: $filename) createUploadSession(type: $type)
} }
} }
`, `,
{ {
type, type,
filename,
} }
) )
.toPromise() .toPromise()
@@ -1,40 +0,0 @@
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
BackgroundJobReceiptByIdQuery,
BackgroundJobReceiptByIdQueryVariables,
} from '../generated';
import { BackgroundJobReceiptNode } from '../types/BackgroundJobReceipt';
export const BackgroundJobReceiptQuery = {
async byIdAsync(
graphqlClient: ExpoGraphqlClient,
backgroundJobReceiptId: string
): Promise<BackgroundJobReceiptByIdQuery['backgroundJobReceipt']['byId'] | null> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<BackgroundJobReceiptByIdQuery, BackgroundJobReceiptByIdQueryVariables>(
gql`
query BackgroundJobReceiptById($id: ID!) {
backgroundJobReceipt {
byId(id: $id) {
id
...BackgroundJobReceiptData
}
}
}
${BackgroundJobReceiptNode}
`,
{ id: backgroundJobReceiptId },
{
additionalTypenames: ['BackgroundJobReceipt'],
}
)
.toPromise()
);
return data.backgroundJobReceipt.byId ?? null;
},
};
@@ -1,17 +0,0 @@
import gql from 'graphql-tag';
export const BackgroundJobReceiptNode = gql`
fragment BackgroundJobReceiptData on BackgroundJobReceipt {
id
state
tries
willRetry
resultId
resultType
resultData
errorCode
errorMessage
createdAt
updatedAt
}
`;
+2 -2
View File
@@ -8,7 +8,7 @@ import { createAppleTasks } from './apple/tasks';
import { getAppStoreAuthAsync } from './auth'; import { getAppStoreAuthAsync } from './auth';
import { createAppleWriter, getStaticConfigFilePath } from './config/resolve'; import { createAppleWriter, getStaticConfigFilePath } from './config/resolve';
import { MetadataDownloadError, MetadataValidationError } from './errors'; import { MetadataDownloadError, MetadataValidationError } from './errors';
import { subscribeTelemetryAsync } from './utils/telemetry'; import { subscribeTelemetry } from './utils/telemetry';
import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager'; import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager';
import { CredentialsContext } from '../credentials/context'; import { CredentialsContext } from '../credentials/context';
import Log from '../log'; import Log from '../log';
@@ -51,7 +51,7 @@ export async function downloadMetadataAsync({
profile, profile,
}); });
const { unsubscribeTelemetry, executionId } = await subscribeTelemetryAsync( const { unsubscribeTelemetry, executionId } = subscribeTelemetry(
analytics, analytics,
MetadataEvent.APPLE_METADATA_DOWNLOAD, MetadataEvent.APPLE_METADATA_DOWNLOAD,
{ app, auth } { app, auth }
+2 -2
View File
@@ -7,7 +7,7 @@ import { getAppStoreAuthAsync } from './auth';
import { createAppleReader, loadConfigAsync } from './config/resolve'; import { createAppleReader, loadConfigAsync } from './config/resolve';
import { MetadataConfig } from './config/schema'; import { MetadataConfig } from './config/schema';
import { MetadataUploadError, MetadataValidationError, logMetadataValidationError } from './errors'; import { MetadataUploadError, MetadataValidationError, logMetadataValidationError } from './errors';
import { subscribeTelemetryAsync } from './utils/telemetry'; import { subscribeTelemetry } from './utils/telemetry';
import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager'; import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager';
import { CredentialsContext } from '../credentials/context'; import { CredentialsContext } from '../credentials/context';
import Log from '../log'; import Log from '../log';
@@ -38,7 +38,7 @@ export async function uploadMetadataAsync({
profile, profile,
}); });
const { unsubscribeTelemetry, executionId } = await subscribeTelemetryAsync( const { unsubscribeTelemetry, executionId } = subscribeTelemetry(
analytics, analytics,
MetadataEvent.APPLE_METADATA_UPLOAD, MetadataEvent.APPLE_METADATA_UPLOAD,
{ app, auth } { app, auth }
@@ -1,8 +1,8 @@
import { App } from '@expo/apple-utils'; import { App } from '@expo/apple-utils';
import { TelemetryContext, makeDataScrubberAsync } from '../telemetry'; import { TelemetryContext, makeDataScrubber } from '../telemetry';
describe(makeDataScrubberAsync, () => { describe(makeDataScrubber, () => {
const stub: TelemetryContext = { const stub: TelemetryContext = {
// Only the App ID is considered to be sensitive // Only the App ID is considered to be sensitive
app: new App({} as any, 'SECRET_APP_ID', {} as any), app: new App({} as any, 'SECRET_APP_ID', {} as any),
@@ -18,31 +18,25 @@ describe(makeDataScrubberAsync, () => {
} as any, } as any,
}; };
it('scrubs the app.id', async () => { it('scrubs the app.id', () => {
expect((await makeDataScrubberAsync(stub))('some text SECRET_APP_ID')).toBe( expect(makeDataScrubber(stub)('some text SECRET_APP_ID')).toBe('some text {APPLE_APP_ID}');
'some text {APPLE_APP_ID}'
);
}); });
it('scrubs the auth.username', async () => { it('scrubs the auth.username', () => {
expect((await makeDataScrubberAsync(stub))('some text SECRET_USERNAME')).toBe( expect(makeDataScrubber(stub)('some text SECRET_USERNAME')).toBe('some text {APPLE_USERNAME}');
'some text {APPLE_USERNAME}'
);
}); });
it('scrubs the auth.password', async () => { it('scrubs the auth.password', () => {
expect((await makeDataScrubberAsync(stub))('SECRET_PASSWORD')).toBe('{APPLE_PASSWORD}'); expect(makeDataScrubber(stub)('SECRET_PASSWORD')).toBe('{APPLE_PASSWORD}');
}); });
it('scrubs the auth.context.token', async () => { it('scrubs the auth.context.token', () => {
expect((await makeDataScrubberAsync(stub))('some text SECRET_TOKEN')).toBe( expect(makeDataScrubber(stub)('some text SECRET_TOKEN')).toBe('some text {APPLE_TOKEN}');
'some text {APPLE_TOKEN}'
);
}); });
it('scrubs the auth.context.token when using token instances', async () => { it('scrubs the auth.context.token when using token instances', () => {
const token = { getToken: () => 'SECRET_TOKEN_INSTANCE' } as any; const token = { getToken: () => 'SECRET_TOKEN_INSTANCE' } as any;
const scrubber = await makeDataScrubberAsync({ const scrubber = makeDataScrubber({
...stub, ...stub,
auth: { auth: {
...stub.auth, ...stub.auth,
@@ -56,31 +50,29 @@ describe(makeDataScrubberAsync, () => {
expect(scrubber('some text SECRET_TOKEN_INSTANCE')).toBe('some text {APPLE_TOKEN}'); expect(scrubber('some text SECRET_TOKEN_INSTANCE')).toBe('some text {APPLE_TOKEN}');
}); });
it('scrubs the auth.context.teamId', async () => { it('scrubs the auth.context.teamId', () => {
expect((await makeDataScrubberAsync(stub))('SECRET_TEAM_ID')).toBe('{APPLE_TEAM_ID}'); expect(makeDataScrubber(stub)('SECRET_TEAM_ID')).toBe('{APPLE_TEAM_ID}');
}); });
it('scrubs the auth.context.teamId', async () => { it('scrubs the auth.context.teamId', () => {
expect((await makeDataScrubberAsync(stub))('some provider 1337')).toBe( expect(makeDataScrubber(stub)('some provider 1337')).toBe('some provider {APPLE_PROVIDER_ID}');
'some provider {APPLE_PROVIDER_ID}'
);
}); });
it('scrubber returns string representation of falsy values', async () => { it('scrubber returns string representation of falsy values', () => {
const scrubber = await makeDataScrubberAsync(stub); const scrubber = makeDataScrubber(stub);
expect(scrubber(null)).toBe('null'); expect(scrubber(null)).toBe('null');
expect(scrubber(undefined)).toBe('undefined'); expect(scrubber(undefined)).toBe('undefined');
expect(scrubber(false)).toBe('false'); expect(scrubber(false)).toBe('false');
}); });
it('scrubs multiple sensitive values at once', async () => { it('scrubs multiple sensitive values at once', () => {
expect( expect(makeDataScrubber(stub)('SECRET_TOKEN SECRET_USERNAME SECRET_PASSWORD')).toBe(
(await makeDataScrubberAsync(stub))('SECRET_TOKEN SECRET_USERNAME SECRET_PASSWORD') '{APPLE_TOKEN} {APPLE_USERNAME} {APPLE_PASSWORD}'
).toBe('{APPLE_TOKEN} {APPLE_USERNAME} {APPLE_PASSWORD}'); );
}); });
it('scrubs json and transforms it to string', async () => { it('scrubs json and transforms it to string', () => {
const scrubber = await makeDataScrubberAsync(stub); const scrubber = makeDataScrubber(stub);
expect(scrubber({ foo: 'bar' })).toBe('{"foo":"bar"}'); expect(scrubber({ foo: 'bar' })).toBe('{"foo":"bar"}');
expect(scrubber({ value: 'SECRET_APP_ID' })).toBe('{"value":"{APPLE_APP_ID}"}'); expect(scrubber({ value: 'SECRET_APP_ID' })).toBe('{"value":"{APPLE_APP_ID}"}');
}); });
@@ -14,18 +14,18 @@ export type TelemetryContext = {
* When providing the app and auth info, we can scrub that data from the telemetry. * When providing the app and auth info, we can scrub that data from the telemetry.
* Returns an execution ID to group all events of a single run together, and a unsubscribe function. * Returns an execution ID to group all events of a single run together, and a unsubscribe function.
*/ */
export async function subscribeTelemetryAsync( export function subscribeTelemetry(
analytics: Analytics, analytics: Analytics,
event: MetadataEvent, event: MetadataEvent,
options: TelemetryContext options: TelemetryContext
): Promise<{ ): {
/** Unsubscribe the telemetry from all apple-utils events */ /** Unsubscribe the telemetry from all apple-utils events */
unsubscribeTelemetry: () => void; unsubscribeTelemetry: () => void;
/** The unique id added to all telemetry events from a single execution */ /** The unique id added to all telemetry events from a single execution */
executionId: string; executionId: string;
}> { } {
const executionId = uuidv4(); const executionId = uuidv4();
const scrubber = await makeDataScrubberAsync(options); const scrubber = makeDataScrubber(options);
const { interceptors } = getRequestClient(); const { interceptors } = getRequestClient();
const responseInterceptorId = interceptors.response.use( const responseInterceptorId = interceptors.response.use(
@@ -68,11 +68,8 @@ export async function subscribeTelemetryAsync(
} }
/** Exposed for testing */ /** Exposed for testing */
export async function makeDataScrubberAsync({ export function makeDataScrubber({ app, auth }: TelemetryContext): <T>(data: T) => string {
app, const token = getAuthTokenString(auth);
auth,
}: TelemetryContext): Promise<<T>(data: T) => string> {
const token = await getAuthTokenStringAsync(auth);
const patterns: Record<string, RegExp | null> = { const patterns: Record<string, RegExp | null> = {
APPLE_APP_ID: new RegExp(app.id, 'gi'), APPLE_APP_ID: new RegExp(app.id, 'gi'),
APPLE_USERNAME: auth.username ? new RegExp(auth.username, 'gi') : null, APPLE_USERNAME: auth.username ? new RegExp(auth.username, 'gi') : null,
@@ -101,13 +98,13 @@ export async function makeDataScrubberAsync({
}; };
} }
async function getAuthTokenStringAsync(auth: TelemetryContext['auth']): Promise<string | null> { function getAuthTokenString(auth: TelemetryContext['auth']): string | null {
if (!auth.context?.token) { if (!auth.context?.token) {
return null; return null;
} }
if (typeof auth.context.token === 'object') { if (typeof auth.context.token === 'object') {
return await auth.context.token.getToken(); return auth.context.token.getToken();
} }
return auth.context.token; return auth.context.token;
@@ -424,7 +424,7 @@ describe(collectAssetsAsync, () => {
expect(await collectAssetsAsync(inputDir)).toEqual({ expect(await collectAssetsAsync(inputDir)).toEqual({
android: { android: {
launchAsset: { launchAsset: {
fileExtension: '.js', fileExtension: '.bundle',
contentType: 'application/javascript', contentType: 'application/javascript',
path: `${inputDir}/bundles/android.js`, path: `${inputDir}/bundles/android.js`,
}, },
@@ -432,7 +432,7 @@ describe(collectAssetsAsync, () => {
}, },
ios: { ios: {
launchAsset: { launchAsset: {
fileExtension: '.js', fileExtension: '.bundle',
contentType: 'application/javascript', contentType: 'application/javascript',
path: `${inputDir}/bundles/ios.js`, path: `${inputDir}/bundles/ios.js`,
}, },
@@ -440,7 +440,7 @@ describe(collectAssetsAsync, () => {
}, },
web: { web: {
launchAsset: { launchAsset: {
fileExtension: '.js', fileExtension: '.bundle',
contentType: 'application/javascript', contentType: 'application/javascript',
path: `${inputDir}/bundles/web.js`, path: `${inputDir}/bundles/web.js`,
}, },
@@ -75,9 +75,7 @@ export async function getApplicationIdFromBareAsync(
gradleContext.flavor gradleContext.flavor
); );
if (applicationIdSuffix) { if (applicationIdSuffix) {
throw new Error( throw new Error('"applicationIdSuffix" in app/build.gradle is not supported.');
'"applicationIdSuffix" in app/build.gradle is not supported, configure the full application ID under productFlavors.'
);
} }
const applicationId = gradleUtils.resolveConfigValue( const applicationId = gradleUtils.resolveConfigValue(
buildGradle, buildGradle,
@@ -91,9 +89,7 @@ export async function getApplicationIdFromBareAsync(
const buildGradle = await fs.readFile(buildGradlePath, 'utf8'); const buildGradle = await fs.readFile(buildGradlePath, 'utf8');
const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/); const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/);
if (buildGradle.match(/applicationIdSuffix/)) { if (buildGradle.match(/applicationIdSuffix/)) {
throw new Error( throw new Error('"applicationIdSuffix" in app/build.gradle is not supported.');
'"applicationIdSuffix" in app/build.gradle is not supported, configure the full application ID under productFlavors.'
);
} }
if (buildGradle.match(/productFlavors/)) { if (buildGradle.match(/productFlavors/)) {
throw new AmbiguousApplicationIdError( throw new AmbiguousApplicationIdError(
@@ -1,53 +0,0 @@
import chalk from 'chalk';
import fs from 'fs-extra';
import path from 'path';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { AssetMapSourceInput, AssetMapSourceType, UploadSessionType } from '../graphql/generated';
import Log from '../log';
import { uploadFileAtPathToGCSAsync } from '../uploads';
import { formatBytes } from '../utils/files';
import { createProgressTracker } from '../utils/progress';
export async function maybeUploadAssetMapAsync(
distRoot: string,
graphqlClient: ExpoGraphqlClient
): Promise<AssetMapSourceInput | null> {
const assetMapPath = path.join(distRoot, 'assetmap.json');
if (!(await fs.pathExists(assetMapPath))) {
return null;
}
let gcsBucketKey = undefined;
const fileStat = await fs.promises.stat(assetMapPath);
try {
gcsBucketKey = await uploadFileAtPathToGCSAsync(
graphqlClient,
UploadSessionType.EasUpdateAssetsMetadata,
assetMapPath,
createProgressTracker({
total: fileStat.size,
message: ratio =>
`Uploading assetmap.json (${formatBytes(fileStat.size * ratio)} / ${formatBytes(
fileStat.size
)})`,
completedMessage: (duration: string) => `Uploaded assetmap.json ${chalk.dim(duration)}`,
})
);
} catch (err: any) {
let errMessage = 'Failed to upload assetmap to EAS';
if (err.message) {
errMessage += `\n\nReason: ${err.message}`;
}
Log.warn(errMessage);
return null;
}
return {
type: AssetMapSourceType.Gcs,
bucketKey: gcsBucketKey,
};
}
+1 -9
View File
@@ -16,18 +16,10 @@ export async function validateMetroConfigForManagedWorkflowAsync(
} }
const metroConfig = await loadConfigAsync(ctx.projectDir); const metroConfig = await loadConfigAsync(ctx.projectDir);
// SDK <50 | plugins whose presence can be used to determine if the config is extending Expo's Metro config
const hasHashAssetFilesPlugin = metroConfig.transformer?.assetPlugins?.find((plugin: string) => const hasHashAssetFilesPlugin = metroConfig.transformer?.assetPlugins?.find((plugin: string) =>
plugin.match(/expo-asset[/|\\]tools[/|\\]hashAssetFiles/) plugin.match(/expo-asset[/|\\]tools[/|\\]hashAssetFiles/)
); );
if (!hasHashAssetFilesPlugin) {
// SDK >=51 | this is a custom property that we inject to ensure cache invalidation between projects.
const isExtendingExpoMetroConfig = metroConfig.transformer.hasOwnProperty(
'_expoRelativeProjectRoot'
);
if (!isExtendingExpoMetroConfig && !hasHashAssetFilesPlugin) {
Log.warn( Log.warn(
`It looks like that you are using a custom ${chalk.bold( `It looks like that you are using a custom ${chalk.bold(
'metro.config.js' 'metro.config.js'
+1 -1
View File
@@ -88,7 +88,7 @@ export function isUsingEASUpdate(exp: ExpoConfig, projectId: string): boolean {
return exp.updates?.url === getEASUpdateURL(projectId); return exp.updates?.url === getEASUpdateURL(projectId);
} }
export async function getExpoUpdatesPackageVersionIfInstalledAsync( async function getExpoUpdatesPackageVersionIfInstalledAsync(
projectDir: string projectDir: string
): Promise<string | null> { ): Promise<string | null> {
const maybePackageJson = resolveFrom.silent(projectDir, 'expo-updates/package.json'); const maybePackageJson = resolveFrom.silent(projectDir, 'expo-updates/package.json');
+4 -49
View File
@@ -17,14 +17,11 @@ import { isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync } from '.
import { resolveRuntimeVersionUsingCLIAsync } from './resolveRuntimeVersionAsync'; import { resolveRuntimeVersionUsingCLIAsync } from './resolveRuntimeVersionAsync';
import { selectBranchOnAppAsync } from '../branch/queries'; import { selectBranchOnAppAsync } from '../branch/queries';
import { getDefaultBranchNameAsync } from '../branch/utils'; import { getDefaultBranchNameAsync } from '../branch/utils';
import { fetchBuildsAsync } from '../commandUtils/builds';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient'; import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { PaginatedQueryOptions } from '../commandUtils/pagination'; import { PaginatedQueryOptions } from '../commandUtils/pagination';
import { FingerprintOptions, createFingerprintsByKeyAsync } from '../fingerprint/cli';
import { import {
AppPlatform, AppPlatform,
AssetMetadataStatus, AssetMetadataStatus,
BuildFragment,
PartialManifestAsset, PartialManifestAsset,
UpdateRolloutInfo, UpdateRolloutInfo,
UpdateRolloutInfoGroup, UpdateRolloutInfoGroup,
@@ -53,6 +50,7 @@ import { truthy } from '../utils/expodash/filter';
import groupBy from '../utils/expodash/groupBy'; import groupBy from '../utils/expodash/groupBy';
import mapMapAsync from '../utils/expodash/mapMapAsync'; import mapMapAsync from '../utils/expodash/mapMapAsync';
import uniqBy from '../utils/expodash/uniqBy'; import uniqBy from '../utils/expodash/uniqBy';
import { FingerprintOptions, createFingerprintsByKeyAsync } from '../utils/fingerprintCli';
import { Client } from '../vcs/vcs'; import { Client } from '../vcs/vcs';
// update publish does not currently support web // update publish does not currently support web
@@ -94,7 +92,7 @@ type UpdateInfoGroup = {
}; };
// Partial copy of `@expo/dev-server` `BundleAssetWithFileHashes` // Partial copy of `@expo/dev-server` `BundleAssetWithFileHashes`
export type AssetMap = Record< type AssetMap = Record<
string, string,
{ {
httpServerLocation: string; httpServerLocation: string;
@@ -374,7 +372,7 @@ export function filterCollectedAssetsByRequestedPlatforms(
} }
/** Try to load the asset map for logging the names of assets published */ /** Try to load the asset map for logging the names of assets published */
async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> { export async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> {
const assetMapPath = path.join(distRoot, 'assetmap.json'); const assetMapPath = path.join(distRoot, 'assetmap.json');
if (!(await fs.pathExists(assetMapPath))) { if (!(await fs.pathExists(assetMapPath))) {
@@ -421,8 +419,7 @@ export async function collectAssetsAsync(dir: string): Promise<CollectedAssets>
for (const platform of Object.keys(metadata.fileMetadata) as ExpoConfigPlatform[]) { for (const platform of Object.keys(metadata.fileMetadata) as ExpoConfigPlatform[]) {
collectedAssets[platform] = { collectedAssets[platform] = {
launchAsset: { launchAsset: {
// path.extname() returns an empty string when there's no extension so we use || to fall back to .bundle fileExtension: '.bundle',
fileExtension: path.extname(metadata.fileMetadata[platform].bundle) || '.bundle',
contentType: 'application/javascript', contentType: 'application/javascript',
path: path.resolve(dir, metadata.fileMetadata[platform].bundle), path: path.resolve(dir, metadata.fileMetadata[platform].bundle),
}, },
@@ -1001,48 +998,6 @@ export async function maybeCalculateFingerprintForRuntimeVersionInfoObjectsWitho
return [...runtimesWithComputedFingerprint, ...runtimesWithPreviouslyComputedFingerprints]; return [...runtimesWithComputedFingerprint, ...runtimesWithPreviouslyComputedFingerprints];
} }
export async function findCompatibleBuildsAsync(
graphqlClient: ExpoGraphqlClient,
appId: string,
runtimeToPlatformsAndFingerprintInfoMapping: {
runtimeVersion: string;
platforms: UpdatePublishPlatform[];
fingerprintInfoGroup: FingerprintInfoGroup;
}
): Promise<{
runtimeVersion: string;
platforms: UpdatePublishPlatform[];
fingerprintInfoGroupWithCompatibleBuilds: {
android?: (FingerprintInfo & { build?: BuildFragment }) | undefined;
ios?: (FingerprintInfo & { build?: BuildFragment }) | undefined;
};
}> {
const { fingerprintInfoGroup } = runtimeToPlatformsAndFingerprintInfoMapping;
const entriesPromises = Object.entries(fingerprintInfoGroup).map(
async ([platform, fingerprintInfo]) => {
const build = (
await fetchBuildsAsync({
graphqlClient,
projectId: appId,
filters: {
fingerprintHash: fingerprintInfo.fingerprintHash,
},
})
)[0] as BuildFragment | undefined;
return [platform, { ...fingerprintInfo, build }];
}
);
const entries = await Promise.all(entriesPromises);
const fingerprintInfoGroupWithCompatibleBuilds = Object.fromEntries(entries) as {
android?: (FingerprintInfo & { build?: BuildFragment }) | undefined;
ios?: (FingerprintInfo & { build?: BuildFragment }) | undefined;
};
return {
...runtimeToPlatformsAndFingerprintInfoMapping,
fingerprintInfoGroupWithCompatibleBuilds,
};
}
export const platformDisplayNames: Record<UpdatePublishPlatform, string> = { export const platformDisplayNames: Record<UpdatePublishPlatform, string> = {
android: 'Android', android: 'Android',
ios: 'iOS', ios: 'iOS',
-40
View File
@@ -1,6 +1,5 @@
import spawnAsync from '@expo/spawn-async'; import spawnAsync from '@expo/spawn-async';
import chalk from 'chalk'; import chalk from 'chalk';
import fg from 'fast-glob';
import semver from 'semver'; import semver from 'semver';
import Log from '../../log'; import Log from '../../log';
@@ -33,45 +32,6 @@ export async function getXcodeVersionAsync(): Promise<string | undefined> {
} }
} }
type XcodeBuildSettings = {
action: string;
buildSettings: {
BUILD_DIR: string;
CONFIGURATION_BUILD_DIR: string;
EXECUTABLE_FOLDER_PATH: string;
PRODUCT_BUNDLE_IDENTIFIER: string;
TARGET_BUILD_DIR: string;
UNLOCALIZED_RESOURCES_FOLDER_PATH: string;
};
target: string;
};
export async function getXcodeBuildSettingsAsync(
xcworkspacePath: string,
scheme: string
): Promise<XcodeBuildSettings[]> {
const { stdout } = await spawnAsync('xcodebuild', [
'-workspace',
xcworkspacePath,
'-scheme',
scheme,
'-showBuildSettings',
'-json',
]);
return JSON.parse(stdout);
}
export async function resolveXcodeProjectAsync(projectRoot: string): Promise<string | undefined> {
const ignoredPaths = ['**/@(Carthage|Pods|vendor|node_modules)/**'];
const paths = await fg(`ios/*.xcworkspace`, {
cwd: projectRoot,
onlyDirectories: true,
ignore: ignoredPaths,
});
return paths[0];
}
export async function openAppStoreAsync(appId: string): Promise<void> { export async function openAppStoreAsync(appId: string): Promise<void> {
const link = getAppStoreLink(appId); const link = getAppStoreLink(appId);
await spawnAsync(`open`, [link]); await spawnAsync(`open`, [link]);
+1 -1
View File
@@ -5,7 +5,7 @@ function isAab(build: BuildFragment): boolean {
} }
function didArtifactsExpire(build: BuildFragment): boolean { function didArtifactsExpire(build: BuildFragment): boolean {
return new Date().getTime() > new Date(build.expirationDate).getTime(); return new Date().getTime() - new Date(build.completedAt).getTime() > 30 * 24 * 60 * 60 * 1000; // 30 days
} }
export function isRunnableOnSimulatorOrEmulator(build: BuildFragment): boolean { export function isRunnableOnSimulatorOrEmulator(build: BuildFragment): boolean {
@@ -3,10 +3,7 @@ import { AndroidConfig, AndroidManifest, XML } from '@expo/config-plugins';
import { Env, Workflow } from '@expo/eas-build-job'; import { Env, Workflow } from '@expo/eas-build-job';
import { RequestedPlatform } from '../../platform'; import { RequestedPlatform } from '../../platform';
import { import { isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync } from '../../project/projectUtils';
getExpoUpdatesPackageVersionIfInstalledAsync,
isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync,
} from '../../project/projectUtils';
import { expoUpdatesCommandAsync } from '../../utils/expoUpdatesCli'; import { expoUpdatesCommandAsync } from '../../utils/expoUpdatesCli';
import { ensureValidVersions } from '../utils'; import { ensureValidVersions } from '../utils';
@@ -35,16 +32,13 @@ export async function syncUpdatesConfigurationAsync({
return; return;
} }
const expoUpdatesPackageVersion = await getExpoUpdatesPackageVersionIfInstalledAsync(projectDir);
// sync AndroidManifest.xml // sync AndroidManifest.xml
const androidManifestPath = await AndroidConfig.Paths.getAndroidManifestAsync(projectDir); const androidManifestPath = await AndroidConfig.Paths.getAndroidManifestAsync(projectDir);
const androidManifest = await getAndroidManifestAsync(projectDir); const androidManifest = await getAndroidManifestAsync(projectDir);
const updatedAndroidManifest = await AndroidConfig.Updates.setUpdatesConfigAsync( const updatedAndroidManifest = await AndroidConfig.Updates.setUpdatesConfigAsync(
projectDir, projectDir,
exp, exp,
androidManifest, androidManifest
expoUpdatesPackageVersion
); );
await AndroidConfig.Manifest.writeAndroidManifestAsync( await AndroidConfig.Manifest.writeAndroidManifestAsync(
androidManifestPath, androidManifestPath,
+1 -3
View File
@@ -371,7 +371,6 @@ export async function ensureEASUpdateIsConfiguredAsync({
vcsClient, vcsClient,
platform, platform,
env, env,
forceNativeConfigSync,
}: { }: {
exp: ExpoConfig; exp: ExpoConfig;
projectId: string; projectId: string;
@@ -379,7 +378,6 @@ export async function ensureEASUpdateIsConfiguredAsync({
vcsClient: Client; vcsClient: Client;
platform: RequestedPlatform | null; platform: RequestedPlatform | null;
env: Env | undefined; env: Env | undefined;
forceNativeConfigSync?: boolean;
}): Promise<void> { }): Promise<void> {
const hasExpoUpdates = isExpoUpdatesInstalledOrAvailable( const hasExpoUpdates = isExpoUpdatesInstalledOrAvailable(
projectDir, projectDir,
@@ -411,7 +409,7 @@ export async function ensureEASUpdateIsConfiguredAsync({
workflows, workflows,
}); });
if (forceNativeConfigSync || projectChanged || !hasExpoUpdates) { if (projectChanged || !hasExpoUpdates) {
await ensureEASUpdateIsConfiguredNativelyAsync(vcsClient, { await ensureEASUpdateIsConfiguredNativelyAsync(vcsClient, {
exp: expWithUpdates, exp: expWithUpdates,
projectDir, projectDir,
@@ -3,10 +3,7 @@ import { IOSConfig } from '@expo/config-plugins';
import { Env, Workflow } from '@expo/eas-build-job'; import { Env, Workflow } from '@expo/eas-build-job';
import { RequestedPlatform } from '../../platform'; import { RequestedPlatform } from '../../platform';
import { import { isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync } from '../../project/projectUtils';
getExpoUpdatesPackageVersionIfInstalledAsync,
isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync,
} from '../../project/projectUtils';
import { expoUpdatesCommandAsync } from '../../utils/expoUpdatesCli'; import { expoUpdatesCommandAsync } from '../../utils/expoUpdatesCli';
import { readPlistAsync, writePlistAsync } from '../../utils/plist'; import { readPlistAsync, writePlistAsync } from '../../utils/plist';
import { Client } from '../../vcs/vcs'; import { Client } from '../../vcs/vcs';
@@ -36,14 +33,12 @@ export async function syncUpdatesConfigurationAsync({
return; return;
} }
const expoUpdatesPackageVersion = await getExpoUpdatesPackageVersionIfInstalledAsync(projectDir);
const expoPlist = await readExpoPlistAsync(projectDir); const expoPlist = await readExpoPlistAsync(projectDir);
// TODO(wschurman): this dependency needs to be updated for fingerprint // TODO(wschurman): this dependency needs to be updated for fingerprint
const updatedExpoPlist = await IOSConfig.Updates.setUpdatesConfigAsync( const updatedExpoPlist = await IOSConfig.Updates.setUpdatesConfigAsync(
projectDir, projectDir,
exp, exp,
expoPlist, expoPlist
expoUpdatesPackageVersion
); );
await writeExpoPlistAsync(vcsClient, projectDir, updatedExpoPlist); await writeExpoPlistAsync(vcsClient, projectDir, updatedExpoPlist);
} }
+1 -5
View File
@@ -20,11 +20,7 @@ export async function uploadFileAtPathToGCSAsync(
path: string, path: string,
handleProgressEvent: ProgressHandler = () => {} handleProgressEvent: ProgressHandler = () => {}
): Promise<string> { ): Promise<string> {
const signedUrl = await UploadSessionMutation.createUploadSessionAsync( const signedUrl = await UploadSessionMutation.createUploadSessionAsync(graphqlClient, type);
graphqlClient,
type,
type === UploadSessionType.EasShareGcsAppArchive ? path : undefined
);
await uploadWithSignedUrlWithProgressAsync(path, signedUrl, handleProgressEvent); await uploadWithSignedUrlWithProgressAsync(path, signedUrl, handleProgressEvent);
return signedUrl.bucketKey; return signedUrl.bucketKey;
@@ -1,5 +1,5 @@
import { abridgedDiff } from '../../fingerprint/diff';
import Log from '../../log'; import Log from '../../log';
import { abridgedDiff } from '../fingerprintDiff';
jest.mock('chalk', () => ({ jest.mock('chalk', () => ({
red: jest.fn(text => `red(${text})`), red: jest.fn(text => `red(${text})`),
@@ -1,164 +0,0 @@
import { instance, mock } from 'ts-mockito';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import {
BackgroundJobReceiptDataFragment,
BackgroundJobResultType,
BackgroundJobState,
} from '../../graphql/generated';
import { BackgroundJobReceiptQuery } from '../../graphql/queries/BackgroundJobReceiptQuery';
import { pollForBackgroundJobReceiptAsync } from '../pollForBackgroundJobReceiptAsync';
jest.mock('../../graphql/queries/BackgroundJobReceiptQuery');
describe(pollForBackgroundJobReceiptAsync, () => {
it('returns successful receipt when polling eventually succeeds', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
const receiptId = '123';
const backgroundJobReceiptInProgress: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.InProgress,
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
const backgroundJobReceiptSuccess: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.Success,
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
jest
.mocked(BackgroundJobReceiptQuery.byIdAsync)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptSuccess);
const result = await pollForBackgroundJobReceiptAsync(
graphqlClient,
backgroundJobReceiptInProgress,
{ pollInterval: 100 }
);
expect(result).toEqual(backgroundJobReceiptSuccess);
});
it('throws error when polling eventually fails', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
const receiptId = '123';
const backgroundJobReceiptInProgress: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.InProgress,
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
const backgroundJobReceiptFailure: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.Failure,
errorMessage: 'Watch out for falling rocks!',
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
jest
.mocked(BackgroundJobReceiptQuery.byIdAsync)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptFailure);
await expect(
pollForBackgroundJobReceiptAsync(graphqlClient, backgroundJobReceiptInProgress, {
pollInterval: 100,
})
).rejects.toThrow('Background job failed with error: Watch out for falling rocks!');
});
it('succeeds when polling eventually succeeds after a failure with retry', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
const receiptId = '123';
const backgroundJobReceiptInProgress: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.InProgress,
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
const backgroundJobReceiptFailureWithRetry: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.Failure,
errorMessage: 'Watch out for falling rocks!',
willRetry: true,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
const backgroundJobReceiptInProgress2: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.InProgress,
willRetry: true,
tries: 1,
resultType: BackgroundJobResultType.Void,
} as any;
const backgroundJobReceiptSuccess: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.Success,
willRetry: true,
tries: 1,
resultType: BackgroundJobResultType.Void,
} as any;
jest
.mocked(BackgroundJobReceiptQuery.byIdAsync)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptInProgress)
.mockResolvedValueOnce(backgroundJobReceiptFailureWithRetry)
.mockResolvedValueOnce(backgroundJobReceiptInProgress2)
.mockResolvedValueOnce(backgroundJobReceiptSuccess);
const result = await pollForBackgroundJobReceiptAsync(
graphqlClient,
backgroundJobReceiptInProgress,
{ pollInterval: 100 }
);
expect(result).toEqual(backgroundJobReceiptSuccess);
});
it('times out after 90 checks', async () => {
const graphqlClient = instance(mock<ExpoGraphqlClient>());
const receiptId = '123';
const backgroundJobReceiptInProgress: BackgroundJobReceiptDataFragment = {
id: receiptId,
state: BackgroundJobState.InProgress,
willRetry: false,
tries: 0,
resultType: BackgroundJobResultType.Void,
} as any;
jest
.mocked(BackgroundJobReceiptQuery.byIdAsync)
.mockResolvedValue(backgroundJobReceiptInProgress);
await expect(
pollForBackgroundJobReceiptAsync(graphqlClient, backgroundJobReceiptInProgress, {
pollInterval: 10,
})
).rejects.toThrow('Background job timed out.');
});
});
@@ -1,10 +1,10 @@
import { Env, Workflow } from '@expo/eas-build-job'; import { Env, Workflow } from '@expo/eas-build-job';
import { silent as silentResolveFrom } from 'resolve-from'; import { silent as silentResolveFrom } from 'resolve-from';
import { Fingerprint, FingerprintDiffItem } from './types'; import mapMapAsync from './expodash/mapMapAsync';
import { Fingerprint, FingerprintDiffItem } from './fingerprint';
import Log from '../log'; import Log from '../log';
import { ora } from '../ora'; import { ora } from '../ora';
import mapMapAsync from '../utils/expodash/mapMapAsync';
export type FingerprintOptions = { export type FingerprintOptions = {
workflow?: Workflow; workflow?: Workflow;
@@ -103,21 +103,8 @@ async function createFingerprintWithoutLoggingAsync(
if (options.debug) { if (options.debug) {
fingerprintOptions.debug = true; fingerprintOptions.debug = true;
} }
// eslint-disable-next-line @typescript-eslint/return-await
return await withTemporaryEnvAsync(options.env ?? {}, () => return await Fingerprint.createFingerprintAsync(projectDir, fingerprintOptions);
Fingerprint.createFingerprintAsync(projectDir, fingerprintOptions)
);
}
async function withTemporaryEnvAsync(envVars: Env, fn: () => Promise<any>): Promise<any> {
const originalEnv = { ...process.env };
Object.assign(process.env, envVars);
try {
return await fn();
} finally {
process.env = originalEnv;
}
} }
/** /**
@@ -1,148 +0,0 @@
import { CombinedError } from '@urql/core';
import { clearIntervalAsync, setIntervalAsync } from 'set-interval-async';
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
import { BackgroundJobReceiptDataFragment, BackgroundJobState } from '../graphql/generated';
import { BackgroundJobReceiptQuery } from '../graphql/queries/BackgroundJobReceiptQuery';
export enum BackgroundJobReceiptPollErrorType {
NULL_RECEIPT,
JOB_FAILED_NO_WILL_RETRY,
TIMEOUT,
}
export type BackgroundJobReceiptPollErrorData =
| {
errorType: BackgroundJobReceiptPollErrorType.NULL_RECEIPT;
}
| {
errorType: BackgroundJobReceiptPollErrorType.JOB_FAILED_NO_WILL_RETRY;
receiptErrorMessage: string | undefined | null;
}
| {
errorType: BackgroundJobReceiptPollErrorType.TIMEOUT;
};
export class BackgroundJobReceiptPollError extends Error {
readonly errorData: BackgroundJobReceiptPollErrorData;
constructor(errorData: BackgroundJobReceiptPollErrorData) {
super(BackgroundJobReceiptPollError.createErrorMessage(errorData));
this.errorData = errorData;
}
static createErrorMessage(errorData: BackgroundJobReceiptPollErrorData): string {
switch (errorData.errorType) {
case BackgroundJobReceiptPollErrorType.NULL_RECEIPT:
return 'Background job receipt was null.';
case BackgroundJobReceiptPollErrorType.JOB_FAILED_NO_WILL_RETRY:
return `Background job failed with error: ${errorData.receiptErrorMessage}`;
case BackgroundJobReceiptPollErrorType.TIMEOUT:
return 'Background job timed out.';
}
}
}
export type BackgroundJobPollErrorCondition = (error: CombinedError) => {
errorIndicatesSuccess: boolean;
};
async function fetchBackgroundJobReceiptAsync(
graphqlClient: ExpoGraphqlClient,
receiptId: string
): Promise<[BackgroundJobReceiptDataFragment | null, CombinedError | null]> {
try {
return [await BackgroundJobReceiptQuery.byIdAsync(graphqlClient, receiptId), null];
} catch (error) {
if (error instanceof CombinedError) {
return [null, error];
}
throw error;
}
}
export function pollForBackgroundJobReceiptAsync(
graphqlClient: ExpoGraphqlClient,
backgroundJobReceipt: BackgroundJobReceiptDataFragment
): Promise<BackgroundJobReceiptDataFragment>;
export function pollForBackgroundJobReceiptAsync(
graphqlClient: ExpoGraphqlClient,
backgroundJobReceipt: BackgroundJobReceiptDataFragment,
options?: {
onBackgroundJobReceiptPollError?: BackgroundJobPollErrorCondition;
pollInterval?: number;
}
): Promise<BackgroundJobReceiptDataFragment | null>;
export async function pollForBackgroundJobReceiptAsync(
graphqlClient: ExpoGraphqlClient,
backgroundJobReceipt: BackgroundJobReceiptDataFragment,
options?: {
onBackgroundJobReceiptPollError?: BackgroundJobPollErrorCondition;
pollInterval?: number;
}
): Promise<BackgroundJobReceiptDataFragment | null> {
return await new Promise<BackgroundJobReceiptDataFragment | null>((resolve, reject) => {
let numChecks = 0;
const intervalHandle = setIntervalAsync(
async function pollForDeletionFinishedAsync() {
function failBackgroundDeletion(error: BackgroundJobReceiptPollError): void {
void clearIntervalAsync(intervalHandle);
reject(error);
}
const [receipt, error] = await fetchBackgroundJobReceiptAsync(
graphqlClient,
backgroundJobReceipt.id
);
if (!receipt) {
if (error instanceof CombinedError) {
const errorResult = options?.onBackgroundJobReceiptPollError?.(error);
if (errorResult?.errorIndicatesSuccess) {
void clearIntervalAsync(intervalHandle);
resolve(null);
return;
}
}
failBackgroundDeletion(
new BackgroundJobReceiptPollError({
errorType: BackgroundJobReceiptPollErrorType.NULL_RECEIPT,
})
);
return;
}
// job failed and will not retry
if (receipt.state === BackgroundJobState.Failure && !receipt.willRetry) {
failBackgroundDeletion(
new BackgroundJobReceiptPollError({
errorType: BackgroundJobReceiptPollErrorType.JOB_FAILED_NO_WILL_RETRY,
receiptErrorMessage: receipt.errorMessage,
})
);
return;
}
// all else fails, stop polling after 90 checks. This should only happen if there's an
// issue with receipts not setting `willRetry` to false when they fail within a reasonable
// amount of time.
if (numChecks > 90) {
failBackgroundDeletion(
new BackgroundJobReceiptPollError({
errorType: BackgroundJobReceiptPollErrorType.TIMEOUT,
})
);
return;
}
if (receipt.state === BackgroundJobState.Success) {
void clearIntervalAsync(intervalHandle);
resolve(receipt);
return;
}
numChecks++;
},
options?.pollInterval ?? 1000
);
});
}
@@ -18,7 +18,7 @@ describe(Ignore, () => {
'/root' '/root'
); );
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(ignore.ignores('aaa')).toBe(true); expect(ignore.ignores('aaa')).toBe(true);
expect(ignore.ignores('bbb')).toBe(false); expect(ignore.ignores('bbb')).toBe(false);
expect(ignore.ignores('dir/aaa')).toBe(true); expect(ignore.ignores('dir/aaa')).toBe(true);
@@ -35,7 +35,7 @@ describe(Ignore, () => {
'/root' '/root'
); );
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(ignore.ignores('aaa')).toBe(false); expect(ignore.ignores('aaa')).toBe(false);
expect(ignore.ignores('bbb')).toBe(false); expect(ignore.ignores('bbb')).toBe(false);
expect(ignore.ignores('ccc')).toBe(true); expect(ignore.ignores('ccc')).toBe(true);
@@ -52,7 +52,7 @@ describe(Ignore, () => {
'/root' '/root'
); );
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(ignore.ignores('aaa')).toBe(true); expect(ignore.ignores('aaa')).toBe(true);
expect(ignore.ignores('bbb')).toBe(false); expect(ignore.ignores('bbb')).toBe(false);
expect(ignore.ignores('node_modules/aaa')).toBe(true); expect(ignore.ignores('node_modules/aaa')).toBe(true);
@@ -69,36 +69,16 @@ describe(Ignore, () => {
'/root' '/root'
); );
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(ignore.ignores('dir/ccc')).toBe(true); expect(ignore.ignores('dir/ccc')).toBe(true);
}); });
it('ignores .git if copying', async () => { it('ignores .git', async () => {
vol.fromJSON({}, '/root'); vol.fromJSON({}, '/root');
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(ignore.ignores('.git')).toBe(true); expect(ignore.ignores('.git')).toBe(true);
}); });
describe('for checking', () => {
it('does not necessarily ignore .git', async () => {
vol.fromJSON({}, '/root');
const ignore = await Ignore.createForCheckingAsync('/root');
expect(ignore.ignores('.git')).toBe(false);
});
it('ignores .git if present in .easignore', async () => {
vol.fromJSON(
{
'.easignore': '.git\n',
},
'/root'
);
const ignore = await Ignore.createForCheckingAsync('/root');
expect(ignore.ignores('.git')).toBe(true);
});
});
it('does not throw an error if there is a trailing backslash in the gitignore', async () => { it('does not throw an error if there is a trailing backslash in the gitignore', async () => {
vol.fromJSON( vol.fromJSON(
@@ -108,7 +88,7 @@ describe(Ignore, () => {
'/root' '/root'
); );
const ignore = await Ignore.createForCopyingAsync('/root'); const ignore = await Ignore.createAsync('/root');
expect(() => ignore.ignores('dir/test')).not.toThrowError(); expect(() => ignore.ignores('dir/test')).not.toThrowError();
}); });
}); });
@@ -3,7 +3,6 @@ import fs from 'fs/promises';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import Log from '../../../log';
import GitClient from '../git'; import GitClient from '../git';
describe('git', () => { describe('git', () => {
@@ -158,42 +157,6 @@ describe('git', () => {
await expect(vcs.makeShallowCopyAsync(repoCloneIgnored)).resolves.not.toThrow(); await expect(vcs.makeShallowCopyAsync(repoCloneIgnored)).resolves.not.toThrow();
await expect(fs.stat(path.join(repoCloneIgnored, 'results'))).rejects.toThrow('ENOENT'); await expect(fs.stat(path.join(repoCloneIgnored, 'results'))).rejects.toThrow('ENOENT');
}); });
it('if .git is present in .easignore', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await spawnAsync('git', ['init'], { cwd: repoRoot });
const vcs = new GitClient({
requireCommit: false,
maybeCwdOverride: repoRoot,
});
await fs.writeFile(`${repoRoot}/.easignore`, '.git\n');
await spawnAsync('git', ['add', '.'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'temp commit'], { cwd: repoRoot });
const repoClone = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(repoClone)).resolves.not.toThrow();
await expect(fs.stat(path.join(repoClone, '.git'))).rejects.toThrow('ENOENT');
});
it('if .git is not present in .easignore', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await spawnAsync('git', ['init'], { cwd: repoRoot });
const vcs = new GitClient({
requireCommit: false,
maybeCwdOverride: repoRoot,
});
await fs.writeFile(`${repoRoot}/.easignore`, '');
await spawnAsync('git', ['add', '.'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'temp commit'], { cwd: repoRoot });
const repoClone = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(repoClone)).resolves.not.toThrow();
await expect(fs.readdir(path.join(repoClone, '.git'))).resolves.toBeDefined();
});
}); });
it('does not include files that have been removed in the working directory', async () => { it('does not include files that have been removed in the working directory', async () => {
@@ -242,92 +205,4 @@ describe('git', () => {
fs.stat(path.join(requireCommitClone, 'new-tracked-file.txt')) fs.stat(path.join(requireCommitClone, 'new-tracked-file.txt'))
).resolves.not.toThrow(); ).resolves.not.toThrow();
}); });
describe('when requireCommit is true', () => {
it('adheres to .easignore', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await spawnAsync('git', ['init'], { cwd: repoRoot });
const vcs = new GitClient({
requireCommit: true,
maybeCwdOverride: repoRoot,
});
const warn = jest.spyOn(Log, 'warn');
await fs.writeFile(`${repoRoot}/.easignore`, '*easignored*\n');
await fs.writeFile(`${repoRoot}/.gitignore`, '*gitignored*\n');
await fs.writeFile(`${repoRoot}/easignored-file.txt`, 'file');
await fs.writeFile(`${repoRoot}/nonignored-file.txt`, 'file');
await fs.writeFile(`${repoRoot}/gitignored-file.txt`, 'file');
await spawnAsync('git', ['add', '.'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'tmp commit'], { cwd: repoRoot });
const copyRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(copyRoot)).resolves.not.toThrow();
expect(warn).toHaveBeenCalledWith(expect.stringContaining('.easignore'));
await expect(fs.stat(path.join(copyRoot, 'easignored-file.txt'))).rejects.toThrow('ENOENT');
await expect(fs.stat(path.join(copyRoot, 'gitignored-file.txt'))).rejects.toThrow('ENOENT');
await expect(fs.stat(path.join(copyRoot, 'nonignored-file.txt'))).resolves.not.toThrow();
});
it('prints a warning only once if .easignore exists', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await spawnAsync('git', ['init'], { cwd: repoRoot });
const vcs = new GitClient({
requireCommit: true,
maybeCwdOverride: repoRoot,
});
const warn = jest.spyOn(Log, 'warn');
await fs.writeFile(`${repoRoot}/.easignore`, '*easignored*\n');
await spawnAsync('git', ['add', '.'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'tmp commit'], { cwd: repoRoot });
const copyRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(copyRoot)).resolves.not.toThrow();
expect(warn).toHaveBeenCalledTimes(1);
expect(warn).toHaveBeenCalledWith(expect.stringContaining('.easignore'));
const anotherCopyRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(anotherCopyRoot)).resolves.not.toThrow();
expect(warn).toHaveBeenCalledTimes(1);
});
describe('when EAS_SUPPRESS_REQUIRE_COMMIT_EASIGNORE_WARNING is set', () => {
beforeAll(() => {
process.env.EAS_SUPPRESS_REQUIRE_COMMIT_EASIGNORE_WARNING = '1';
});
afterAll(() => {
delete process.env.EAS_SUPPRESS_REQUIRE_COMMIT_EASIGNORE_WARNING;
});
it('does not print a warning', async () => {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await spawnAsync('git', ['init'], { cwd: repoRoot });
const vcs = new GitClient({
requireCommit: true,
maybeCwdOverride: repoRoot,
});
const warn = jest.spyOn(Log, 'warn');
warn.mockClear();
await fs.writeFile(`${repoRoot}/.easignore`, '*easignored*\n');
await spawnAsync('git', ['add', '.'], { cwd: repoRoot });
await spawnAsync('git', ['commit', '-m', 'tmp commit'], { cwd: repoRoot });
const copyRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
await expect(vcs.makeShallowCopyAsync(copyRoot)).resolves.not.toThrow();
expect(warn).toHaveBeenCalledTimes(0);
});
});
});
}); });
@@ -1,44 +0,0 @@
import spawnAsync from '@expo/spawn-async';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import NoVcsClient from '../noVcs';
describe('noVcs', () => {
describe('NoVcsClient', () => {
let vcs: NoVcsClient;
let repoRoot: string;
let globalEasProjectRoot: string | undefined;
afterEach(async () => {
await fs.rm(repoRoot, { recursive: true, force: true });
process.env.EAS_PROJECT_ROOT = globalEasProjectRoot;
});
beforeEach(async () => {
repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'eas-cli-git-test-'));
vcs = new NoVcsClient({ cwdOverride: repoRoot });
globalEasProjectRoot = process.env.EAS_PROJECT_ROOT;
delete process.env.EAS_PROJECT_ROOT;
});
it('should return the current working directory when not in Git repository', async () => {
expect(await vcs.getRootPathAsync()).toBe(process.cwd());
});
it('should return the Git root when in Git repository', async () => {
await spawnAsync('git', ['init'], { cwd: repoRoot });
expect(await fs.realpath(await vcs.getRootPathAsync())).toBe(await fs.realpath(repoRoot));
});
it('should return the project root when EAS_PROJECT_ROOT is set', async () => {
process.env.EAS_PROJECT_ROOT = 'project-root';
expect(await vcs.getRootPathAsync()).toBe(path.resolve(process.cwd(), 'project-root'));
process.env.EAS_PROJECT_ROOT = '/app';
expect(await vcs.getRootPathAsync()).toBe('/app');
});
});
});
+2 -18
View File
@@ -3,7 +3,6 @@ import spawnAsync from '@expo/spawn-async';
import { Errors } from '@oclif/core'; import { Errors } from '@oclif/core';
import chalk from 'chalk'; import chalk from 'chalk';
import fs from 'fs-extra'; import fs from 'fs-extra';
import getenv from 'getenv';
import path from 'path'; import path from 'path';
import Log, { learnMore } from '../../log'; import Log, { learnMore } from '../../log';
@@ -19,8 +18,6 @@ import {
import { EASIGNORE_FILENAME, Ignore, makeShallowCopyAsync } from '../local'; import { EASIGNORE_FILENAME, Ignore, makeShallowCopyAsync } from '../local';
import { Client } from '../vcs'; import { Client } from '../vcs';
let hasWarnedAboutEasignoreInRequireCommit = false;
export default class GitClient extends Client { export default class GitClient extends Client {
private readonly maybeCwdOverride?: string; private readonly maybeCwdOverride?: string;
public requireCommit: boolean; public requireCommit: boolean;
@@ -166,17 +163,6 @@ export default class GitClient extends Client {
} }
const rootPath = await this.getRootPathAsync(); const rootPath = await this.getRootPathAsync();
const sourceEasignorePath = path.join(rootPath, EASIGNORE_FILENAME);
const doesEasignoreExist = await fs.exists(sourceEasignorePath);
const shouldSuppressWarning =
hasWarnedAboutEasignoreInRequireCommit ||
getenv.boolish('EAS_SUPPRESS_REQUIRE_COMMIT_EASIGNORE_WARNING', false);
if (this.requireCommit && doesEasignoreExist && !shouldSuppressWarning) {
Log.warn(
`You have "requireCommit: true" in "eas.json" and also ".easignore". If ".easignore" does remove files, note that the repository checked out in EAS will not longer be Git-clean.`
);
hasWarnedAboutEasignoreInRequireCommit = true;
}
let gitRepoUri; let gitRepoUri;
if (process.platform === 'win32') { if (process.platform === 'win32') {
@@ -252,9 +238,7 @@ export default class GitClient extends Client {
); );
// Special-case `.git` which `git ls-files` will never consider ignored. // Special-case `.git` which `git ls-files` will never consider ignored.
// We don't want to ignore anything by default. We want to know what does const ignore = await Ignore.createAsync(rootPath);
// the user want to ignore.
const ignore = await Ignore.createForCheckingAsync(rootPath);
if (ignore.ignores('.git')) { if (ignore.ignores('.git')) {
await fs.rm(path.join(destinationPath, '.git'), { recursive: true, force: true }); await fs.rm(path.join(destinationPath, '.git'), { recursive: true, force: true });
Log.debug('deleted .git', { Log.debug('deleted .git', {
@@ -357,7 +341,7 @@ export default class GitClient extends Client {
const easIgnorePath = path.join(rootPath, EASIGNORE_FILENAME); const easIgnorePath = path.join(rootPath, EASIGNORE_FILENAME);
if (await fs.exists(easIgnorePath)) { if (await fs.exists(easIgnorePath)) {
const ignore = await Ignore.createForCheckingAsync(rootPath); const ignore = await Ignore.createAsync(rootPath);
const wouldNotBeCopiedToClone = ignore.ignores(filePath); const wouldNotBeCopiedToClone = ignore.ignores(filePath);
const wouldBeDeletedFromClone = const wouldBeDeletedFromClone =
( (
+4 -44
View File
@@ -1,58 +1,18 @@
import spawnAsync from '@expo/spawn-async'; import { Ignore, getRootPath, makeShallowCopyAsync } from '../local';
import path from 'path';
import Log from '../../log';
import { Ignore, makeShallowCopyAsync } from '../local';
import { Client } from '../vcs'; import { Client } from '../vcs';
let hasWarnedAboutEasProjectRoot = false;
export default class NoVcsClient extends Client { export default class NoVcsClient extends Client {
private readonly cwdOverride?: string;
constructor(options: { cwdOverride?: string } = {}) {
super();
this.cwdOverride = options.cwdOverride;
}
public async getRootPathAsync(): Promise<string> { public async getRootPathAsync(): Promise<string> {
// If EAS_PROJECT_ROOT is absolute, return it. return getRootPath();
// If it is relative or empty, resolve it from Git root or process.cwd().
// Honor `EAS_PROJECT_ROOT` if it is set.
if (process.env.EAS_PROJECT_ROOT && path.isAbsolute(process.env.EAS_PROJECT_ROOT)) {
return path.normalize(process.env.EAS_PROJECT_ROOT);
}
// If `EAS_PROJECT_ROOT` is not set, try to get the root path from Git.
try {
return (
await spawnAsync('git', ['rev-parse', '--show-toplevel'], {
cwd: this.cwdOverride,
})
).stdout.trim();
} catch (err) {
if (!hasWarnedAboutEasProjectRoot) {
Log.warn(`Failed to get Git root path with \`git rev-parse --show-toplevel\`.`, err);
Log.warn('Falling back to using current working directory as project root.');
Log.warn(
'You can set `EAS_PROJECT_ROOT` environment variable to let eas-cli know where your project is located.'
);
hasWarnedAboutEasProjectRoot = true;
}
}
return path.resolve(process.cwd(), process.env.EAS_PROJECT_ROOT ?? '.');
} }
public async makeShallowCopyAsync(destinationPath: string): Promise<void> { public async makeShallowCopyAsync(destinationPath: string): Promise<void> {
const srcPath = path.normalize(await this.getRootPathAsync()); const srcPath = getRootPath();
await makeShallowCopyAsync(srcPath, destinationPath); await makeShallowCopyAsync(srcPath, destinationPath);
} }
public override async isFileIgnoredAsync(filePath: string): Promise<boolean> { public override async isFileIgnoredAsync(filePath: string): Promise<boolean> {
const rootPath = path.normalize(await this.getRootPathAsync()); const ignore = await Ignore.createAsync(getRootPath());
const ignore = await Ignore.createForCheckingAsync(rootPath);
return ignore.ignores(filePath); return ignore.ignores(filePath);
} }
+19 -20
View File
@@ -9,6 +9,19 @@ import Log from '../log';
export const EASIGNORE_FILENAME = '.easignore'; export const EASIGNORE_FILENAME = '.easignore';
const GITIGNORE_FILENAME = '.gitignore'; const GITIGNORE_FILENAME = '.gitignore';
const DEFAULT_IGNORE = `
.git
node_modules
`;
export function getRootPath(): string {
const rootPath = process.env.EAS_PROJECT_ROOT ?? process.cwd();
if (!path.isAbsolute(rootPath)) {
return path.resolve(process.cwd(), rootPath);
}
return rootPath;
}
/** /**
* Ignore wraps the 'ignore' package to support multiple .gitignore files * Ignore wraps the 'ignore' package to support multiple .gitignore files
* in subdirectories. * in subdirectories.
@@ -24,31 +37,17 @@ export class Ignore {
private constructor(private readonly rootDir: string) {} private constructor(private readonly rootDir: string) {}
static async createForCopyingAsync(rootDir: string): Promise<Ignore> { static async createAsync(rootDir: string): Promise<Ignore> {
const ignore = new Ignore(rootDir); const ignore = new Ignore(rootDir);
await ignore.initIgnoreAsync({ await ignore.initIgnoreAsync();
defaultIgnore: `
.git
node_modules
`,
});
return ignore; return ignore;
} }
/** Does not include the default .git and node_modules ignore rules. */ public async initIgnoreAsync(): Promise<void> {
static async createForCheckingAsync(rootDir: string): Promise<Ignore> {
const ignore = new Ignore(rootDir);
await ignore.initIgnoreAsync({
defaultIgnore: ``,
});
return ignore;
}
public async initIgnoreAsync({ defaultIgnore }: { defaultIgnore: string }): Promise<void> {
const easIgnorePath = path.join(this.rootDir, EASIGNORE_FILENAME); const easIgnorePath = path.join(this.rootDir, EASIGNORE_FILENAME);
if (await fsExtra.pathExists(easIgnorePath)) { if (await fsExtra.pathExists(easIgnorePath)) {
this.ignoreMapping = [ this.ignoreMapping = [
['', createIgnore().add(defaultIgnore)], ['', createIgnore().add(DEFAULT_IGNORE)],
['', createIgnore().add(await fsExtra.readFile(easIgnorePath, 'utf-8'))], ['', createIgnore().add(await fsExtra.readFile(easIgnorePath, 'utf-8'))],
]; ];
@@ -75,7 +74,7 @@ node_modules
] as const; ] as const;
}) })
); );
this.ignoreMapping = [['', createIgnore().add(defaultIgnore)], ...ignoreMapping]; this.ignoreMapping = [['', createIgnore().add(DEFAULT_IGNORE)], ...ignoreMapping];
Log.debug('initializing ignore mapping with .gitignore files', { Log.debug('initializing ignore mapping with .gitignore files', {
ignoreFilePaths, ignoreFilePaths,
@@ -101,7 +100,7 @@ export async function makeShallowCopyAsync(_src: string, dst: string): Promise<v
const src = path.toNamespacedPath(path.normalize(_src)); const src = path.toNamespacedPath(path.normalize(_src));
Log.debug('makeShallowCopyAsync', { src, dst }); Log.debug('makeShallowCopyAsync', { src, dst });
const ignore = await Ignore.createForCopyingAsync(src); const ignore = await Ignore.createAsync(src);
Log.debug('makeShallowCopyAsync ignoreMapping', { ignoreMapping: ignore.ignoreMapping }); Log.debug('makeShallowCopyAsync ignoreMapping', { ignoreMapping: ignore.ignoreMapping });
await fs.cp(src, dst, { await fs.cp(src, dst, {
+5 -59
View File
@@ -141,53 +141,6 @@ export async function createManifestAsync(
return { conflictingVariableNames, manifest }; return { conflictingVariableNames, manifest };
} }
interface RawSourceMap {
version: number;
sources: string[];
names: string[];
sourceRoot?: string;
sourcesContent?: string[];
mappings: string;
file: string;
}
const sourceMapExtensionRe = /\.map$/;
async function normalizeSourceMapAsync(
file: RecursiveFileEntry
): Promise<WorkerFileEntry | undefined> {
// This logic normalizes source maps like Wrangler does
// See: https://github.com/cloudflare/workers-sdk/blob/508a1a3/packages/wrangler/src/deployment-bundle/source-maps.ts#L31-L63
const data = await fs.promises.readFile(file.path, 'utf-8');
try {
const map = JSON.parse(data) as RawSourceMap;
if (typeof map !== 'object' || !map) {
return undefined;
}
let sources = map.sources || [];
if (Array.isArray(map.sources)) {
// Remote null-byte prefixes from virtual file names
sources = map.sources.map(sourceName =>
sourceName.charCodeAt(0) === 0 ? sourceName.slice(1) : sourceName
);
}
return {
normalizedPath: file.normalizedPath,
path: file.path,
data: JSON.stringify({
version: 3,
file: file.normalizedPath.replace(sourceMapExtensionRe, ''),
sourcesContent: sources.map(() => null),
sources,
names: map.names,
mappings: map.mappings,
}),
};
} catch {
return undefined;
}
}
interface WorkerFileEntry { interface WorkerFileEntry {
normalizedPath: string; normalizedPath: string;
path: string; path: string;
@@ -197,18 +150,11 @@ interface WorkerFileEntry {
/** Reads worker files while normalizing sourcemaps and providing normalized paths */ /** Reads worker files while normalizing sourcemaps and providing normalized paths */
async function* listWorkerFilesAsync(workerPath: string): AsyncGenerator<WorkerFileEntry> { async function* listWorkerFilesAsync(workerPath: string): AsyncGenerator<WorkerFileEntry> {
for await (const file of listFilesRecursively(workerPath)) { for await (const file of listFilesRecursively(workerPath)) {
if (file.normalizedPath.endsWith('.map')) { yield {
const normalizedSourceMap = await normalizeSourceMapAsync(file); normalizedPath: file.normalizedPath,
if (normalizedSourceMap) { path: file.path,
yield normalizedSourceMap; data: await fs.promises.readFile(file.path),
} };
} else {
yield {
normalizedPath: file.normalizedPath,
path: file.path,
data: await fs.promises.readFile(file.path),
};
}
} }
} }
+2 -16
View File
@@ -130,22 +130,8 @@ export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
} }
const getErrorMessageAsync = async (): Promise<string> => { const getErrorMessageAsync = async (): Promise<string> => {
const rayId = response.headers.get('cf-ray'); const body = await response.json().catch(() => null);
const contentType = response.headers.get('Content-Type'); return body?.error ?? `Upload of "${filePath}" failed: ${response.statusText}`;
if (contentType?.startsWith('text/html')) {
// NOTE(@kitten): We've received a CDN error most likely. There's not much we can do
// except for quoting the Request ID, so a user can send it to us. We can check
// why a request was blocked by looking up a WAF event via the "Ray ID" here:
// https://dash.cloudflare.com/e6f39f67f543faa6038768e8f37e4234/expo.app/security/events
let message = `CDN firewall has aborted the upload with ${response.statusText}.`;
if (rayId) {
message += `\nReport this error quoting Request ID ${rayId}`;
}
return `Upload of "${filePath}" failed: ${message}`;
} else {
const json = await response.json().catch(() => null);
return json?.error ?? `Upload of "${filePath}" failed: ${response.statusText}`;
}
}; };
if ( if (
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@expo/eas-json", "name": "@expo/eas-json",
"description": "A library for interacting with eas.json", "description": "A library for interacting with eas.json",
"version": "16.3.3", "version": "15.0.0",
"author": "Expo <[email protected]>", "author": "Expo <[email protected]>",
"bugs": "https://github.com/expo/eas-cli/issues", "bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": { "dependencies": {
"@babel/code-frame": "7.23.5", "@babel/code-frame": "7.23.5",
"@expo/eas-build-job": "1.0.173", "@expo/eas-build-job": "1.0.165",
"chalk": "4.1.2", "chalk": "4.1.2",
"env-string": "1.0.1", "env-string": "1.0.1",
"fs-extra": "11.2.0", "fs-extra": "11.2.0",
-8
View File
@@ -144,12 +144,6 @@
"description": "The version of Node.js to use for the build. See: https://nodejs.org/en/about/releases/", "description": "The version of Node.js to use for the build. See: https://nodejs.org/en/about/releases/",
"markdownDescription": "The exact version of [Node.js](https://nodejs.org/en/about/releases/) to use for the build." "markdownDescription": "The exact version of [Node.js](https://nodejs.org/en/about/releases/) to use for the build."
}, },
"corepack": {
"type": "boolean",
"description": "If set to true, corepack will be enabled at the beginning of the build process. Learn more: https://nodejs.org/api/corepack.html",
"markdownDescription": "If set to true, [corepack](https://nodejs.org/api/corepack.html) will be enabled at the beginning of the build process.",
"default": false
},
"yarn": { "yarn": {
"type": "string", "type": "string",
"description": "The version of Yarn to use for the build. See: https://www.npmjs.com/package/yarn", "description": "The version of Yarn to use for the build. See: https://www.npmjs.com/package/yarn",
@@ -243,7 +237,6 @@
"enum": [ "enum": [
"auto", "auto",
"latest", "latest",
"sdk-52",
"sdk-51", "sdk-51",
"sdk-50", "sdk-50",
"sdk-49", "sdk-49",
@@ -256,7 +249,6 @@
"markdownEnumDescriptions": [ "markdownEnumDescriptions": [
"When using this option the build image is selected automatically based on the project configuration, detected Expo SDK and React Native versions.", "When using this option the build image is selected automatically based on the project configuration, detected Expo SDK and React Native versions.",
"The latest Android image currently available. It is resolved to `ubuntu-22.04-jdk-17-ndk-r26b`. The `latest` to image mapping will be updated as new images are released.", "The latest Android image currently available. It is resolved to `ubuntu-22.04-jdk-17-ndk-r26b`. The `latest` to image mapping will be updated as new images are released.",
"The recommended image for SDK 52 builds",
"The recommended image for SDK 51 builds", "The recommended image for SDK 51 builds",
"The recommended image for SDK 50 builds", "The recommended image for SDK 50 builds",
"The recommended image for SDK 49 builds" "The recommended image for SDK 49 builds"
-1
View File
@@ -38,7 +38,6 @@ const CommonBuildProfileSchema = Joi.object({
// build environment // build environment
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)), env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
node: Joi.string().empty(null).custom(semverCheck), node: Joi.string().empty(null).custom(semverCheck),
corepack: Joi.boolean(),
pnpm: Joi.string().empty(null).custom(semverCheck), pnpm: Joi.string().empty(null).custom(semverCheck),
bun: Joi.string().empty(null).custom(semverCheck), bun: Joi.string().empty(null).custom(semverCheck),
yarn: Joi.string().empty(null).custom(semverCheck), yarn: Joi.string().empty(null).custom(semverCheck),
-1
View File
@@ -35,7 +35,6 @@ export interface CommonBuildProfile {
// build environment // build environment
env?: Record<string, string>; env?: Record<string, string>;
node?: string; node?: string;
corepack?: boolean;
pnpm?: string; pnpm?: string;
bun?: string; bun?: string;
yarn?: string; yarn?: string;
Generated
+64 -77
View File
@@ -116,15 +116,6 @@
"@babel/highlight" "^7.22.13" "@babel/highlight" "^7.22.13"
chalk "^2.4.2" chalk "^2.4.2"
"@babel/code-frame@^7.26.2":
version "7.26.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
dependencies:
"@babel/helper-validator-identifier" "^7.25.9"
js-tokens "^4.0.0"
picocolors "^1.0.0"
"@babel/compat-data@^7.16.0": "@babel/compat-data@^7.16.0":
version "7.16.4" version "7.16.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e"
@@ -659,11 +650,6 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
"@babel/helper-string-parser@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
"@babel/helper-validator-identifier@^7.10.4": "@babel/helper-validator-identifier@^7.10.4":
version "7.10.4" version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
@@ -704,11 +690,6 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
"@babel/helper-validator-option@^7.14.5": "@babel/helper-validator-option@^7.14.5":
version "7.14.5" version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@@ -724,13 +705,41 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
"@babel/helpers@^7.16.5", "@babel/helpers@^7.16.7", "@babel/helpers@^7.17.9", "@babel/helpers@^7.23.7": "@babel/helpers@^7.16.5":
version "7.26.10" version "7.16.5"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.5.tgz#29a052d4b827846dd76ece16f565b9634c554ebd"
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g== integrity sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==
dependencies: dependencies:
"@babel/template" "^7.26.9" "@babel/template" "^7.16.0"
"@babel/types" "^7.26.10" "@babel/traverse" "^7.16.5"
"@babel/types" "^7.16.0"
"@babel/helpers@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc"
integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==
dependencies:
"@babel/template" "^7.16.7"
"@babel/traverse" "^7.16.7"
"@babel/types" "^7.16.7"
"@babel/helpers@^7.17.9":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a"
integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==
dependencies:
"@babel/template" "^7.16.7"
"@babel/traverse" "^7.17.9"
"@babel/types" "^7.17.0"
"@babel/helpers@^7.23.7":
version "7.23.8"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34"
integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==
dependencies:
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.7"
"@babel/types" "^7.23.6"
"@babel/highlight@^7.10.4": "@babel/highlight@^7.10.4":
version "7.10.4" version "7.10.4"
@@ -830,13 +839,6 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
"@babel/parser@^7.26.9":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749"
integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==
dependencies:
"@babel/types" "^7.26.10"
"@babel/plugin-proposal-class-properties@^7.0.0": "@babel/plugin-proposal-class-properties@^7.0.0":
version "7.12.1" version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de"
@@ -1130,10 +1132,17 @@
dependencies: dependencies:
"@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.0", "@babel/runtime@^7.21.0": "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.0":
version "7.26.10" version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.10.tgz#a07b4d8fa27af131a633d7b3524db803eb4764c2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.21.0":
version "7.23.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db"
integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==
dependencies: dependencies:
regenerator-runtime "^0.14.0" regenerator-runtime "^0.14.0"
@@ -1182,16 +1191,7 @@
"@babel/parser" "^7.22.15" "@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15" "@babel/types" "^7.22.15"
"@babel/template@^7.26.9": "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.5", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
dependencies:
"@babel/code-frame" "^7.26.2"
"@babel/parser" "^7.26.9"
"@babel/types" "^7.26.9"
"@babel/traverse@^7.12.1", "@babel/traverse@^7.12.5", "@babel/traverse@^7.14.0", "@babel/traverse@^7.16.5", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3":
version "7.23.2" version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
@@ -1300,14 +1300,6 @@
"@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.26.10", "@babel/types@^7.26.9":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
dependencies:
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
"@bcoe/v8-coverage@^0.2.3": "@bcoe/v8-coverage@^0.2.3":
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -1367,10 +1359,10 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
"@expo/[email protected]2": "@expo/[email protected]1":
version "2.1.12" version "2.1.11"
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.12.tgz#9c40a6294820e59d8b1a677318dccb1ccefc5b00" resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.11.tgz#4cd4552ba8f589bd8f3f5fdf7622d8ea94ac58f7"
integrity sha512-ugpL2URxNFxIRw943AIpX3dcvL5rhNCumpL8XTosYqZPyQQ7JRLVNd1m8FNyPg3pmFrz8M4tSucY2pt2ATuKOA== integrity sha512-rfwTPGlyFXhtCSdaKUOEu277tFfL3ulkL3tJKx6/TnRg1nK40a748VBTQZ8CczRrlpU49egJRYvjXBt/M11wWA==
"@expo/bunyan@^4.0.0": "@expo/bunyan@^4.0.0":
version "4.0.0" version "4.0.0"
@@ -1473,10 +1465,10 @@
slugify "^1.3.4" slugify "^1.3.4"
sucrase "3.35.0" sucrase "3.35.0"
"@expo/[email protected]73": "@expo/[email protected]65":
version "1.0.173" version "1.0.165"
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.173.tgz#e19e0563743cc947fb5419fbeb063f03aed2a796" resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.165.tgz#71ec010330d23bb6f6c257ce1ca7c0258e8feaaa"
integrity sha512-OtayVwXQaQEjJkpeKYvB4N6lsXVX+QSEz5okNx/d+8esI2KCjxOy/hzaqKBTovkxRlmWgE3XLw8RhrjAaBY2Gw== integrity sha512-l4bPVRmc6SlrgiC7AOid6s+o3kyP47x17cGIPQ0d4FOaOxu6pP3bPZ3BuPjts1mfvrKg/c/Phe1fJ1nnNlo3Xw==
dependencies: dependencies:
"@expo/logger" "1.0.117" "@expo/logger" "1.0.117"
joi "^17.13.1" joi "^17.13.1"
@@ -1653,12 +1645,12 @@
dependencies: dependencies:
cross-spawn "^7.0.3" cross-spawn "^7.0.3"
"@expo/[email protected]73": "@expo/[email protected]66":
version "1.0.173" version "1.0.166"
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.173.tgz#157bf104d3137012bf82b106d5bd0227fd6f67f1" resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.166.tgz#b606ed72def361860ef5bfd3bd2df601112612af"
integrity sha512-XT8+FsZWIzwK+/TGnPrHe0HMQrzRGjTsjVDx8Yt922tL5TkX69XeB/twA0PcPWBLMxq+JiKtFm68yPTPqjIMNg== integrity sha512-dzEFjbQr4ji65XeSrUgesE+lnnhJYMNdXMZV5PRb19mMGTYYbyeT20+RvSo1a+TOPdO6sPdSBZqfiQ7P4mGBmQ==
dependencies: dependencies:
"@expo/eas-build-job" "1.0.173" "@expo/eas-build-job" "1.0.165"
"@expo/logger" "1.0.117" "@expo/logger" "1.0.117"
"@expo/spawn-async" "^1.7.2" "@expo/spawn-async" "^1.7.2"
arg "^5.0.2" arg "^5.0.2"
@@ -10968,11 +10960,6 @@ node-rsa@^1.1.1:
dependencies: dependencies:
asn1 "^0.2.4" asn1 "^0.2.4"
[email protected]:
version "1.15.0"
resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea"
integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==
nopt@^5.0.0: nopt@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@@ -12384,6 +12371,11 @@ reflect.getprototypeof@^1.0.4:
globalthis "^1.0.3" globalthis "^1.0.3"
which-builtin-type "^1.1.3" which-builtin-type "^1.1.3"
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
regenerator-runtime@^0.14.0: regenerator-runtime@^0.14.0:
version "0.14.0" version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
@@ -12860,11 +12852,6 @@ set-function-name@^2.0.2:
functions-have-names "^1.2.3" functions-have-names "^1.2.3"
has-property-descriptors "^1.0.2" has-property-descriptors "^1.0.2"
[email protected]:
version "3.0.3"
resolved "https://registry.yarnpkg.com/set-interval-async/-/set-interval-async-3.0.3.tgz#99dd13098eebc2bf1ad0b22e03fe25c516b982c3"
integrity sha512-o4DyBv6mko+A9cH3QKek4SAAT5UyJRkfdTi6JHii6ZCKUYFun8SwgBmQrOXd158JOwBQzA+BnO8BvT64xuCaSw==
setimmediate@^1.0.5: setimmediate@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"