diff --git a/packages/eas-build-job/src/__tests__/metadata.test.ts b/packages/eas-build-job/src/__tests__/metadata.test.ts index d88968ae..c8ab89c9 100644 --- a/packages/eas-build-job/src/__tests__/metadata.test.ts +++ b/packages/eas-build-job/src/__tests__/metadata.test.ts @@ -1,39 +1,40 @@ -import { MetadataSchema } from '../metadata'; +import { FingerprintSourceType, Metadata, MetadataSchema } from '../metadata'; + +const validMetadata: Metadata = { + appName: 'testapp', + appVersion: '1.0.0', + appBuildVersion: '123', + runtimeVersion: '3.2.1', + cliVersion: '1.2.3', + buildProfile: 'release', + credentialsSource: 'remote', + distribution: 'store', + gitCommitHash: '752e99d2b8fde1bf07ebb8af1b4a3c26a6703943', + gitCommitMessage: 'Lorem ipsum', + trackingContext: {}, + workflow: 'generic' as any, + username: 'notdominik', + iosEnterpriseProvisioning: 'adhoc', + message: 'fix foo, bar, and baz', + runFromCI: true, + runWithNoWaitFlag: true, + customWorkflowName: 'blah blah', + developmentClient: true, + requiredPackageManager: 'yarn', + simulator: true, + selectedImage: 'default', + customNodeVersion: '12.0.0', +}; describe('MetadataSchema', () => { test('valid metadata', () => { - const metadata = { - appName: 'testapp', - appVersion: '1.0.0', - appBuildVersion: '123', - runtimeVersion: '3.2.1', - cliVersion: '1.2.3', - buildProfile: 'release', - credentialsSource: 'remote', - distribution: 'store', - gitCommitHash: '752e99d2b8fde1bf07ebb8af1b4a3c26a6703943', - gitCommitMessage: 'Lorem ipsum', - trackingContext: {}, - workflow: 'generic', - username: 'notdominik', - iosEnterpriseProvisioning: 'adhoc', - message: 'fix foo, bar, and baz', - runFromCI: true, - runWithNoWaitFlag: true, - customWorkflowName: 'blah blah', - developmentClient: true, - requiredPackageManager: 'yarn', - simulator: true, - selectedImage: 'default', - customNodeVersion: '12.0.0', - }; - const { value, error } = MetadataSchema.validate(metadata, { + const { value, error } = MetadataSchema.validate(validMetadata, { stripUnknown: true, convert: true, abortEarly: false, }); expect(error).toBeFalsy(); - expect(value).toEqual(metadata); + expect(value).toEqual(validMetadata); }); test('invalid metadata', () => { const metadata = { @@ -69,4 +70,56 @@ describe('MetadataSchema', () => { '"credentialsSource" must be one of [local, remote]. "gitCommitHash" length must be 40 characters long. "gitCommitHash" must only contain hexadecimal characters. "gitCommitMessage" length must be less than or equal to 4096 characters long. "message" length must be less than or equal to 1024 characters long' ); }); + + test('Allows correct fingerprint', () => { + const metadata: Metadata = { + ...validMetadata, + fingerprintSource: { + type: 'GCS' as any, + bucketKey: + 'development/8a9c5554-cfbe-4b4c-814c-c476a1047db9/fd6f8af4-7293-46bd-bec7-3fe639f4fd3e', + }, + }; + const { value, error } = MetadataSchema.validate(metadata, { + stripUnknown: true, + convert: true, + abortEarly: false, + }); + expect(error).toBeFalsy(); + expect(value).toEqual(metadata); + }); + + test('Validates incorrect fingerprint type', () => { + const metadata: Metadata = { + ...validMetadata, + fingerprintSource: { + type: 'BOO' as any, + bucketKey: + 'development/8a9c5554-cfbe-4b4c-814c-c476a1047db9/fd6f8af4-7293-46bd-bec7-3fe639f4fd3e', + }, + }; + const { error } = MetadataSchema.validate(metadata, { + stripUnknown: true, + convert: true, + abortEarly: false, + }); + expect(error?.message).toEqual('"fingerprintSource.type" must be one of [GCS, PATH, URL]'); + }); + + test('Validates incorrect fingerprint key', () => { + const metadata: Metadata = { + ...validMetadata, + fingerprintSource: { + type: FingerprintSourceType.GCS, + // @ts-expect-error TypeScript is too smart. Need to ignore this for the failing test + url: 'development/8a9c5554-cfbe-4b4c-814c-c476a1047db9/fd6f8af4-7293-46bd-bec7-3fe639f4fd3e', + }, + }; + const { error } = MetadataSchema.validate(metadata, { + stripUnknown: true, + convert: true, + abortEarly: false, + }); + expect(error?.message).toEqual('"fingerprintSource.bucketKey" is required'); + }); }); diff --git a/packages/eas-build-job/src/index.ts b/packages/eas-build-job/src/index.ts index edd01fb6..ddbf325a 100644 --- a/packages/eas-build-job/src/index.ts +++ b/packages/eas-build-job/src/index.ts @@ -13,7 +13,7 @@ export { Platform, Cache, } from './common'; -export { Metadata, sanitizeMetadata } from './metadata'; +export { Metadata, sanitizeMetadata, FingerprintSource, FingerprintSourceType } from './metadata'; export * from './job'; export * from './logs'; export * as errors from './errors'; diff --git a/packages/eas-build-job/src/metadata.ts b/packages/eas-build-job/src/metadata.ts index 32fe6c58..087bfa09 100644 --- a/packages/eas-build-job/src/metadata.ts +++ b/packages/eas-build-job/src/metadata.ts @@ -2,6 +2,17 @@ import Joi from 'joi'; import { Workflow } from './common'; +export enum FingerprintSourceType { + 'GCS' = 'GCS', + 'PATH' = 'PATH', + 'URL' = 'URL', +} + +export type FingerprintSource = + | { type: FingerprintSourceType.GCS; bucketKey: string } + | { type: FingerprintSourceType.PATH; path: string } + | { type: FingerprintSourceType.URL; url: string }; + export type Metadata = { /** * Tracking context @@ -54,6 +65,11 @@ export type Metadata = { */ runtimeVersion?: string; + /** + * The location of the fingerprint file if one exists + */ + fingerprintSource?: FingerprintSource; + /** * Version of the react-native package used in the project. */ @@ -161,6 +177,30 @@ export type Metadata = { customNodeVersion?: string; }; +const FingerprintSourceSchema = Joi.object({ + type: Joi.string() + .valid(...Object.values(FingerprintSourceType)) + .required(), +}) + .when(Joi.object({ type: FingerprintSourceType.GCS }).unknown(), { + then: Joi.object({ + type: Joi.string().valid(FingerprintSourceType.GCS).required(), + bucketKey: Joi.string().required(), + }), + }) + .when(Joi.object({ type: FingerprintSourceType.PATH }).unknown(), { + then: Joi.object({ + type: Joi.string().valid(FingerprintSourceType.PATH).required(), + path: Joi.string().required(), + }), + }) + .when(Joi.object({ type: FingerprintSourceType.URL }).unknown(), { + then: Joi.object({ + type: Joi.string().valid(FingerprintSourceType.URL).required(), + url: Joi.string().uri().required(), + }), + }); + export const MetadataSchema = Joi.object({ trackingContext: Joi.object() .pattern(Joi.string(), [Joi.string(), Joi.number(), Joi.boolean()]) @@ -173,6 +213,7 @@ export const MetadataSchema = Joi.object({ credentialsSource: Joi.string().valid('local', 'remote'), sdkVersion: Joi.string(), runtimeVersion: Joi.string(), + fingerprintSource: FingerprintSourceSchema, reactNativeVersion: Joi.string(), channel: Joi.string(), appName: Joi.string(),