From 621303bf3dfff38ef8dee55c8f98fd583226f568 Mon Sep 17 00:00:00 2001 From: Piotr Szeremeta Date: Wed, 29 Nov 2023 14:40:42 +0100 Subject: [PATCH] [ENG-10412] Upload projectMetadata file --- CHANGELOG.md | 2 + packages/eas-cli/graphql.schema.json | 12 +++++ packages/eas-cli/src/build/build.ts | 44 ++++++++++++++----- packages/eas-cli/src/build/graphql.ts | 3 ++ .../eas-cli/src/build/utils/repository.ts | 29 ++++++++++++ packages/eas-cli/src/graphql/generated.ts | 1 + 6 files changed, 79 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c866442f..b3b7547b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐ŸŽ‰ New features +- Generate metadata file for project archive ([#2149](https://github.com/expo/eas-cli/pull/2149) by [@khamilowicz](https://github.com/khamilowicz)) + ### ๐Ÿ› Bug fixes ### ๐Ÿงน Chores diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index 6b0ce2d649..38a90171f3 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -29512,6 +29512,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "metadataLocation", + "description": null, + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "gitRef", "description": null, diff --git a/packages/eas-cli/src/build/build.ts b/packages/eas-cli/src/build/build.ts index 836c7ead5c..871c3ac277 100644 --- a/packages/eas-cli/src/build/build.ts +++ b/packages/eas-cli/src/build/build.ts @@ -53,7 +53,7 @@ import { transformMetadata } from './graphql'; import { LocalBuildMode, runLocalBuildAsync } from './local'; import { collectMetadataAsync } from './metadata'; import { printDeprecationWarnings } from './utils/printBuildInfo'; -import { makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository'; +import { makeProjectMetadataFileAsync, makeProjectTarballAsync, reviewAndCommitChangesAsync } from './utils/repository'; export interface CredentialsResult { source: CredentialsSource.LOCAL | CredentialsSource.REMOTE; @@ -133,9 +133,11 @@ export async function prepareBuildRequestForPlatformAsync< let projectArchive: ArchiveSource | undefined; if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) { + const projectPath = (await makeProjectTarballAsync(ctx.vcsClient)).path; projectArchive = { type: ArchiveSourceType.PATH, - path: (await makeProjectTarballAsync(ctx.vcsClient)).path, + path: projectPath, + metadataLocation: (await makeProjectMetadataFileAsync(projectPath)).path, }; } else if (ctx.localBuildOptions.localBuildMode === LocalBuildMode.INTERNAL) { projectArchive = { @@ -145,7 +147,7 @@ export async function prepareBuildRequestForPlatformAsync< } else if (!ctx.localBuildOptions.localBuildMode) { projectArchive = { type: ArchiveSourceType.GCS, - bucketKey: await uploadProjectAsync(ctx), + ...(await uploadProjectAsync(ctx)), }; } assert(projectArchive); @@ -231,7 +233,10 @@ export function handleBuildRequestError(error: any, platform: Platform): never { async function uploadProjectAsync( ctx: BuildContext -): Promise { +): Promise<{ + bucketKey: string; + metadataLocation: string; +}> { let projectTarballPath; try { return await withAnalyticsAsync( @@ -274,7 +279,24 @@ async function uploadProjectAsync( completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`, }) ); - return bucketKey; + + const projectMetadataFile = await makeProjectMetadataFileAsync(projectTarball.path); + + const metadataLocation = await uploadFileAtPathToGCSAsync( + ctx.graphqlClient, + UploadSessionType.EasBuildGcsProjectSources, + projectMetadataFile.path, + createProgressTracker({ + total: projectMetadataFile.size, + message: ratio => + `Uploading metadata to EAS Build (${formatBytes(projectMetadataFile.size * ratio)} / ${formatBytes( + projectMetadataFile.size + )})`, + completedMessage: (duration: string) => `Uploaded to EAS ${chalk.dim(duration)}`, + }) + ); + + return { bucketKey, metadataLocation }; }, { attemptEvent: BuildEvent.PROJECT_UPLOAD_ATTEMPT, @@ -536,9 +558,8 @@ function formatSettledBuildsText(builds: BuildFragment[]): string { builds.find(build => build.platform === platform), `Build for platform ${platform} must be defined in this context` ); - return `${appPlatformEmojis[platform]} ${ - appPlatformDisplayNames[platform] - } build - status: ${chalk.bold(statusToDisplayName[build.status])}`; + return `${appPlatformEmojis[platform]} ${appPlatformDisplayNames[platform] + } build - status: ${chalk.bold(statusToDisplayName[build.status])}`; }) .join('\n '); } @@ -558,7 +579,7 @@ function formatPendingBuildsText(originalSpinnerText: string, builds: BuildFragm const percent = Math.floor( ((build.initialQueuePosition - build.queuePosition + 1) / (build.initialQueuePosition + 1)) * - 100 + 100 ); const estimatedWaitTime = typeof build.estimatedWaitTimeLeftSeconds === 'number' @@ -566,9 +587,8 @@ function formatPendingBuildsText(originalSpinnerText: string, builds: BuildFragm : ''; extraInfo = ` - queue progress: ${chalk.bold(`${percent}%`)}${estimatedWaitTime}`; } - return `${appPlatformEmojis[platform]} ${ - appPlatformDisplayNames[platform] - } build - status: ${chalk.bold(status)}${extraInfo}`; + return `${appPlatformEmojis[platform]} ${appPlatformDisplayNames[platform] + } build - status: ${chalk.bold(status)}${extraInfo}`; }), ].join('\n '); } diff --git a/packages/eas-cli/src/build/graphql.ts b/packages/eas-cli/src/build/graphql.ts index 0ba65b6553..1d569a1034 100644 --- a/packages/eas-cli/src/build/graphql.ts +++ b/packages/eas-cli/src/build/graphql.ts @@ -23,16 +23,19 @@ export function transformProjectArchive(archiveSource: ArchiveSource): ProjectAr return { type: ProjectArchiveSourceType.S3, bucketKey: archiveSource.bucketKey, + metadataLocation: archiveSource.metadataLocation, }; } else if (archiveSource.type === ArchiveSourceType.GCS) { return { type: ProjectArchiveSourceType.Gcs, bucketKey: archiveSource.bucketKey, + metadataLocation: archiveSource.metadataLocation, }; } else if (archiveSource.type === ArchiveSourceType.URL) { return { type: ProjectArchiveSourceType.Url, url: archiveSource.url, + metadataLocation: archiveSource.metadataLocation, }; } else { throw new Error(`Unsupported project archive source type: '${archiveSource.type}'`); diff --git a/packages/eas-cli/src/build/utils/repository.ts b/packages/eas-cli/src/build/utils/repository.ts index 5c6874c4c4..0886d02969 100644 --- a/packages/eas-cli/src/build/utils/repository.ts +++ b/packages/eas-cli/src/build/utils/repository.ts @@ -83,6 +83,35 @@ export async function commitPromptAsync( }); } +export async function makeProjectMetadataFileAsync(archivePath: string): Promise<{ path: string; size: number }> { + const spinner = ora('Creating project metadata file'); + const timerLabel = 'makeProjectMetadataFileAsync'; + startTimer(timerLabel); + + const metadataLocation = path.join(getTmpDirectory(), `${uuidv4()}.json`); + const archiveContent: string[] = []; + + await tar.list({ + file: archivePath, + onentry: (entry: tar.ReadEntry) => { + if (entry.type === 'File') { + archiveContent.push(entry.path); + } + } + }); + + await fs.writeJSON(metadataLocation, { + archiveContent + }); + + const duration = endTimer(timerLabel); + const prettyTime = formatMilliseconds(duration); + spinner.succeed( + `Created project metadata file ${chalk.dim(prettyTime)}`); + + return { path: metadataLocation, size: await fs.stat(metadataLocation).then(stat => stat.size) }; +} + export async function makeProjectTarballAsync( vcsClient: Client ): Promise<{ path: string; size: number }> { diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index f3cb437c16..97ea0c89d4 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -4239,6 +4239,7 @@ export type Project = { export type ProjectArchiveSourceInput = { bucketKey?: InputMaybe; + metadataLocation?: InputMaybe; gitRef?: InputMaybe; repositoryUrl?: InputMaybe; type: ProjectArchiveSourceType;