mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb3caef49b | ||
|
|
6ec2ea0530 | ||
|
|
d5a6b13dae | ||
|
|
41eb9f1499 | ||
|
|
5c9b93fc34 | ||
|
|
fbc57831cc | ||
|
|
34ca7b0ab9 | ||
|
|
7d6e4db4e8 |
No files matched your search
+6
-2
@@ -10,11 +10,15 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [19.0.1](https://github.com/expo/eas-cli/releases/tag/v19.0.1) - 2026-05-19
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- [eas-cli] Disable dotenv loading when resolving managed iOS entitlements. ([#3752](https://github.com/expo/eas-cli/pull/3752) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
- [eas-cli] Restore the bundled config fallback for managed iOS entitlements introspection. ([#3753](https://github.com/expo/eas-cli/pull/3753) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [19.0.0](https://github.com/expo/eas-cli/releases/tag/v19.0.0) - 2026-05-19
|
||||
|
||||
### 🛠 Breaking changes
|
||||
|
||||
@@ -10,8 +10,10 @@ export interface GradleProfileTask {
|
||||
|
||||
export async function parseGradleProfile(androidDir: string): Promise<GradleProfileTask[]> {
|
||||
const profileDir = path.join(androidDir, 'build', 'reports', 'profile');
|
||||
// Gradle profile may not exist for all builds (e.g. custom, repack
|
||||
// jobs). We should short circuit and not unnecessarily log.
|
||||
if (!(await fs.pathExists(profileDir))) {
|
||||
throw new Error(`Gradle profile directory not found at ${profileDir}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = await fs.readdir(profileDir);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { runFastlaneGym, runFastlaneResign } from '../ios/fastlane';
|
||||
import { installPods } from '../ios/pod';
|
||||
import { downloadApplicationArchiveAsync } from '../ios/resign';
|
||||
import { resolveArtifactPath, resolveBuildConfiguration, resolveScheme } from '../ios/resolve';
|
||||
import { Sentry } from '../sentry';
|
||||
import { parseAndReportXcactivitylog } from '../steps/utils/ios/xcactivitylog';
|
||||
import { cacheStatsAsync, restoreCcacheAsync } from '../steps/functions/restoreBuildCache';
|
||||
import { saveCcacheAsync } from '../steps/functions/saveBuildCache';
|
||||
@@ -173,17 +174,25 @@ async function buildAsync(ctx: BuildContext<Ios.Job>): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
if (ctx.env.EXPERIMENTAL_EAS_XCACTIVITYLOG === '1') {
|
||||
const { derivedDataPath, workspacePath } = nullthrows(fastlaneResult);
|
||||
await ctx.runBuildPhase(BuildPhase.PARSE_XCACTIVITYLOG, async () => {
|
||||
await ctx.runBuildPhase(BuildPhase.PARSE_XCACTIVITYLOG, async () => {
|
||||
if (ctx.isLocal) {
|
||||
ctx.logger.info('Local builds skip build performance analysis.');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { derivedDataPath, workspacePath } = nullthrows(fastlaneResult);
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath,
|
||||
workspacePath,
|
||||
logger: ctx.logger,
|
||||
proxyBaseUrl: ctx.env.EAS_BUILD_COCOAPODS_CACHE_URL,
|
||||
env: ctx.env,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
Sentry.capture('Failed to parse xcactivitylog', err);
|
||||
ctx.markBuildPhaseSkipped();
|
||||
}
|
||||
});
|
||||
|
||||
await ctx.runBuildPhase(BuildPhase.PRE_UPLOAD_ARTIFACTS_HOOK, async () => {
|
||||
await runHookIfPresent(ctx, Hook.PRE_UPLOAD_ARTIFACTS);
|
||||
|
||||
@@ -45,6 +45,7 @@ export function parseXcactivitylogFunction(): BuildFunction {
|
||||
xclogparserVersion: version,
|
||||
logger: stepCtx.logger,
|
||||
proxyBaseUrl: env.EAS_BUILD_COCOAPODS_CACHE_URL,
|
||||
env,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -195,6 +195,8 @@ const FIXTURE_OVERLAP_WITH_GAP = {
|
||||
const mockedDownloadFile = jest.mocked(downloadFile);
|
||||
const mockedSpawn = jest.mocked(spawn);
|
||||
|
||||
const TEST_ENV = { PATH: '/custom/bin:/usr/bin' };
|
||||
|
||||
function mockFilesystem({
|
||||
tempDir = '/tmp/xclogparser-123',
|
||||
jsonOutput = JSON.stringify(FIXTURE_TWO_MODULES),
|
||||
@@ -224,32 +226,38 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('downloads xclogparser from GCS and logs only the final report on success', async () => {
|
||||
it('downloads xclogparser from GCS and logs resolution + final report when no binary is preinstalled', async () => {
|
||||
const logger = createMockLogger();
|
||||
const { tempDir, reportPath } = mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(1, 'xclogparser', ['version'], {
|
||||
stdio: 'pipe',
|
||||
env: TEST_ENV,
|
||||
});
|
||||
expect(mockedDownloadFile).toHaveBeenCalledWith(
|
||||
'https://storage.googleapis.com/turtle-v2/xclogparser/XCLogParser-macOS-x86-64-arm64-v0.2.47.zip',
|
||||
path.join(tempDir, 'XCLogParser-macOS-x86-64-arm64-v0.2.47.zip'),
|
||||
{ retry: 3, timeout: 20_000 }
|
||||
);
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
2,
|
||||
'unzip',
|
||||
['-q', path.join(tempDir, 'XCLogParser-macOS-x86-64-arm64-v0.2.47.zip'), '-d', tempDir],
|
||||
{ stdio: 'pipe' }
|
||||
{ stdio: 'pipe', env: TEST_ENV }
|
||||
);
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
3,
|
||||
path.join(tempDir, 'xclogparser'),
|
||||
[
|
||||
'parse',
|
||||
@@ -262,10 +270,10 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
'--output',
|
||||
reportPath,
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
{ stdio: 'pipe', env: TEST_ENV }
|
||||
);
|
||||
expect(fs.readFile).toHaveBeenCalledWith(reportPath, 'utf8');
|
||||
expect(logger.info).toHaveBeenCalledTimes(1);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using downloaded xclogparser v0.2.47.');
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Xcode Build — Compile Metrics by Module')
|
||||
);
|
||||
@@ -281,6 +289,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
mockedDownloadFile
|
||||
.mockRejectedValueOnce(new Error('proxy failed'))
|
||||
.mockResolvedValueOnce(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
@@ -288,6 +297,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
proxyBaseUrl: 'https://cache.example.com',
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenNthCalledWith(
|
||||
@@ -315,6 +325,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn
|
||||
.mockRejectedValueOnce(new Error('xclogparser not found'))
|
||||
.mockRejectedValueOnce(new Error('invalid zip'))
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any)
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any);
|
||||
@@ -324,6 +335,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
proxyBaseUrl: 'https://cache.example.com',
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenNthCalledWith(
|
||||
@@ -353,6 +365,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
mockFilesystem({ jsonOutput: '{not valid json' });
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await expect(
|
||||
@@ -360,6 +373,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
@@ -382,6 +396,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
});
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await expect(
|
||||
@@ -389,6 +404,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
@@ -407,6 +423,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn
|
||||
.mockRejectedValueOnce(new Error('xclogparser not found')) // version detect
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any) // unzip
|
||||
.mockRejectedValueOnce(new Error('spawn xclogparser ENOENT')); // xclogparser run
|
||||
|
||||
@@ -415,6 +432,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
@@ -432,6 +450,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockRejectedValue(new Error('network unreachable'));
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await expect(
|
||||
@@ -439,6 +458,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
@@ -459,6 +479,7 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
});
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await expect(
|
||||
@@ -466,13 +487,171 @@ describe('parseAndReportXcactivitylog', () => {
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
})
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(logger.info).toHaveBeenCalledTimes(1);
|
||||
expect(logger.debug).not.toHaveBeenCalled();
|
||||
expect(logger.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('xclogparser version resolution', () => {
|
||||
it('uses preinstalled binary when no version is requested', async () => {
|
||||
const logger = createMockLogger();
|
||||
const { reportPath } = mockFilesystem();
|
||||
|
||||
mockedSpawn
|
||||
.mockResolvedValueOnce({ stdout: 'XCLogParser 0.2.50\n', stderr: '' } as any) // version detect
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any); // xclogparser parse
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).not.toHaveBeenCalled();
|
||||
expect(mockedSpawn).not.toHaveBeenCalledWith('unzip', expect.any(Array), expect.any(Object));
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'xclogparser',
|
||||
[
|
||||
'parse',
|
||||
'--derived_data',
|
||||
'/tmp/derived-data',
|
||||
'--workspace',
|
||||
'/tmp/workspace',
|
||||
'--reporter',
|
||||
'json',
|
||||
'--output',
|
||||
reportPath,
|
||||
],
|
||||
{ stdio: 'pipe', env: TEST_ENV }
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using preinstalled xclogparser v0.2.50.');
|
||||
expect(logger.info).not.toHaveBeenCalledWith(expect.stringContaining('downloaded'));
|
||||
expect(Sentry.capture).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('downloads default version when no version requested and no preinstall found', async () => {
|
||||
const logger = createMockLogger();
|
||||
mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenCalledWith(
|
||||
expect.stringContaining('XCLogParser-macOS-x86-64-arm64-v0.2.47.zip'),
|
||||
expect.any(String),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using downloaded xclogparser v0.2.47.');
|
||||
});
|
||||
|
||||
it('uses preinstalled binary when requested version matches', async () => {
|
||||
const logger = createMockLogger();
|
||||
mockFilesystem();
|
||||
|
||||
mockedSpawn
|
||||
.mockResolvedValueOnce({ stdout: 'XCLogParser 0.2.50\n', stderr: '' } as any) // version detect
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any); // xclogparser parse
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
xclogparserVersion: 'v0.2.50',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).not.toHaveBeenCalled();
|
||||
expect(logger.info).toHaveBeenCalledWith('Using preinstalled xclogparser v0.2.50.');
|
||||
expect(logger.info).not.toHaveBeenCalledWith(expect.stringContaining('downloaded'));
|
||||
});
|
||||
|
||||
it('downloads requested version when it does not match the preinstalled binary', async () => {
|
||||
const logger = createMockLogger();
|
||||
mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn
|
||||
.mockResolvedValueOnce({ stdout: 'XCLogParser 0.2.48\n', stderr: '' } as any) // version detect
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any) // unzip
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any); // xclogparser parse
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
xclogparserVersion: 'v0.2.50',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenCalledWith(
|
||||
expect.stringContaining('XCLogParser-macOS-x86-64-arm64-v0.2.50.zip'),
|
||||
expect.any(String),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using downloaded xclogparser v0.2.50.');
|
||||
});
|
||||
|
||||
it('downloads requested version when no preinstall is found', async () => {
|
||||
const logger = createMockLogger();
|
||||
mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn.mockRejectedValueOnce(new Error('xclogparser not found'));
|
||||
mockedSpawn.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
xclogparserVersion: 'v0.2.50',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenCalledWith(
|
||||
expect.stringContaining('XCLogParser-macOS-x86-64-arm64-v0.2.50.zip'),
|
||||
expect.any(String),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using downloaded xclogparser v0.2.50.');
|
||||
});
|
||||
|
||||
it('falls back to download when the version output cannot be parsed', async () => {
|
||||
const logger = createMockLogger();
|
||||
mockFilesystem();
|
||||
|
||||
mockedDownloadFile.mockResolvedValue(undefined);
|
||||
mockedSpawn
|
||||
.mockResolvedValueOnce({ stdout: 'something unexpected', stderr: '' } as any) // version detect
|
||||
.mockResolvedValue({ stdout: '', stderr: '' } as any);
|
||||
|
||||
await parseAndReportXcactivitylog({
|
||||
derivedDataPath: '/tmp/derived-data',
|
||||
workspacePath: '/tmp/workspace',
|
||||
logger,
|
||||
env: TEST_ENV,
|
||||
});
|
||||
|
||||
expect(mockedDownloadFile).toHaveBeenCalledWith(
|
||||
expect.stringContaining('XCLogParser-macOS-x86-64-arm64-v0.2.47.zip'),
|
||||
expect.any(String),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith('Using downloaded xclogparser v0.2.47.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isCompileStep', () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import downloadFile from '@expo/downloader';
|
||||
import { bunyan } from '@expo/logger';
|
||||
import { asyncResult } from '@expo/results';
|
||||
import { BuildStepEnv } from '@expo/steps';
|
||||
import spawn from '@expo/turtle-spawn';
|
||||
import fs from 'fs-extra';
|
||||
import os from 'os';
|
||||
@@ -22,35 +23,54 @@ const XCLOGPARSER_OUTPUT_FILENAME = 'xcactivitylog.json';
|
||||
export async function parseAndReportXcactivitylog({
|
||||
derivedDataPath,
|
||||
workspacePath,
|
||||
xclogparserVersion = DEFAULT_XCLOGPARSER_VERSION,
|
||||
xclogparserVersion,
|
||||
logger,
|
||||
proxyBaseUrl,
|
||||
env,
|
||||
}: {
|
||||
derivedDataPath: string;
|
||||
workspacePath: string;
|
||||
xclogparserVersion?: string;
|
||||
logger: bunyan;
|
||||
proxyBaseUrl?: string;
|
||||
env: BuildStepEnv;
|
||||
}): Promise<void> {
|
||||
let tempDir: string | undefined;
|
||||
let phase = 'creating_temp_directory';
|
||||
try {
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'xclogparser-'));
|
||||
|
||||
phase = 'downloading_xclogparser';
|
||||
const xclogparserPath = await downloadXclogparser(
|
||||
tempDir,
|
||||
xclogparserVersion,
|
||||
logger,
|
||||
proxyBaseUrl
|
||||
);
|
||||
phase = 'resolving_xclogparser';
|
||||
const preinstalledVersion = await detectPreinstalledXclogparserVersion(env);
|
||||
let binaryPath: string;
|
||||
let resolvedVersion: string;
|
||||
if (
|
||||
preinstalledVersion &&
|
||||
(!xclogparserVersion || preinstalledVersion === xclogparserVersion)
|
||||
) {
|
||||
binaryPath = 'xclogparser';
|
||||
resolvedVersion = preinstalledVersion;
|
||||
logger.info(`Using preinstalled xclogparser ${resolvedVersion}.`);
|
||||
} else {
|
||||
phase = 'downloading_xclogparser';
|
||||
resolvedVersion = xclogparserVersion ?? DEFAULT_XCLOGPARSER_VERSION;
|
||||
binaryPath = await downloadXclogparser({
|
||||
tempDir,
|
||||
version: resolvedVersion,
|
||||
logger,
|
||||
proxyBaseUrl,
|
||||
env,
|
||||
});
|
||||
logger.info(`Using downloaded xclogparser ${resolvedVersion}.`);
|
||||
}
|
||||
|
||||
phase = 'running_xclogparser';
|
||||
const jsonOutputPath = await runXclogparser({
|
||||
binaryPath: xclogparserPath,
|
||||
binaryPath,
|
||||
derivedDataPath,
|
||||
workspacePath,
|
||||
outputDir: tempDir,
|
||||
env,
|
||||
});
|
||||
|
||||
phase = 'parsing_xclogparser_output';
|
||||
@@ -70,12 +90,28 @@ export async function parseAndReportXcactivitylog({
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadXclogparser(
|
||||
tempDir: string,
|
||||
version: string,
|
||||
logger: bunyan,
|
||||
proxyBaseUrl?: string
|
||||
): Promise<string> {
|
||||
async function detectPreinstalledXclogparserVersion(env: BuildStepEnv): Promise<string | null> {
|
||||
const result = await asyncResult(spawn('xclogparser', ['version'], { stdio: 'pipe', env }));
|
||||
if (!result.ok) {
|
||||
return null;
|
||||
}
|
||||
const match = result.value.stdout.match(/(\d+\.\d+\.\d+)/);
|
||||
return match ? `v${match[1]}` : null;
|
||||
}
|
||||
|
||||
async function downloadXclogparser({
|
||||
tempDir,
|
||||
version,
|
||||
logger,
|
||||
proxyBaseUrl,
|
||||
env,
|
||||
}: {
|
||||
tempDir: string;
|
||||
version: string;
|
||||
logger: bunyan;
|
||||
proxyBaseUrl?: string;
|
||||
env: BuildStepEnv;
|
||||
}): Promise<string> {
|
||||
const zipName = getXclogparserZipName(version);
|
||||
const zipPath = path.join(tempDir, zipName);
|
||||
const directUrl = `${XCLOGPARSER_DOWNLOAD_URL}/${zipName}`;
|
||||
@@ -83,7 +119,7 @@ async function downloadXclogparser(
|
||||
|
||||
if (proxiedUrl) {
|
||||
const proxiedDownloadResult = await asyncResult(
|
||||
downloadAndUnpackXclogparser({ tempDir, zipPath, sourceUrl: proxiedUrl })
|
||||
downloadAndUnpackXclogparser({ tempDir, zipPath, sourceUrl: proxiedUrl, env })
|
||||
);
|
||||
if (!proxiedDownloadResult.ok) {
|
||||
logger.debug(
|
||||
@@ -96,31 +132,35 @@ async function downloadXclogparser(
|
||||
}
|
||||
}
|
||||
|
||||
return await downloadAndUnpackXclogparser({ tempDir, zipPath, sourceUrl: directUrl });
|
||||
return await downloadAndUnpackXclogparser({ tempDir, zipPath, sourceUrl: directUrl, env });
|
||||
}
|
||||
|
||||
async function downloadAndUnpackXclogparser({
|
||||
tempDir,
|
||||
zipPath,
|
||||
sourceUrl,
|
||||
env,
|
||||
}: {
|
||||
tempDir: string;
|
||||
zipPath: string;
|
||||
sourceUrl: string;
|
||||
env: BuildStepEnv;
|
||||
}): Promise<string> {
|
||||
await downloadFile(sourceUrl, zipPath, { retry: 3, timeout: XCLOGPARSER_DOWNLOAD_TIMEOUT_MS });
|
||||
|
||||
return await unpackXclogparser({ tempDir, zipPath });
|
||||
return await unpackXclogparser({ tempDir, zipPath, env });
|
||||
}
|
||||
|
||||
async function unpackXclogparser({
|
||||
tempDir,
|
||||
zipPath,
|
||||
env,
|
||||
}: {
|
||||
tempDir: string;
|
||||
zipPath: string;
|
||||
env: BuildStepEnv;
|
||||
}): Promise<string> {
|
||||
await spawn('unzip', ['-q', zipPath, '-d', tempDir], { stdio: 'pipe' });
|
||||
await spawn('unzip', ['-q', zipPath, '-d', tempDir], { stdio: 'pipe', env });
|
||||
|
||||
const binaryPath = path.join(tempDir, 'xclogparser');
|
||||
await fs.chmod(binaryPath, 0o755);
|
||||
@@ -143,11 +183,13 @@ async function runXclogparser({
|
||||
derivedDataPath,
|
||||
workspacePath,
|
||||
outputDir,
|
||||
env,
|
||||
}: {
|
||||
binaryPath: string;
|
||||
derivedDataPath: string;
|
||||
workspacePath: string;
|
||||
outputDir: string;
|
||||
env: BuildStepEnv;
|
||||
}): Promise<string> {
|
||||
const outputPath = path.join(outputDir, XCLOGPARSER_OUTPUT_FILENAME);
|
||||
|
||||
@@ -164,7 +206,7 @@ async function runXclogparser({
|
||||
'--output',
|
||||
outputPath,
|
||||
],
|
||||
{ stdio: 'pipe' }
|
||||
{ stdio: 'pipe', env }
|
||||
);
|
||||
|
||||
return outputPath;
|
||||
@@ -420,7 +462,7 @@ export function formatReport(data: XcactivitylogData): string {
|
||||
' │ ' +
|
||||
'100.0%'.padStart(pctWidth) +
|
||||
' │ ' +
|
||||
'n/a'.padStart(wallWidth) +
|
||||
' '.repeat(wallWidth) +
|
||||
' │ ' +
|
||||
' '.repeat(barMaxWidth) +
|
||||
' │'
|
||||
|
||||
Generated
+441
@@ -24410,6 +24410,45 @@
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ArgentRunSessionRemoteConfig",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "toolsUrl",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webPreviewUrl",
|
||||
"description": "URL of the web preview surface for the session. Null when web previews are\nnot available for the platform (e.g. Android).",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "AscApiKeyInput",
|
||||
@@ -29910,6 +29949,12 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "GRADLE_BUILD_PROFILE",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "INSTALL_CUSTOM_TOOLS",
|
||||
"description": null,
|
||||
@@ -37510,6 +37555,11 @@
|
||||
"name": "AgentDeviceRunSessionRemoteConfig",
|
||||
"ofType": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ArgentRunSessionRemoteConfig",
|
||||
"ofType": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ServeSimRunSessionRemoteConfig",
|
||||
@@ -37566,6 +37616,12 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "ARGENT",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "SERVE_SIM",
|
||||
"description": null,
|
||||
@@ -42507,6 +42563,216 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdate",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "channel",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"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": "id",
|
||||
"description": "The manifest UUID baked into the binary by expo-updates at build time.",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "launchAsset",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdateAsset",
|
||||
"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": "runtimeVersion",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdateAsset",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "contentType",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "fileSHA256",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "fileSize",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "finalFileSize",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"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": "storageKey",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdateAssetMutation",
|
||||
@@ -42642,6 +42908,50 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdateMutation",
|
||||
"description": null,
|
||||
"fields": [
|
||||
{
|
||||
"name": "uploadEmbeddedUpdate",
|
||||
"description": "Register an embedded bundle as the launch asset for a given app/platform/channel.\nReturns EMBEDDED_UPDATE_ASSET_NOT_AVAILABLE if the asset has not been finalized yet.",
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UploadEmbeddedUpdateInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdate",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "EntityTypeName",
|
||||
@@ -52001,6 +52311,18 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "enqueuedAt",
|
||||
"description": null,
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "DateTime",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": null,
|
||||
@@ -58474,6 +58796,22 @@
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "embeddedUpdate",
|
||||
"description": "Mutations that register embedded update bundles for bundle diffing.",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "EmbeddedUpdateMutation",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "embeddedUpdateAsset",
|
||||
"description": null,
|
||||
@@ -69638,6 +69976,109 @@
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "UploadEmbeddedUpdateInput",
|
||||
"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": "channel",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "embeddedUpdateId",
|
||||
"description": "UUID baked into the binary by expo-updates at build time (from app.manifest id field).",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"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": "runtimeVersion",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "turtleBuildId",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "UploadSession",
|
||||
|
||||
@@ -221,6 +221,9 @@
|
||||
"secret": {
|
||||
"description": "manage project and account secrets"
|
||||
},
|
||||
"simulator": {
|
||||
"description": "manage remote simulator sessions"
|
||||
},
|
||||
"update": {
|
||||
"description": "manage individual updates"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,6 @@ import { ora } from '../../ora';
|
||||
import { formatRemoteSessionInstructions } from '../../simulator/utils';
|
||||
|
||||
export default class SimulatorGet extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description =
|
||||
'[EXPERIMENTAL] get info about a remote simulator session on EAS by its device run session ID';
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ import { Flags } from '@oclif/core';
|
||||
import { getBareJobRunUrl } from '../../build/utils/url';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
|
||||
import { EASNonInteractiveFlag } from '../../commandUtils/flags';
|
||||
import {
|
||||
EasNonInteractiveAndJsonFlags,
|
||||
resolveNonInteractiveAndJsonFlags,
|
||||
} from '../../commandUtils/flags';
|
||||
import {
|
||||
AppPlatform,
|
||||
DeviceRunSessionStatus,
|
||||
@@ -18,6 +21,7 @@ import {
|
||||
DeviceRunSessionRemoteConfig,
|
||||
formatRemoteSessionInstructions,
|
||||
} from '../../simulator/utils';
|
||||
import { enableJsonOutput, printJsonOnlyOutput } from '../../utils/json';
|
||||
import { sleepAsync } from '../../utils/promise';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
@@ -28,6 +32,7 @@ const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // 15 minutes
|
||||
// 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',
|
||||
[DeviceRunSessionType.Argent]: 'argent',
|
||||
[DeviceRunSessionType.ServeSim]: 'serve-sim',
|
||||
};
|
||||
|
||||
@@ -38,7 +43,6 @@ const DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE = Object.fromEntries(
|
||||
) as Record<string, DeviceRunSessionType>;
|
||||
|
||||
export default class SimulatorStart extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description =
|
||||
'[EXPERIMENTAL] start a remote simulator session on EAS and get instructions to connect to it';
|
||||
|
||||
@@ -57,7 +61,7 @@ export default class SimulatorStart extends EasCommand {
|
||||
description:
|
||||
'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
|
||||
}),
|
||||
...EASNonInteractiveFlag,
|
||||
...EasNonInteractiveAndJsonFlags,
|
||||
};
|
||||
|
||||
static override contextDefinition = {
|
||||
@@ -67,12 +71,17 @@ export default class SimulatorStart extends EasCommand {
|
||||
|
||||
async runAsync(): Promise<void> {
|
||||
const { flags } = await this.parse(SimulatorStart);
|
||||
const { json: jsonFlag, nonInteractive } = resolveNonInteractiveAndJsonFlags(flags);
|
||||
|
||||
if (jsonFlag) {
|
||||
enableJsonOutput();
|
||||
}
|
||||
|
||||
const {
|
||||
projectId,
|
||||
loggedIn: { graphqlClient },
|
||||
} = await this.getContextAsync(SimulatorStart, {
|
||||
nonInteractive: flags['non-interactive'],
|
||||
nonInteractive,
|
||||
});
|
||||
|
||||
const platform = flags.platform === 'android' ? AppPlatform.Android : AppPlatform.Ios;
|
||||
@@ -148,11 +157,21 @@ export default class SimulatorStart extends EasCommand {
|
||||
);
|
||||
}
|
||||
|
||||
if (jsonFlag) {
|
||||
printJsonOnlyOutput({
|
||||
id: deviceRunSessionId,
|
||||
type: flags.type,
|
||||
jobRunUrl,
|
||||
remoteConfig,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Log.newLine();
|
||||
Log.log(formatRemoteSessionInstructions(remoteConfig));
|
||||
Log.newLine();
|
||||
|
||||
if (flags['non-interactive']) {
|
||||
if (nonInteractive) {
|
||||
Log.log(
|
||||
`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { DeviceRunSessionMutation } from '../../graphql/mutations/DeviceRunSessi
|
||||
import { ora } from '../../ora';
|
||||
|
||||
export default class SimulatorStop extends EasCommand {
|
||||
static override hidden = true;
|
||||
static override description =
|
||||
'[EXPERIMENTAL] stop a remote simulator session on EAS by its device run session ID';
|
||||
|
||||
|
||||
+62
-2
@@ -3503,6 +3503,16 @@ export enum AppsFilter {
|
||||
New = 'NEW'
|
||||
}
|
||||
|
||||
export type ArgentRunSessionRemoteConfig = {
|
||||
__typename?: 'ArgentRunSessionRemoteConfig';
|
||||
toolsUrl: Scalars['String']['output'];
|
||||
/**
|
||||
* URL of the web preview surface for the session. Null when web previews are
|
||||
* not available for the platform (e.g. Android).
|
||||
*/
|
||||
webPreviewUrl?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type AscApiKeyInput = {
|
||||
issuerIdentifier: Scalars['String']['input'];
|
||||
keyIdentifier: Scalars['String']['input'];
|
||||
@@ -4212,6 +4222,7 @@ export enum BuildPhase {
|
||||
EasBuildInternal = 'EAS_BUILD_INTERNAL',
|
||||
FailBuild = 'FAIL_BUILD',
|
||||
FixGradlew = 'FIX_GRADLEW',
|
||||
GradleBuildProfile = 'GRADLE_BUILD_PROFILE',
|
||||
InstallCustomTools = 'INSTALL_CUSTOM_TOOLS',
|
||||
InstallDependencies = 'INSTALL_DEPENDENCIES',
|
||||
InstallPods = 'INSTALL_PODS',
|
||||
@@ -5266,7 +5277,7 @@ export type DeviceRunSessionQueryByIdArgs = {
|
||||
deviceRunSessionId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeviceRunSessionRemoteConfig = AgentDeviceRunSessionRemoteConfig | ServeSimRunSessionRemoteConfig;
|
||||
export type DeviceRunSessionRemoteConfig = AgentDeviceRunSessionRemoteConfig | ArgentRunSessionRemoteConfig | ServeSimRunSessionRemoteConfig;
|
||||
|
||||
export enum DeviceRunSessionStatus {
|
||||
Errored = 'ERRORED',
|
||||
@@ -5277,6 +5288,7 @@ export enum DeviceRunSessionStatus {
|
||||
|
||||
export enum DeviceRunSessionType {
|
||||
AgentDevice = 'AGENT_DEVICE',
|
||||
Argent = 'ARGENT',
|
||||
ServeSim = 'SERVE_SIM'
|
||||
}
|
||||
|
||||
@@ -6036,6 +6048,27 @@ export type EditUpdateBranchInput = {
|
||||
newName: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type EmbeddedUpdate = {
|
||||
__typename?: 'EmbeddedUpdate';
|
||||
channel: Scalars['String']['output'];
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
/** The manifest UUID baked into the binary by expo-updates at build time. */
|
||||
id: Scalars['ID']['output'];
|
||||
launchAsset: EmbeddedUpdateAsset;
|
||||
platform: AppPlatform;
|
||||
runtimeVersion: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type EmbeddedUpdateAsset = {
|
||||
__typename?: 'EmbeddedUpdateAsset';
|
||||
contentType: Scalars['String']['output'];
|
||||
fileSHA256: Scalars['String']['output'];
|
||||
fileSize: Scalars['Int']['output'];
|
||||
finalFileSize?: Maybe<Scalars['Int']['output']>;
|
||||
id: Scalars['ID']['output'];
|
||||
storageKey: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type EmbeddedUpdateAssetMutation = {
|
||||
__typename?: 'EmbeddedUpdateAssetMutation';
|
||||
/**
|
||||
@@ -6062,6 +6095,20 @@ export type EmbeddedUpdateAssetUploadSpec = {
|
||||
storageKey: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type EmbeddedUpdateMutation = {
|
||||
__typename?: 'EmbeddedUpdateMutation';
|
||||
/**
|
||||
* Register an embedded bundle as the launch asset for a given app/platform/channel.
|
||||
* Returns EMBEDDED_UPDATE_ASSET_NOT_AVAILABLE if the asset has not been finalized yet.
|
||||
*/
|
||||
uploadEmbeddedUpdate: EmbeddedUpdate;
|
||||
};
|
||||
|
||||
|
||||
export type EmbeddedUpdateMutationUploadEmbeddedUpdateArgs = {
|
||||
input: UploadEmbeddedUpdateInput;
|
||||
};
|
||||
|
||||
export enum EntityTypeName {
|
||||
AccountEntity = 'AccountEntity',
|
||||
AccountSsoConfigurationEntity = 'AccountSSOConfigurationEntity',
|
||||
@@ -7326,6 +7373,7 @@ export type JobRun = {
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
displayName?: Maybe<Scalars['String']['output']>;
|
||||
endedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
enqueuedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
errors: Array<JobRunError>;
|
||||
expiresAt: Scalars['DateTime']['output'];
|
||||
gitCommitHash?: Maybe<Scalars['String']['output']>;
|
||||
@@ -8218,6 +8266,8 @@ export type RootMutation = {
|
||||
echoTurn: EchoTurnMutation;
|
||||
/** Mutations for Echo versions */
|
||||
echoVersion: EchoVersionMutation;
|
||||
/** Mutations that register embedded update bundles for bundle diffing. */
|
||||
embeddedUpdate: EmbeddedUpdateMutation;
|
||||
embeddedUpdateAsset: EmbeddedUpdateAssetMutation;
|
||||
/** Mutations that create and delete EnvironmentSecrets */
|
||||
environmentSecret: EnvironmentSecretMutation;
|
||||
@@ -9766,6 +9816,16 @@ export type UpdatesMetricsData = {
|
||||
labels: Array<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type UploadEmbeddedUpdateInput = {
|
||||
appId: Scalars['ID']['input'];
|
||||
channel: Scalars['String']['input'];
|
||||
/** UUID baked into the binary by expo-updates at build time (from app.manifest id field). */
|
||||
embeddedUpdateId: Scalars['ID']['input'];
|
||||
platform: AppPlatform;
|
||||
runtimeVersion: Scalars['String']['input'];
|
||||
turtleBuildId?: InputMaybe<Scalars['ID']['input']>;
|
||||
};
|
||||
|
||||
export type UploadSession = {
|
||||
__typename?: 'UploadSession';
|
||||
/** Create an Upload Session for a specific account */
|
||||
@@ -12846,7 +12906,7 @@ export type DeviceRunSessionByIdQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type DeviceRunSessionByIdQuery = { __typename?: 'RootQuery', deviceRunSessions: { __typename?: 'DeviceRunSessionQuery', byId: { __typename?: 'DeviceRunSession', id: string, status: DeviceRunSessionStatus, type: DeviceRunSessionType, app: { __typename?: 'App', id: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, remoteConfig?: { __typename: 'AgentDeviceRunSessionRemoteConfig', agentDeviceRemoteSessionUrl: string, agentDeviceRemoteSessionToken: string, webPreviewUrl?: string | null } | { __typename: 'ServeSimRunSessionRemoteConfig', previewUrl: string, streamUrl: string } | null, turtleJobRun?: { __typename?: 'JobRun', id: string, status: JobRunStatus } | null } } };
|
||||
export type DeviceRunSessionByIdQuery = { __typename?: 'RootQuery', deviceRunSessions: { __typename?: 'DeviceRunSessionQuery', byId: { __typename?: 'DeviceRunSession', id: string, status: DeviceRunSessionStatus, type: DeviceRunSessionType, app: { __typename?: 'App', id: string, slug: string, ownerAccount: { __typename?: 'Account', id: string, name: string } }, remoteConfig?: { __typename: 'AgentDeviceRunSessionRemoteConfig', agentDeviceRemoteSessionUrl: string, agentDeviceRemoteSessionToken: string, webPreviewUrl?: string | null } | { __typename: 'ArgentRunSessionRemoteConfig', toolsUrl: string, webPreviewUrl?: string | null } | { __typename: 'ServeSimRunSessionRemoteConfig', previewUrl: string, streamUrl: string } | null, turtleJobRun?: { __typename?: 'JobRun', id: string, status: JobRunStatus } | null } } };
|
||||
|
||||
export type EnvironmentSecretsByAppIdQueryVariables = Exact<{
|
||||
appId: Scalars['String']['input'];
|
||||
|
||||
@@ -34,6 +34,10 @@ export const DeviceRunSessionQuery = {
|
||||
agentDeviceRemoteSessionToken
|
||||
webPreviewUrl
|
||||
}
|
||||
... on ArgentRunSessionRemoteConfig {
|
||||
toolsUrl
|
||||
webPreviewUrl
|
||||
}
|
||||
... on ServeSimRunSessionRemoteConfig {
|
||||
previewUrl
|
||||
streamUrl
|
||||
|
||||
@@ -3,6 +3,7 @@ import { JSONObject } from '@expo/json-file';
|
||||
import { getPrebuildConfigAsync } from '@expo/prebuild-config';
|
||||
|
||||
import Log from '../../log';
|
||||
import Sentry from '../../sentry';
|
||||
import { spawnExpoCommand } from '../../utils/expoCli';
|
||||
import { readPlistAsync } from '../../utils/plist';
|
||||
import { Client } from '../../vcs/vcs';
|
||||
@@ -35,6 +36,24 @@ export async function getManagedApplicationTargetEntitlementsAsync(
|
||||
const expWithMods: ExportedConfig = JSON.parse(stdout);
|
||||
return expWithMods.ios?.entitlements ?? {};
|
||||
} catch (error: any) {
|
||||
try {
|
||||
Sentry.withScope(scope => {
|
||||
if (process.env.EAS_BUILD_PROJECT_ID) {
|
||||
scope.setTag('app_id', process.env.EAS_BUILD_PROJECT_ID);
|
||||
}
|
||||
if (process.env.EAS_BUILD_ID) {
|
||||
scope.setTag('build_id', process.env.EAS_BUILD_ID);
|
||||
}
|
||||
scope.setTag('config_resolution', 'ios_entitlements_introspection');
|
||||
scope.setExtra('message', 'iOS entitlements config fallback');
|
||||
scope.setExtra('stdout', error.stdout);
|
||||
scope.setExtra('stderr', error.stderr);
|
||||
Sentry.captureException(error);
|
||||
});
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
expoConfigError = error;
|
||||
Log.warn(
|
||||
`Failed to read the app config from the project using the local Expo CLI: ${formatError(error)}`
|
||||
|
||||
@@ -24,6 +24,22 @@ export function formatRemoteSessionInstructions(
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
case 'ArgentRunSessionRemoteConfig': {
|
||||
const lines = [
|
||||
'🔑 Open the following URL to access the Argent tools for this session:',
|
||||
'',
|
||||
remoteConfig.toolsUrl,
|
||||
];
|
||||
if (remoteConfig.webPreviewUrl) {
|
||||
lines.push(
|
||||
'',
|
||||
'🌐 Open the following URL in your browser to preview the simulator:',
|
||||
'',
|
||||
remoteConfig.webPreviewUrl
|
||||
);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
case 'ServeSimRunSessionRemoteConfig':
|
||||
return [
|
||||
'🌐 Open the following URL in your browser to access the simulator:',
|
||||
|
||||
@@ -103,7 +103,6 @@ export async function build({
|
||||
}
|
||||
});
|
||||
} catch (err: any) {
|
||||
logger.error({ err }, 'Failed to parse Gradle build profile');
|
||||
Sentry.capture('Failed to parse Gradle build profile', err);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user