From 09829694081c9e5365e2d9a3f5bd24ff4830d38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Fri, 19 Jun 2026 09:55:57 +0200 Subject: [PATCH 1/2] Download Argent artifacts before upload --- .../utils/__tests__/argentArtifacts.test.ts | 16 +++++- .../src/steps/utils/argentArtifacts.ts | 51 ++++++++++++------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts b/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts index 67e2125480..6428d515d4 100644 --- a/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts +++ b/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts @@ -1,5 +1,6 @@ import { bunyan } from '@expo/logger'; import fetch from 'node-fetch'; +import { Readable } from 'node:stream'; import { CustomBuildContext } from '../../../customBuildContext'; import { uploadDeviceRunSessionArtifactAsync } from '../deviceRunSessionArtifacts'; @@ -10,6 +11,12 @@ jest.mock('node-fetch'); const { Response } = jest.requireActual('node-fetch') as typeof import('node-fetch'); +async function readStreamAsync(stream: NodeJS.ReadableStream): Promise { + for await (const chunk of stream as Readable) { + void chunk; + } +} + function createLoggerMock(): bunyan { return { info: jest.fn(), @@ -74,7 +81,12 @@ describe(uploadArgentArtifactAsync, () => { logger: createLoggerMock(), } as unknown as CustomBuildContext; - jest.mocked(fetch).mockResolvedValueOnce(new Response(data)); + jest.mocked(fetch).mockResolvedValueOnce(new Response(Readable.from([data]))); + jest + .mocked(uploadDeviceRunSessionArtifactAsync) + .mockImplementationOnce(async (_ctx, { stream }) => { + await readStreamAsync(stream); + }); await uploadArgentArtifactAsync(ctx, { deviceRunSessionId: 'drs-id', @@ -96,7 +108,7 @@ describe(uploadArgentArtifactAsync, () => { artifactId: 'artifact-id', name: 'report.json (artifact-id)', filename: 'report.json', - size: reportedSize, + size: data.length, stream: expect.anything(), }); }); diff --git a/packages/build-tools/src/steps/utils/argentArtifacts.ts b/packages/build-tools/src/steps/utils/argentArtifacts.ts index 6c8975087f..4b83c58a78 100644 --- a/packages/build-tools/src/steps/utils/argentArtifacts.ts +++ b/packages/build-tools/src/steps/utils/argentArtifacts.ts @@ -1,5 +1,10 @@ import { SystemError } from '@expo/eas-build-job'; +import { createReadStream, createWriteStream } from 'node:fs'; +import { mkdtemp, rm, stat } from 'node:fs/promises'; import fetch from 'node-fetch'; +import os from 'node:os'; +import path from 'node:path'; +import { pipeline } from 'node:stream/promises'; import { z } from 'zod'; import { CustomBuildContext } from '../../customBuildContext'; @@ -112,31 +117,43 @@ export async function uploadArgentArtifactAsync( } ): Promise { const filename = artifact.isDirectory ? `${artifact.filename}.tar.gz` : artifact.filename; - ctx.logger.info(`Uploading artifact ${filename} (${formatBytes(artifact.size)}).`); - const stream = await createArgentArtifactDownloadStreamAsync({ - artifact, - toolsUrl, - toolsAuthToken, - }); - await uploadDeviceRunSessionArtifactAsync(ctx, { - deviceRunSessionId, - artifactId: artifact.id, - name: `${filename} (${artifact.id})`, - filename, - size: artifact.size, - stream, - }); + const { logger } = ctx; + logger.info(`Downloading artifact ${filename}.`); + const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'argent-artifact-')); + try { + const temporaryArtifactPath = path.join(temporaryDirectory, path.basename(filename)); + await downloadArgentArtifactToFileAsync({ + artifact, + toolsUrl, + toolsAuthToken, + destinationPath: temporaryArtifactPath, + }); + const { size } = await stat(temporaryArtifactPath); + logger.info(`Uploading artifact ${filename} (${formatBytes(size)}).`); + await uploadDeviceRunSessionArtifactAsync(ctx, { + deviceRunSessionId, + artifactId: artifact.id, + name: `${filename} (${artifact.id})`, + filename, + size, + stream: createReadStream(temporaryArtifactPath), + }); + } finally { + await rm(temporaryDirectory, { recursive: true, force: true }); + } } -async function createArgentArtifactDownloadStreamAsync({ +async function downloadArgentArtifactToFileAsync({ artifact, toolsUrl, toolsAuthToken, + destinationPath, }: { artifact: ArgentArtifact; toolsUrl: string; toolsAuthToken?: string; -}): Promise { + destinationPath: string; +}): Promise { const response = await fetch(new URL(`/artifacts/${artifact.id}`, toolsUrl).toString(), { headers: toolsAuthToken ? { Authorization: `Bearer ${toolsAuthToken}` } : {}, }); @@ -150,5 +167,5 @@ async function createArgentArtifactDownloadStreamAsync({ `Argent artifact ${artifact.id} response did not include a readable body.` ); } - return response.body; + await pipeline(response.body, createWriteStream(destinationPath)); } From 55ecce89765a2d57b50fccfbd893fe1414a33a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Chmiela?= Date: Fri, 19 Jun 2026 09:57:01 +0200 Subject: [PATCH 2/2] Use step logger for Argent artifact uploads --- .../src/steps/functions/startArgentRemoteSession.ts | 1 + .../src/steps/utils/__tests__/argentArtifacts.test.ts | 10 +++------- .../build-tools/src/steps/utils/argentArtifacts.ts | 9 ++++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts index aaac3a6dca..babeaf0761 100644 --- a/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts +++ b/packages/build-tools/src/steps/functions/startArgentRemoteSession.ts @@ -125,6 +125,7 @@ export function createStartArgentRemoteSessionBuildFunction( deviceRunSessionId, toolsUrl: `http://127.0.0.1:${toolServerPort}`, toolsAuthToken: toolServerToken, + logger, }); const publicToolsUrl = await startNgrokTunnelAsync({ diff --git a/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts b/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts index 6428d515d4..6c8b83d88f 100644 --- a/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts +++ b/packages/build-tools/src/steps/utils/__tests__/argentArtifacts.test.ts @@ -40,7 +40,6 @@ describe(listArgentArtifactsAsync, () => { id: 'artifact-id', filename: 'report.json', mimeType: 'application/json', - size: 12, isDirectory: false, }, ], @@ -58,7 +57,6 @@ describe(listArgentArtifactsAsync, () => { id: 'artifact-id', filename: 'report.json', mimeType: 'application/json', - size: 12, isDirectory: false, }, ]); @@ -76,10 +74,8 @@ describe(uploadArgentArtifactAsync, () => { it('downloads an Argent artifact and uploads it as a device run session artifact', async () => { const data = Buffer.from('artifact-data'); - const reportedSize = 1024; - const ctx = { - logger: createLoggerMock(), - } as unknown as CustomBuildContext; + const logger = createLoggerMock(); + const ctx = {} as unknown as CustomBuildContext; jest.mocked(fetch).mockResolvedValueOnce(new Response(Readable.from([data]))); jest @@ -92,11 +88,11 @@ describe(uploadArgentArtifactAsync, () => { deviceRunSessionId: 'drs-id', toolsUrl: 'http://127.0.0.1:1234', toolsAuthToken: 'tools-token', + logger, artifact: { id: 'artifact-id', filename: 'report.json', mimeType: 'application/json', - size: reportedSize, }, }); diff --git a/packages/build-tools/src/steps/utils/argentArtifacts.ts b/packages/build-tools/src/steps/utils/argentArtifacts.ts index 4b83c58a78..918614fa13 100644 --- a/packages/build-tools/src/steps/utils/argentArtifacts.ts +++ b/packages/build-tools/src/steps/utils/argentArtifacts.ts @@ -1,4 +1,5 @@ import { SystemError } from '@expo/eas-build-job'; +import { type bunyan } from '@expo/logger'; import { createReadStream, createWriteStream } from 'node:fs'; import { mkdtemp, rm, stat } from 'node:fs/promises'; import fetch from 'node-fetch'; @@ -19,7 +20,6 @@ const ArgentArtifactSchema = z.object({ id: z.string(), filename: z.string(), mimeType: z.string(), - size: z.number(), isDirectory: z.boolean().optional(), }); const ArgentArtifactsListResponseSchema = z.object({ @@ -34,13 +34,14 @@ export async function pollArgentArtifactsForUploadAsync( deviceRunSessionId, toolsUrl, toolsAuthToken, + logger, }: { deviceRunSessionId: string; toolsUrl: string; toolsAuthToken?: string; + logger: bunyan; } ): Promise { - const { logger } = ctx; logger.info('Started polling Argent tool-server for artifacts.'); const seenArtifactIds = new Set(); let listArtifactsErrorCount = 0; @@ -59,6 +60,7 @@ export async function pollArgentArtifactsForUploadAsync( toolsUrl, toolsAuthToken, artifact, + logger, }).catch(err => { const error = err instanceof Error ? err : new Error(String(err)); Sentry.capture('Could not upload Argent remote session artifact', error); @@ -109,15 +111,16 @@ export async function uploadArgentArtifactAsync( toolsUrl, toolsAuthToken, artifact, + logger, }: { deviceRunSessionId: string; toolsUrl: string; toolsAuthToken?: string; artifact: ArgentArtifact; + logger: bunyan; } ): Promise { const filename = artifact.isDirectory ? `${artifact.filename}.tar.gz` : artifact.filename; - const { logger } = ctx; logger.info(`Downloading artifact ${filename}.`); const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'argent-artifact-')); try {