Compare commits

..
Author SHA1 Message Date
Expo CI 714aec69c7 v18.8.1 2026-04-23 15:59:34 +00:00
Szymon Dziedzic bec6e670de [eas-cli] add eas simulator:start command (#3625) 2026-04-23 15:55:57 +00:00
19 changed files with 1478 additions and 723 deletions

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"version": "18.8.0",
"version": "18.8.1",
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+1 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/build-tools",
"version": "18.8.0",
"version": "18.8.1",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
"author": "Expo <[email protected]>",
@@ -39,7 +39,6 @@
"@expo/config-plugins": "55.0.7",
"@expo/downloader": "18.5.0",
"@expo/eas-build-job": "18.8.0",
"@expo/eas-json": "18.8.0",
"@expo/env": "^0.4.0",
"@expo/logger": "18.5.0",
"@expo/package-manager": "1.9.10",
@@ -27,7 +27,6 @@ import { createRepackBuildFunction } from './functions/repack';
import { createReportMaestroTestResultsFunction } from './functions/reportMaestroTestResults';
import { resolveAppleTeamIdFromCredentialsFunction } from './functions/resolveAppleTeamIdFromCredentials';
import { createResolveBuildConfigBuildFunction } from './functions/resolveBuildConfig';
import { createResolveSubmitConfigBuildFunction } from './functions/resolveSubmitConfig';
import {
createCacheStatsBuildFunction,
createRestoreBuildCacheFunction,
@@ -39,6 +38,7 @@ import { runGradleFunction } from './functions/runGradle';
import { createSaveBuildCacheFunction } from './functions/saveBuildCache';
import { createSaveCacheFunction } from './functions/saveCache';
import { createSendSlackMessageFunction } from './functions/sendSlackMessage';
import { createStartAgentDeviceRemoteSessionBuildFunction } from './functions/startAgentDeviceRemoteSession';
import { createStartAndroidEmulatorBuildFunction } from './functions/startAndroidEmulator';
import { createStartCuttlefishDeviceBuildFunction } from './functions/startCuttlefishDevice';
import { createStartIosSimulatorBuildFunction } from './functions/startIosSimulator';
@@ -78,6 +78,7 @@ export function getEasFunctions(ctx: CustomBuildContext): BuildFunction[] {
generateGymfileFromTemplateFunction(),
runFastlaneFunction(),
parseXcactivitylogFunction(),
createStartAgentDeviceRemoteSessionBuildFunction(),
createStartAndroidEmulatorBuildFunction(),
createStartCuttlefishDeviceBuildFunction(),
createStartIosSimulatorBuildFunction(),
@@ -89,7 +90,6 @@ export function getEasFunctions(ctx: CustomBuildContext): BuildFunction[] {
calculateEASUpdateRuntimeVersionFunction(),
createSubmissionEntityFunction(),
createResolveSubmitConfigBuildFunction(ctx),
createUploadToAscBuildFunction(),
createReportMaestroTestResultsFunction(ctx),
@@ -1,182 +0,0 @@
import { Platform, SubmissionConfig, SystemError, UserError } from '@expo/eas-build-job';
import { AndroidReleaseStatus, SubmitProfile } from '@expo/eas-json';
import { bunyan } from '@expo/logger';
import { asyncResult } from '@expo/results';
import spawn from '@expo/turtle-spawn';
import { BuildStepEnv } from '@expo/steps';
import fs from 'fs-extra';
import { graphql } from 'gql.tada';
import path from 'node:path';
import { z } from 'zod';
import { BuildInfo, ResolvedSubmitConfig } from './common';
import { CustomBuildContext } from '../../../customBuildContext';
const ANDROID_APP_CREDENTIALS_QUERY = graphql(`
query ResolveSubmitConfigAndroidAppCredentials(
$appId: String!
$applicationIdentifier: String
) {
app {
byId(appId: $appId) {
id
androidAppCredentials(filter: { applicationIdentifier: $applicationIdentifier }) {
id
googleServiceAccountKeyForSubmissions {
id
keyJson
}
}
}
}
}
`);
const AndroidReleaseStatusToSubmissionReleaseStatus: Record<
AndroidReleaseStatus,
SubmissionConfig.Android.ReleaseStatus
> = {
[AndroidReleaseStatus.completed]: SubmissionConfig.Android.ReleaseStatus.COMPLETED,
[AndroidReleaseStatus.draft]: SubmissionConfig.Android.ReleaseStatus.DRAFT,
[AndroidReleaseStatus.halted]: SubmissionConfig.Android.ReleaseStatus.HALTED,
[AndroidReleaseStatus.inProgress]: SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS,
};
export async function resolveAndroidSubmitConfigAsync({
artifactPath,
build,
ctx,
env,
logger,
profile,
workingDirectory,
}: {
artifactPath: string;
build: BuildInfo;
ctx: CustomBuildContext;
env: BuildStepEnv;
logger: bunyan;
profile: SubmitProfile<Platform.ANDROID>;
workingDirectory: string;
}): Promise<ResolvedSubmitConfig> {
const applicationId =
build.appIdentifier ??
profile.applicationId ??
(await readAndroidApplicationIdAsync(artifactPath, env, logger));
if (!applicationId) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_ANDROID_APPLICATION_ID_NOT_FOUND',
'Could not resolve Android applicationId from build metadata or artifact. Set applicationId in the Android submit profile, or pass an APK/AAB artifact with a readable package name.'
);
}
const googleServiceAccountKeyJson = profile.serviceAccountKeyPath
? await fs.readFile(path.resolve(workingDirectory, profile.serviceAccountKeyPath), 'utf8')
: await getGoogleServiceAccountKeyJsonAsync({
applicationId,
appId: build.appId,
ctx,
});
const releaseStatus = AndroidReleaseStatusToSubmissionReleaseStatus[profile.releaseStatus];
const baseConfigInput = {
changesNotSentForReview: profile.changesNotSentForReview,
googleServiceAccountKeyJson,
track: profile.track,
};
const configInput: z.input<typeof SubmissionConfig.Android.SchemaZ> =
releaseStatus === SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS
? {
...baseConfigInput,
releaseStatus,
rollout: profile.rollout,
}
: {
...baseConfigInput,
releaseStatus,
};
return {
appIdentifier: applicationId,
config: SubmissionConfig.Android.SchemaZ.parse(configInput),
platform: Platform.ANDROID,
};
}
async function getGoogleServiceAccountKeyJsonAsync({
applicationId,
appId,
ctx,
}: {
applicationId: string;
appId: string;
ctx: CustomBuildContext;
}): Promise<string> {
const credentialsResult = await ctx.graphqlClient
.query(ANDROID_APP_CREDENTIALS_QUERY, {
appId,
applicationIdentifier: applicationId,
})
.toPromise();
if (credentialsResult.error) {
throw credentialsResult.error;
}
const key =
credentialsResult.data?.app.byId.androidAppCredentials[0]
?.googleServiceAccountKeyForSubmissions;
if (!key) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_ANDROID_SERVICE_ACCOUNT_KEY_NOT_CONFIGURED',
`Google Service Account Key for submissions is not configured for ${applicationId}. Configure a Google Service Account Key for submissions in EAS credentials, or set serviceAccountKeyPath in the Android submit profile.`
);
}
if (!key.keyJson) {
throw new SystemError(`Google Service Account Key ${key.id} could not be resolved.`);
}
return key.keyJson;
}
async function readAndroidApplicationIdAsync(
artifactPath: string,
env: BuildStepEnv,
logger: bunyan
): Promise<string | undefined> {
if (artifactPath.endsWith('.apk')) {
return await readAndroidApplicationIdFromApkAsync(artifactPath, env, logger);
} else if (artifactPath.endsWith('.aab')) {
return await readAndroidApplicationIdFromAabAsync(artifactPath, env, logger);
}
return undefined;
}
async function readAndroidApplicationIdFromApkAsync(
apkPath: string,
env: BuildStepEnv,
logger: bunyan
): Promise<string | undefined> {
const aapt2Result = await asyncResult(spawn('aapt2', ['dump', 'packagename', apkPath], { env }));
if (aapt2Result.ok) {
return aapt2Result.value.stdout.trim();
}
logger.warn('Failed to read APK package name with aapt2.');
return undefined;
}
async function readAndroidApplicationIdFromAabAsync(
aabPath: string,
env: BuildStepEnv,
logger: bunyan
): Promise<string | undefined> {
const result = await asyncResult(
spawn('bundletool', ['dump', 'manifest', `--bundle=${aabPath}`, '--xpath=/manifest/@package'], {
env,
})
);
if (!result.ok) {
logger.warn('Failed to read AAB package name with bundletool.');
return undefined;
}
return result.value.stdout.trim();
}
@@ -1,84 +0,0 @@
import { Platform, SubmissionConfig, UserError } from '@expo/eas-build-job';
import { EasJsonAccessor, EasJsonUtils, SubmitProfile } from '@expo/eas-json';
import { graphql } from 'gql.tada';
import { CustomBuildContext } from '../../../customBuildContext';
export type AppPlatform = 'ANDROID' | 'IOS';
export const AppPlatformToPlatform: Record<AppPlatform, Platform> = {
ANDROID: Platform.ANDROID,
IOS: Platform.IOS,
};
export type BuildInfo = {
appIdentifier?: string | null;
appId: string;
buildId: string;
projectOwnerAccountId: string;
platform: Platform;
};
export type ResolvedSubmitConfig = {
appIdentifier?: string;
config: SubmissionConfig.Android | SubmissionConfig.Ios;
platform: Platform;
};
const BUILD_BY_ID_QUERY = graphql(`
query ResolveSubmitConfigBuildById($buildId: ID!) {
builds {
byId(buildId: $buildId) {
id
platform
appIdentifier
app {
id
ownerAccount {
id
}
}
}
}
}
`);
export async function getBuildInfoAsync(
ctx: CustomBuildContext,
buildId: string
): Promise<BuildInfo> {
const result = await ctx.graphqlClient.query(BUILD_BY_ID_QUERY, { buildId }).toPromise();
if (result.error) {
throw result.error;
}
const build = result.data?.builds.byId;
if (!build) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_BUILD_NOT_FOUND',
`Could not find build ${buildId}.`
);
}
return {
appIdentifier: build.appIdentifier,
appId: build.app.id,
buildId: build.id,
projectOwnerAccountId: build.app.ownerAccount.id,
platform: AppPlatformToPlatform[build.platform],
};
}
export async function getSubmitProfileAsync<T extends Platform>({
platform,
profileName,
workingDirectory,
}: {
platform: T;
profileName: string;
workingDirectory: string;
}): Promise<SubmitProfile<T>> {
return await EasJsonUtils.getSubmitProfileAsync(
EasJsonAccessor.fromProjectPath(workingDirectory),
platform,
profileName
);
}
@@ -1,139 +0,0 @@
import { Platform, SystemError } from '@expo/eas-build-job';
import { bunyan } from '@expo/logger';
import {
BuildFunction,
BuildStepEnv,
BuildStepInput,
BuildStepInputValueTypeName,
BuildStepOutput,
} from '@expo/steps';
import path from 'node:path';
import { z } from 'zod';
import { resolveAndroidSubmitConfigAsync } from './android';
import { ResolvedSubmitConfig, getBuildInfoAsync, getSubmitProfileAsync } from './common';
import { resolveIosSubmitConfigAsync } from './ios';
import { CustomBuildContext } from '../../../customBuildContext';
export function createResolveSubmitConfigBuildFunction(ctx: CustomBuildContext): BuildFunction {
return new BuildFunction({
namespace: 'eas',
id: 'resolve_submit_config',
name: 'Resolve submit config',
__metricsId: 'eas/resolve_submit_config',
inputProviders: [
BuildStepInput.createProvider({
id: 'build_id',
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
}),
BuildStepInput.createProvider({
id: 'profile',
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
}),
BuildStepInput.createProvider({
id: 'artifact_path',
required: true,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
}),
],
outputProviders: [
BuildStepOutput.createProvider({
id: 'config_json',
required: true,
}),
BuildStepOutput.createProvider({
id: 'platform',
required: true,
}),
BuildStepOutput.createProvider({
id: 'app_identifier',
required: false,
}),
],
fn: async (stepsCtx, { env, inputs, outputs }) => {
const buildId = z.string().uuid().parse(inputs.build_id.value);
const profileName = z.string().parse(inputs.profile.value);
const artifactPath = path.resolve(
stepsCtx.workingDirectory,
z.string().parse(inputs.artifact_path.value)
);
const { config, platform, appIdentifier } = await resolveSubmitConfigAsync({
artifactPath,
buildId,
ctx,
env,
logger: stepsCtx.logger,
profileName,
workingDirectory: stepsCtx.workingDirectory,
});
outputs.config_json.set(JSON.stringify(config));
outputs.platform.set(platform);
if (appIdentifier) {
outputs.app_identifier.set(appIdentifier);
}
},
});
}
export async function resolveSubmitConfigAsync({
artifactPath,
buildId,
ctx,
env,
logger,
profileName,
workingDirectory,
}: {
artifactPath: string;
buildId: string;
ctx: CustomBuildContext;
env: BuildStepEnv;
logger: bunyan;
profileName: string;
workingDirectory: string;
}): Promise<ResolvedSubmitConfig> {
logger.info('Resolving submit config...');
const build = await getBuildInfoAsync(ctx, buildId);
switch (build.platform) {
case Platform.ANDROID: {
const profile = await getSubmitProfileAsync({
platform: build.platform,
profileName,
workingDirectory,
});
return await resolveAndroidSubmitConfigAsync({
artifactPath,
build,
ctx,
env,
logger,
profile,
workingDirectory,
});
}
case Platform.IOS: {
const profile = await getSubmitProfileAsync({
platform: build.platform,
profileName,
workingDirectory,
});
return await resolveIosSubmitConfigAsync({
artifactPath,
appId: build.appId,
buildAppIdentifier: build.appIdentifier,
ctx,
env,
profile,
projectOwnerAccountId: build.projectOwnerAccountId,
workingDirectory,
});
}
default:
throw new SystemError(`Unsupported platform: ${build.platform}`);
}
}
@@ -1,214 +0,0 @@
import { Platform, SubmissionConfig, UserError } from '@expo/eas-build-job';
import { SubmitProfile } from '@expo/eas-json';
import { BuildStepEnv } from '@expo/steps';
import fs from 'fs-extra';
import { graphql } from 'gql.tada';
import path from 'node:path';
import { z } from 'zod';
import { readIpaInfoAsync } from '../readIpaInfo';
import { ResolvedSubmitConfig } from './common';
import { CustomBuildContext } from '../../../customBuildContext';
const APPLE_APP_IDENTIFIER_QUERY = graphql(`
query ResolveSubmitConfigAppleAppIdentifier(
$accountId: String!
$bundleIdentifier: String!
) {
account {
byId(accountId: $accountId) {
id
appleAppIdentifiers(bundleIdentifier: $bundleIdentifier) {
id
bundleIdentifier
}
}
}
}
`);
const IOS_APP_CREDENTIALS_QUERY = graphql(`
query ResolveSubmitConfigIosAppCredentials(
$appId: String!
$appleAppIdentifierId: String!
) {
app {
byId(appId: $appId) {
id
iosAppCredentials(filter: { appleAppIdentifierId: $appleAppIdentifierId }) {
id
appStoreConnectApiKeyForSubmissions {
id
issuerIdentifier
keyIdentifier
keyP8
}
}
}
}
}
`);
export async function resolveIosSubmitConfigAsync({
appId,
artifactPath,
buildAppIdentifier,
ctx,
env,
profile,
projectOwnerAccountId,
workingDirectory,
}: {
appId: string;
artifactPath: string;
buildAppIdentifier?: string | null;
ctx: CustomBuildContext;
env: BuildStepEnv;
profile: SubmitProfile<Platform.IOS>;
projectOwnerAccountId: string;
workingDirectory: string;
}): Promise<ResolvedSubmitConfig> {
const bundleIdentifier =
buildAppIdentifier ??
profile.bundleIdentifier ??
(await readIpaInfoAsync(artifactPath)).bundleIdentifier;
if (!profile.ascAppId) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_ASC_APP_ID_NOT_CONFIGURED',
'Set ascAppId in the submit profile in eas.json.'
);
}
const appSpecificPassword = env.EXPO_APPLE_APP_SPECIFIC_PASSWORD;
const configInput: z.input<typeof SubmissionConfig.Ios.SchemaZ> = {
ascAppIdentifier: profile.ascAppId,
groups: profile.groups,
...(appSpecificPassword
? {
appleAppSpecificPassword: validateAppSpecificPassword(appSpecificPassword),
appleIdUsername: requireAppleIdUsername(profile.appleId ?? env.EXPO_APPLE_ID),
}
: {
ascApiJsonKey: await getAscApiJsonKeyAsync({
accountId: projectOwnerAccountId,
appId,
bundleIdentifier,
ctx,
profile,
workingDirectory,
}),
}),
};
return {
appIdentifier: bundleIdentifier,
config: SubmissionConfig.Ios.SchemaZ.parse(configInput),
platform: Platform.IOS,
};
}
async function getAscApiJsonKeyAsync({
accountId,
appId,
bundleIdentifier,
ctx,
profile,
workingDirectory,
}: {
accountId: string;
appId: string;
bundleIdentifier?: string;
ctx: CustomBuildContext;
profile: SubmitProfile<Platform.IOS>;
workingDirectory: string;
}): Promise<string> {
// Prefer explicit local ASC API key fields from eas.json when all are present.
if (profile.ascApiKeyPath && profile.ascApiKeyIssuerId && profile.ascApiKeyId) {
return JSON.stringify({
issuer_id: profile.ascApiKeyIssuerId,
key: await fs.readFile(path.resolve(workingDirectory, profile.ascApiKeyPath), 'utf8'),
key_id: profile.ascApiKeyId,
});
}
if (profile.ascApiKeyPath || profile.ascApiKeyIssuerId || profile.ascApiKeyId) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_ASC_API_KEY_INCOMPLETE',
'ascApiKeyPath, ascApiKeyIssuerId and ascApiKeyId must all be defined in eas.json.'
);
}
if (!bundleIdentifier) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_BUNDLE_IDENTIFIER_NOT_FOUND',
'Could not resolve iOS bundleIdentifier from build metadata or artifact. Set bundleIdentifier in the iOS submit profile, or pass an IPA artifact with a readable bundle identifier.'
);
}
// Server credentials are keyed by the Apple App Identifier, so first map the bundle id to it.
const appleAppIdentifierResult = await ctx.graphqlClient
.query(APPLE_APP_IDENTIFIER_QUERY, {
accountId,
bundleIdentifier,
})
.toPromise();
if (appleAppIdentifierResult.error) {
throw appleAppIdentifierResult.error;
}
const appleAppIdentifierId =
appleAppIdentifierResult.data?.account.byId.appleAppIdentifiers[0]?.id;
if (!appleAppIdentifierId) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_APP_IDENTIFIER_NOT_CONFIGURED',
`Apple App Identifier is not configured for ${bundleIdentifier}. Configure the bundle identifier in EAS credentials for this account.`
);
}
// The app credentials record points at the ASC API key selected for submissions.
const credentialsResult = await ctx.graphqlClient
.query(IOS_APP_CREDENTIALS_QUERY, {
appId,
appleAppIdentifierId,
})
.toPromise();
if (credentialsResult.error) {
throw credentialsResult.error;
}
const ascApiKey =
credentialsResult.data?.app.byId.iosAppCredentials[0]?.appStoreConnectApiKeyForSubmissions;
if (!ascApiKey) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_ASC_API_KEY_NOT_CONFIGURED',
`App Store Connect API Key for submissions is not configured for ${bundleIdentifier}. Configure an App Store Connect API Key for submissions in EAS credentials, or set ascApiKeyPath, ascApiKeyIssuerId, and ascApiKeyId in the iOS submit profile.`
);
}
return JSON.stringify({
issuer_id: ascApiKey.issuerIdentifier,
key: ascApiKey.keyP8,
key_id: ascApiKey.keyIdentifier,
});
}
function validateAppSpecificPassword(password: string): string {
if (!/^[a-z]{4}-[a-z]{4}-[a-z]{4}-[a-z]{4}$/.test(password)) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_APP_SPECIFIC_PASSWORD_INVALID',
'EXPO_APPLE_APP_SPECIFIC_PASSWORD must be in the format xxxx-xxxx-xxxx-xxxx, where x is a lowercase letter.'
);
}
return password;
}
function requireAppleIdUsername(appleIdUsername?: string): string {
if (!appleIdUsername) {
throw new UserError(
'EAS_RESOLVE_SUBMIT_CONFIG_IOS_APPLE_ID_NOT_CONFIGURED',
'Set appleId in the submit profile or EXPO_APPLE_ID when using EXPO_APPLE_APP_SPECIFIC_PASSWORD.'
);
}
return appleIdUsername;
}
@@ -0,0 +1,267 @@
import { bunyan } from '@expo/logger';
import { BuildFunction, BuildStepInput, BuildStepInputValueTypeName } from '@expo/steps';
import spawn from '@expo/turtle-spawn';
import childProcess from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { sleepAsync } from '../../utils/retry';
const AGENT_DEVICE_REPO_URL = 'https://github.com/callstackincubator/agent-device.git';
const SRC_DIR = '/tmp/agent-device-src';
const RUN_DIR = '/tmp/agent-device';
const DAEMON_LOG = path.join(RUN_DIR, 'daemon.log');
const TUNNEL_LOG = path.join(RUN_DIR, 'cloudflared.log');
const DAEMON_JSON_PATH = path.join(os.homedir(), '.agent-device', 'daemon.json');
const XCODE_DEVELOPER_DIR = '/Applications/Xcode.app/Contents/Developer';
const STARTUP_TIMEOUT_MS = 60_000;
export function createStartAgentDeviceRemoteSessionBuildFunction(): BuildFunction {
return new BuildFunction({
namespace: 'eas',
id: 'start_agent_device_remote_session',
name: 'Start agent device remote session',
__metricsId: 'eas/start_agent_device_remote_session',
inputProviders: [
BuildStepInput.createProvider({
id: 'package_version',
required: false,
allowedValueTypeName: BuildStepInputValueTypeName.STRING,
}),
],
fn: async ({ logger }, { inputs, env }) => {
const packageVersion = inputs.package_version.value as string | undefined;
logger.info(`Starting agent-device remote session (version: ${packageVersion ?? 'latest'}).`);
logger.info(`Preparing runtime directory at ${RUN_DIR}.`);
await fs.promises.mkdir(RUN_DIR, { recursive: true });
logger.info(`Selecting Xcode developer directory: ${XCODE_DEVELOPER_DIR}.`);
await spawn('sudo', ['xcode-select', '-s', XCODE_DEVELOPER_DIR], { env, logger });
logger.info('Ensuring cloudflared is installed.');
await ensureCloudflaredInstalledAsync({ env, logger });
logger.info('Ensuring bun is installed.');
await ensureBunInstalledAsync({ env, logger });
logger.info(
packageVersion
? `Cloning agent-device @ v${packageVersion} into ${SRC_DIR}.`
: `Cloning agent-device (latest) into ${SRC_DIR}.`
);
await cloneAgentDeviceAsync({ packageVersion, env, logger });
logger.info('Installing agent-device dependencies.');
await spawn('bun', ['install', '--production'], {
cwd: SRC_DIR,
env,
logger,
});
logger.info(`Launching agent-device daemon (log file: ${DAEMON_LOG}).`);
await spawnDetachedAsync({
command: 'bun run src/daemon.ts',
cwd: SRC_DIR,
logFile: DAEMON_LOG,
env: { ...env, AGENT_DEVICE_DAEMON_SERVER_MODE: 'http' },
logger,
});
logger.info(`Waiting for daemon credentials at ${DAEMON_JSON_PATH}.`);
await waitForFileAsync({
filePath: DAEMON_JSON_PATH,
timeoutMs: STARTUP_TIMEOUT_MS,
description: 'agent-device daemon',
});
const { port: daemonPort, token: daemonToken } = readDaemonInfo(DAEMON_JSON_PATH);
logger.info(`Daemon is listening on port ${daemonPort}; loaded auth token.`);
logger.info(
`Starting cloudflared tunnel to http://localhost:${daemonPort} (log file: ${TUNNEL_LOG}).`
);
await spawnDetachedAsync({
command: `cloudflared tunnel --url "http://localhost:${daemonPort}"`,
logFile: TUNNEL_LOG,
env,
logger,
});
logger.info('Waiting for a public tunnel URL.');
const tunnelUrl = await waitForMatchInLogAsync({
logFile: TUNNEL_LOG,
pattern: /https:\/\/[a-z0-9-]+\.trycloudflare\.com/,
timeoutMs: STARTUP_TIMEOUT_MS,
description: 'cloudflared tunnel',
});
logger.info(`Tunnel is ready at ${tunnelUrl}.`);
logger.info('Emitting agent-device credentials for the CLI to pick up:');
logger.info(`export AGENT_DEVICE_DAEMON_BASE_URL="${tunnelUrl}"`);
logger.info(`export AGENT_DEVICE_DAEMON_AUTH_TOKEN="${daemonToken}"`);
logger.info('Remote session is live. Keeping the job alive until the session is stopped.');
// Keep the turtle job alive so the daemon and tunnel stay reachable
// until stopDeviceRunSession cancels the run.
await new Promise<never>(() => {});
},
});
}
async function ensureCloudflaredInstalledAsync({
env,
logger,
}: {
env: NodeJS.ProcessEnv;
logger: bunyan;
}): Promise<void> {
await ensureBrewPackageInstalledAsync({ name: 'cloudflared', env, logger });
}
async function ensureBunInstalledAsync({
env,
logger,
}: {
env: NodeJS.ProcessEnv;
logger: bunyan;
}): Promise<void> {
await ensureBrewPackageInstalledAsync({ name: 'bun', env, logger });
}
async function ensureBrewPackageInstalledAsync({
name,
env,
logger,
}: {
name: string;
env: NodeJS.ProcessEnv;
logger: bunyan;
}): Promise<void> {
await spawn(
'bash',
['-c', `command -v ${name} >/dev/null 2>&1 || HOMEBREW_NO_AUTO_UPDATE=1 brew install ${name}`],
{ env, logger }
);
}
async function cloneAgentDeviceAsync({
packageVersion,
env,
logger,
}: {
packageVersion: string | undefined;
env: NodeJS.ProcessEnv;
logger: bunyan;
}): Promise<void> {
const branchArgs = packageVersion ? ['--branch', `v${packageVersion}`] : [];
await spawn('git', ['clone', '--depth', '1', ...branchArgs, AGENT_DEVICE_REPO_URL, SRC_DIR], {
env,
logger,
});
}
async function spawnDetachedAsync({
command,
cwd,
logFile,
env,
logger,
}: {
command: string;
cwd?: string;
logFile: string;
env: NodeJS.ProcessEnv;
logger: bunyan;
}): Promise<void> {
// Launch the process fully detached so this function returns immediately and
// the grandchild survives the step. Stdio goes to a log file so the daemon
// output can be polled, and we unref so Node doesn't wait on it.
const fd = fs.openSync(logFile, 'a');
try {
const child = childProcess.spawn('bash', ['-c', command], {
cwd,
env,
detached: true,
stdio: ['ignore', fd, fd],
});
if (!child.pid) {
throw new Error(`Failed to spawn detached process: ${command}`);
}
child.unref();
logger.info(`Started detached process (pid ${child.pid}).`);
} finally {
fs.closeSync(fd);
}
}
async function waitForMatchInLogAsync({
logFile,
pattern,
timeoutMs,
description,
}: {
logFile: string;
pattern: RegExp;
timeoutMs: number;
description: string;
}): Promise<string> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const content = await readFileOrEmptyAsync(logFile);
const match = pattern.exec(content);
if (match) {
return match[1] ?? match[0];
}
await sleepAsync(1_000);
}
const tail = await readFileOrEmptyAsync(logFile);
throw new Error(
`Timed out waiting for ${description} to start. Last log contents:\n${tail || '<empty>'}`
);
}
async function waitForFileAsync({
filePath,
timeoutMs,
description,
}: {
filePath: string;
timeoutMs: number;
description: string;
}): Promise<void> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
try {
await fs.promises.access(filePath);
return;
} catch {
// not yet; keep polling
}
await sleepAsync(1_000);
}
throw new Error(`Timed out waiting for ${description} to write ${filePath}.`);
}
async function readFileOrEmptyAsync(filePath: string): Promise<string> {
try {
return await fs.promises.readFile(filePath, 'utf8');
} catch {
return '';
}
}
function readDaemonInfo(filePath: string): { port: number; token: string } {
const raw = fs.readFileSync(filePath, 'utf8');
const parsed = JSON.parse(raw) as unknown;
if (
!parsed ||
typeof parsed !== 'object' ||
typeof (parsed as { httpPort: unknown }).httpPort !== 'number' ||
typeof (parsed as { token: unknown }).token !== 'string'
) {
throw new Error(`Expected ${filePath} to contain { "httpPort": <number>, "token": "..." }.`);
}
const { httpPort, token } = parsed as { httpPort: number; token: string };
return { port: httpPort, token };
}
+90 -90
View File
@@ -180,7 +180,7 @@ ALIASES
$ eas login
```
_See code: [packages/eas-cli/src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -197,7 +197,7 @@ ALIASES
$ eas logout
```
_See code: [packages/eas-cli/src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:usage [ACCOUNT_NAME]`
@@ -219,7 +219,7 @@ DESCRIPTION
view account usage and billing for the current cycle
```
_See code: [packages/eas-cli/src/commands/account/usage.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/account/usage.ts)_
_See code: [packages/eas-cli/src/commands/account/usage.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/account/usage.ts)_
## `eas account:view`
@@ -236,7 +236,7 @@ ALIASES
$ eas whoami
```
_See code: [packages/eas-cli/src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -250,7 +250,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [packages/eas-cli/src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -302,7 +302,7 @@ DESCRIPTION
create a branch
```
_See code: [packages/eas-cli/src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -323,7 +323,7 @@ DESCRIPTION
delete a branch
```
_See code: [packages/eas-cli/src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -343,7 +343,7 @@ DESCRIPTION
list all branches
```
_See code: [packages/eas-cli/src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -363,7 +363,7 @@ DESCRIPTION
rename a branch
```
_See code: [packages/eas-cli/src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -386,7 +386,7 @@ DESCRIPTION
view a branch
```
_See code: [packages/eas-cli/src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -424,7 +424,7 @@ DESCRIPTION
start a build
```
_See code: [packages/eas-cli/src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -444,7 +444,7 @@ DESCRIPTION
cancel a build
```
_See code: [packages/eas-cli/src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -462,7 +462,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:delete [BUILD_ID]`
@@ -482,7 +482,7 @@ DESCRIPTION
delete a build
```
_See code: [packages/eas-cli/src/commands/build/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/delete.ts)_
## `eas build:dev`
@@ -504,7 +504,7 @@ 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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/dev.ts)_
## `eas build:download`
@@ -525,7 +525,7 @@ 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/v18.8.0/packages/eas-cli/src/commands/build/download.ts)_
_See code: [packages/eas-cli/src/commands/build/download.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/build/download.ts)_
## `eas build:inspect`
@@ -563,7 +563,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -603,7 +603,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:resign`
@@ -633,7 +633,7 @@ DESCRIPTION
re-sign a build archive
```
_See code: [packages/eas-cli/src/commands/build/resign.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/resign.ts)_
## `eas build:run`
@@ -659,7 +659,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/run.ts)_
## `eas build:submit`
@@ -711,7 +711,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/version/get.ts)_
## `eas build:version:set`
@@ -729,7 +729,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/version/set.ts)_
## `eas build:version:sync`
@@ -747,7 +747,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/version/sync.ts)_
## `eas build:view [BUILD_ID]`
@@ -764,7 +764,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -785,7 +785,7 @@ DESCRIPTION
create a channel
```
_See code: [packages/eas-cli/src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:delete [NAME]`
@@ -806,7 +806,7 @@ DESCRIPTION
Delete a channel
```
_See code: [packages/eas-cli/src/commands/channel/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/channel/delete.ts)_
_See code: [packages/eas-cli/src/commands/channel/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/channel/delete.ts)_
## `eas channel:edit [NAME]`
@@ -828,7 +828,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:insights`
@@ -852,7 +852,7 @@ DESCRIPTION
display adoption, crash, and unique-user insights for a channel + runtime version
```
_See code: [packages/eas-cli/src/commands/channel/insights.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/channel/insights.ts)_
_See code: [packages/eas-cli/src/commands/channel/insights.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/channel/insights.ts)_
## `eas channel:list`
@@ -872,7 +872,7 @@ DESCRIPTION
list all channels
```
_See code: [packages/eas-cli/src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:pause [NAME]`
@@ -894,7 +894,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/pause.ts)_
## `eas channel:resume [NAME]`
@@ -916,7 +916,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/resume.ts)_
## `eas channel:rollout [CHANNEL]`
@@ -951,7 +951,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/rollout.ts)_
## `eas channel:view [NAME]`
@@ -974,7 +974,7 @@ DESCRIPTION
view a channel
```
_See code: [packages/eas-cli/src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -995,7 +995,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/v18.8.0/packages/eas-cli/src/commands/config.ts)_
_See code: [packages/eas-cli/src/commands/config.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -1012,7 +1012,7 @@ DESCRIPTION
manage credentials
```
_See code: [packages/eas-cli/src/commands/credentials/index.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/credentials/index.ts)_
## `eas credentials:configure-build`
@@ -1030,7 +1030,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/credentials/configure-build.ts)_
## `eas deploy [options]`
@@ -1059,7 +1059,7 @@ ALIASES
$ eas worker:deploy
```
_See code: [packages/eas-cli/src/commands/deploy/index.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/deploy/index.ts)_
## `eas deploy:alias`
@@ -1084,7 +1084,7 @@ ALIASES
$ eas deploy:promote
```
_See code: [packages/eas-cli/src/commands/deploy/alias/index.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/deploy/alias/index.ts)_
_See code: [packages/eas-cli/src/commands/deploy/alias/index.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/deploy/alias/index.ts)_
## `eas deploy:alias:delete [ALIAS_NAME]`
@@ -1105,7 +1105,7 @@ ALIASES
$ eas worker:alias:delete
```
_See code: [packages/eas-cli/src/commands/deploy/alias/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/deploy/alias/delete.ts)_
_See code: [packages/eas-cli/src/commands/deploy/alias/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/deploy/alias/delete.ts)_
## `eas deploy:delete [DEPLOYMENT_ID]`
@@ -1126,7 +1126,7 @@ ALIASES
$ eas worker:delete
```
_See code: [packages/eas-cli/src/commands/deploy/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/deploy/delete.ts)_
_See code: [packages/eas-cli/src/commands/deploy/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/deploy/delete.ts)_
## `eas deploy:promote`
@@ -1163,7 +1163,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -1183,7 +1183,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -1204,7 +1204,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:rename`
@@ -1225,7 +1225,7 @@ DESCRIPTION
rename a registered device
```
_See code: [packages/eas-cli/src/commands/device/rename.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/device/rename.ts)_
## `eas device:view [UDID]`
@@ -1239,7 +1239,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -1253,7 +1253,7 @@ DESCRIPTION
display environment info
```
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [packages/eas-cli/src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas env:create [ENVIRONMENT]`
@@ -1285,7 +1285,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/create.ts)_
## `eas env:delete [ENVIRONMENT]`
@@ -1311,7 +1311,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/delete.ts)_
## `eas env:exec ENVIRONMENT BASH_COMMAND`
@@ -1333,7 +1333,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/exec.ts)_
## `eas env:get [ENVIRONMENT]`
@@ -1361,7 +1361,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/get.ts)_
## `eas env:list [ENVIRONMENT]`
@@ -1389,7 +1389,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/list.ts)_
## `eas env:pull [ENVIRONMENT]`
@@ -1412,7 +1412,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/pull.ts)_
## `eas env:push [ENVIRONMENT]`
@@ -1434,7 +1434,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/push.ts)_
## `eas env:update [ENVIRONMENT]`
@@ -1468,7 +1468,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/env/update.ts)_
## `eas fingerprint:compare [HASH1] [HASH2]`
@@ -1512,7 +1512,7 @@ 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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/fingerprint/compare.ts)_
## `eas fingerprint:generate`
@@ -1543,7 +1543,7 @@ EXAMPLES
$ 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/v18.8.0/packages/eas-cli/src/commands/fingerprint/generate.ts)_
_See code: [packages/eas-cli/src/commands/fingerprint/generate.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/fingerprint/generate.ts)_
## `eas help [COMMAND]`
@@ -1652,7 +1652,7 @@ DESCRIPTION
validate the local store configuration
```
_See code: [packages/eas-cli/src/commands/metadata/lint.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/metadata/lint.ts)_
## `eas metadata:pull`
@@ -1670,7 +1670,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/metadata/pull.ts)_
## `eas metadata:push`
@@ -1688,7 +1688,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/metadata/push.ts)_
## `eas new [PATH]`
@@ -1740,7 +1740,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/v18.8.0/packages/eas-cli/src/commands/open.ts)_
_See code: [packages/eas-cli/src/commands/open.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/open.ts)_
## `eas project:info`
@@ -1754,7 +1754,7 @@ DESCRIPTION
information about the current project
```
_See code: [packages/eas-cli/src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -1777,7 +1777,7 @@ ALIASES
$ eas init
```
_See code: [packages/eas-cli/src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/project/init.ts)_
## `eas project:new [PATH]`
@@ -1801,7 +1801,7 @@ ALIASES
$ eas new
```
_See code: [packages/eas-cli/src/commands/project/new.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/project/new.ts)_
_See code: [packages/eas-cli/src/commands/project/new.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/project/new.ts)_
## `eas project:onboarding [TARGET_PROJECT_DIRECTORY]`
@@ -1819,7 +1819,7 @@ ALIASES
$ eas onboarding
```
_See code: [packages/eas-cli/src/commands/project/onboarding.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/project/onboarding.ts)_
## `eas submit`
@@ -1852,7 +1852,7 @@ ALIASES
$ eas build:submit
```
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [packages/eas-cli/src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -1895,7 +1895,7 @@ DESCRIPTION
publish an update group
```
_See code: [packages/eas-cli/src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -1916,7 +1916,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -1937,7 +1937,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:edit [GROUPID]`
@@ -1962,7 +1962,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/edit.ts)_
## `eas update:insights GROUPID`
@@ -1989,7 +1989,7 @@ DESCRIPTION
display launch, crash, unique-user, and size insights for an update group
```
_See code: [packages/eas-cli/src/commands/update/insights.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/update/insights.ts)_
_See code: [packages/eas-cli/src/commands/update/insights.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/update/insights.ts)_
## `eas update:list`
@@ -2016,7 +2016,7 @@ DESCRIPTION
view the recent updates
```
_See code: [packages/eas-cli/src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:republish`
@@ -2054,7 +2054,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/republish.ts)_
## `eas update:revert-update-rollout`
@@ -2082,7 +2082,7 @@ DESCRIPTION
revert a rollout update for a project
```
_See code: [packages/eas-cli/src/commands/update/revert-update-rollout.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/update/revert-update-rollout.ts)_
_See code: [packages/eas-cli/src/commands/update/revert-update-rollout.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/update/revert-update-rollout.ts)_
## `eas update:roll-back-to-embedded`
@@ -2112,7 +2112,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/roll-back-to-embedded.ts)_
## `eas update:rollback`
@@ -2133,7 +2133,7 @@ DESCRIPTION
instead execute "eas update:republish" or "eas update:roll-back-to-embedded".
```
_See code: [packages/eas-cli/src/commands/update/rollback.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/rollback.ts)_
## `eas update:view GROUPID`
@@ -2157,7 +2157,7 @@ DESCRIPTION
update group details
```
_See code: [packages/eas-cli/src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/update/view.ts)_
## `eas upload`
@@ -2178,7 +2178,7 @@ DESCRIPTION
upload a local build and generate a sharable link
```
_See code: [packages/eas-cli/src/commands/upload.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/upload.ts)_
_See code: [packages/eas-cli/src/commands/upload.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/upload.ts)_
## `eas webhook:create`
@@ -2200,7 +2200,7 @@ DESCRIPTION
create a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -2220,7 +2220,7 @@ DESCRIPTION
delete a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -2239,7 +2239,7 @@ DESCRIPTION
list webhooks
```
_See code: [packages/eas-cli/src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -2262,7 +2262,7 @@ DESCRIPTION
update a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -2279,7 +2279,7 @@ DESCRIPTION
view a webhook
```
_See code: [packages/eas-cli/src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
@@ -2400,7 +2400,7 @@ DESCRIPTION
to cancel.
```
_See code: [packages/eas-cli/src/commands/workflow/cancel.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/workflow/cancel.ts)_
_See code: [packages/eas-cli/src/commands/workflow/cancel.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/workflow/cancel.ts)_
## `eas workflow:create [NAME]`
@@ -2420,7 +2420,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/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/workflow/create.ts)_
## `eas workflow:logs [ID]`
@@ -2444,7 +2444,7 @@ DESCRIPTION
If no ID is passed in, you will be prompted to select from recent workflow runs for the current project.
```
_See code: [packages/eas-cli/src/commands/workflow/logs.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/workflow/logs.ts)_
_See code: [packages/eas-cli/src/commands/workflow/logs.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/workflow/logs.ts)_
## `eas workflow:run FILE`
@@ -2484,7 +2484,7 @@ FLAG DESCRIPTIONS
Exit codes: 0 = success, 11 = failure, 12 = canceled, 13 = wait aborted.
```
_See code: [packages/eas-cli/src/commands/workflow/run.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/workflow/run.ts)_
## `eas workflow:runs`
@@ -2506,7 +2506,7 @@ DESCRIPTION
list recent workflow runs for this project, with their IDs, statuses, and timestamps
```
_See code: [packages/eas-cli/src/commands/workflow/runs.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/workflow/runs.ts)_
_See code: [packages/eas-cli/src/commands/workflow/runs.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/workflow/runs.ts)_
## `eas workflow:status [WORKFLOW_RUN_ID]`
@@ -2534,7 +2534,7 @@ FLAG DESCRIPTIONS
Exit codes: 0 = success, 11 = failure, 12 = canceled, 13 = wait aborted.
```
_See code: [packages/eas-cli/src/commands/workflow/status.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/workflow/status.ts)_
_See code: [packages/eas-cli/src/commands/workflow/status.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/workflow/status.ts)_
## `eas workflow:validate PATH`
@@ -2554,7 +2554,7 @@ DESCRIPTION
validate a workflow configuration yaml file
```
_See code: [packages/eas-cli/src/commands/workflow/validate.ts](https://github.com/expo/eas-cli/blob/v18.8.0/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/v18.8.1/packages/eas-cli/src/commands/workflow/validate.ts)_
## `eas workflow:view [ID]`
@@ -2576,5 +2576,5 @@ DESCRIPTION
workflow runs for the current project.
```
_See code: [packages/eas-cli/src/commands/workflow/view.ts](https://github.com/expo/eas-cli/blob/v18.8.0/packages/eas-cli/src/commands/workflow/view.ts)_
_See code: [packages/eas-cli/src/commands/workflow/view.ts](https://github.com/expo/eas-cli/blob/v18.8.1/packages/eas-cli/src/commands/workflow/view.ts)_
<!-- commandsstop -->
+648
View File
@@ -4534,6 +4534,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "firstProjectCreatedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "firstSubmissionCompletedAt",
"description": null,
@@ -4546,6 +4558,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "firstUpdateCreatedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasConfiguredUpdate",
"description": null,
@@ -4562,6 +4586,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasConfiguredWorkflow",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hasTeamMembers",
"description": null,
@@ -19450,6 +19490,73 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AppleDeviceRegistrationRequestPublicData",
"description": "Publicly visible data for an AppleDeviceRegistrationRequest.",
"fields": [
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AppleDeviceRegistrationRequestPublicDataQuery",
"description": null,
"fields": [
{
"name": "byId",
"description": "Get AppleDeviceRegistrationRequest public data by ID",
"args": [
{
"name": "id",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "OBJECT",
"name": "AppleDeviceRegistrationRequestPublicData",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AppleDeviceRegistrationRequestQuery",
@@ -28553,6 +28660,39 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "sendConvexTeamInviteToVerifiedEmail",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "SendConvexTeamInviteToVerifiedEmailInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
@@ -29098,6 +29238,77 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateDeviceRunSessionInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "packageVersion",
"description": "The version of the package backing the device run session (e.g. \"0.1.3-alpha.3\").\nIf omitted, consumers treat the session as pinned to \"latest\".",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "platform",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AppPlatform",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "type",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "DeviceRunSessionType",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateEchoChatInput",
@@ -33599,6 +33810,362 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeviceRunSession",
"description": null,
"fields": [
{
"name": "app",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "App",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createdAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "finishedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "initiatingActor",
"description": null,
"args": [],
"type": {
"kind": "INTERFACE",
"name": "Actor",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "packageVersion",
"description": "The version of the package backing the device run session. Null means the session is\npinned to \"latest\" at the consumer side.",
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "platform",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AppPlatform",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "startedAt",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "status",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "DeviceRunSessionStatus",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "turtleJobRun",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "JobRun",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "type",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "DeviceRunSessionType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeviceRunSessionMutation",
"description": null,
"fields": [
{
"name": "createDeviceRunSession",
"description": "Create a device run session",
"args": [
{
"name": "deviceRunSessionInput",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "CreateDeviceRunSessionInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeviceRunSession",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "stopDeviceRunSession",
"description": "Stop a device run session",
"args": [
{
"name": "deviceRunSessionId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeviceRunSession",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DeviceRunSessionQuery",
"description": null,
"fields": [
{
"name": "byId",
"description": null,
"args": [
{
"name": "deviceRunSessionId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeviceRunSession",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "DeviceRunSessionStatus",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "ERRORED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "IN_PROGRESS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "NEW",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "STOPPED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "DeviceRunSessionType",
"description": null,
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "AGENT_DEVICE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DiscordUser",
@@ -38546,6 +39113,12 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "ConvexTeamConnectionEntity",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CustomerEntity",
"description": null,
@@ -53861,6 +54434,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deviceRunSession",
"description": "Mutations that create and stop device run sessions",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeviceRunSessionMutation",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "discordUser",
"description": "Mutations for Discord users",
@@ -54882,6 +55471,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "appleDeviceRegistrationRequestPublicData",
"description": "Top-level query object for querying AppleDeviceRegistrationRequest publicly.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AppleDeviceRegistrationRequestPublicDataQuery",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "appleDistributionCertificate",
"description": "Top-level query object for querying Apple distribution certificates.",
@@ -55082,6 +55687,22 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "deviceRunSessions",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DeviceRunSessionQuery",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "echoChat",
"description": "Top-level query object for querying Echo chats.",
@@ -57587,6 +58208,33 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "SendConvexTeamInviteToVerifiedEmailInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "convexTeamConnectionId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "SentryInstallation",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eas-cli",
"version": "18.8.0",
"version": "18.8.1",
"description": "EAS command line tool",
"keywords": [
"cli",
+18
View File
@@ -64,6 +64,24 @@ export function getWorkflowRunUrl(
).toString();
}
/**
* @deprecated Links to the raw job-run page; prefer a higher-level URL (e.g. the workflow run
* or the feature-specific dashboard) that gives users more context. Use this only for internal
* tooling where no richer URL exists.
*/
export function getBareJobRunUrl(
accountName: string,
projectName: string,
jobRunId: string
): string {
return new URL(
`/accounts/${encodeURIComponent(accountName)}/projects/${encodeURIComponent(
projectName
)}/job-runs/${encodeURIComponent(jobRunId)}`,
getExpoWebsiteBaseUrl()
).toString();
}
export function getProjectGitHubSettingsUrl(accountName: string, projectName: string): string {
return new URL(
`/accounts/${encodeURIComponent(accountName)}/projects/${encodeURIComponent(
@@ -0,0 +1,252 @@
import { Flags } from '@oclif/core';
import { getBareJobRunUrl } from '../../build/utils/url';
import EasCommand from '../../commandUtils/EasCommand';
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
import {
AppPlatform,
DeviceRunSessionStatus,
DeviceRunSessionType,
JobRunStatus,
} from '../../graphql/generated';
import { DeviceRunSessionMutation } from '../../graphql/mutations/DeviceRunSessionMutation';
import { DeviceRunSessionQuery } from '../../graphql/queries/DeviceRunSessionQuery';
import Log, { link } from '../../log';
import { ora } from '../../ora';
import { sleepAsync } from '../../utils/promise';
import nullthrows from 'nullthrows';
const POLL_INTERVAL_MS = 5_000; // 5 seconds
const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // 15 minutes
// Mapping enum → CLI flag value. Declared as Record<DeviceRunSessionType, string>
// so adding a new enum value in codegen fails the build until it is wired up here.
const DEVICE_RUN_SESSION_TYPE_FLAG_VALUES: Record<DeviceRunSessionType, string> = {
[DeviceRunSessionType.AgentDevice]: 'agent-device',
};
const DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE = Object.fromEntries(
(Object.entries(DEVICE_RUN_SESSION_TYPE_FLAG_VALUES) as [DeviceRunSessionType, string][]).map(
([type, value]) => [value, type]
)
) as Record<string, DeviceRunSessionType>;
type ReadinessResult = { ready: true; message: string } | { ready: false };
type ReadinessChecker = (logMessages: readonly string[]) => ReadinessResult;
export default class SimulatorStart extends EasCommand {
static override hidden = true;
static override description =
'[EXPERIMENTAL] start a remote simulator session on EAS and get the credentials to connect to it with the CLI tool of your choice';
static override flags = {
platform: Flags.option({
description: 'Device platform',
options: ['android', 'ios'] as const,
required: true,
})(),
type: Flags.option({
description: 'Type of device run session to create',
options: Object.values(DEVICE_RUN_SESSION_TYPE_FLAG_VALUES),
default: DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[DeviceRunSessionType.AgentDevice],
})(),
'package-version': Flags.string({
description:
'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
}),
...EASNonInteractiveFlag,
};
static override contextDefinition = {
...this.ContextOptions.ProjectId,
...this.ContextOptions.LoggedIn,
};
async runAsync(): Promise<void> {
const { flags } = await this.parse(SimulatorStart);
const {
projectId,
loggedIn: { graphqlClient },
} = await this.getContextAsync(SimulatorStart, {
nonInteractive: flags['non-interactive'],
});
const platform = flags.platform === 'android' ? AppPlatform.Android : AppPlatform.Ios;
const createSpinner = ora('🚀 Creating device run session').start();
let deviceRunSessionId: string;
try {
const session = await DeviceRunSessionMutation.createDeviceRunSessionAsync(graphqlClient, {
appId: projectId,
platform,
type: DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type],
packageVersion: flags['package-version'],
});
deviceRunSessionId = session.id;
const jobRunId = nullthrows(session.turtleJobRun?.id, 'Expected device run session to start');
const jobRunUrl = getBareJobRunUrl(session.app.ownerAccount.name, session.app.slug, jobRunId);
createSpinner.succeed(
`Device run session created (id: ${deviceRunSessionId}) ${link(jobRunUrl)}`
);
} catch (err) {
createSpinner.fail('Failed to create device run session');
throw err;
}
const checkReadiness = getReadinessCheckerForType(flags.type);
const pollSpinner = ora(`⏳ Waiting for ${flags.type} daemon to start`).start();
const deadline = Date.now() + POLL_TIMEOUT_MS;
let result: ReadinessResult = { ready: false };
try {
while (Date.now() < deadline) {
const session = await DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
if (
session.status === DeviceRunSessionStatus.Errored ||
session.status === DeviceRunSessionStatus.Stopped
) {
throw new Error(
`Device run session ${deviceRunSessionId} ${session.status.toLowerCase()} before the ${flags.type} daemon was ready.`
);
}
const jobRunStatus = session.turtleJobRun?.status;
if (
jobRunStatus === JobRunStatus.Errored ||
jobRunStatus === JobRunStatus.Canceled ||
jobRunStatus === JobRunStatus.Finished
) {
throw new Error(
`Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready.`
);
}
const logMessages = await fetchLogMessagesAsync(session.turtleJobRun?.logFileUrls ?? []);
result = checkReadiness(logMessages);
if (result.ready) {
pollSpinner.succeed(`🎉 ${flags.type} daemon is ready`);
break;
}
await sleepAsync(POLL_INTERVAL_MS);
}
} catch (err) {
pollSpinner.fail(`Failed while polling for ${flags.type} daemon logs`);
throw err;
}
if (!result.ready) {
pollSpinner.fail(`Timed out waiting for ${flags.type} daemon to start`);
throw new Error(
`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} daemon to start.`
);
}
Log.newLine();
Log.log(`🔑 Run the following in your shell to attach to ${flags.type}:`);
Log.newLine();
Log.log(result.message);
}
}
function getReadinessCheckerForType(type: string): ReadinessChecker {
switch (type) {
case DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[DeviceRunSessionType.AgentDevice]:
return checkAgentDeviceReadiness;
default:
throw new Error(`Unsupported device run session type: ${type}`);
}
}
const AGENT_DEVICE_BASE_URL_ENV_VAR = 'AGENT_DEVICE_DAEMON_BASE_URL';
const AGENT_DEVICE_AUTH_TOKEN_ENV_VAR = 'AGENT_DEVICE_DAEMON_AUTH_TOKEN';
function checkAgentDeviceReadiness(logMessages: readonly string[]): ReadinessResult {
let baseUrl: string | undefined;
let authToken: string | undefined;
for (const msg of logMessages) {
baseUrl = baseUrl ?? extractExportedEnvValue(msg, AGENT_DEVICE_BASE_URL_ENV_VAR);
authToken = authToken ?? extractExportedEnvValue(msg, AGENT_DEVICE_AUTH_TOKEN_ENV_VAR);
if (baseUrl && authToken) {
break;
}
}
if (baseUrl && authToken) {
return {
ready: true,
message: [
`export ${AGENT_DEVICE_BASE_URL_ENV_VAR}='${baseUrl}'`,
`export ${AGENT_DEVICE_AUTH_TOKEN_ENV_VAR}='${authToken}'`,
].join('\n'),
};
}
return { ready: false };
}
async function fetchLogMessagesAsync(logUrls: readonly string[]): Promise<string[]> {
const messages: string[] = [];
for (const url of logUrls) {
const text = await fetchLogTextAsync(url);
if (!text) {
continue;
}
for (const line of text.split('\n')) {
if (!line.trim()) {
continue;
}
messages.push(extractLogMessage(line));
}
}
return messages;
}
async function fetchLogTextAsync(url: string): Promise<string | undefined> {
try {
const response = await fetch(url);
if (!response.ok) {
return undefined;
}
return await response.text();
} catch {
return undefined;
}
}
function extractLogMessage(line: string): string {
// Turtle job run logs are JSONL (bunyan-shaped), e.g.
// {"msg":"export FOO=\"bar\"","time":"...","logId":"..."}
// Fall back to the raw line if it's not JSON or doesn't have a string msg.
const trimmed = line.trim();
if (!trimmed.startsWith('{')) {
return line;
}
try {
const parsed = JSON.parse(trimmed) as unknown;
if (parsed && typeof parsed === 'object' && 'msg' in parsed) {
const msg = (parsed as { msg: unknown }).msg;
if (typeof msg === 'string') {
return msg;
}
}
} catch {
// not JSON, fall through
}
return line;
}
function extractExportedEnvValue(text: string, varName: string): string | undefined {
// Matches: export NAME=value | export NAME="value" | export NAME='value'
const pattern = new RegExp(`export\\s+${escapeRegExp(varName)}=(?:"([^"]*)"|'([^']*)'|(\\S+))`);
const match = pattern.exec(text);
if (!match) {
return undefined;
}
return match[1] ?? match[2] ?? match[3];
}
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
+107
View File
@@ -2850,6 +2850,23 @@ export type AppleDeviceRegistrationRequestMutationCreateAppleDeviceRegistrationR
appleTeamId: Scalars['ID']['input'];
};
/** Publicly visible data for an AppleDeviceRegistrationRequest. */
export type AppleDeviceRegistrationRequestPublicData = {
__typename?: 'AppleDeviceRegistrationRequestPublicData';
id: Scalars['ID']['output'];
};
export type AppleDeviceRegistrationRequestPublicDataQuery = {
__typename?: 'AppleDeviceRegistrationRequestPublicDataQuery';
/** Get AppleDeviceRegistrationRequest public data by ID */
byId?: Maybe<AppleDeviceRegistrationRequestPublicData>;
};
export type AppleDeviceRegistrationRequestPublicDataQueryByIdArgs = {
id: Scalars['ID']['input'];
};
export type AppleDeviceRegistrationRequestQuery = {
__typename?: 'AppleDeviceRegistrationRequestQuery';
byId: AppleDeviceRegistrationRequest;
@@ -4173,6 +4190,17 @@ export type CreateConvexTeamConnectionInput = {
deploymentRegion: Scalars['String']['input'];
};
export type CreateDeviceRunSessionInput = {
appId: Scalars['ID']['input'];
/**
* The version of the package backing the device run session (e.g. "0.1.3-alpha.3").
* If omitted, consumers treat the session as pinned to "latest".
*/
packageVersion?: InputMaybe<Scalars['String']['input']>;
platform: AppPlatform;
type: DeviceRunSessionType;
};
export type CreateEchoChatInput = {
agentMetadata?: InputMaybe<Scalars['JSONObject']['input']>;
agentType?: InputMaybe<EchoAgentType>;
@@ -4770,6 +4798,65 @@ export type DeploymentsMutationDeleteWorkerDeploymentByIdentifierArgs = {
deploymentIdentifier: Scalars['ID']['input'];
};
export type DeviceRunSession = {
__typename?: 'DeviceRunSession';
app: App;
createdAt: Scalars['DateTime']['output'];
finishedAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['ID']['output'];
initiatingActor?: Maybe<Actor>;
/**
* The version of the package backing the device run session. Null means the session is
* pinned to "latest" at the consumer side.
*/
packageVersion?: Maybe<Scalars['String']['output']>;
platform: AppPlatform;
startedAt?: Maybe<Scalars['DateTime']['output']>;
status: DeviceRunSessionStatus;
turtleJobRun?: Maybe<JobRun>;
type: DeviceRunSessionType;
updatedAt: Scalars['DateTime']['output'];
};
export type DeviceRunSessionMutation = {
__typename?: 'DeviceRunSessionMutation';
/** Create a device run session */
createDeviceRunSession: DeviceRunSession;
/** Stop a device run session */
stopDeviceRunSession: DeviceRunSession;
};
export type DeviceRunSessionMutationCreateDeviceRunSessionArgs = {
deviceRunSessionInput: CreateDeviceRunSessionInput;
};
export type DeviceRunSessionMutationStopDeviceRunSessionArgs = {
deviceRunSessionId: Scalars['ID']['input'];
};
export type DeviceRunSessionQuery = {
__typename?: 'DeviceRunSessionQuery';
byId: DeviceRunSession;
};
export type DeviceRunSessionQueryByIdArgs = {
deviceRunSessionId: Scalars['ID']['input'];
};
export enum DeviceRunSessionStatus {
Errored = 'ERRORED',
InProgress = 'IN_PROGRESS',
New = 'NEW',
Stopped = 'STOPPED'
}
export enum DeviceRunSessionType {
AgentDevice = 'AGENT_DEVICE'
}
export type DiscordUser = {
__typename?: 'DiscordUser';
discordIdentifier: Scalars['String']['output'];
@@ -5523,6 +5610,7 @@ export enum EntityTypeName {
BillingContractEntity = 'BillingContractEntity',
BranchEntity = 'BranchEntity',
ChannelEntity = 'ChannelEntity',
ConvexTeamConnectionEntity = 'ConvexTeamConnectionEntity',
CustomerEntity = 'CustomerEntity',
EchoProjectEntity = 'EchoProjectEntity',
EchoVersionEntity = 'EchoVersionEntity',
@@ -7610,6 +7698,8 @@ export type RootMutation = {
deployments: DeploymentsMutation;
/** Mutations that assign or modify DevDomainNames for apps */
devDomainName: AppDevDomainNameMutation;
/** Mutations that create and stop device run sessions */
deviceRunSession: DeviceRunSessionMutation;
/** Mutations for Discord users */
discordUser: DiscordUserMutation;
/** Mutations for Echo chats */
@@ -7745,6 +7835,8 @@ export type RootQuery = {
appStoreConnectApiKey: AppStoreConnectApiKeyQuery;
/** Top-level query object for querying Apple Device registration requests. */
appleDeviceRegistrationRequest: AppleDeviceRegistrationRequestQuery;
/** Top-level query object for querying AppleDeviceRegistrationRequest publicly. */
appleDeviceRegistrationRequestPublicData: AppleDeviceRegistrationRequestPublicDataQuery;
/** Top-level query object for querying Apple distribution certificates. */
appleDistributionCertificate?: Maybe<AppleDistributionCertificateQuery>;
/** Top-level query object for querying Apple provisioning profiles. */
@@ -7768,6 +7860,7 @@ export type RootQuery = {
convexIntegration: ConvexIntegrationQuery;
/** Top-level query object for querying Deployments. */
deployments: DeploymentQuery;
deviceRunSessions: DeviceRunSessionQuery;
/** Top-level query object for querying Echo chats. */
echoChat: EchoChatQuery;
/** Top-level query object for querying Echo messages. */
@@ -11654,6 +11747,13 @@ export type RetryIosBuildMutationVariables = Exact<{
export type RetryIosBuildMutation = { __typename?: 'RootMutation', build: { __typename?: 'BuildMutation', retryIosBuild: { __typename?: 'Build', id: string, status: BuildStatus, platform: AppPlatform, logFiles: Array<string>, 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, fingerprint?: { __typename?: 'Fingerprint', id: string, hash: string } | null, initiatingActor?: { __typename: 'PartnerActor', id: string, displayName: string } | { __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 CreateDeviceRunSessionMutationVariables = Exact<{
deviceRunSessionInput: CreateDeviceRunSessionInput;
}>;
export type CreateDeviceRunSessionMutation = { __typename?: 'RootMutation', deviceRunSession: { __typename?: 'DeviceRunSessionMutation', createDeviceRunSession: { __typename?: 'DeviceRunSession', id: string, status: DeviceRunSessionStatus, app: { __typename?: 'App', id: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, turtleJobRun?: { __typename?: 'JobRun', id: string } | null } } };
export type CreateEnvironmentSecretForAccountMutationVariables = Exact<{
input: CreateEnvironmentSecretInput;
accountId: Scalars['String']['input'];
@@ -12106,6 +12206,13 @@ export type ViewUpdateChannelsPaginatedOnAppQueryVariables = Exact<{
export type ViewUpdateChannelsPaginatedOnAppQuery = { __typename?: 'RootQuery', app: { __typename?: 'AppQuery', byId: { __typename?: 'App', id: string, channelsPaginated: { __typename?: 'AppChannelsConnection', edges: Array<{ __typename?: 'AppChannelEdge', cursor: string, node: { __typename?: 'UpdateChannel', id: string, name: string, branchMapping: string } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null } } } } };
export type DeviceRunSessionByIdQueryVariables = Exact<{
deviceRunSessionId: Scalars['ID']['input'];
}>;
export type DeviceRunSessionByIdQuery = { __typename?: 'RootQuery', deviceRunSessions: { __typename?: 'DeviceRunSessionQuery', byId: { __typename?: 'DeviceRunSession', id: string, status: DeviceRunSessionStatus, turtleJobRun?: { __typename?: 'JobRun', id: string, status: JobRunStatus, logFileUrls: Array<string> } | null } } };
export type EnvironmentSecretsByAppIdQueryVariables = Exact<{
appId: Scalars['String']['input'];
}>;
@@ -0,0 +1,47 @@
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import {
CreateDeviceRunSessionInput,
CreateDeviceRunSessionMutation,
CreateDeviceRunSessionMutationVariables,
} from '../generated';
export const DeviceRunSessionMutation = {
async createDeviceRunSessionAsync(
graphqlClient: ExpoGraphqlClient,
deviceRunSessionInput: CreateDeviceRunSessionInput
): Promise<CreateDeviceRunSessionMutation['deviceRunSession']['createDeviceRunSession']> {
const data = await withErrorHandlingAsync(
graphqlClient
.mutation<CreateDeviceRunSessionMutation, CreateDeviceRunSessionMutationVariables>(
gql`
mutation CreateDeviceRunSessionMutation($deviceRunSessionInput: CreateDeviceRunSessionInput!) {
deviceRunSession {
createDeviceRunSession(deviceRunSessionInput: $deviceRunSessionInput) {
id
status
app {
id
slug
ownerAccount {
id
name
}
}
turtleJobRun {
id
}
}
}
}
`,
{ deviceRunSessionInput },
{ noRetry: true }
)
.toPromise()
);
return data.deviceRunSession.createDeviceRunSession;
},
};
@@ -0,0 +1,37 @@
import gql from 'graphql-tag';
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
import { withErrorHandlingAsync } from '../client';
import { DeviceRunSessionByIdQuery, DeviceRunSessionByIdQueryVariables } from '../generated';
export const DeviceRunSessionQuery = {
async byIdAsync(
graphqlClient: ExpoGraphqlClient,
deviceRunSessionId: string
): Promise<DeviceRunSessionByIdQuery['deviceRunSessions']['byId']> {
const data = await withErrorHandlingAsync(
graphqlClient
.query<DeviceRunSessionByIdQuery, DeviceRunSessionByIdQueryVariables>(
gql`
query DeviceRunSessionByIdQuery($deviceRunSessionId: ID!) {
deviceRunSessions {
byId(deviceRunSessionId: $deviceRunSessionId) {
id
status
turtleJobRun {
id
status
logFileUrls
}
}
}
}
`,
{ deviceRunSessionId },
{ requestPolicy: 'network-only' }
)
.toPromise()
);
return data.deviceRunSessions.byId;
},
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "eas-cli-local-build-plugin",
"version": "18.8.0",
"version": "18.8.1",
"description": "Tool for running EAS compatible builds on a local machine.",
"bugs": "https://github.com/expo/eas-cli/issues",
"license": "BUSL-1.1",
@@ -26,7 +26,7 @@
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
},
"dependencies": {
"@expo/build-tools": "18.8.0",
"@expo/build-tools": "18.8.1",
"@expo/eas-build-job": "18.8.0",
"@expo/logger": "18.5.0",
"@expo/spawn-async": "^1.7.2",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/worker",
"version": "18.8.0",
"version": "18.8.1",
"private": true,
"scripts": {
"start": "yarn watch",
@@ -19,7 +19,7 @@
},
"dependencies": {
"@babel/core": "7.24.7",
"@expo/build-tools": "18.8.0",
"@expo/build-tools": "18.8.1",
"@expo/config-plugins": "55.0.7",
"@expo/config-types": "55.0.5",
"@expo/downloader": "18.5.0",
Generated
+3 -4
View File
@@ -2856,7 +2856,7 @@ __metadata:
languageName: node
linkType: hard
"@expo/build-tools@npm:18.8.0, @expo/build-tools@workspace:packages/build-tools":
"@expo/build-tools@npm:18.8.1, @expo/build-tools@workspace:packages/build-tools":
version: 0.0.0-use.local
resolution: "@expo/build-tools@workspace:packages/build-tools"
dependencies:
@@ -2864,7 +2864,6 @@ __metadata:
"@expo/config-plugins": "npm:55.0.7"
"@expo/downloader": "npm:18.5.0"
"@expo/eas-build-job": "npm:18.8.0"
"@expo/eas-json": "npm:18.8.0"
"@expo/env": "npm:^0.4.0"
"@expo/logger": "npm:18.5.0"
"@expo/package-manager": "npm:1.9.10"
@@ -3555,7 +3554,7 @@ __metadata:
resolution: "@expo/worker@workspace:packages/worker"
dependencies:
"@babel/core": "npm:7.24.7"
"@expo/build-tools": "npm:18.8.0"
"@expo/build-tools": "npm:18.8.1"
"@expo/config-plugins": "npm:55.0.7"
"@expo/config-types": "npm:55.0.5"
"@expo/downloader": "npm:18.5.0"
@@ -10945,7 +10944,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "eas-cli-local-build-plugin@workspace:packages/local-build-plugin"
dependencies:
"@expo/build-tools": "npm:18.8.0"
"@expo/build-tools": "npm:18.8.1"
"@expo/eas-build-job": "npm:18.8.0"
"@expo/logger": "npm:18.5.0"
"@expo/spawn-async": "npm:^1.7.2"