diff --git a/packages/eas-cli/src/metadata/apple/config/reader.ts b/packages/eas-cli/src/metadata/apple/config/reader.ts index 2677267304..07ee59b9d4 100644 --- a/packages/eas-cli/src/metadata/apple/config/reader.ts +++ b/packages/eas-cli/src/metadata/apple/config/reader.ts @@ -7,7 +7,7 @@ import { ReleaseType, } from '@expo/apple-utils'; -import { unique } from '../../utils/array'; +import uniq from '../../../utils/expodash/uniq'; import { AttributesOf } from '../../utils/asc'; import { removeDatePrecision } from '../../utils/date'; import { AppleMetadata } from '../types'; @@ -22,18 +22,18 @@ export const DEFAULT_WHATSNEW = 'Bug fixes and improved stability'; * This uses version 0 of the config schema. */ export class AppleConfigReader { - constructor(public readonly schema: AppleMetadata) {} + public constructor(public readonly schema: AppleMetadata) {} - getAgeRating(): Partial> | null { + public getAgeRating(): Partial> | null { return this.schema.advisory || null; } - getLocales(): string[] { + public getLocales(): string[] { // TODO: filter "default" locales, add option to add non-localized info to the config - return unique(Object.keys(this.schema.info || {})); + return uniq(Object.keys(this.schema.info || {})); } - getInfoLocale( + public getInfoLocale( locale: string ): PartialExcept, 'locale' | 'name'> | null { const info = this.schema.info?.[locale]; @@ -43,7 +43,7 @@ export class AppleConfigReader { return { locale, - name: info.title || 'no name provided', + name: info.title ?? 'no name provided', subtitle: info.subtitle, privacyChoicesUrl: info.privacyChoicesUrl, privacyPolicyText: info.privacyPolicyText, @@ -51,7 +51,7 @@ export class AppleConfigReader { }; } - getCategories(): CategoryIds | null { + public getCategories(): CategoryIds | null { if (Array.isArray(this.schema.categories) && this.schema.categories.length > 0) { return { primaryCategory: this.schema.categories[0], @@ -63,13 +63,13 @@ export class AppleConfigReader { } /** Get the `AppStoreVersion` object. */ - getVersion(): Partial< + public getVersion(): Partial< Omit, 'releaseType' | 'earliestReleaseDate'> > | null { return this.schema.copyright ? { copyright: this.schema.copyright } : null; } - getVersionRelease(): Partial< + public getVersionRelease(): Partial< Pick, 'releaseType' | 'earliestReleaseDate'> > | null { const { release } = this.schema; @@ -99,7 +99,7 @@ export class AppleConfigReader { return null; } - getVersionLocale( + public getVersionLocale( locale: string, context: { versionIsFirst: boolean } ): Partial> | null { diff --git a/packages/eas-cli/src/metadata/apple/config/writer.ts b/packages/eas-cli/src/metadata/apple/config/writer.ts index 95566af1e0..69e48411b6 100644 --- a/packages/eas-cli/src/metadata/apple/config/writer.ts +++ b/packages/eas-cli/src/metadata/apple/config/writer.ts @@ -18,18 +18,18 @@ export class AppleConfigWriter { constructor(public readonly schema: Partial = {}) {} /** Get the schema result to write it to the config file */ - toSchema(): { configVersion: number; apple: Partial } { + public toSchema(): { configVersion: number; apple: Partial } { return { configVersion: 0, apple: this.schema, }; } - setAgeRating(attributes: AttributesOf): void { + public setAgeRating(attributes: AttributesOf): void { this.schema.advisory = attributes; } - setInfoLocale(attributes: AttributesOf): void { + public setInfoLocale(attributes: AttributesOf): void { this.schema.info = this.schema.info ?? {}; const existing = this.schema.info[attributes.locale] ?? {}; @@ -43,7 +43,7 @@ export class AppleConfigWriter { }; } - setCategories({ primaryCategory, secondaryCategory }: AttributesOf): void { + public setCategories({ primaryCategory, secondaryCategory }: AttributesOf): void { this.schema.categories = []; // TODO: see why these types are conflicting @@ -55,13 +55,13 @@ export class AppleConfigWriter { } } - setVersion( + public setVersion( attributes: Omit, 'releaseType' | 'earliestReleaseDate'> ): void { this.schema.copyright = optional(attributes.copyright); } - setVersionRelease( + public setVersionRelease( attributes: Pick, 'releaseType' | 'earliestReleaseDate'> ): void { if (attributes.releaseType === ReleaseType.SCHEDULED) { @@ -83,7 +83,7 @@ export class AppleConfigWriter { } } - setVersionLocale(attributes: AttributesOf): void { + public setVersionLocale(attributes: AttributesOf): void { this.schema.info = this.schema.info ?? {}; const existing = this.schema.info[attributes.locale] ?? {}; diff --git a/packages/eas-cli/src/metadata/apple/task.ts b/packages/eas-cli/src/metadata/apple/task.ts index 037ab9cc55..9f7c1f0265 100644 --- a/packages/eas-cli/src/metadata/apple/task.ts +++ b/packages/eas-cli/src/metadata/apple/task.ts @@ -4,16 +4,16 @@ import { AppleData, PartialAppleData } from './data'; export abstract class AppleTask { /** Get a description from the task to use as section headings in the log */ - abstract name(): string; + public abstract name(): string; /** Prepare the data from the App Store to start syncing with the store configuration */ - abstract prepareAsync(options: TaskPrepareOptions): Promise; + public abstract prepareAsync(options: TaskPrepareOptions): Promise; /** Download all information from the App Store to generate the store configuration */ - abstract downloadAsync(options: TaskDownloadOptions): Promise; + public abstract downloadAsync(options: TaskDownloadOptions): Promise; /** Upload all information from the store configuration to the App Store */ - abstract uploadAsync(options: TaskUploadOptions): Promise; + public abstract uploadAsync(options: TaskUploadOptions): Promise; } export type TaskPrepareOptions = { diff --git a/packages/eas-cli/src/metadata/apple/tasks/age-rating.ts b/packages/eas-cli/src/metadata/apple/tasks/age-rating.ts index 6cb6319d4e..32fd755174 100644 --- a/packages/eas-cli/src/metadata/apple/tasks/age-rating.ts +++ b/packages/eas-cli/src/metadata/apple/tasks/age-rating.ts @@ -12,20 +12,20 @@ export type AgeRatingData = { }; export class AgeRatingTask extends AppleTask { - name = (): string => 'age rating declarations'; + public name = (): string => 'age rating declarations'; - async prepareAsync({ context }: TaskPrepareOptions): Promise { + public async prepareAsync({ context }: TaskPrepareOptions): Promise { assert(context.version, `App version information is not prepared, can't update age rating`); context.ageRating = (await context.version.getAgeRatingDeclarationAsync()) || undefined; } - async downloadAsync({ config, context }: TaskDownloadOptions): Promise { + public async downloadAsync({ config, context }: TaskDownloadOptions): Promise { if (context.ageRating) { config.setAgeRating(context.ageRating.attributes); } } - async uploadAsync({ config, context }: TaskUploadOptions): Promise { + public async uploadAsync({ config, context }: TaskUploadOptions): Promise { assert(context.ageRating, `Age rating not initialized, can't update age rating`); const ageRating = config.getAgeRating(); diff --git a/packages/eas-cli/src/metadata/apple/tasks/app-info.ts b/packages/eas-cli/src/metadata/apple/tasks/app-info.ts index aaa49d43e3..fc9e798393 100644 --- a/packages/eas-cli/src/metadata/apple/tasks/app-info.ts +++ b/packages/eas-cli/src/metadata/apple/tasks/app-info.ts @@ -15,9 +15,9 @@ export type AppInfoData = { }; export class AppInfoTask extends AppleTask { - name = (): string => 'app information'; + public name = (): string => 'app information'; - async prepareAsync({ context }: TaskPrepareOptions): Promise { + public async prepareAsync({ context }: TaskPrepareOptions): Promise { const info = await retryIfNullAsync(() => context.app.getEditAppInfoAsync()); assert(info, 'Could not resolve the editable app info to update'); @@ -25,7 +25,7 @@ export class AppInfoTask extends AppleTask { context.infoLocales = await info.getLocalizationsAsync(); } - async downloadAsync({ config, context }: TaskDownloadOptions): Promise { + public async downloadAsync({ config, context }: TaskDownloadOptions): Promise { assert(context.info, `App info not initialized, can't download info`); config.setCategories(context.info.attributes); @@ -35,7 +35,7 @@ export class AppInfoTask extends AppleTask { } } - async uploadAsync({ config, context }: TaskUploadOptions): Promise { + public async uploadAsync({ config, context }: TaskUploadOptions): Promise { assert(context.info, `App info not initialized, can't update info`); const categories = config.getCategories(); @@ -68,10 +68,11 @@ export class AppInfoTask extends AppleTask { const model = context.infoLocales.find(model => model.attributes.locale === locale); await logAsync( - () => - model - ? model.updateAsync(attributes) - : context.info.createLocalizationAsync({ ...attributes, locale }), + async () => { + return model + ? await model.updateAsync(attributes) + : await context.info.createLocalizationAsync({ ...attributes, locale }); + }, { pending: `${model ? 'Updating' : 'Creating'} localized info for ${locale}...`, success: `${model ? 'Updated' : 'Created'} localized info for ${locale}`, diff --git a/packages/eas-cli/src/metadata/apple/tasks/app-version.ts b/packages/eas-cli/src/metadata/apple/tasks/app-version.ts index f8e862eccc..117bff20d8 100644 --- a/packages/eas-cli/src/metadata/apple/tasks/app-version.ts +++ b/packages/eas-cli/src/metadata/apple/tasks/app-version.ts @@ -28,17 +28,17 @@ export type AppVersionData = { export class AppVersionTask extends AppleTask { private options: AppVersionOptions; - constructor(options: Partial = {}) { + public constructor(options: Partial = {}) { super(); this.options = { - platform: options.platform || Platform.IOS, - editLive: options.editLive || false, + platform: options.platform ?? Platform.IOS, + editLive: options.editLive ?? false, }; } - name = (): string => (this.options.editLive ? 'live app version' : 'editable app version'); + public name = (): string => (this.options.editLive ? 'live app version' : 'editable app version'); - async prepareAsync({ context }: TaskPrepareOptions): Promise { + public async prepareAsync({ context }: TaskPrepareOptions): Promise { const { version, versionIsFirst, versionIsLive } = await resolveVersionAsync( context.app, this.options @@ -52,7 +52,7 @@ export class AppVersionTask extends AppleTask { context.versionLocales = await version.getLocalizationsAsync(); } - async downloadAsync({ config, context }: TaskDownloadOptions): Promise { + public async downloadAsync({ config, context }: TaskDownloadOptions): Promise { assert(context.version, `App version not initialized, can't download version`); config.setVersion(context.version.attributes); @@ -63,7 +63,7 @@ export class AppVersionTask extends AppleTask { } } - async uploadAsync({ config, context }: TaskUploadOptions): Promise { + public async uploadAsync({ config, context }: TaskUploadOptions): Promise { assert(context.version, `App version not initialized, can't update version`); const version = config.getVersion(); @@ -97,10 +97,11 @@ export class AppVersionTask extends AppleTask { const oldModel = context.versionLocales.find(model => model.attributes.locale === locale); await logAsync( - () => - oldModel - ? oldModel.updateAsync(attributes) - : context.version.createLocalizationAsync({ ...attributes, locale }), + async () => { + return oldModel + ? await oldModel.updateAsync(attributes) + : await context.version.createLocalizationAsync({ ...attributes, locale }); + }, { pending: `${oldModel ? 'Updating' : 'Creating'} localized version for ${locale}...`, success: `${oldModel ? 'Updated' : 'Created'} localized version for ${locale}`, diff --git a/packages/eas-cli/src/metadata/context.ts b/packages/eas-cli/src/metadata/context.ts index 161d47c9c7..63a518de3a 100644 --- a/packages/eas-cli/src/metadata/context.ts +++ b/packages/eas-cli/src/metadata/context.ts @@ -64,7 +64,9 @@ export async function createMetadataContextAsync(params: { const exp = params.exp ?? getExpoConfig(params.projectDir); const user = await ensureLoggedInAsync(); - const bundleIdentifier = await getBundleIdentifierAsync(params.projectDir, exp); + const bundleIdentifier = + iosSubmissionProfile.bundleIdentifier ?? + (await getBundleIdentifierAsync(params.projectDir, exp)); return { platform: Platform.IOS, diff --git a/packages/eas-cli/src/metadata/download.ts b/packages/eas-cli/src/metadata/download.ts index 70e7441afe..2f607f8bef 100644 --- a/packages/eas-cli/src/metadata/download.ts +++ b/packages/eas-cli/src/metadata/download.ts @@ -54,8 +54,11 @@ export async function downloadMetadataAsync(metadataCtx: MetadataContext): Promi } } - await fs.writeJson(filePath, config.toSchema(), { spaces: 2 }); - unsubscribeTelemetry(); + try { + await fs.writeJson(filePath, config.toSchema(), { spaces: 2 }); + } finally { + unsubscribeTelemetry(); + } if (errors.length > 0) { throw new MetadataDownloadError(errors, executionId); diff --git a/packages/eas-cli/src/metadata/errors.ts b/packages/eas-cli/src/metadata/errors.ts index 6142d96b2f..a4f5bc2465 100644 --- a/packages/eas-cli/src/metadata/errors.ts +++ b/packages/eas-cli/src/metadata/errors.ts @@ -8,7 +8,7 @@ import Log, { link } from '../log'; * and should contain useful information for the user to solve before trying again. */ export class MetadataValidationError extends Error { - constructor(message?: string, public readonly errors?: ErrorObject[]) { + public constructor(message?: string, public readonly errors?: ErrorObject[]) { super(message ?? 'Store configuration validation failed'); } } @@ -20,7 +20,7 @@ export class MetadataValidationError extends Error { * It contains that list of encountered errors to present to the user. */ export class MetadataUploadError extends Error { - constructor(public readonly errors: Error[], public readonly executionId: string) { + public constructor(public readonly errors: Error[], public readonly executionId: string) { super( `Store configuration upload encountered ${ errors.length === 1 ? 'an error' : `${errors.length} errors` @@ -36,7 +36,7 @@ export class MetadataUploadError extends Error { * It contains that list of encountered errors to present to the user. */ export class MetadataDownloadError extends Error { - constructor(public readonly errors: Error[], public readonly executionId: string) { + public constructor(public readonly errors: Error[], public readonly executionId: string) { super( `Store configuration download encountered ${ errors.length === 1 ? 'an error' : `${errors.length} errors` diff --git a/packages/eas-cli/src/metadata/utils/__tests__/date.test.ts b/packages/eas-cli/src/metadata/utils/__tests__/date.test.ts index a08779429b..03e32a4ca6 100644 --- a/packages/eas-cli/src/metadata/utils/__tests__/date.test.ts +++ b/packages/eas-cli/src/metadata/utils/__tests__/date.test.ts @@ -4,7 +4,6 @@ describe(removeDatePrecision, () => { it('returns null for falsy values', () => { expect(removeDatePrecision(null)).toBeNull(); expect(removeDatePrecision(undefined)).toBeNull(); - expect(removeDatePrecision(false)).toBeNull(); }); it('returns null for invalid dates', () => { diff --git a/packages/eas-cli/src/metadata/utils/array.ts b/packages/eas-cli/src/metadata/utils/array.ts deleted file mode 100644 index ab00e45a3c..0000000000 --- a/packages/eas-cli/src/metadata/utils/array.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function unique(items: T[]): T[] { - const set = new Set(items); - return [...set]; -} diff --git a/packages/eas-cli/src/metadata/utils/date.ts b/packages/eas-cli/src/metadata/utils/date.ts index 7ac64b9719..28f427518b 100644 --- a/packages/eas-cli/src/metadata/utils/date.ts +++ b/packages/eas-cli/src/metadata/utils/date.ts @@ -9,7 +9,7 @@ * "pointer": "/data/attributes/earliestReleaseDate" * } */ -export function removeDatePrecision(date: any): null | Date { +export function removeDatePrecision(date: null | undefined | string | number | Date): null | Date { if (date) { try { const result = new Date(date); diff --git a/packages/eas-cli/src/utils/expodash/__tests__/uniq-test.ts b/packages/eas-cli/src/utils/expodash/__tests__/uniq-test.ts new file mode 100644 index 0000000000..eb3e6de63f --- /dev/null +++ b/packages/eas-cli/src/utils/expodash/__tests__/uniq-test.ts @@ -0,0 +1,15 @@ +import uniq from '../uniq'; + +describe(uniq, () => { + it('returns unique numbers from a list', () => { + expect(uniq([1, 2, 2, 3, 4, 2, 3])).toEqual([1, 2, 3, 4]); + }); + + it('returns unique strings from a list', () => { + expect(uniq(['hi', 'hello', 'hello', 'ola'])).toEqual(['hi', 'hello', 'ola']); + }); + + it('returns unique mixed types from a list', () => { + expect(uniq([1, 2, 2, 'hi', 'hi', 'hello', 3])).toEqual([1, 2, 'hi', 'hello', 3]); + }); +}); diff --git a/packages/eas-cli/src/utils/expodash/uniq.ts b/packages/eas-cli/src/utils/expodash/uniq.ts new file mode 100644 index 0000000000..8f8b093cba --- /dev/null +++ b/packages/eas-cli/src/utils/expodash/uniq.ts @@ -0,0 +1,4 @@ +export default function uniq(items: T[]): T[] { + const set = new Set(items); + return [...set]; +}