From 3f2dcc7e09825e837cecf0a1d6213f09352c1216 Mon Sep 17 00:00:00 2001 From: Quinlan Jung Date: Tue, 5 Nov 2024 19:20:44 -0800 Subject: [PATCH] [eas-cli] compute fingerprint on each build (#2663) * Temporary Commit at 11/4/2024, 4:33:33 PM * Temporary Commit at 11/5/2024, 4:17:31 PM * Temporary Commit at 11/5/2024, 4:22:18 PM --- CHANGELOG.md | 2 + packages/eas-cli/src/build/build.ts | 74 +++++++++++++++++-- packages/eas-cli/src/build/metadata.ts | 8 +- packages/eas-cli/src/commands/update/index.ts | 2 +- .../project/maybeUploadFingerprintAsync.ts | 14 ++-- packages/eas-cli/src/utils/fingerprintCli.ts | 36 +++++++++ 6 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 packages/eas-cli/src/utils/fingerprintCli.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c6b65363cf..93aca9b196 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 +- Compute fingerprint for builds with SDK 52 and higher ([#2663](https://github.com/expo/eas-cli/pull/2663) by [@quinlanj](https://github.com/quinlanj)) + ### ๐Ÿ› Bug fixes ### ๐Ÿงน Chores diff --git a/packages/eas-cli/src/build/build.ts b/packages/eas-cli/src/build/build.ts index e2d67332a1..c8fec7e583 100644 --- a/packages/eas-cli/src/build/build.ts +++ b/packages/eas-cli/src/build/build.ts @@ -66,6 +66,7 @@ import { maybeUploadFingerprintAsync } from '../project/maybeUploadFingerprintAs import { resolveRuntimeVersionAsync } from '../project/resolveRuntimeVersionAsync'; import { uploadFileAtPathToGCSAsync } from '../uploads'; import { formatBytes } from '../utils/files'; +import { createFingerprintAsync } from '../utils/fingerprintCli'; import { printJsonOnlyOutput } from '../utils/json'; import { createProgressTracker } from '../utils/progress'; import { sleepAsync } from '../utils/promise'; @@ -174,8 +175,9 @@ export async function prepareBuildRequestForPlatformAsync< } assert(projectArchive); - const runtimeMetadata = await createAndMaybeUploadFingerprintAsync(ctx); - const metadata = await collectMetadataAsync(ctx, runtimeMetadata); + const runtimeAndFingerprintMetadata = + await computeAndMaybeUploadRuntimeAndFingerprintMetadataAsync(ctx); + const metadata = await collectMetadataAsync(ctx, runtimeAndFingerprintMetadata); const buildParams = resolveBuildParamsInput(ctx, metadata); const job = await builder.prepareJobAsync(ctx, { projectArchive, @@ -660,11 +662,38 @@ function formatAccountBillingUrl(accountName: string): string { return new URL(`/accounts/${accountName}/settings/billing`, getExpoWebsiteBaseUrl()).toString(); } -async function createAndMaybeUploadFingerprintAsync( +async function computeAndMaybeUploadRuntimeAndFingerprintMetadataAsync( + ctx: BuildContext +): Promise<{ + runtimeVersion?: string | undefined; + fingerprintHash?: string | undefined; + fingerprintSource?: FingerprintSource | undefined; +}> { + const runtimeAndFingerprintMetadata = + await computeAndMaybeUploadFingerprintFromExpoUpdatesAsync(ctx); + if (!runtimeAndFingerprintMetadata?.fingerprint) { + const fingerprint = await computeAndMaybeUploadFingerprintWithoutExpoUpdatesAsync(ctx); + return { + ...runtimeAndFingerprintMetadata, + ...fingerprint, + }; + } else { + return { + ...runtimeAndFingerprintMetadata, + fingerprintHash: runtimeAndFingerprintMetadata.runtimeVersion, + }; + } +} + +async function computeAndMaybeUploadFingerprintFromExpoUpdatesAsync( ctx: BuildContext ): Promise<{ runtimeVersion?: string; fingerprintSource?: FingerprintSource; + fingerprint?: { + fingerprintSources: object[]; + isDebugFingerprintSource: boolean; + }; }> { const resolvedRuntimeVersion = await resolveRuntimeVersionAsync({ exp: ctx.exp, @@ -689,10 +718,45 @@ async function createAndMaybeUploadFingerprintAsync( }; } - return await maybeUploadFingerprintAsync({ - runtimeVersion: resolvedRuntimeVersion.runtimeVersion, + const uploadedFingerprint = await maybeUploadFingerprintAsync({ + hash: resolvedRuntimeVersion.runtimeVersion, fingerprint: resolvedRuntimeVersion.fingerprint, graphqlClient: ctx.graphqlClient, localBuildMode: ctx.localBuildOptions.localBuildMode, }); + return { + runtimeVersion: uploadedFingerprint.hash, + fingerprintSource: uploadedFingerprint.fingerprintSource, + fingerprint: resolvedRuntimeVersion.fingerprint, + }; +} + +async function computeAndMaybeUploadFingerprintWithoutExpoUpdatesAsync( + ctx: BuildContext, + { debug }: { debug?: boolean } = {} +): Promise<{ + fingerprintHash?: string; + fingerprintSource?: FingerprintSource; +}> { + const fingerprint = await createFingerprintAsync(ctx.projectDir, { + workflow: ctx.workflow, + platform: ctx.platform, + env: ctx.env, + }); + if (!fingerprint) { + return {}; + } + const uploadedFingerprint = await maybeUploadFingerprintAsync({ + hash: fingerprint.hash, + fingerprint: { + fingerprintSources: fingerprint.sources, + isDebugFingerprintSource: debug ?? false, + }, + graphqlClient: ctx.graphqlClient, + localBuildMode: ctx.localBuildOptions.localBuildMode, + }); + return { + fingerprintHash: uploadedFingerprint.hash, + fingerprintSource: uploadedFingerprint.fingerprintSource, + }; } diff --git a/packages/eas-cli/src/build/metadata.ts b/packages/eas-cli/src/build/metadata.ts index c81e15c8ea..0cde2393f6 100644 --- a/packages/eas-cli/src/build/metadata.ts +++ b/packages/eas-cli/src/build/metadata.ts @@ -16,8 +16,9 @@ import { easCliVersion } from '../utils/easCli'; export async function collectMetadataAsync( ctx: BuildContext, - runtimeMetadata: { + runtimeAndFingerprintMetadata: { runtimeVersion?: string | undefined; + fingerprintHash?: string | undefined; fingerprintSource?: FingerprintSource | undefined; } ): Promise { @@ -30,8 +31,9 @@ export async function collectMetadataAsync( workflow: ctx.workflow, credentialsSource: ctx.buildProfile.credentialsSource, sdkVersion: ctx.exp.sdkVersion, - runtimeVersion: runtimeMetadata?.runtimeVersion, - fingerprintSource: runtimeMetadata?.fingerprintSource, + runtimeVersion: runtimeAndFingerprintMetadata?.runtimeVersion, + fingerprintHash: runtimeAndFingerprintMetadata?.fingerprintHash, + fingerprintSource: runtimeAndFingerprintMetadata?.fingerprintSource, reactNativeVersion: await getReactNativeVersionAsync(ctx.projectDir), ...channelObject, distribution, diff --git a/packages/eas-cli/src/commands/update/index.ts b/packages/eas-cli/src/commands/update/index.ts index c5e4ad3218..1cdd2074d4 100644 --- a/packages/eas-cli/src/commands/update/index.ts +++ b/packages/eas-cli/src/commands/update/index.ts @@ -413,7 +413,7 @@ export default class UpdatePublish extends EasCommand { fingerprintSource: info.fingerprint ? ( await maybeUploadFingerprintAsync({ - runtimeVersion: info.runtimeVersion, + hash: info.runtimeVersion, fingerprint: info.fingerprint, graphqlClient, }) diff --git a/packages/eas-cli/src/project/maybeUploadFingerprintAsync.ts b/packages/eas-cli/src/project/maybeUploadFingerprintAsync.ts index 4ce4a6595b..8d069b2341 100644 --- a/packages/eas-cli/src/project/maybeUploadFingerprintAsync.ts +++ b/packages/eas-cli/src/project/maybeUploadFingerprintAsync.ts @@ -11,12 +11,12 @@ import { uploadFileAtPathToGCSAsync } from '../uploads'; import { getTmpDirectory } from '../utils/paths'; export async function maybeUploadFingerprintAsync({ - runtimeVersion, + hash, fingerprint, graphqlClient, localBuildMode, }: { - runtimeVersion: string; + hash: string; fingerprint: { fingerprintSources: object[]; isDebugFingerprintSource: boolean; @@ -24,20 +24,20 @@ export async function maybeUploadFingerprintAsync({ graphqlClient: ExpoGraphqlClient; localBuildMode?: LocalBuildMode; }): Promise<{ - runtimeVersion: string; + hash: string; fingerprintSource?: FingerprintSource; }> { await fs.mkdirp(getTmpDirectory()); const fingerprintLocation = path.join(getTmpDirectory(), `${uuidv4()}-runtime-fingerprint.json`); await fs.writeJSON(fingerprintLocation, { - hash: runtimeVersion, + hash, sources: fingerprint.fingerprintSources, }); if (localBuildMode === LocalBuildMode.LOCAL_BUILD_PLUGIN) { return { - runtimeVersion, + hash, fingerprintSource: { type: FingerprintSourceType.PATH, path: fingerprintLocation, @@ -62,14 +62,14 @@ export async function maybeUploadFingerprintAsync({ Log.warn(errMessage); return { - runtimeVersion, + hash, }; } finally { await fs.remove(fingerprintLocation); } return { - runtimeVersion, + hash, fingerprintSource: { type: FingerprintSourceType.GCS, bucketKey: fingerprintGCSBucketKey, diff --git a/packages/eas-cli/src/utils/fingerprintCli.ts b/packages/eas-cli/src/utils/fingerprintCli.ts new file mode 100644 index 0000000000..4066012c31 --- /dev/null +++ b/packages/eas-cli/src/utils/fingerprintCli.ts @@ -0,0 +1,36 @@ +import { Env, Workflow } from '@expo/eas-build-job'; +import { silent as silentResolveFrom } from 'resolve-from'; + +export async function createFingerprintAsync( + projectDir: string, + options: { + workflow: Workflow; + platform: string; + debug?: boolean; + env: Env | undefined; + cwd?: string; + } +): Promise<{ + hash: string; + sources: object[]; + isDebugSource: boolean; +} | null> { + // @expo/fingerprint is exported in the expo package for SDK 52+ + const fingerprintPath = silentResolveFrom(projectDir, 'expo/fingerprint'); + if (!fingerprintPath) { + return null; + } + const Fingerprint = require(fingerprintPath); + const fingerprintOptions: Record = {}; + if (options.platform) { + fingerprintOptions.platforms = [options.platform]; + } + if (options.workflow === Workflow.MANAGED) { + fingerprintOptions.ignorePaths = ['android/**/*', 'ios/**/*']; + } + if (options.debug) { + fingerprintOptions.debug = true; + } + // eslint-disable-next-line @typescript-eslint/return-await + return await Fingerprint.createFingerprintAsync(projectDir, fingerprintOptions); +}