diff --git a/packages/build-tools/src/steps/functions/repack.ts b/packages/build-tools/src/steps/functions/repack.ts index f3d05cdac6..dcfb9bee00 100644 --- a/packages/build-tools/src/steps/functions/repack.ts +++ b/packages/build-tools/src/steps/functions/repack.ts @@ -23,6 +23,7 @@ import resolveFrom from 'resolve-from'; import { COMMON_FASTLANE_ENV } from '../../common/fastlane'; import IosCredentialsManager from '../utils/ios/credentials/manager'; +import { resolvePackageVersionAsync } from '../../utils/packageManager'; export function createRepackBuildFunction(): BuildFunction { return new BuildFunction({ @@ -108,12 +109,18 @@ export function createRepackBuildFunction(): BuildFunction { : undefined; const jsBundleOnly = (inputs.js_bundle_only.value as boolean | undefined) ?? false; + const resolvedRepackVersion = + (await resolvePackageVersionAsync({ + logger: stepsCtx.logger, + packageName: inputs.repack_package.value as string, + distTag: inputs.repack_version.value as string, + })) ?? (inputs.repack_version.value as string); stepsCtx.logger.info( - `Using repack from: ${inputs.repack_package.value}@${inputs.repack_version.value}` + `Using repack from: ${inputs.repack_package.value}@${inputs.repack_version.value} (${resolvedRepackVersion})` ); const repackApp = await installAndImportRepackAsync({ packageName: inputs.repack_package.value as string, - version: inputs.repack_version.value as string, + version: resolvedRepackVersion, }); const { repackAppIosAsync, repackAppAndroidAsync } = repackApp; diff --git a/packages/build-tools/src/utils/__tests__/packageManager.test.ts b/packages/build-tools/src/utils/__tests__/packageManager.test.ts index 2e45197d3a..69f996098f 100644 --- a/packages/build-tools/src/utils/__tests__/packageManager.test.ts +++ b/packages/build-tools/src/utils/__tests__/packageManager.test.ts @@ -1,11 +1,14 @@ +import { type bunyan } from '@expo/logger'; import fs from 'fs-extra'; import { vol } from 'memfs'; import path from 'path'; +import semver from 'semver'; import { findPackagerRootDir, getPackageVersionFromPackageJson, resolvePackageManager, + resolvePackageVersionAsync, shouldUseFrozenLockfile, } from '../packageManager'; @@ -68,6 +71,33 @@ describe(resolvePackageManager, () => { }); }); +describe(resolvePackageVersionAsync, () => { + const logger = { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + } as unknown as bunyan; + + it('should resolve version from package name and dist tag', async () => { + const version = await resolvePackageVersionAsync({ + logger, + packageName: '@expo/repack-app', + distTag: 'latest', + }); + expect(semver.valid(version)).toBeTruthy(); + }); + + it('should return null if version cannot be resolved', async () => { + const version = await resolvePackageVersionAsync({ + logger, + packageName: '@expo/repack-app', + distTag: 'nonexistent-dist-tag', + }); + expect(version).toBeNull(); + }); +}); + describe(findPackagerRootDir, () => { beforeEach(() => { vol.reset(); diff --git a/packages/build-tools/src/utils/packageManager.ts b/packages/build-tools/src/utils/packageManager.ts index 6245a34099..8e6a6b36dc 100644 --- a/packages/build-tools/src/utils/packageManager.ts +++ b/packages/build-tools/src/utils/packageManager.ts @@ -1,3 +1,4 @@ +import { type bunyan } from '@expo/logger'; import * as PackageManagerUtils from '@expo/package-manager'; import spawnAsync from '@expo/turtle-spawn'; import semver from 'semver'; @@ -27,6 +28,34 @@ export function resolvePackageManager(directory: string): PackageManager { } } +/** + * Get the version of a package from the dist-tags. + * Returns null if the version cannot be resolved. + */ +export async function resolvePackageVersionAsync({ + logger, + packageName, + distTag, +}: { + logger: bunyan; + packageName: string; + distTag: string; +}): Promise { + try { + const { stdout } = await spawnAsync('npm', ['view', packageName, 'dist-tags', '--json'], { + stdio: 'pipe', + }); + const distTags = JSON.parse(stdout); + if (distTag in distTags) { + return distTags[distTag]; + } + } catch (e: unknown) { + const message = e instanceof Error ? e.message : String(e); + logger.warn(`Unable to resolve version for ${packageName}@${distTag}: ${message}`); + } + return null; +} + export function findPackagerRootDir(currentDir: string): string { return PackageManagerUtils.resolveWorkspaceRoot(currentDir) ?? currentDir; }