mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a8d76c02a | ||
|
|
69ab03d96e | ||
|
|
69ed426842 | ||
|
|
3ca333747f | ||
|
|
6997df3450 | ||
|
|
f939f3ceda | ||
|
|
716728a624 | ||
|
|
f8a01bade3 | ||
|
|
2cb426522d | ||
|
|
573373066d | ||
|
|
feba42f92b | ||
|
|
f373c3fc6c | ||
|
|
e1f86d779e | ||
|
|
c42a34092f | ||
|
|
ced7f924e7 | ||
|
|
93e16284ae | ||
|
|
379036a03d | ||
|
|
6035ce06ef | ||
|
|
0333fd3519 | ||
|
|
20b48326a4 | ||
|
|
199bb78b15 | ||
|
|
7e71b8eacd | ||
|
|
14bcf917a6 | ||
|
|
6ae19df305 | ||
|
|
eedc841ea6 | ||
|
|
98308bb3bd | ||
|
|
8b9de84855 | ||
|
|
496d6f5a6e | ||
|
|
89d1572f9d | ||
|
|
76f591583d | ||
|
|
5d40cc1bc0 | ||
|
|
60a349f2f5 | ||
|
|
439d45d6e1 | ||
|
|
cc48482595 | ||
|
|
a4fb2e20d3 | ||
|
|
ec16d92724 | ||
|
|
34e7720e6f | ||
|
|
938511d5fd | ||
|
|
b473e72c73 | ||
|
|
2816b432d8 | ||
|
|
56f0f7792d | ||
|
|
5236de797b | ||
|
|
fb33298024 | ||
|
|
66c6c8ab0b | ||
|
|
552ef1afe9 | ||
|
|
b35a4efdaa | ||
|
|
7244fc83dc | ||
|
|
1da9573040 | ||
|
|
4e8fc059a4 |
No files matched your search
@@ -3,7 +3,7 @@ commentConfiguration:
|
||||
epilogue: 'Generated by [CodeMention](https://github.com/expo/expo/actions/workflows/codemention.yaml)'
|
||||
rules:
|
||||
- patterns: ['**/*']
|
||||
mentions: ['szdziedzic', 'khamilowicz', 'sjchmiela', 'radoslawkrzemien']
|
||||
mentions: ['khamilowicz', 'sjchmiela']
|
||||
- patterns: ['packages/eas-cli/src/update/**']
|
||||
mentions: ['wschurman', 'quinlanj']
|
||||
- patterns: ['packages/eas-cli/src/commands/update/**']
|
||||
@@ -18,3 +18,7 @@ rules:
|
||||
mentions: ['bycedric']
|
||||
- patterns: ['packages/eas-cli/src/commands/metadata/**']
|
||||
mentions: ['bycedric']
|
||||
- patterns: ['packages/eas-cli/src/worker/**']
|
||||
mentions: ['kitten', 'kadikraman']
|
||||
- patterns: ['packages/eas-cli/src/commands/deploy/**']
|
||||
mentions: ['kitten', 'kadikraman']
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
destination_branch: 'main'
|
||||
pr_title: 'Upgrade eas-build dependencies'
|
||||
pr_body: ':arrow_up: Upgrades [`eas-build`](https://github.com/expo/eas-build) dependencies.'
|
||||
pr_reviewer: 'szdziedzic,radoslawkrzemien,sjchmiela,khamilowicz'
|
||||
pr_reviewer: 'radoslawkrzemien,sjchmiela,khamilowicz'
|
||||
github_token: ${{ secrets.EXPO_BOT_PAT }}
|
||||
|
||||
- uses: actions/github-script@v3
|
||||
|
||||
+72
-2
@@ -8,12 +8,82 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 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
|
||||
|
||||
- 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))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [16.0.0](https://github.com/expo/eas-cli/releases/tag/v16.0.0) - 2025-03-19
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "16.0.1",
|
||||
"version": "16.3.3",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# eas-build-cache-provider
|
||||
|
||||
A build cache provider plugin for the Expo CLI
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
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'];
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
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;
|
||||
@@ -0,0 +1,121 @@
|
||||
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 });
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig",
|
||||
"exclude": ["**/__mocks__/*", "**/__tests__/*"]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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/**/*"]
|
||||
}
|
||||
+139
-81
@@ -70,6 +70,7 @@ eas --help COMMAND
|
||||
* [`eas build:configure`](#eas-buildconfigure)
|
||||
* [`eas build:delete [BUILD_ID]`](#eas-builddelete-build_id)
|
||||
* [`eas build:dev`](#eas-builddev)
|
||||
* [`eas build:download`](#eas-builddownload)
|
||||
* [`eas build:inspect`](#eas-buildinspect)
|
||||
* [`eas build:list`](#eas-buildlist)
|
||||
* [`eas build:resign`](#eas-buildresign)
|
||||
@@ -107,6 +108,7 @@ eas --help COMMAND
|
||||
* [`eas env:push [ENVIRONMENT]`](#eas-envpush-environment)
|
||||
* [`eas env:update [ENVIRONMENT]`](#eas-envupdate-environment)
|
||||
* [`eas fingerprint:compare [HASH1] [HASH2]`](#eas-fingerprintcompare-hash1-hash2)
|
||||
* [`eas fingerprint:generate`](#eas-fingerprintgenerate)
|
||||
* [`eas help [COMMAND]`](#eas-help-command)
|
||||
* [`eas init`](#eas-init)
|
||||
* [`eas init:onboarding [TARGET_PROJECT_DIRECTORY]`](#eas-initonboarding-target_project_directory)
|
||||
@@ -160,7 +162,7 @@ ALIASES
|
||||
$ eas login
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/account/login.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/login.ts)_
|
||||
|
||||
## `eas account:logout`
|
||||
|
||||
@@ -177,7 +179,7 @@ ALIASES
|
||||
$ eas logout
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/logout.ts)_
|
||||
|
||||
## `eas account:view`
|
||||
|
||||
@@ -194,7 +196,7 @@ ALIASES
|
||||
$ eas whoami
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/account/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/account/view.ts)_
|
||||
|
||||
## `eas analytics [STATUS]`
|
||||
|
||||
@@ -208,7 +210,7 @@ DESCRIPTION
|
||||
display or change analytics settings
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/analytics.ts)_
|
||||
_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)_
|
||||
|
||||
## `eas autocomplete [SHELL]`
|
||||
|
||||
@@ -260,7 +262,7 @@ DESCRIPTION
|
||||
create a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/create.ts)_
|
||||
|
||||
## `eas branch:delete [NAME]`
|
||||
|
||||
@@ -281,7 +283,7 @@ DESCRIPTION
|
||||
delete a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/delete.ts)_
|
||||
|
||||
## `eas branch:list`
|
||||
|
||||
@@ -301,7 +303,7 @@ DESCRIPTION
|
||||
list all branches
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/list.ts)_
|
||||
|
||||
## `eas branch:rename`
|
||||
|
||||
@@ -321,7 +323,7 @@ DESCRIPTION
|
||||
rename a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/rename.ts)_
|
||||
|
||||
## `eas branch:view [NAME]`
|
||||
|
||||
@@ -344,7 +346,7 @@ DESCRIPTION
|
||||
view a branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/branch/view.ts)_
|
||||
|
||||
## `eas build`
|
||||
|
||||
@@ -381,7 +383,7 @@ DESCRIPTION
|
||||
start a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/index.ts)_
|
||||
|
||||
## `eas build:cancel [BUILD_ID]`
|
||||
|
||||
@@ -400,7 +402,7 @@ DESCRIPTION
|
||||
cancel a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/cancel.ts)_
|
||||
|
||||
## `eas build:configure`
|
||||
|
||||
@@ -417,7 +419,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/configure.ts)_
|
||||
|
||||
## `eas build:delete [BUILD_ID]`
|
||||
|
||||
@@ -436,7 +438,7 @@ DESCRIPTION
|
||||
delete a build
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/delete.ts)_
|
||||
|
||||
## `eas build:dev`
|
||||
|
||||
@@ -456,7 +458,28 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/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`
|
||||
|
||||
@@ -491,7 +514,7 @@ DESCRIPTION
|
||||
inspect the state of the project at specific build stages, useful for troubleshooting
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/inspect.ts)_
|
||||
|
||||
## `eas build:list`
|
||||
|
||||
@@ -543,7 +566,7 @@ DESCRIPTION
|
||||
list all builds for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/list.ts)_
|
||||
|
||||
## `eas build:resign`
|
||||
|
||||
@@ -572,7 +595,7 @@ DESCRIPTION
|
||||
re-sign a build archive
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/resign.ts)_
|
||||
|
||||
## `eas build:run`
|
||||
|
||||
@@ -598,7 +621,7 @@ DESCRIPTION
|
||||
run simulator/emulator builds from eas-cli
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/run.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/run.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/run.ts)_
|
||||
|
||||
## `eas build:submit`
|
||||
|
||||
@@ -648,7 +671,7 @@ DESCRIPTION
|
||||
get the latest version from EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/get.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/version/get.ts)_
|
||||
|
||||
## `eas build:version:set`
|
||||
|
||||
@@ -667,7 +690,7 @@ DESCRIPTION
|
||||
update version of an app
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/set.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/version/set.ts)_
|
||||
|
||||
## `eas build:version:sync`
|
||||
|
||||
@@ -686,7 +709,7 @@ DESCRIPTION
|
||||
update a version in native code with a value stored on EAS servers
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/version/sync.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/version/sync.ts)_
|
||||
|
||||
## `eas build:view [BUILD_ID]`
|
||||
|
||||
@@ -703,7 +726,7 @@ DESCRIPTION
|
||||
view a build for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/build/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/build/view.ts)_
|
||||
|
||||
## `eas channel:create [NAME]`
|
||||
|
||||
@@ -724,7 +747,7 @@ DESCRIPTION
|
||||
create a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/create.ts)_
|
||||
|
||||
## `eas channel:edit [NAME]`
|
||||
|
||||
@@ -746,7 +769,7 @@ DESCRIPTION
|
||||
point a channel at a new branch
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/edit.ts)_
|
||||
|
||||
## `eas channel:list`
|
||||
|
||||
@@ -766,7 +789,7 @@ DESCRIPTION
|
||||
list all channels
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/list.ts)_
|
||||
|
||||
## `eas channel:pause [NAME]`
|
||||
|
||||
@@ -788,7 +811,7 @@ DESCRIPTION
|
||||
pause a channel to stop it from sending updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/pause.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/channel/pause.ts)_
|
||||
|
||||
## `eas channel:resume [NAME]`
|
||||
|
||||
@@ -810,7 +833,7 @@ DESCRIPTION
|
||||
resume a channel to start sending updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/resume.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/channel/resume.ts)_
|
||||
|
||||
## `eas channel:rollout [CHANNEL]`
|
||||
|
||||
@@ -843,7 +866,7 @@ DESCRIPTION
|
||||
Roll a new branch out on a channel incrementally.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/rollout.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/rollout.ts)_
|
||||
|
||||
## `eas channel:view [NAME]`
|
||||
|
||||
@@ -866,7 +889,7 @@ DESCRIPTION
|
||||
view a channel
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/channel/view.ts)_
|
||||
|
||||
## `eas config`
|
||||
|
||||
@@ -887,7 +910,7 @@ DESCRIPTION
|
||||
display project configuration (app.json + eas.json)
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/config.ts)_
|
||||
_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)_
|
||||
|
||||
## `eas credentials`
|
||||
|
||||
@@ -904,7 +927,7 @@ DESCRIPTION
|
||||
manage credentials
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/credentials/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/credentials/index.ts)_
|
||||
|
||||
## `eas credentials:configure-build`
|
||||
|
||||
@@ -922,7 +945,7 @@ DESCRIPTION
|
||||
Set up credentials for building your project.
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/credentials/configure-build.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/credentials/configure-build.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/credentials/configure-build.ts)_
|
||||
|
||||
## `eas deploy [2m[options][22m`
|
||||
|
||||
@@ -950,7 +973,7 @@ ALIASES
|
||||
$ eas worker:deploy
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/deploy/index.ts)_
|
||||
|
||||
## `eas deploy:alias`
|
||||
|
||||
@@ -975,7 +998,7 @@ ALIASES
|
||||
$ eas deploy:promote
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/deploy/alias.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/deploy/alias.ts)_
|
||||
|
||||
## `eas deploy:promote`
|
||||
|
||||
@@ -1012,7 +1035,7 @@ DESCRIPTION
|
||||
register new Apple Devices to use for internal distribution
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/device/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/create.ts)_
|
||||
|
||||
## `eas device:delete`
|
||||
|
||||
@@ -1032,7 +1055,7 @@ DESCRIPTION
|
||||
remove a registered device from your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/delete.ts)_
|
||||
|
||||
## `eas device:list`
|
||||
|
||||
@@ -1053,7 +1076,7 @@ DESCRIPTION
|
||||
list all registered devices for your account
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/device/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/list.ts)_
|
||||
|
||||
## `eas device:rename`
|
||||
|
||||
@@ -1074,7 +1097,7 @@ DESCRIPTION
|
||||
rename a registered device
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/rename.ts)_
|
||||
|
||||
## `eas device:view [UDID]`
|
||||
|
||||
@@ -1088,7 +1111,7 @@ DESCRIPTION
|
||||
view a device for your project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/device/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/device/view.ts)_
|
||||
|
||||
## `eas diagnostics`
|
||||
|
||||
@@ -1102,7 +1125,7 @@ DESCRIPTION
|
||||
display environment info
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/diagnostics.ts)_
|
||||
_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)_
|
||||
|
||||
## `eas env:create [ENVIRONMENT]`
|
||||
|
||||
@@ -1131,7 +1154,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/create.ts)_
|
||||
|
||||
## `eas env:delete [ENVIRONMENT]`
|
||||
|
||||
@@ -1155,7 +1178,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/delete.ts)_
|
||||
|
||||
## `eas env:exec ENVIRONMENT BASH_COMMAND`
|
||||
|
||||
@@ -1176,7 +1199,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/exec.ts)_
|
||||
|
||||
## `eas env:get [ENVIRONMENT]`
|
||||
|
||||
@@ -1201,7 +1224,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/get.ts)_
|
||||
|
||||
## `eas env:list [ENVIRONMENT]`
|
||||
|
||||
@@ -1226,7 +1249,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/list.ts)_
|
||||
|
||||
## `eas env:pull [ENVIRONMENT]`
|
||||
|
||||
@@ -1249,7 +1272,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/pull.ts)_
|
||||
|
||||
## `eas env:push [ENVIRONMENT]`
|
||||
|
||||
@@ -1270,7 +1293,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/push.ts)_
|
||||
|
||||
## `eas env:update [ENVIRONMENT]`
|
||||
|
||||
@@ -1301,7 +1324,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/env/update.ts)_
|
||||
|
||||
## `eas fingerprint:compare [HASH1] [HASH2]`
|
||||
|
||||
@@ -1309,19 +1332,21 @@ compare fingerprints of the current project, builds, and updates
|
||||
|
||||
```
|
||||
USAGE
|
||||
$ eas fingerprint:compare [HASH1] [HASH2] [--build-id <value>] [--update-id <value>] [--open] [--json
|
||||
--non-interactive]
|
||||
$ eas fingerprint:compare [HASH1] [HASH2] [--build-id <value>] [--update-id <value>] [--open] [--environment
|
||||
development|preview|production] [--json --non-interactive]
|
||||
|
||||
ARGUMENTS
|
||||
HASH1 If provided alone, HASH1 is compared against the current project's fingerprint.
|
||||
HASH2 If two hashes are provided, HASH1 is compared against HASH2.
|
||||
|
||||
FLAGS
|
||||
--build-id=<value>... Compare the fingerprint with the build with the specified ID
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--open Open the fingerprint comparison in the browser
|
||||
--update-id=<value>... Compare the fingerprint with the update 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
|
||||
specified environment.
|
||||
--json Enable JSON output, non-JSON messages will be printed to stderr.
|
||||
--non-interactive Run the command in non-interactive mode.
|
||||
--open Open the fingerprint comparison in the browser
|
||||
--update-id=<value>... Compare the fingerprint with the update with the specified ID
|
||||
|
||||
DESCRIPTION
|
||||
compare fingerprints of the current project, builds, and updates
|
||||
@@ -1335,6 +1360,8 @@ EXAMPLES
|
||||
|
||||
$ 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> --update-id <UPDATE-ID> # Compare fingerprint from build against fingerprint from update
|
||||
@@ -1342,7 +1369,38 @@ EXAMPLES
|
||||
$ 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.0.1/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/v16.3.3/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]`
|
||||
|
||||
@@ -1450,7 +1508,7 @@ DESCRIPTION
|
||||
validate the local store configuration
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/metadata/lint.ts)_
|
||||
|
||||
## `eas metadata:pull`
|
||||
|
||||
@@ -1467,7 +1525,7 @@ DESCRIPTION
|
||||
generate the local store configuration from the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/pull.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/metadata/pull.ts)_
|
||||
|
||||
## `eas metadata:push`
|
||||
|
||||
@@ -1484,7 +1542,7 @@ DESCRIPTION
|
||||
sync the local store configuration to the app stores
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/metadata/push.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/metadata/push.ts)_
|
||||
|
||||
## `eas onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1514,7 +1572,7 @@ DESCRIPTION
|
||||
open the project page in a web browser
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/open.ts)_
|
||||
_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)_
|
||||
|
||||
## `eas project:info`
|
||||
|
||||
@@ -1528,7 +1586,7 @@ DESCRIPTION
|
||||
information about the current project
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/project/info.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/info.ts)_
|
||||
|
||||
## `eas project:init`
|
||||
|
||||
@@ -1551,7 +1609,7 @@ ALIASES
|
||||
$ eas init
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/project/init.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/init.ts)_
|
||||
|
||||
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
|
||||
|
||||
@@ -1569,7 +1627,7 @@ ALIASES
|
||||
$ eas onboarding
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/project/onboarding.ts)_
|
||||
|
||||
## `eas submit`
|
||||
|
||||
@@ -1600,7 +1658,7 @@ ALIASES
|
||||
$ eas build:submit
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/submit.ts)_
|
||||
_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)_
|
||||
|
||||
## `eas update`
|
||||
|
||||
@@ -1642,7 +1700,7 @@ DESCRIPTION
|
||||
publish an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/index.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/index.ts)_
|
||||
|
||||
## `eas update:configure`
|
||||
|
||||
@@ -1662,7 +1720,7 @@ DESCRIPTION
|
||||
configure the project to support EAS Update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/configure.ts)_
|
||||
|
||||
## `eas update:delete GROUPID`
|
||||
|
||||
@@ -1683,7 +1741,7 @@ DESCRIPTION
|
||||
delete all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/delete.ts)_
|
||||
|
||||
## `eas update:edit [GROUPID]`
|
||||
|
||||
@@ -1707,7 +1765,7 @@ DESCRIPTION
|
||||
edit all the updates in an update group
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/edit.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/update/edit.ts)_
|
||||
|
||||
## `eas update:list`
|
||||
|
||||
@@ -1729,7 +1787,7 @@ DESCRIPTION
|
||||
view the recent updates
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/list.ts)_
|
||||
|
||||
## `eas update:republish`
|
||||
|
||||
@@ -1761,7 +1819,7 @@ DESCRIPTION
|
||||
roll back to an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/republish.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/republish.ts)_
|
||||
|
||||
## `eas update:roll-back-to-embedded`
|
||||
|
||||
@@ -1789,7 +1847,7 @@ DESCRIPTION
|
||||
roll back to the embedded update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/roll-back-to-embedded.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
|
||||
|
||||
## `eas update:rollback`
|
||||
|
||||
@@ -1809,7 +1867,7 @@ DESCRIPTION
|
||||
roll back to an embedded update or an existing update
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/rollback.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/rollback.ts)_
|
||||
|
||||
## `eas update:view GROUPID`
|
||||
|
||||
@@ -1829,7 +1887,7 @@ DESCRIPTION
|
||||
update group details
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/update/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/update/view.ts)_
|
||||
|
||||
## `eas webhook:create`
|
||||
|
||||
@@ -1850,7 +1908,7 @@ DESCRIPTION
|
||||
create a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/create.ts)_
|
||||
|
||||
## `eas webhook:delete [ID]`
|
||||
|
||||
@@ -1870,7 +1928,7 @@ DESCRIPTION
|
||||
delete a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/delete.ts)_
|
||||
|
||||
## `eas webhook:list`
|
||||
|
||||
@@ -1888,7 +1946,7 @@ DESCRIPTION
|
||||
list webhooks
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/list.ts)_
|
||||
|
||||
## `eas webhook:update`
|
||||
|
||||
@@ -1910,7 +1968,7 @@ DESCRIPTION
|
||||
update a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/update.ts)_
|
||||
|
||||
## `eas webhook:view ID`
|
||||
|
||||
@@ -1927,7 +1985,7 @@ DESCRIPTION
|
||||
view a webhook
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v16.0.1/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v16.3.3/packages/eas-cli/src/commands/webhook/view.ts)_
|
||||
|
||||
## `eas whoami`
|
||||
|
||||
@@ -2011,7 +2069,7 @@ DESCRIPTION
|
||||
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.0.1/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/v16.3.3/packages/eas-cli/src/commands/workflow/create.ts)_
|
||||
|
||||
## `eas workflow:run [FILE]`
|
||||
|
||||
@@ -2031,7 +2089,7 @@ DESCRIPTION
|
||||
Run an EAS workflow
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/workflow/run.ts)_
|
||||
|
||||
## `eas workflow:validate PATH`
|
||||
|
||||
@@ -2051,5 +2109,5 @@ DESCRIPTION
|
||||
validate a workflow configuration yaml file
|
||||
```
|
||||
|
||||
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v16.0.1/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/v16.3.3/packages/eas-cli/src/commands/workflow/validate.ts)_
|
||||
<!-- commandsstop -->
|
||||
Generated
+1842
-20
File diff suppressed because it is too large.
Load diff
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"description": "EAS command line tool",
|
||||
"version": "16.0.1",
|
||||
"version": "16.3.3",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bin": {
|
||||
"eas": "./bin/run"
|
||||
},
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@expo/apple-utils": "2.1.11",
|
||||
"@expo/apple-utils": "2.1.12",
|
||||
"@expo/code-signing-certificates": "0.0.5",
|
||||
"@expo/config": "10.0.6",
|
||||
"@expo/config-plugins": "9.0.12",
|
||||
"@expo/eas-build-job": "1.0.170",
|
||||
"@expo/eas-json": "16.0.0",
|
||||
"@expo/eas-build-job": "1.0.173",
|
||||
"@expo/eas-json": "16.3.3",
|
||||
"@expo/env": "^1.0.0",
|
||||
"@expo/json-file": "8.3.3",
|
||||
"@expo/logger": "1.0.117",
|
||||
@@ -28,7 +28,7 @@
|
||||
"@expo/results": "1.0.0",
|
||||
"@expo/rudder-sdk-node": "1.1.1",
|
||||
"@expo/spawn-async": "1.7.2",
|
||||
"@expo/steps": "1.0.170",
|
||||
"@expo/steps": "1.0.173",
|
||||
"@expo/timeago.js": "1.0.0",
|
||||
"@oclif/core": "^1.26.2",
|
||||
"@oclif/plugin-autocomplete": "^2.3.10",
|
||||
@@ -68,6 +68,7 @@
|
||||
"nanoid": "3.3.8",
|
||||
"node-fetch": "2.6.7",
|
||||
"node-forge": "1.3.1",
|
||||
"node-stream-zip": "1.15.0",
|
||||
"nullthrows": "1.1.1",
|
||||
"ora": "5.1.0",
|
||||
"pkg-dir": "4.2.0",
|
||||
@@ -78,6 +79,7 @@
|
||||
"qrcode-terminal": "0.12.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"semver": "7.5.4",
|
||||
"set-interval-async": "3.0.3",
|
||||
"slash": "3.0.0",
|
||||
"tar": "6.2.1",
|
||||
"tar-stream": "3.1.7",
|
||||
|
||||
@@ -73,11 +73,15 @@ export async function prepareJobAsync(
|
||||
builderEnvironment: {
|
||||
image: buildProfile.image,
|
||||
node: buildProfile.node,
|
||||
corepack: buildProfile.corepack,
|
||||
pnpm: buildProfile.pnpm,
|
||||
bun: buildProfile.bun,
|
||||
yarn: buildProfile.yarn,
|
||||
ndk: buildProfile.ndk,
|
||||
env: buildProfile.env,
|
||||
env: {
|
||||
...buildProfile.env,
|
||||
...(ctx.isVerboseLoggingEnabled ? { EAS_VERBOSE: '1' } : {}),
|
||||
},
|
||||
},
|
||||
cache: {
|
||||
...cacheDefaults,
|
||||
|
||||
@@ -63,5 +63,6 @@ export interface BuildContext<T extends Platform> {
|
||||
vcsClient: Client;
|
||||
loggerLevel?: LoggerLevel;
|
||||
repack: boolean;
|
||||
isVerboseLoggingEnabled: boolean;
|
||||
env: Record<string, string>;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
buildLoggerLevel,
|
||||
freezeCredentials,
|
||||
repack,
|
||||
isVerboseLoggingEnabled,
|
||||
env,
|
||||
}: {
|
||||
buildProfileName: string;
|
||||
@@ -65,6 +66,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
isVerboseLoggingEnabled: boolean;
|
||||
env: Record<string, string>;
|
||||
}): Promise<BuildContext<T>> {
|
||||
const { exp, projectId } = await getDynamicPrivateProjectConfigAsync({
|
||||
@@ -151,6 +153,7 @@ export async function createBuildContextAsync<T extends Platform>({
|
||||
requiredPackageManager,
|
||||
loggerLevel: buildLoggerLevel,
|
||||
repack,
|
||||
isVerboseLoggingEnabled,
|
||||
env,
|
||||
};
|
||||
if (platform === Platform.ANDROID) {
|
||||
|
||||
@@ -65,13 +65,17 @@ export async function prepareJobAsync(
|
||||
builderEnvironment: {
|
||||
image: buildProfile.image,
|
||||
node: buildProfile.node,
|
||||
corepack: buildProfile.corepack,
|
||||
pnpm: buildProfile.pnpm,
|
||||
bun: buildProfile.bun,
|
||||
yarn: buildProfile.yarn,
|
||||
bundler: buildProfile.bundler,
|
||||
cocoapods: buildProfile.cocoapods,
|
||||
fastlane: buildProfile.fastlane,
|
||||
env: buildProfile.env,
|
||||
env: {
|
||||
...buildProfile.env,
|
||||
...(ctx.isVerboseLoggingEnabled ? { EAS_VERBOSE: '1' } : {}),
|
||||
},
|
||||
},
|
||||
cache: {
|
||||
...cacheDefaults,
|
||||
|
||||
@@ -98,6 +98,7 @@ export interface BuildFlags {
|
||||
buildLoggerLevel?: LoggerLevel;
|
||||
freezeCredentials: boolean;
|
||||
repack: boolean;
|
||||
isVerboseLoggingEnabled?: boolean;
|
||||
}
|
||||
|
||||
export async function runBuildAndSubmitAsync({
|
||||
@@ -401,6 +402,7 @@ async function prepareAndStartBuildAsync({
|
||||
buildLoggerLevel: flags.buildLoggerLevel ?? (Log.isDebug ? LoggerLevel.DEBUG : undefined),
|
||||
freezeCredentials: flags.freezeCredentials,
|
||||
repack: flags.repack,
|
||||
isVerboseLoggingEnabled: flags.isVerboseLoggingEnabled ?? false,
|
||||
env,
|
||||
});
|
||||
|
||||
|
||||
@@ -72,3 +72,12 @@ export function getProjectGitHubSettingsUrl(accountName: string, projectName: st
|
||||
getExpoWebsiteBaseUrl()
|
||||
).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,
|
||||
} from '../analytics/AnalyticsManager';
|
||||
import { EnvironmentVariableEnvironment } from '../graphql/generated';
|
||||
import Log from '../log';
|
||||
import Log, { link } from '../log';
|
||||
import SessionManager from '../user/SessionManager';
|
||||
import { Client } from '../vcs/vcs';
|
||||
|
||||
@@ -264,6 +264,16 @@ export default abstract class EasCommand extends Command {
|
||||
const defaultMsg = `${messageLine}${requestIdLine}`;
|
||||
|
||||
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(
|
||||
`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(
|
||||
|
||||
@@ -43,6 +43,7 @@ export async function fetchBuildsAsync({
|
||||
platform?: RequestedPlatform;
|
||||
profile?: string;
|
||||
hasFingerprint?: boolean;
|
||||
fingerprintHash?: string;
|
||||
};
|
||||
}): Promise<BuildFragment[]> {
|
||||
let builds: BuildFragment[];
|
||||
@@ -56,6 +57,9 @@ export async function fetchBuildsAsync({
|
||||
if (filters?.hasFingerprint) {
|
||||
queryFilters['hasFingerprint'] = filters.hasFingerprint;
|
||||
}
|
||||
if (filters?.fingerprintHash) {
|
||||
queryFilters['fingerprintHash'] = filters.fingerprintHash;
|
||||
}
|
||||
if (!filters?.statuses) {
|
||||
builds = await BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
|
||||
appId: projectId,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { getProjectIdAsync } from './contextUtils/getProjectIdAsync';
|
||||
import { loadServerSideEnvironmentVariablesAsync } from './contextUtils/loadServerSideEnvironmentVariablesAsync';
|
||||
import { getPublicExpoConfigAsync } from '../../project/expoConfig';
|
||||
|
||||
type GetServerSideEnvironmentVariablesFn = (
|
||||
export type GetServerSideEnvironmentVariablesFn = (
|
||||
maybeEnv?: Record<string, string>
|
||||
) => Promise<Record<string, string>>;
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
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;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ interface RawBuildFlags {
|
||||
message?: string;
|
||||
'build-logger-level'?: LoggerLevel;
|
||||
'freeze-credentials': boolean;
|
||||
'verbose-logs'?: boolean;
|
||||
repack: boolean;
|
||||
}
|
||||
|
||||
@@ -118,6 +119,10 @@ export default class Build extends EasCommand {
|
||||
hidden: true,
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -238,6 +243,7 @@ export default class Build extends EasCommand {
|
||||
message,
|
||||
buildLoggerLevel: flags['build-logger-level'],
|
||||
freezeCredentials: flags['freeze-credentials'],
|
||||
isVerboseLoggingEnabled: flags['verbose-logs'],
|
||||
repack: flags.repack,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { format as formatTimeAgo } from '@expo/timeago.js';
|
||||
import { Flags } from '@oclif/core';
|
||||
import { CombinedError } from '@urql/core';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import { getHostingDeploymentsUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { EASEnvironmentFlag, EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { EnvironmentVariableEnvironment } from '../../graphql/generated';
|
||||
import Log from '../../log';
|
||||
import Log, { link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { createProgressTracker } from '../../utils/progress';
|
||||
import * as WorkerAssets from '../../worker/assets';
|
||||
@@ -120,7 +123,10 @@ export default class WorkerDeploy extends EasCommand {
|
||||
} = await this.getContextAsync(WorkerDeploy, { ...flags, withServerSideEnvironment: null });
|
||||
|
||||
const projectDist = await resolveExportedProjectAsync(flags, projectDir);
|
||||
const { projectId } = await getDynamicPrivateProjectConfigAsync();
|
||||
const { projectId, exp } = await getDynamicPrivateProjectConfigAsync();
|
||||
|
||||
const projectName = exp.slug;
|
||||
const accountName = (await getOwnerAccountForProjectIdAsync(graphqlClient, projectId)).name;
|
||||
|
||||
logExportedProjectInfo(projectDist);
|
||||
|
||||
@@ -299,6 +305,32 @@ export default class WorkerDeploy extends EasCommand {
|
||||
progress.succeed('Created deployment');
|
||||
} catch (error: any) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,12 @@ import { getExpoWebsiteBaseUrl } from '../../api';
|
||||
import { selectBranchOnAppAsync } from '../../branch/queries';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { fetchBuildsAsync, formatBuild } from '../../commandUtils/builds';
|
||||
import { GetServerSideEnvironmentVariablesFn } from '../../commandUtils/context/ServerSideEnvironmentVariablesContextField';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import {
|
||||
EasEnvironmentFlagParameters,
|
||||
EasNonInteractiveAndJsonFlags,
|
||||
} from '../../commandUtils/flags';
|
||||
import { diffFingerprint } from '../../fingerprint/cli';
|
||||
import { abridgedDiff } from '../../fingerprint/diff';
|
||||
import { Fingerprint, FingerprintDiffItem } from '../../fingerprint/types';
|
||||
@@ -19,6 +23,7 @@ import {
|
||||
AppPlatform,
|
||||
BuildFragment,
|
||||
BuildStatus,
|
||||
EnvironmentVariableEnvironment,
|
||||
FingerprintFragment,
|
||||
UpdateFragment,
|
||||
} from '../../graphql/generated';
|
||||
@@ -58,6 +63,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
'$ 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 --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> --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',
|
||||
@@ -91,6 +97,11 @@ export default class FingerprintCompare extends EasCommand {
|
||||
open: Flags.boolean({
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -99,6 +110,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
...this.ContextOptions.ProjectConfig,
|
||||
...this.ContextOptions.LoggedIn,
|
||||
...this.ContextOptions.Vcs,
|
||||
...this.ContextOptions.ServerSideEnvironmentVariables,
|
||||
};
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
@@ -110,6 +122,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
'build-id': buildIds,
|
||||
'update-id': updateIds,
|
||||
open,
|
||||
environment,
|
||||
} = flags;
|
||||
const [buildId1, buildId2] = buildIds ?? [];
|
||||
const [updateId1, updateId2] = updateIds ?? [];
|
||||
@@ -119,9 +132,10 @@ export default class FingerprintCompare extends EasCommand {
|
||||
privateProjectConfig: { projectDir },
|
||||
loggedIn: { graphqlClient },
|
||||
vcsClient,
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
} = await this.getContextAsync(FingerprintCompare, {
|
||||
nonInteractive,
|
||||
withServerSideEnvironment: null,
|
||||
withServerSideEnvironment: environment ?? null,
|
||||
});
|
||||
if (json) {
|
||||
enableJsonOutput();
|
||||
@@ -132,6 +146,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
{
|
||||
nonInteractive,
|
||||
buildId: buildId1,
|
||||
@@ -148,6 +163,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
{
|
||||
nonInteractive,
|
||||
buildId: buildId2,
|
||||
@@ -155,6 +171,7 @@ export default class FingerprintCompare extends EasCommand {
|
||||
hash: hash2,
|
||||
useProjectFingerprint:
|
||||
isFirstFingerprintSpecifiedByFlagOrArg && !isSecondFingerprintSpecifiedByFlagOrArg,
|
||||
environmentForProjectFingerprint: environment,
|
||||
},
|
||||
firstFingerprintInfo
|
||||
);
|
||||
@@ -260,17 +277,20 @@ async function getFingerprintInfoAsync(
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn,
|
||||
{
|
||||
buildId,
|
||||
updateId,
|
||||
hash,
|
||||
useProjectFingerprint,
|
||||
environmentForProjectFingerprint,
|
||||
nonInteractive,
|
||||
}: {
|
||||
buildId?: string;
|
||||
updateId?: string;
|
||||
hash: string;
|
||||
useProjectFingerprint?: boolean;
|
||||
environmentForProjectFingerprint?: string;
|
||||
nonInteractive: boolean;
|
||||
},
|
||||
firstFingerprintInfo?: {
|
||||
@@ -296,13 +316,15 @@ async function getFingerprintInfoAsync(
|
||||
'First fingerprint must be provided in order to compare against the project.'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoFromLocalProjectAsync(
|
||||
return await getFingerprintInfoFromLocalProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
firstFingerprintInfo,
|
||||
environment: environmentForProjectFingerprint,
|
||||
});
|
||||
}
|
||||
|
||||
if (nonInteractive) {
|
||||
@@ -310,26 +332,38 @@ async function getFingerprintInfoAsync(
|
||||
'Insufficent arguments provided for fingerprint comparison in non-interactive mode'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoInteractiveAsync(
|
||||
return await getFingerprintInfoInteractiveAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
firstFingerprintInfo,
|
||||
environmentForProjectFingerprint,
|
||||
});
|
||||
}
|
||||
|
||||
async function getFingerprintInfoInteractiveAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
async function getFingerprintInfoInteractiveAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
firstFingerprintInfo,
|
||||
environmentForProjectFingerprint,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
vcsClient: Client;
|
||||
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn;
|
||||
firstFingerprintInfo?: {
|
||||
fingerprint: Fingerprint;
|
||||
platforms?: AppPlatform[];
|
||||
origin: FingerprintOrigin;
|
||||
}
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
};
|
||||
environmentForProjectFingerprint?: string;
|
||||
}): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const prompt = firstFingerprintInfo
|
||||
? 'Select the second fingerprint to compare against'
|
||||
: 'Select a reference fingerprint for comparison';
|
||||
@@ -347,13 +381,15 @@ async function getFingerprintInfoInteractiveAsync(
|
||||
'First fingerprint must be provided in order to compare against the project.'
|
||||
);
|
||||
}
|
||||
return await getFingerprintInfoFromLocalProjectAsync(
|
||||
return await getFingerprintInfoFromLocalProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintInfo
|
||||
);
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
firstFingerprintInfo,
|
||||
environment: environmentForProjectFingerprint,
|
||||
});
|
||||
} else if (originType === FingerprintOriginType.Build) {
|
||||
const displayName = await getDisplayNameForProjectIdAsync(graphqlClient, projectId);
|
||||
const buildId = await selectBuildToCompareAsync(graphqlClient, projectId, displayName, {
|
||||
@@ -407,30 +443,47 @@ async function getFingerprintInfoInteractiveAsync(
|
||||
}
|
||||
}
|
||||
|
||||
async function getFingerprintInfoFromLocalProjectAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
async function getFingerprintInfoFromLocalProjectAsync({
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
getServerSideEnvironmentVariablesAsync,
|
||||
firstFingerprintInfo,
|
||||
environment,
|
||||
}: {
|
||||
graphqlClient: ExpoGraphqlClient;
|
||||
projectDir: string;
|
||||
projectId: string;
|
||||
vcsClient: Client;
|
||||
getServerSideEnvironmentVariablesAsync: GetServerSideEnvironmentVariablesFn;
|
||||
firstFingerprintInfo: {
|
||||
fingerprint: Fingerprint;
|
||||
platforms?: AppPlatform[];
|
||||
origin: FingerprintOrigin;
|
||||
}
|
||||
): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
};
|
||||
environment?: string;
|
||||
}): Promise<{ fingerprint: Fingerprint; platforms?: AppPlatform[]; origin: FingerprintOrigin }> {
|
||||
const firstFingerprintPlatforms = firstFingerprintInfo.platforms;
|
||||
if (!firstFingerprintPlatforms) {
|
||||
if (!firstFingerprintPlatforms || firstFingerprintPlatforms.length === 0) {
|
||||
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.`
|
||||
);
|
||||
}
|
||||
|
||||
if (environment) {
|
||||
Log.log(`🔧 Using environment: ${environment}`);
|
||||
}
|
||||
const env = environment
|
||||
? { ...(await getServerSideEnvironmentVariablesAsync()), EXPO_NO_DOTENV: '1' }
|
||||
: undefined;
|
||||
const fingerprint = await getFingerprintInfoFromLocalProjectForPlatformsAsync(
|
||||
graphqlClient,
|
||||
projectDir,
|
||||
projectId,
|
||||
vcsClient,
|
||||
firstFingerprintPlatforms
|
||||
firstFingerprintPlatforms,
|
||||
{ env }
|
||||
);
|
||||
return { fingerprint, origin: { type: FingerprintOriginType.Project } };
|
||||
}
|
||||
@@ -757,7 +810,7 @@ function isJSON(str: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export async function selectBuildToCompareAsync(
|
||||
async function selectBuildToCompareAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
projectId: string,
|
||||
projectDisplayName: string,
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
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 { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import {
|
||||
EASEnvironmentFlag,
|
||||
EasEnvironmentFlagParameters,
|
||||
EasNonInteractiveAndJsonFlags,
|
||||
} from '../../commandUtils/flags';
|
||||
import {
|
||||
appPlatformToPlatform,
|
||||
getFingerprintInfoFromLocalProjectForPlatformsAsync,
|
||||
stringToAppPlatform,
|
||||
} from '../../fingerprint/utils';
|
||||
import { AppPlatform } from '../../graphql/generated';
|
||||
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 hidden = true;
|
||||
|
||||
static override examples = [
|
||||
'$ eas fingerprint:generate',
|
||||
'$ eas fingerprint:generate --json --non-interactive -p android',
|
||||
'$ 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 = {
|
||||
@@ -28,6 +38,16 @@ export default class FingerprintGenerate extends EasCommand {
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -36,20 +56,30 @@ export default class FingerprintGenerate extends EasCommand {
|
||||
...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 } = flags;
|
||||
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: null,
|
||||
withServerSideEnvironment: environment ?? null,
|
||||
});
|
||||
if (json) {
|
||||
enableJsonOutput();
|
||||
@@ -64,12 +94,43 @@ export default class FingerprintGenerate extends EasCommand {
|
||||
}
|
||||
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]
|
||||
[platform],
|
||||
{ env }
|
||||
);
|
||||
|
||||
if (json) {
|
||||
@@ -89,7 +150,7 @@ export default class FingerprintGenerate extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
export async function selectRequestedPlatformAsync(): Promise<AppPlatform> {
|
||||
async function selectRequestedPlatformAsync(): Promise<AppPlatform> {
|
||||
const { requestedPlatform } = await promptAsync({
|
||||
type: 'select',
|
||||
message: 'Select platform',
|
||||
|
||||
@@ -6,37 +6,43 @@ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/creat
|
||||
import { EasNonInteractiveAndJsonFlags } from '../../commandUtils/flags';
|
||||
import { withErrorHandlingAsync } from '../../graphql/client';
|
||||
import {
|
||||
DeleteUpdateGroupMutation,
|
||||
UpdateMutationDeleteUpdateGroupArgs,
|
||||
BackgroundJobReceiptDataFragment,
|
||||
ScheduleUpdateGroupDeletionMutation,
|
||||
ScheduleUpdateGroupDeletionMutationVariables,
|
||||
} from '../../graphql/generated';
|
||||
import { BackgroundJobReceiptNode } from '../../graphql/types/BackgroundJobReceipt';
|
||||
import Log from '../../log';
|
||||
import { confirmAsync } from '../../prompts';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { pollForBackgroundJobReceiptAsync } from '../../utils/pollForBackgroundJobReceiptAsync';
|
||||
|
||||
async function deleteUpdateGroupAsync(
|
||||
async function scheduleUpdateGroupDeletionAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
{
|
||||
group,
|
||||
}: {
|
||||
group: string;
|
||||
}
|
||||
): Promise<DeleteUpdateGroupMutation> {
|
||||
return await withErrorHandlingAsync(
|
||||
): Promise<BackgroundJobReceiptDataFragment> {
|
||||
const result = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<DeleteUpdateGroupMutation, UpdateMutationDeleteUpdateGroupArgs>(
|
||||
.mutation<ScheduleUpdateGroupDeletionMutation, ScheduleUpdateGroupDeletionMutationVariables>(
|
||||
gql`
|
||||
mutation DeleteUpdateGroup($group: ID!) {
|
||||
mutation ScheduleUpdateGroupDeletion($group: ID!) {
|
||||
update {
|
||||
deleteUpdateGroup(group: $group) {
|
||||
group
|
||||
scheduleUpdateGroupDeletion(group: $group) {
|
||||
id
|
||||
...BackgroundJobReceiptData
|
||||
}
|
||||
}
|
||||
}
|
||||
${BackgroundJobReceiptNode}
|
||||
`,
|
||||
{ group }
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
return result.update.scheduleUpdateGroupDeletion;
|
||||
}
|
||||
|
||||
export default class UpdateDelete extends EasCommand {
|
||||
@@ -90,7 +96,9 @@ export default class UpdateDelete extends EasCommand {
|
||||
}
|
||||
}
|
||||
|
||||
await deleteUpdateGroupAsync(graphqlClient, { group });
|
||||
const receipt = await scheduleUpdateGroupDeletionAsync(graphqlClient, { group });
|
||||
const successfulReceipt = await pollForBackgroundJobReceiptAsync(graphqlClient, receipt);
|
||||
Log.debug('Deletion result', { successfulReceipt });
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput({ group });
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Errors, Flags } from '@oclif/core';
|
||||
import chalk from 'chalk';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { getExpoWebsiteBaseUrl } from '../../api';
|
||||
import { ensureBranchExistsAsync } from '../../branch/queries';
|
||||
import { transformFingerprintSource } from '../../build/graphql';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
@@ -12,8 +13,9 @@ import { EasNonInteractiveAndJsonFlags, EasUpdateEnvironmentFlag } from '../../c
|
||||
import { getPaginatedQueryOptions } from '../../commandUtils/pagination';
|
||||
import fetch from '../../fetch';
|
||||
import {
|
||||
AssetMapSourceInput,
|
||||
EnvironmentVariableEnvironment,
|
||||
FingerprintInfoGroup,
|
||||
FingerprintInfoGroup as GraphqlFingerprintInfoGroup,
|
||||
PublishUpdateGroupInput,
|
||||
StatuspageServiceName,
|
||||
UpdateInfoGroup,
|
||||
@@ -21,9 +23,11 @@ import {
|
||||
UpdateRolloutInfoGroup,
|
||||
} from '../../graphql/generated';
|
||||
import { PublishMutation } from '../../graphql/mutations/PublishMutation';
|
||||
import { AppQuery } from '../../graphql/queries/AppQuery';
|
||||
import Log, { learnMore, link } from '../../log';
|
||||
import { ora } from '../../ora';
|
||||
import { RequestedPlatform } from '../../platform';
|
||||
import { maybeUploadAssetMapAsync } from '../../project/maybeUploadAssetMapAsync';
|
||||
import { maybeUploadFingerprintAsync } from '../../project/maybeUploadFingerprintAsync';
|
||||
import { getOwnerAccountForProjectIdAsync } from '../../project/projectUtils';
|
||||
import {
|
||||
@@ -33,6 +37,7 @@ import {
|
||||
buildUnsortedUpdateInfoGroupAsync,
|
||||
collectAssetsAsync,
|
||||
filterCollectedAssetsByRequestedPlatforms,
|
||||
findCompatibleBuildsAsync,
|
||||
generateEasMetadataAsync,
|
||||
getBranchNameForCommandAsync,
|
||||
getRuntimeToPlatformsAndFingerprintInfoMappingFromRuntimeVersionInfoObjects,
|
||||
@@ -268,9 +273,14 @@ export default class UpdatePublish extends EasCommand {
|
||||
let uploadedAssetCount = 0;
|
||||
let assetLimitPerUpdateGroup = 0;
|
||||
let realizedPlatforms: UpdatePublishPlatform[] = [];
|
||||
let assetMapSource: AssetMapSourceInput | null = null;
|
||||
|
||||
try {
|
||||
const collectedAssets = await collectAssetsAsync(distRoot);
|
||||
const [collectedAssets, maybeAssetMapSource] = await Promise.all([
|
||||
collectAssetsAsync(distRoot),
|
||||
maybeUploadAssetMapAsync(distRoot, graphqlClient),
|
||||
]);
|
||||
assetMapSource = maybeAssetMapSource;
|
||||
const assets = filterCollectedAssetsByRequestedPlatforms(collectedAssets, requestedPlatform);
|
||||
realizedPlatforms = Object.keys(assets) as UpdatePublishPlatform[];
|
||||
|
||||
@@ -478,14 +488,19 @@ export default class UpdatePublish extends EasCommand {
|
||||
},
|
||||
};
|
||||
},
|
||||
{} as FingerprintInfoGroup
|
||||
{} as GraphqlFingerprintInfoGroup
|
||||
);
|
||||
|
||||
const assetMapGroup = assetMapSource
|
||||
? Object.fromEntries(platforms.map(platform => [platform, assetMapSource]))
|
||||
: null;
|
||||
|
||||
return {
|
||||
branchId: branch.id,
|
||||
updateInfoGroup: localUpdateInfoGroup,
|
||||
rolloutInfoGroup: localRolloutInfoGroup,
|
||||
fingerprintInfoGroup: transformedFingerprintInfoGroup,
|
||||
assetMapGroup,
|
||||
runtimeVersion,
|
||||
message: updateMessage,
|
||||
gitCommitHash,
|
||||
@@ -559,95 +574,143 @@ export default class UpdatePublish extends EasCommand {
|
||||
}
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput(getUpdateJsonInfosForUpdates(newUpdates));
|
||||
} else {
|
||||
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
'👉 Since multiple runtime versions are defined, multiple update groups have been published.'
|
||||
);
|
||||
}
|
||||
|
||||
for (const runtime of uniqBy(
|
||||
runtimeToPlatformsAndFingerprintInfoMapping,
|
||||
version => version.runtimeVersion
|
||||
)) {
|
||||
const newUpdatesForRuntimeVersion = newUpdates.filter(
|
||||
update => update.runtimeVersion === runtime.runtimeVersion
|
||||
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}.`
|
||||
);
|
||||
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 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 });
|
||||
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.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();
|
||||
if (isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)) {
|
||||
Log.warn(
|
||||
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
|
||||
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
|
||||
learnMoreMessage: 'Consider optimizing your usage of assets',
|
||||
dim: false,
|
||||
})}.`
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
if (
|
||||
isUploadedAssetCountAboveWarningThreshold(uploadedAssetCount, assetLimitPerUpdateGroup)
|
||||
) {
|
||||
Log.warn(
|
||||
`This update group contains ${uploadedAssetCount} assets and is nearing the server cap of ${assetLimitPerUpdateGroup}.\n` +
|
||||
`${learnMore('https://docs.expo.dev/eas-update/optimize-assets/', {
|
||||
learnMoreMessage: 'Consider optimizing your usage of assets',
|
||||
dim: false,
|
||||
})}.`
|
||||
);
|
||||
Log.addNewLineIfNone();
|
||||
}
|
||||
|
||||
const fingerprintsWithoutCompatibleBuilds = runtimeToCompatibleBuilds.find(
|
||||
({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion
|
||||
)?.fingerprintInfoGroupWithCompatibleBuilds;
|
||||
if (fingerprintsWithoutCompatibleBuilds) {
|
||||
const missingBuilds = Object.entries(fingerprintsWithoutCompatibleBuilds).filter(
|
||||
([_platform, fingerprintInfo]) => !fingerprintInfo.build
|
||||
);
|
||||
if (missingBuilds.length > 0) {
|
||||
const project = await AppQuery.byIdAsync(graphqlClient, projectId);
|
||||
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 {
|
||||
const nonInteractive = flags['non-interactive'] ?? false;
|
||||
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
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) {
|
||||
if (isAppleError(error)) {
|
||||
spinner.text = `TestFlight not ready, retrying in 25 seconds...`;
|
||||
spinner.text = `TestFlight still preparing, retrying in 10 seconds...`;
|
||||
|
||||
return error.data.errors.some(
|
||||
error => error.code === 'ENTITY_ERROR.RELATIONSHIP.INVALID'
|
||||
@@ -199,9 +199,10 @@ async function pollRetryAsync<T>(
|
||||
fn: () => Promise<T>,
|
||||
{
|
||||
shouldRetry,
|
||||
retries = 10,
|
||||
retries = 15,
|
||||
// 25 seconds was the minium interval I calculated when measuring against 5 second intervals.
|
||||
interval = 25000,
|
||||
// Switching to 10 seconds to account for days where Apple APIs are faster.
|
||||
interval = 10000,
|
||||
}: { shouldRetry?: (error: Error) => boolean; retries?: number; interval?: number } = {}
|
||||
): Promise<T> {
|
||||
let lastError: Error | null = null;
|
||||
|
||||
@@ -103,8 +103,21 @@ async function createFingerprintWithoutLoggingAsync(
|
||||
if (options.debug) {
|
||||
fingerprintOptions.debug = true;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/return-await
|
||||
return await Fingerprint.createFingerprintAsync(projectDir, fingerprintOptions);
|
||||
|
||||
return await withTemporaryEnvAsync(options.env ?? {}, () =>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,8 @@ export async function getFingerprintInfoFromLocalProjectForPlatformsAsync(
|
||||
projectDir: string,
|
||||
projectId: string,
|
||||
vcsClient: Client,
|
||||
platforms: AppPlatform[]
|
||||
platforms: AppPlatform[],
|
||||
{ env }: { env?: Record<string, string> } = {}
|
||||
): Promise<Fingerprint> {
|
||||
const workflows = await resolveWorkflowPerPlatformAsync(projectDir, vcsClient);
|
||||
const optionsFromWorkflow = getFingerprintOptionsFromWorkflow(platforms, workflows);
|
||||
@@ -24,7 +25,7 @@ export async function getFingerprintInfoFromLocalProjectForPlatformsAsync(
|
||||
...optionsFromWorkflow,
|
||||
platforms: platforms.map(appPlatformToString),
|
||||
debug: true,
|
||||
env: undefined,
|
||||
env,
|
||||
});
|
||||
if (!projectFingerprint) {
|
||||
throw new Error('Project fingerprints can only be computed for projects with SDK 52 or higher');
|
||||
@@ -45,6 +46,7 @@ export async function getFingerprintInfoFromLocalProjectForPlatformsAsync(
|
||||
|
||||
return projectFingerprint;
|
||||
}
|
||||
|
||||
function getFingerprintOptionsFromWorkflow(
|
||||
platforms: AppPlatform[],
|
||||
workflowsByPlatform: Record<Platform, Workflow>
|
||||
|
||||
+269
-9
@@ -131,6 +131,7 @@ export type Account = {
|
||||
googleServiceAccountKeys: Array<GoogleServiceAccountKey>;
|
||||
/** Android credentials for account */
|
||||
googleServiceAccountKeysPaginated: AccountGoogleServiceAccountKeysConnection;
|
||||
hasBuilds: Scalars['Boolean']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
isCurrent: Scalars['Boolean']['output'];
|
||||
isDisabled: Scalars['Boolean']['output'];
|
||||
@@ -147,10 +148,12 @@ export type Account = {
|
||||
owner?: Maybe<User>;
|
||||
/** Owning UserActor of this account if personal account */
|
||||
ownerUserActor?: Maybe<UserActor>;
|
||||
pendingSentryInstallation?: Maybe<PendingSentryInstallation>;
|
||||
profileImageUrl: Scalars['String']['output'];
|
||||
pushSecurityEnabled: Scalars['Boolean']['output'];
|
||||
/** @deprecated Legacy access tokens are deprecated */
|
||||
requiresAccessTokenForPushSecurity: Scalars['Boolean']['output'];
|
||||
sentryInstallation?: Maybe<SentryInstallation>;
|
||||
/** Snacks associated with this account */
|
||||
snacks: Array<Snack>;
|
||||
/** SSO configuration for this account */
|
||||
@@ -1022,6 +1025,7 @@ export enum AndroidBuildType {
|
||||
|
||||
export type AndroidBuilderEnvironmentInput = {
|
||||
bun?: InputMaybe<Scalars['String']['input']>;
|
||||
corepack?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
env?: InputMaybe<Scalars['JSONObject']['input']>;
|
||||
expoCli?: InputMaybe<Scalars['String']['input']>;
|
||||
image?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -1053,9 +1057,12 @@ export type AndroidFcmInput = {
|
||||
|
||||
export type 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;
|
||||
/** Delete an FCM credential */
|
||||
/** Delete an FCM V0/Legacy credential */
|
||||
deleteAndroidFcm: DeleteAndroidFcmResult;
|
||||
};
|
||||
|
||||
@@ -1318,6 +1325,7 @@ export type App = Project & {
|
||||
privacy: Scalars['String']['output'];
|
||||
/** @deprecated No longer supported */
|
||||
privacySetting: AppPrivacy;
|
||||
profileImageUrl?: Maybe<Scalars['String']['output']>;
|
||||
/**
|
||||
* Whether there have been any classic update publishes
|
||||
* @deprecated Classic updates have been deprecated.
|
||||
@@ -1342,6 +1350,7 @@ export type App = Project & {
|
||||
* @deprecated Classic updates have been deprecated.
|
||||
*/
|
||||
sdkVersion: Scalars['String']['output'];
|
||||
sentryProject?: Maybe<SentryProject>;
|
||||
slug: Scalars['String']['output'];
|
||||
/** EAS Submissions associated with this app */
|
||||
submissions: Array<Submission>;
|
||||
@@ -1525,6 +1534,7 @@ export type AppLikedByArgs = {
|
||||
export type AppRuntimesArgs = {
|
||||
after?: InputMaybe<Scalars['String']['input']>;
|
||||
before?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<RuntimeFilterInput>;
|
||||
first?: InputMaybe<Scalars['Int']['input']>;
|
||||
last?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
@@ -2540,6 +2550,21 @@ export type AscApiKeyInput = {
|
||||
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 = {
|
||||
__typename?: 'AssetMetadataResult';
|
||||
status: AssetMetadataStatus;
|
||||
@@ -2658,6 +2683,11 @@ export enum AuthProviderIdentifier {
|
||||
StubIdp = 'STUB_IDP'
|
||||
}
|
||||
|
||||
export type AverageMetrics = {
|
||||
__typename?: 'AverageMetrics';
|
||||
averageDownloadSize: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type BackgroundJobReceipt = {
|
||||
__typename?: 'BackgroundJobReceipt';
|
||||
account: Account;
|
||||
@@ -2749,6 +2779,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
/** @deprecated Use 'updateChannel' field instead. */
|
||||
channel?: Maybe<Scalars['String']['output']>;
|
||||
childBuild?: Maybe<Build>;
|
||||
cliVersion?: Maybe<Scalars['String']['output']>;
|
||||
completedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
customNodeVersion?: Maybe<Scalars['String']['output']>;
|
||||
@@ -2764,6 +2795,7 @@ export type Build = ActivityTimelineProjectActivity & BuildOrBuildJob & {
|
||||
gitCommitHash?: Maybe<Scalars['String']['output']>;
|
||||
gitCommitMessage?: Maybe<Scalars['String']['output']>;
|
||||
gitRef?: Maybe<Scalars['String']['output']>;
|
||||
/** @deprecated Use 'githubRepository' field instead */
|
||||
githubRepositoryOwnerAndName?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
/** Queue position is 1-indexed */
|
||||
@@ -3020,6 +3052,7 @@ export type BuildMetrics = {
|
||||
export enum BuildMode {
|
||||
Build = 'BUILD',
|
||||
Custom = 'CUSTOM',
|
||||
Local = 'LOCAL',
|
||||
Repack = 'REPACK',
|
||||
Resign = 'RESIGN'
|
||||
}
|
||||
@@ -3037,6 +3070,8 @@ export type BuildMutation = {
|
||||
createAndroidBuild: CreateBuildResult;
|
||||
/** Create an iOS build */
|
||||
createIosBuild: CreateBuildResult;
|
||||
/** Create a local build */
|
||||
createLocalBuild: CreateBuildResult;
|
||||
/** Delete an EAS Build build */
|
||||
deleteBuild: Build;
|
||||
/** Retry an Android EAS Build */
|
||||
@@ -3074,6 +3109,14 @@ export type BuildMutationCreateIosBuildArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationCreateLocalBuildArgs = {
|
||||
appId: Scalars['ID']['input'];
|
||||
artifactSource: LocalBuildArchiveSourceInput;
|
||||
job: LocalBuildJobInput;
|
||||
metadata?: InputMaybe<BuildMetadataInput>;
|
||||
};
|
||||
|
||||
|
||||
export type BuildMutationDeleteBuildArgs = {
|
||||
buildId: Scalars['ID']['input'];
|
||||
};
|
||||
@@ -3173,6 +3216,7 @@ export enum BuildPriority {
|
||||
export type BuildPublicData = {
|
||||
__typename?: 'BuildPublicData';
|
||||
artifacts: PublicArtifacts;
|
||||
buildMode?: Maybe<BuildMode>;
|
||||
distribution?: Maybe<DistributionType>;
|
||||
id: Scalars['ID']['output'];
|
||||
isForIosSimulator: Scalars['Boolean']['output'];
|
||||
@@ -3380,6 +3424,11 @@ export type CreateAccessTokenResponse = {
|
||||
token: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type CreateAndConfigureRepositoryInput = {
|
||||
appId: Scalars['ID']['input'];
|
||||
installationIdentifier: Scalars['Int']['input'];
|
||||
};
|
||||
|
||||
export type CreateAndroidSubmissionInput = {
|
||||
appId: Scalars['ID']['input'];
|
||||
archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
|
||||
@@ -3472,6 +3521,12 @@ export type CreateIosSubmissionInput = {
|
||||
submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
|
||||
};
|
||||
|
||||
export type CreateSentryProjectInput = {
|
||||
appId: Scalars['ID']['input'];
|
||||
sentryProjectId: Scalars['String']['input'];
|
||||
sentryProjectSlug: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type CreateSharedEnvironmentVariableInput = {
|
||||
environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
|
||||
fileName?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -3691,6 +3746,11 @@ export type DeleteSsoUserResult = {
|
||||
id: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type DeleteSentryProjectResult = {
|
||||
__typename?: 'DeleteSentryProjectResult';
|
||||
id: Scalars['ID']['output'];
|
||||
};
|
||||
|
||||
export type DeleteUpdateBranchResult = {
|
||||
__typename?: 'DeleteUpdateBranchResult';
|
||||
id: Scalars['ID']['output'];
|
||||
@@ -4001,6 +4061,8 @@ export enum EntityTypeName {
|
||||
CustomerEntity = 'CustomerEntity',
|
||||
GoogleServiceAccountKeyEntity = 'GoogleServiceAccountKeyEntity',
|
||||
IosAppCredentialsEntity = 'IosAppCredentialsEntity',
|
||||
LogRocketOrganizationEntity = 'LogRocketOrganizationEntity',
|
||||
LogRocketProjectEntity = 'LogRocketProjectEntity',
|
||||
UserInvitationEntity = 'UserInvitationEntity',
|
||||
UserPermissionEntity = 'UserPermissionEntity',
|
||||
WorkerCustomDomainEntity = 'WorkerCustomDomainEntity',
|
||||
@@ -4388,6 +4450,13 @@ export type GenerateLogRocketReplayTokenResult = {
|
||||
replayToken: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GenerateSentryTokenResult = {
|
||||
__typename?: 'GenerateSentryTokenResult';
|
||||
installationId: Scalars['ID']['output'];
|
||||
orgSlug: Scalars['String']['output'];
|
||||
token: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GetSignedAssetUploadSpecificationsResult = {
|
||||
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
||||
specifications: Array<Scalars['String']['output']>;
|
||||
@@ -4643,6 +4712,7 @@ export type 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. */
|
||||
configureEAS: BackgroundJobReceipt;
|
||||
createAndConfigureRepository: BackgroundJobReceipt;
|
||||
/** Create a GitHub repository for an App */
|
||||
createGitHubRepository: GitHubRepository;
|
||||
/** Delete a GitHub repository by ID */
|
||||
@@ -4655,6 +4725,11 @@ export type GitHubRepositoryMutationConfigureEasArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type GitHubRepositoryMutationCreateAndConfigureRepositoryArgs = {
|
||||
input: CreateAndConfigureRepositoryInput;
|
||||
};
|
||||
|
||||
|
||||
export type GitHubRepositoryMutationCreateGitHubRepositoryArgs = {
|
||||
githubRepositoryData: CreateGitHubRepositoryInput;
|
||||
};
|
||||
@@ -5039,6 +5114,7 @@ export type IosBuilderEnvironmentInput = {
|
||||
bun?: InputMaybe<Scalars['String']['input']>;
|
||||
bundler?: InputMaybe<Scalars['String']['input']>;
|
||||
cocoapods?: InputMaybe<Scalars['String']['input']>;
|
||||
corepack?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
env?: InputMaybe<Scalars['JSONObject']['input']>;
|
||||
expoCli?: InputMaybe<Scalars['String']['input']>;
|
||||
fastlane?: InputMaybe<Scalars['String']['input']>;
|
||||
@@ -5161,6 +5237,7 @@ export type IosSubmissionConfigInput = {
|
||||
ascApiKey?: InputMaybe<AscApiKeyInput>;
|
||||
ascApiKeyId?: InputMaybe<Scalars['String']['input']>;
|
||||
ascAppIdentifier: Scalars['String']['input'];
|
||||
groups?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
isVerboseFastlaneEnabled?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
};
|
||||
|
||||
@@ -5287,12 +5364,34 @@ export type LinkLogRocketOrganizationToExpoAccountInput = {
|
||||
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 = {
|
||||
appId: Scalars['ID']['input'];
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
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 = {
|
||||
__typename?: 'LogNameTypeMapping';
|
||||
publicName: Scalars['String']['output'];
|
||||
@@ -5695,6 +5794,15 @@ export type PaymentDetails = {
|
||||
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 {
|
||||
Admin = 'ADMIN',
|
||||
Own = 'OWN',
|
||||
@@ -5730,7 +5838,6 @@ export enum ProjectArchiveSourceType {
|
||||
Gcs = 'GCS',
|
||||
Git = 'GIT',
|
||||
None = 'NONE',
|
||||
S3 = 'S3',
|
||||
Url = 'URL'
|
||||
}
|
||||
|
||||
@@ -5761,6 +5868,7 @@ export type PublicArtifacts = {
|
||||
};
|
||||
|
||||
export type PublishUpdateGroupInput = {
|
||||
assetMapGroup?: InputMaybe<AssetMapGroup>;
|
||||
awaitingCodeSigningInfo?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
branchId: Scalars['String']['input'];
|
||||
environment?: InputMaybe<EnvironmentVariableEnvironment>;
|
||||
@@ -5797,6 +5905,7 @@ export type RequestsFilters = {
|
||||
method?: InputMaybe<Array<RequestMethod>>;
|
||||
os?: InputMaybe<Array<UserAgentOs>>;
|
||||
pathname?: InputMaybe<Scalars['String']['input']>;
|
||||
platform?: InputMaybe<Array<UserAgentPlatform>>;
|
||||
requestId?: InputMaybe<Array<Scalars['WorkerDeploymentRequestID']['input']>>;
|
||||
responseType?: InputMaybe<Array<ResponseType>>;
|
||||
status?: InputMaybe<Array<Scalars['Int']['input']>>;
|
||||
@@ -5945,7 +6054,7 @@ export type RootMutation = {
|
||||
androidAppBuildCredentials: AndroidAppBuildCredentialsMutation;
|
||||
/** Mutations that modify the credentials for an Android app */
|
||||
androidAppCredentials: AndroidAppCredentialsMutation;
|
||||
/** Mutations that modify an FCM credential */
|
||||
/** Mutations that modify an FCM V0/Legacy credential */
|
||||
androidFcm: AndroidFcmMutation;
|
||||
/** Mutations that modify a Keystore */
|
||||
androidKeystore: AndroidKeystoreMutation;
|
||||
@@ -6021,6 +6130,10 @@ export type RootMutation = {
|
||||
notificationSubscription: NotificationSubscriptionMutation;
|
||||
/** Mutations that create, update, and delete Robots */
|
||||
robot: RobotMutation;
|
||||
/** Mutations for Sentry installations */
|
||||
sentryInstallation: SentryInstallationMutation;
|
||||
/** Mutations for Sentry projects */
|
||||
sentryProject: SentryProjectMutation;
|
||||
/** Mutations that modify an EAS Submit submission */
|
||||
submission: SubmissionMutation;
|
||||
update: UpdateMutation;
|
||||
@@ -6211,6 +6324,7 @@ export type Runtime = {
|
||||
fingerprint?: Maybe<Fingerprint>;
|
||||
firstBuildCreatedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
isFingerprint: Scalars['Boolean']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
updates: AppUpdatesConnection;
|
||||
version: Scalars['String']['output'];
|
||||
@@ -6264,6 +6378,7 @@ export type RuntimeEdge = {
|
||||
export type RuntimeFilterInput = {
|
||||
/** Only return runtimes shared with this branch */
|
||||
branchId?: InputMaybe<Scalars['String']['input']>;
|
||||
runtimeVersions?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
};
|
||||
|
||||
export type RuntimeQuery = {
|
||||
@@ -6328,6 +6443,7 @@ export type SsoUser = Actor & UserActor & {
|
||||
preferences: UserPreferences;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
|
||||
profilePhoto: Scalars['String']['output'];
|
||||
/** Snacks associated with this account */
|
||||
snacks: Array<Snack>;
|
||||
@@ -6423,6 +6539,69 @@ export type SecondFactorRegenerateBackupCodesResult = {
|
||||
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 & {
|
||||
__typename?: 'Snack';
|
||||
/** Description of the Snack */
|
||||
@@ -6806,6 +6985,7 @@ export type Update = ActivityTimelineProjectActivity & {
|
||||
activityTimestamp: Scalars['DateTime']['output'];
|
||||
actor?: Maybe<Actor>;
|
||||
app: App;
|
||||
assetMapUrl?: Maybe<Scalars['String']['output']>;
|
||||
awaitingCodeSigningInfo: Scalars['Boolean']['output'];
|
||||
branch: UpdateBranch;
|
||||
branchId: Scalars['ID']['output'];
|
||||
@@ -7047,6 +7227,7 @@ export type UpdateInfoGroup = {
|
||||
|
||||
export type UpdateInsights = {
|
||||
__typename?: 'UpdateInsights';
|
||||
averageMetrics: AverageMetrics;
|
||||
cumulativeMetrics: CumulativeMetrics;
|
||||
id: Scalars['ID']['output'];
|
||||
totalUniqueUsers: Scalars['Int']['output'];
|
||||
@@ -7064,8 +7245,13 @@ export type UpdateInsightsTotalUniqueUsersArgs = {
|
||||
|
||||
export type UpdateMutation = {
|
||||
__typename?: 'UpdateMutation';
|
||||
/** Delete an EAS update group */
|
||||
/**
|
||||
* Delete an EAS update group
|
||||
* @deprecated Use scheduleUpdateGroupDeletion instead
|
||||
*/
|
||||
deleteUpdateGroup: DeleteUpdateGroupResult;
|
||||
/** Delete an EAS update group in the background */
|
||||
scheduleUpdateGroupDeletion: BackgroundJobReceipt;
|
||||
/** Set code signing info for an update */
|
||||
setCodeSigningInfo: Update;
|
||||
/** Set rollout percentage for an update */
|
||||
@@ -7078,6 +7264,11 @@ export type UpdateMutationDeleteUpdateGroupArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type UpdateMutationScheduleUpdateGroupDeletionArgs = {
|
||||
group: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type UpdateMutationSetCodeSigningInfoArgs = {
|
||||
codeSigningInfo: CodeSigningInfoInput;
|
||||
updateId: Scalars['ID']['input'];
|
||||
@@ -7154,6 +7345,7 @@ export type UploadSessionCreateAppScopedUploadSessionArgs = {
|
||||
|
||||
|
||||
export type UploadSessionCreateUploadSessionArgs = {
|
||||
filename?: InputMaybe<Scalars['String']['input']>;
|
||||
type: UploadSessionType;
|
||||
};
|
||||
|
||||
@@ -7162,9 +7354,11 @@ export enum UploadSessionType {
|
||||
EasBuildGcsProjectSources = 'EAS_BUILD_GCS_PROJECT_SOURCES',
|
||||
/** @deprecated Use EAS_BUILD_GCS_PROJECT_SOURCES instead. */
|
||||
EasBuildProjectSources = 'EAS_BUILD_PROJECT_SOURCES',
|
||||
EasShareGcsAppArchive = 'EAS_SHARE_GCS_APP_ARCHIVE',
|
||||
/** @deprecated Use EAS_SUBMIT_GCS_APP_ARCHIVE instead. */
|
||||
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE',
|
||||
EasSubmitGcsAppArchive = 'EAS_SUBMIT_GCS_APP_ARCHIVE',
|
||||
EasUpdateAssetsMetadata = 'EAS_UPDATE_ASSETS_METADATA',
|
||||
EasUpdateFingerprint = 'EAS_UPDATE_FINGERPRINT'
|
||||
}
|
||||
|
||||
@@ -7252,6 +7446,7 @@ export type User = Actor & UserActor & {
|
||||
preferences: UserPreferences;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
|
||||
profilePhoto: Scalars['String']['output'];
|
||||
/** Get all certified second factor authentication methods */
|
||||
secondFactorDevices: Array<UserSecondFactorDevice>;
|
||||
@@ -7357,6 +7552,7 @@ export type UserActor = {
|
||||
preferences: UserPreferences;
|
||||
/** Associated accounts */
|
||||
primaryAccount: Account;
|
||||
primaryAccountProfileImageUrl?: Maybe<Scalars['String']['output']>;
|
||||
profilePhoto: Scalars['String']['output'];
|
||||
/** Snacks associated with this user's personal account */
|
||||
snacks: Array<Snack>;
|
||||
@@ -7492,6 +7688,13 @@ export enum UserAgentOs {
|
||||
Windows = 'WINDOWS'
|
||||
}
|
||||
|
||||
export enum UserAgentPlatform {
|
||||
Android = 'ANDROID',
|
||||
Apple = 'APPLE',
|
||||
Unknown = 'UNKNOWN',
|
||||
Web = 'WEB'
|
||||
}
|
||||
|
||||
export type UserAppPinMutation = {
|
||||
__typename?: 'UserAppPinMutation';
|
||||
pinApp: Scalars['ID']['output'];
|
||||
@@ -7519,7 +7722,9 @@ export type UserAuditLog = {
|
||||
targetEntityMutationType: TargetEntityMutationType;
|
||||
targetEntityTypeName: UserEntityTypeName;
|
||||
targetEntityTypePublicName: Scalars['String']['output'];
|
||||
/** @deprecated Use userActor instead */
|
||||
user: User;
|
||||
userActor: UserActor;
|
||||
websiteMessage: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
@@ -8108,6 +8313,7 @@ export type WorkerDeploymentRequestNode = {
|
||||
method: Scalars['String']['output'];
|
||||
os?: Maybe<UserAgentOs>;
|
||||
pathname: Scalars['String']['output'];
|
||||
platform: UserAgentPlatform;
|
||||
region?: Maybe<Scalars['String']['output']>;
|
||||
requestId: Scalars['WorkerDeploymentRequestID']['output'];
|
||||
requestTimestamp: Scalars['DateTime']['output'];
|
||||
@@ -8126,6 +8332,8 @@ export type WorkerDeploymentRequests = {
|
||||
byCountry: Array<WorkerDeploymentRequestsCountryEdge>;
|
||||
byMethod: Array<WorkerDeploymentRequestsMethodEdge>;
|
||||
byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>;
|
||||
byPathname: Array<WorkerDeploymentRequestsPathnameEdge>;
|
||||
byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>;
|
||||
byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>;
|
||||
byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>;
|
||||
interval: Scalars['Int']['output'];
|
||||
@@ -8172,6 +8380,18 @@ 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 = {
|
||||
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||
orderBy?: InputMaybe<RequestsOrderBy>;
|
||||
@@ -8240,7 +8460,7 @@ export type WorkerDeploymentRequestsCacheStatusEdge = {
|
||||
|
||||
export type WorkerDeploymentRequestsContinentEdge = {
|
||||
__typename?: 'WorkerDeploymentRequestsContinentEdge';
|
||||
continent: ContinentCode;
|
||||
continent?: Maybe<ContinentCode>;
|
||||
node: WorkerDeploymentRequestsAggregationNode;
|
||||
};
|
||||
|
||||
@@ -8262,6 +8482,18 @@ export type WorkerDeploymentRequestsOperatingSystemEdge = {
|
||||
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 = {
|
||||
__typename?: 'WorkerDeploymentRequestsResponseTypeEdge';
|
||||
node: WorkerDeploymentRequestsAggregationNode;
|
||||
@@ -8282,6 +8514,8 @@ export type WorkerDeploymentRequestsTimeseriesEdge = {
|
||||
byCountry: Array<WorkerDeploymentRequestsCountryEdge>;
|
||||
byMethod: Array<WorkerDeploymentRequestsMethodEdge>;
|
||||
byOS: Array<WorkerDeploymentRequestsOperatingSystemEdge>;
|
||||
byPathname: Array<WorkerDeploymentRequestsPathnameEdge>;
|
||||
byPlatform: Array<WorkerDeploymentRequestsPlatformEdge>;
|
||||
byResponseType: Array<WorkerDeploymentRequestsResponseTypeEdge>;
|
||||
byStatusType: Array<WorkerDeploymentRequestsStatusTypeEdge>;
|
||||
node?: Maybe<WorkerDeploymentRequestsAggregationNode>;
|
||||
@@ -8405,9 +8639,11 @@ export enum WorkflowJobType {
|
||||
Build = 'BUILD',
|
||||
Custom = 'CUSTOM',
|
||||
Deploy = 'DEPLOY',
|
||||
Fingerprint = 'FINGERPRINT',
|
||||
GetBuild = 'GET_BUILD',
|
||||
MaestroTest = 'MAESTRO_TEST',
|
||||
RequireApproval = 'REQUIRE_APPROVAL',
|
||||
Slack = 'SLACK',
|
||||
Submission = 'SUBMISSION',
|
||||
Update = 'UPDATE'
|
||||
}
|
||||
@@ -8503,6 +8739,8 @@ export type WorkflowRun = ActivityTimelineProjectActivity & {
|
||||
sourceExpiresAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
status: WorkflowRunStatus;
|
||||
triggerEventType: WorkflowRunTriggerEventType;
|
||||
triggeringLabelName?: Maybe<Scalars['String']['output']>;
|
||||
triggeringSchedule?: Maybe<Scalars['String']['output']>;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
workflow: Workflow;
|
||||
workflowRevision?: Maybe<WorkflowRevision>;
|
||||
@@ -8570,11 +8808,13 @@ export enum WorkflowRunStatus {
|
||||
}
|
||||
|
||||
export enum WorkflowRunTriggerEventType {
|
||||
GithubPullRequestLabeled = 'GITHUB_PULL_REQUEST_LABELED',
|
||||
GithubPullRequestOpened = 'GITHUB_PULL_REQUEST_OPENED',
|
||||
GithubPullRequestReopened = 'GITHUB_PULL_REQUEST_REOPENED',
|
||||
GithubPullRequestSynchronize = 'GITHUB_PULL_REQUEST_SYNCHRONIZE',
|
||||
GithubPush = 'GITHUB_PUSH',
|
||||
Manual = 'MANUAL'
|
||||
Manual = 'MANUAL',
|
||||
Schedule = 'SCHEDULE'
|
||||
}
|
||||
|
||||
export type WorkflowRunsConnection = {
|
||||
@@ -8692,12 +8932,12 @@ export type AppInfoQueryVariables = Exact<{
|
||||
|
||||
export type AppInfoQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, fullName: string } } };
|
||||
|
||||
export type DeleteUpdateGroupMutationVariables = Exact<{
|
||||
export type ScheduleUpdateGroupDeletionMutationVariables = Exact<{
|
||||
group: Scalars['ID']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteUpdateGroupMutation = { __typename?: 'RootMutation', update: { __typename?: 'UpdateMutation', deleteUpdateGroup: { __typename?: 'DeleteUpdateGroupResult', group: string } } };
|
||||
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 CreateAndroidAppBuildCredentialsMutationVariables = Exact<{
|
||||
androidAppBuildCredentialsInput: AndroidAppBuildCredentialsInput;
|
||||
@@ -9252,6 +9492,16 @@ export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: 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<{
|
||||
contentTypes: Array<Scalars['String']['input']> | Scalars['String']['input'];
|
||||
}>;
|
||||
@@ -9304,6 +9554,7 @@ export type CreateIosSubmissionMutation = { __typename?: 'RootMutation', submiss
|
||||
|
||||
export type CreateUploadSessionMutationVariables = Exact<{
|
||||
type: UploadSessionType;
|
||||
filename?: InputMaybe<Scalars['String']['input']>;
|
||||
}>;
|
||||
|
||||
|
||||
@@ -9394,6 +9645,13 @@ 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 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<{
|
||||
appId: Scalars['String']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
@@ -9687,6 +9945,8 @@ 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 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 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 };
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
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,20 +20,22 @@ export interface SignedUrl {
|
||||
export const UploadSessionMutation = {
|
||||
async createUploadSessionAsync(
|
||||
graphqlClient: ExpoGraphqlClient,
|
||||
type: UploadSessionType
|
||||
type: UploadSessionType,
|
||||
filename?: string
|
||||
): Promise<SignedUrl> {
|
||||
const data = await withErrorHandlingAsync(
|
||||
graphqlClient
|
||||
.mutation<CreateUploadSessionMutation, CreateUploadSessionMutationVariables>(
|
||||
gql`
|
||||
mutation CreateUploadSessionMutation($type: UploadSessionType!) {
|
||||
mutation CreateUploadSessionMutation($type: UploadSessionType!, $filename: String) {
|
||||
uploadSession {
|
||||
createUploadSession(type: $type)
|
||||
createUploadSession(type: $type, filename: $filename)
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
type,
|
||||
filename,
|
||||
}
|
||||
)
|
||||
.toPromise()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const BackgroundJobReceiptNode = gql`
|
||||
fragment BackgroundJobReceiptData on BackgroundJobReceipt {
|
||||
id
|
||||
state
|
||||
tries
|
||||
willRetry
|
||||
resultId
|
||||
resultType
|
||||
resultData
|
||||
errorCode
|
||||
errorMessage
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
`;
|
||||
@@ -8,7 +8,7 @@ import { createAppleTasks } from './apple/tasks';
|
||||
import { getAppStoreAuthAsync } from './auth';
|
||||
import { createAppleWriter, getStaticConfigFilePath } from './config/resolve';
|
||||
import { MetadataDownloadError, MetadataValidationError } from './errors';
|
||||
import { subscribeTelemetry } from './utils/telemetry';
|
||||
import { subscribeTelemetryAsync } from './utils/telemetry';
|
||||
import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import Log from '../log';
|
||||
@@ -51,7 +51,7 @@ export async function downloadMetadataAsync({
|
||||
profile,
|
||||
});
|
||||
|
||||
const { unsubscribeTelemetry, executionId } = subscribeTelemetry(
|
||||
const { unsubscribeTelemetry, executionId } = await subscribeTelemetryAsync(
|
||||
analytics,
|
||||
MetadataEvent.APPLE_METADATA_DOWNLOAD,
|
||||
{ app, auth }
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getAppStoreAuthAsync } from './auth';
|
||||
import { createAppleReader, loadConfigAsync } from './config/resolve';
|
||||
import { MetadataConfig } from './config/schema';
|
||||
import { MetadataUploadError, MetadataValidationError, logMetadataValidationError } from './errors';
|
||||
import { subscribeTelemetry } from './utils/telemetry';
|
||||
import { subscribeTelemetryAsync } from './utils/telemetry';
|
||||
import { Analytics, MetadataEvent } from '../analytics/AnalyticsManager';
|
||||
import { CredentialsContext } from '../credentials/context';
|
||||
import Log from '../log';
|
||||
@@ -38,7 +38,7 @@ export async function uploadMetadataAsync({
|
||||
profile,
|
||||
});
|
||||
|
||||
const { unsubscribeTelemetry, executionId } = subscribeTelemetry(
|
||||
const { unsubscribeTelemetry, executionId } = await subscribeTelemetryAsync(
|
||||
analytics,
|
||||
MetadataEvent.APPLE_METADATA_UPLOAD,
|
||||
{ app, auth }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { App } from '@expo/apple-utils';
|
||||
|
||||
import { TelemetryContext, makeDataScrubber } from '../telemetry';
|
||||
import { TelemetryContext, makeDataScrubberAsync } from '../telemetry';
|
||||
|
||||
describe(makeDataScrubber, () => {
|
||||
describe(makeDataScrubberAsync, () => {
|
||||
const stub: TelemetryContext = {
|
||||
// Only the App ID is considered to be sensitive
|
||||
app: new App({} as any, 'SECRET_APP_ID', {} as any),
|
||||
@@ -18,25 +18,31 @@ describe(makeDataScrubber, () => {
|
||||
} as any,
|
||||
};
|
||||
|
||||
it('scrubs the app.id', () => {
|
||||
expect(makeDataScrubber(stub)('some text SECRET_APP_ID')).toBe('some text {APPLE_APP_ID}');
|
||||
it('scrubs the app.id', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('some text SECRET_APP_ID')).toBe(
|
||||
'some text {APPLE_APP_ID}'
|
||||
);
|
||||
});
|
||||
|
||||
it('scrubs the auth.username', () => {
|
||||
expect(makeDataScrubber(stub)('some text SECRET_USERNAME')).toBe('some text {APPLE_USERNAME}');
|
||||
it('scrubs the auth.username', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('some text SECRET_USERNAME')).toBe(
|
||||
'some text {APPLE_USERNAME}'
|
||||
);
|
||||
});
|
||||
|
||||
it('scrubs the auth.password', () => {
|
||||
expect(makeDataScrubber(stub)('SECRET_PASSWORD')).toBe('{APPLE_PASSWORD}');
|
||||
it('scrubs the auth.password', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('SECRET_PASSWORD')).toBe('{APPLE_PASSWORD}');
|
||||
});
|
||||
|
||||
it('scrubs the auth.context.token', () => {
|
||||
expect(makeDataScrubber(stub)('some text SECRET_TOKEN')).toBe('some text {APPLE_TOKEN}');
|
||||
it('scrubs the auth.context.token', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('some text SECRET_TOKEN')).toBe(
|
||||
'some text {APPLE_TOKEN}'
|
||||
);
|
||||
});
|
||||
|
||||
it('scrubs the auth.context.token when using token instances', () => {
|
||||
it('scrubs the auth.context.token when using token instances', async () => {
|
||||
const token = { getToken: () => 'SECRET_TOKEN_INSTANCE' } as any;
|
||||
const scrubber = makeDataScrubber({
|
||||
const scrubber = await makeDataScrubberAsync({
|
||||
...stub,
|
||||
auth: {
|
||||
...stub.auth,
|
||||
@@ -50,29 +56,31 @@ describe(makeDataScrubber, () => {
|
||||
expect(scrubber('some text SECRET_TOKEN_INSTANCE')).toBe('some text {APPLE_TOKEN}');
|
||||
});
|
||||
|
||||
it('scrubs the auth.context.teamId', () => {
|
||||
expect(makeDataScrubber(stub)('SECRET_TEAM_ID')).toBe('{APPLE_TEAM_ID}');
|
||||
it('scrubs the auth.context.teamId', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('SECRET_TEAM_ID')).toBe('{APPLE_TEAM_ID}');
|
||||
});
|
||||
|
||||
it('scrubs the auth.context.teamId', () => {
|
||||
expect(makeDataScrubber(stub)('some provider 1337')).toBe('some provider {APPLE_PROVIDER_ID}');
|
||||
it('scrubs the auth.context.teamId', async () => {
|
||||
expect((await makeDataScrubberAsync(stub))('some provider 1337')).toBe(
|
||||
'some provider {APPLE_PROVIDER_ID}'
|
||||
);
|
||||
});
|
||||
|
||||
it('scrubber returns string representation of falsy values', () => {
|
||||
const scrubber = makeDataScrubber(stub);
|
||||
it('scrubber returns string representation of falsy values', async () => {
|
||||
const scrubber = await makeDataScrubberAsync(stub);
|
||||
expect(scrubber(null)).toBe('null');
|
||||
expect(scrubber(undefined)).toBe('undefined');
|
||||
expect(scrubber(false)).toBe('false');
|
||||
});
|
||||
|
||||
it('scrubs multiple sensitive values at once', () => {
|
||||
expect(makeDataScrubber(stub)('SECRET_TOKEN SECRET_USERNAME SECRET_PASSWORD')).toBe(
|
||||
'{APPLE_TOKEN} {APPLE_USERNAME} {APPLE_PASSWORD}'
|
||||
);
|
||||
it('scrubs multiple sensitive values at once', async () => {
|
||||
expect(
|
||||
(await makeDataScrubberAsync(stub))('SECRET_TOKEN SECRET_USERNAME SECRET_PASSWORD')
|
||||
).toBe('{APPLE_TOKEN} {APPLE_USERNAME} {APPLE_PASSWORD}');
|
||||
});
|
||||
|
||||
it('scrubs json and transforms it to string', () => {
|
||||
const scrubber = makeDataScrubber(stub);
|
||||
it('scrubs json and transforms it to string', async () => {
|
||||
const scrubber = await makeDataScrubberAsync(stub);
|
||||
expect(scrubber({ foo: 'bar' })).toBe('{"foo":"bar"}');
|
||||
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.
|
||||
* Returns an execution ID to group all events of a single run together, and a unsubscribe function.
|
||||
*/
|
||||
export function subscribeTelemetry(
|
||||
export async function subscribeTelemetryAsync(
|
||||
analytics: Analytics,
|
||||
event: MetadataEvent,
|
||||
options: TelemetryContext
|
||||
): {
|
||||
): Promise<{
|
||||
/** Unsubscribe the telemetry from all apple-utils events */
|
||||
unsubscribeTelemetry: () => void;
|
||||
/** The unique id added to all telemetry events from a single execution */
|
||||
executionId: string;
|
||||
} {
|
||||
}> {
|
||||
const executionId = uuidv4();
|
||||
const scrubber = makeDataScrubber(options);
|
||||
const scrubber = await makeDataScrubberAsync(options);
|
||||
const { interceptors } = getRequestClient();
|
||||
|
||||
const responseInterceptorId = interceptors.response.use(
|
||||
@@ -68,8 +68,11 @@ export function subscribeTelemetry(
|
||||
}
|
||||
|
||||
/** Exposed for testing */
|
||||
export function makeDataScrubber({ app, auth }: TelemetryContext): <T>(data: T) => string {
|
||||
const token = getAuthTokenString(auth);
|
||||
export async function makeDataScrubberAsync({
|
||||
app,
|
||||
auth,
|
||||
}: TelemetryContext): Promise<<T>(data: T) => string> {
|
||||
const token = await getAuthTokenStringAsync(auth);
|
||||
const patterns: Record<string, RegExp | null> = {
|
||||
APPLE_APP_ID: new RegExp(app.id, 'gi'),
|
||||
APPLE_USERNAME: auth.username ? new RegExp(auth.username, 'gi') : null,
|
||||
@@ -98,13 +101,13 @@ export function makeDataScrubber({ app, auth }: TelemetryContext): <T>(data: T)
|
||||
};
|
||||
}
|
||||
|
||||
function getAuthTokenString(auth: TelemetryContext['auth']): string | null {
|
||||
async function getAuthTokenStringAsync(auth: TelemetryContext['auth']): Promise<string | null> {
|
||||
if (!auth.context?.token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof auth.context.token === 'object') {
|
||||
return auth.context.token.getToken();
|
||||
return await auth.context.token.getToken();
|
||||
}
|
||||
|
||||
return auth.context.token;
|
||||
|
||||
@@ -424,7 +424,7 @@ describe(collectAssetsAsync, () => {
|
||||
expect(await collectAssetsAsync(inputDir)).toEqual({
|
||||
android: {
|
||||
launchAsset: {
|
||||
fileExtension: '.bundle',
|
||||
fileExtension: '.js',
|
||||
contentType: 'application/javascript',
|
||||
path: `${inputDir}/bundles/android.js`,
|
||||
},
|
||||
@@ -432,7 +432,7 @@ describe(collectAssetsAsync, () => {
|
||||
},
|
||||
ios: {
|
||||
launchAsset: {
|
||||
fileExtension: '.bundle',
|
||||
fileExtension: '.js',
|
||||
contentType: 'application/javascript',
|
||||
path: `${inputDir}/bundles/ios.js`,
|
||||
},
|
||||
@@ -440,7 +440,7 @@ describe(collectAssetsAsync, () => {
|
||||
},
|
||||
web: {
|
||||
launchAsset: {
|
||||
fileExtension: '.bundle',
|
||||
fileExtension: '.js',
|
||||
contentType: 'application/javascript',
|
||||
path: `${inputDir}/bundles/web.js`,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -16,10 +16,18 @@ export async function validateMetroConfigForManagedWorkflowAsync(
|
||||
}
|
||||
|
||||
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) =>
|
||||
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(
|
||||
`It looks like that you are using a custom ${chalk.bold(
|
||||
'metro.config.js'
|
||||
|
||||
@@ -17,12 +17,14 @@ import { isModernExpoUpdatesCLIWithRuntimeVersionCommandSupportedAsync } from '.
|
||||
import { resolveRuntimeVersionUsingCLIAsync } from './resolveRuntimeVersionAsync';
|
||||
import { selectBranchOnAppAsync } from '../branch/queries';
|
||||
import { getDefaultBranchNameAsync } from '../branch/utils';
|
||||
import { fetchBuildsAsync } from '../commandUtils/builds';
|
||||
import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { PaginatedQueryOptions } from '../commandUtils/pagination';
|
||||
import { FingerprintOptions, createFingerprintsByKeyAsync } from '../fingerprint/cli';
|
||||
import {
|
||||
AppPlatform,
|
||||
AssetMetadataStatus,
|
||||
BuildFragment,
|
||||
PartialManifestAsset,
|
||||
UpdateRolloutInfo,
|
||||
UpdateRolloutInfoGroup,
|
||||
@@ -92,7 +94,7 @@ type UpdateInfoGroup = {
|
||||
};
|
||||
|
||||
// Partial copy of `@expo/dev-server` `BundleAssetWithFileHashes`
|
||||
type AssetMap = Record<
|
||||
export type AssetMap = Record<
|
||||
string,
|
||||
{
|
||||
httpServerLocation: string;
|
||||
@@ -372,7 +374,7 @@ export function filterCollectedAssetsByRequestedPlatforms(
|
||||
}
|
||||
|
||||
/** Try to load the asset map for logging the names of assets published */
|
||||
export async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> {
|
||||
async function loadAssetMapAsync(distRoot: string): Promise<AssetMap | null> {
|
||||
const assetMapPath = path.join(distRoot, 'assetmap.json');
|
||||
|
||||
if (!(await fs.pathExists(assetMapPath))) {
|
||||
@@ -419,7 +421,8 @@ export async function collectAssetsAsync(dir: string): Promise<CollectedAssets>
|
||||
for (const platform of Object.keys(metadata.fileMetadata) as ExpoConfigPlatform[]) {
|
||||
collectedAssets[platform] = {
|
||||
launchAsset: {
|
||||
fileExtension: '.bundle',
|
||||
// path.extname() returns an empty string when there's no extension so we use || to fall back to .bundle
|
||||
fileExtension: path.extname(metadata.fileMetadata[platform].bundle) || '.bundle',
|
||||
contentType: 'application/javascript',
|
||||
path: path.resolve(dir, metadata.fileMetadata[platform].bundle),
|
||||
},
|
||||
@@ -998,6 +1001,48 @@ export async function maybeCalculateFingerprintForRuntimeVersionInfoObjectsWitho
|
||||
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> = {
|
||||
android: 'Android',
|
||||
ios: 'iOS',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import spawnAsync from '@expo/spawn-async';
|
||||
import chalk from 'chalk';
|
||||
import fg from 'fast-glob';
|
||||
import semver from 'semver';
|
||||
|
||||
import Log from '../../log';
|
||||
@@ -32,6 +33,45 @@ 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> {
|
||||
const link = getAppStoreLink(appId);
|
||||
await spawnAsync(`open`, [link]);
|
||||
|
||||
@@ -20,7 +20,11 @@ export async function uploadFileAtPathToGCSAsync(
|
||||
path: string,
|
||||
handleProgressEvent: ProgressHandler = () => {}
|
||||
): Promise<string> {
|
||||
const signedUrl = await UploadSessionMutation.createUploadSessionAsync(graphqlClient, type);
|
||||
const signedUrl = await UploadSessionMutation.createUploadSessionAsync(
|
||||
graphqlClient,
|
||||
type,
|
||||
type === UploadSessionType.EasShareGcsAppArchive ? path : undefined
|
||||
);
|
||||
|
||||
await uploadWithSignedUrlWithProgressAsync(path, signedUrl, handleProgressEvent);
|
||||
return signedUrl.bucketKey;
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
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.');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,148 @@
|
||||
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
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -141,6 +141,53 @@ export async function createManifestAsync(
|
||||
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 {
|
||||
normalizedPath: string;
|
||||
path: string;
|
||||
@@ -150,11 +197,18 @@ interface WorkerFileEntry {
|
||||
/** Reads worker files while normalizing sourcemaps and providing normalized paths */
|
||||
async function* listWorkerFilesAsync(workerPath: string): AsyncGenerator<WorkerFileEntry> {
|
||||
for await (const file of listFilesRecursively(workerPath)) {
|
||||
yield {
|
||||
normalizedPath: file.normalizedPath,
|
||||
path: file.path,
|
||||
data: await fs.promises.readFile(file.path),
|
||||
};
|
||||
if (file.normalizedPath.endsWith('.map')) {
|
||||
const normalizedSourceMap = await normalizeSourceMapAsync(file);
|
||||
if (normalizedSourceMap) {
|
||||
yield normalizedSourceMap;
|
||||
}
|
||||
} else {
|
||||
yield {
|
||||
normalizedPath: file.normalizedPath,
|
||||
path: file.path,
|
||||
data: await fs.promises.readFile(file.path),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,8 +130,22 @@ export async function uploadAsync(params: UploadParams): Promise<UploadResult> {
|
||||
}
|
||||
|
||||
const getErrorMessageAsync = async (): Promise<string> => {
|
||||
const body = await response.json().catch(() => null);
|
||||
return body?.error ?? `Upload of "${filePath}" failed: ${response.statusText}`;
|
||||
const rayId = response.headers.get('cf-ray');
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
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 (
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@expo/eas-json",
|
||||
"description": "A library for interacting with eas.json",
|
||||
"version": "16.0.0",
|
||||
"version": "16.3.3",
|
||||
"author": "Expo <[email protected]>",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "7.23.5",
|
||||
"@expo/eas-build-job": "1.0.170",
|
||||
"@expo/eas-build-job": "1.0.173",
|
||||
"chalk": "4.1.2",
|
||||
"env-string": "1.0.1",
|
||||
"fs-extra": "11.2.0",
|
||||
|
||||
@@ -144,6 +144,12 @@
|
||||
"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."
|
||||
},
|
||||
"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": {
|
||||
"type": "string",
|
||||
"description": "The version of Yarn to use for the build. See: https://www.npmjs.com/package/yarn",
|
||||
@@ -237,6 +243,7 @@
|
||||
"enum": [
|
||||
"auto",
|
||||
"latest",
|
||||
"sdk-52",
|
||||
"sdk-51",
|
||||
"sdk-50",
|
||||
"sdk-49",
|
||||
@@ -249,6 +256,7 @@
|
||||
"markdownEnumDescriptions": [
|
||||
"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 recommended image for SDK 52 builds",
|
||||
"The recommended image for SDK 51 builds",
|
||||
"The recommended image for SDK 50 builds",
|
||||
"The recommended image for SDK 49 builds"
|
||||
|
||||
@@ -38,6 +38,7 @@ const CommonBuildProfileSchema = Joi.object({
|
||||
// build environment
|
||||
env: Joi.object().pattern(Joi.string(), Joi.string().empty(null)),
|
||||
node: Joi.string().empty(null).custom(semverCheck),
|
||||
corepack: Joi.boolean(),
|
||||
pnpm: Joi.string().empty(null).custom(semverCheck),
|
||||
bun: Joi.string().empty(null).custom(semverCheck),
|
||||
yarn: Joi.string().empty(null).custom(semverCheck),
|
||||
|
||||
@@ -35,6 +35,7 @@ export interface CommonBuildProfile {
|
||||
// build environment
|
||||
env?: Record<string, string>;
|
||||
node?: string;
|
||||
corepack?: boolean;
|
||||
pnpm?: string;
|
||||
bun?: string;
|
||||
yarn?: string;
|
||||
|
||||
@@ -1367,10 +1367,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
|
||||
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
|
||||
|
||||
"@expo/[email protected]1":
|
||||
version "2.1.11"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.11.tgz#4cd4552ba8f589bd8f3f5fdf7622d8ea94ac58f7"
|
||||
integrity sha512-rfwTPGlyFXhtCSdaKUOEu277tFfL3ulkL3tJKx6/TnRg1nK40a748VBTQZ8CczRrlpU49egJRYvjXBt/M11wWA==
|
||||
"@expo/[email protected]2":
|
||||
version "2.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@expo/apple-utils/-/apple-utils-2.1.12.tgz#9c40a6294820e59d8b1a677318dccb1ccefc5b00"
|
||||
integrity sha512-ugpL2URxNFxIRw943AIpX3dcvL5rhNCumpL8XTosYqZPyQQ7JRLVNd1m8FNyPg3pmFrz8M4tSucY2pt2ATuKOA==
|
||||
|
||||
"@expo/bunyan@^4.0.0":
|
||||
version "4.0.0"
|
||||
@@ -1473,10 +1473,10 @@
|
||||
slugify "^1.3.4"
|
||||
sucrase "3.35.0"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
version "1.0.170"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.170.tgz#9fa8e458119252f4dd628502c7181a309100d359"
|
||||
integrity sha512-B00HtoMss08V4G8Hz4z+UMriNekN+jHg4rE46TpN/N5JyzEk4rKN2zmOCL2VsQR348RqHLZxVEcSQvAbZW8heA==
|
||||
"@expo/[email protected]3":
|
||||
version "1.0.173"
|
||||
resolved "https://registry.yarnpkg.com/@expo/eas-build-job/-/eas-build-job-1.0.173.tgz#e19e0563743cc947fb5419fbeb063f03aed2a796"
|
||||
integrity sha512-OtayVwXQaQEjJkpeKYvB4N6lsXVX+QSEz5okNx/d+8esI2KCjxOy/hzaqKBTovkxRlmWgE3XLw8RhrjAaBY2Gw==
|
||||
dependencies:
|
||||
"@expo/logger" "1.0.117"
|
||||
joi "^17.13.1"
|
||||
@@ -1653,12 +1653,12 @@
|
||||
dependencies:
|
||||
cross-spawn "^7.0.3"
|
||||
|
||||
"@expo/[email protected]0":
|
||||
version "1.0.170"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.170.tgz#3168216659b54e9747dd66c27a4e32b2deebce77"
|
||||
integrity sha512-477NTNalpbBLvBNclgQTxh71h9QO3mcUPQvIBYsjSyWXO01w9N6Pfh6gY591pQrmmWrzWCv3fOFW9AU9a3TjYA==
|
||||
"@expo/[email protected]3":
|
||||
version "1.0.173"
|
||||
resolved "https://registry.yarnpkg.com/@expo/steps/-/steps-1.0.173.tgz#157bf104d3137012bf82b106d5bd0227fd6f67f1"
|
||||
integrity sha512-XT8+FsZWIzwK+/TGnPrHe0HMQrzRGjTsjVDx8Yt922tL5TkX69XeB/twA0PcPWBLMxq+JiKtFm68yPTPqjIMNg==
|
||||
dependencies:
|
||||
"@expo/eas-build-job" "1.0.170"
|
||||
"@expo/eas-build-job" "1.0.173"
|
||||
"@expo/logger" "1.0.117"
|
||||
"@expo/spawn-async" "^1.7.2"
|
||||
arg "^5.0.2"
|
||||
@@ -10968,6 +10968,11 @@ node-rsa@^1.1.1:
|
||||
dependencies:
|
||||
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:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
|
||||
@@ -12855,6 +12860,11 @@ set-function-name@^2.0.2:
|
||||
functions-have-names "^1.2.3"
|
||||
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:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
@@ -13236,16 +13246,7 @@ string-length@^5.0.1:
|
||||
char-regex "^2.0.0"
|
||||
strip-ansi "^7.0.1"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@@ -13401,7 +13402,7 @@ string_decoder@~1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@@ -13422,13 +13423,6 @@ strip-ansi@^6.0.0:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
||||
@@ -14635,16 +14629,7 @@ wordwrap@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
[email protected], wrap-ansi@^7.0.0:
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", [email protected], wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
||||
Reference in new issue
Block a user