From bb39a03113f198ad6d446da190727971bfb55132 Mon Sep 17 00:00:00 2001 From: benwolfram Date: Fri, 15 Nov 2024 10:26:51 -0600 Subject: [PATCH] make getHydrogenVersion shared --- packages/cli/oclif.manifest.json | 2 +- packages/cli/src/commands/hydrogen/deploy.ts | 2 +- .../cli/src/commands/hydrogen/upgrade.test.ts | 49 +------ packages/cli/src/commands/hydrogen/upgrade.ts | 35 +---- packages/cli/src/lib/get-version.test.ts | 132 ++++++++++++++++++ packages/cli/src/lib/get-version.ts | 49 +++++++ 6 files changed, 185 insertions(+), 84 deletions(-) create mode 100644 packages/cli/src/lib/get-version.test.ts create mode 100644 packages/cli/src/lib/get-version.ts diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 4928d2e908..c482ec024c 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -1749,4 +1749,4 @@ } }, "version": "9.0.2" -} +} \ No newline at end of file diff --git a/packages/cli/src/commands/hydrogen/deploy.ts b/packages/cli/src/commands/hydrogen/deploy.ts index 869c726ebe..e89396ad69 100644 --- a/packages/cli/src/commands/hydrogen/deploy.ts +++ b/packages/cli/src/commands/hydrogen/deploy.ts @@ -54,7 +54,7 @@ import {prepareDiffDirectory} from '../../lib/template-diff.js'; import {isClassicProject} from '../../lib/remix-config.js'; import {packageManagers} from '../../lib/package-managers.js'; import {setupResourceCleanup} from '../../lib/resource-cleanup.js'; -import {getHydrogenVersion} from './upgrade.js'; +import {getHydrogenVersion} from '../../lib/get-version.js'; const DEPLOY_OUTPUT_FILE_HANDLE = 'h2_deploy_log.json'; diff --git a/packages/cli/src/commands/hydrogen/upgrade.test.ts b/packages/cli/src/commands/hydrogen/upgrade.test.ts index bf8c85697e..b51268c092 100644 --- a/packages/cli/src/commands/hydrogen/upgrade.test.ts +++ b/packages/cli/src/commands/hydrogen/upgrade.test.ts @@ -20,7 +20,6 @@ import { getAbsoluteVersion, getAvailableUpgrades, getCummulativeRelease, - getHydrogenVersion, getSelectedRelease, runUpgrade, type CumulativeRelease, @@ -29,6 +28,7 @@ import { getChangelog, displayDevUpgradeNotice, } from './upgrade.js'; +import {getHydrogenVersion} from '../../lib/get-version.js'; import {getSkeletonSourceDir} from '../../lib/build.js'; vi.mock('@shopify/cli-kit/node/session'); @@ -168,53 +168,6 @@ describe('upgrade', async () => { }); describe('getHydrogenVersion', () => { - it('throws if no package.json is found', async () => { - await inTemporaryHydrogenRepo( - async (appPath) => { - await expect(runUpgrade({appPath})).rejects.toThrowError( - 'valid package.json', - ); - }, - {packageJson: undefined}, - ); - }); - - it('throws if no hydrogen version is found in package.json', async () => { - await inTemporaryHydrogenRepo( - async (appPath) => { - await expect(runUpgrade({appPath})).rejects.toThrowError( - 'version in package.json', - ); - }, - { - cleanGitRepo: true, - packageJson: { - name: 'some-name', - dependencies: {}, - }, - }, - ); - }); - - it('returns the current hydrogen version from the package.json', async () => { - await inTemporaryHydrogenRepo( - async (appPath) => { - const hydrogen = await getHydrogenVersion({appPath}); - - expect(hydrogen).toBeDefined(); - expect(hydrogen.currentVersion).toMatch('^2023.1.6'); - expect(hydrogen.currentDependencies).toMatchObject({ - ...OUTDATED_HYDROGEN_PACKAGE_JSON.dependencies, - ...OUTDATED_HYDROGEN_PACKAGE_JSON.devDependencies, - }); - }, - { - cleanGitRepo: true, - packageJson: OUTDATED_HYDROGEN_PACKAGE_JSON, - }, - ); - }); - it('exists when run over a prerelease "next" version', async () => { await inTemporaryHydrogenRepo( async (appPath) => { diff --git a/packages/cli/src/commands/hydrogen/upgrade.ts b/packages/cli/src/commands/hydrogen/upgrade.ts index c1682258bc..1a73ecfdb0 100644 --- a/packages/cli/src/commands/hydrogen/upgrade.ts +++ b/packages/cli/src/commands/hydrogen/upgrade.ts @@ -33,6 +33,7 @@ import {getCliCommand} from '../../lib/shell.js'; import {commonFlags, flagsToCamelObject} from '../../lib/flags.js'; import {getProjectPaths} from '../../lib/remix-config.js'; import {hydrogenPackagesPath, isHydrogenMonorepo} from '../../lib/build.js'; +import {getHydrogenVersion} from '../../lib/get-version.js'; type ReleaseItem = { breaking?: boolean; @@ -232,40 +233,6 @@ async function checkDirtyGitBranch(appPath: string) { } } -/** - * Gets the current @shopify/hydrogen version from the app's package.json - */ -export async function getHydrogenVersion({appPath}: {appPath: string}) { - const {root} = getProjectPaths(appPath); - const packageJsonPath = joinPath(root, 'package.json'); - - let packageJson: PackageJson | undefined; - - try { - packageJson = JSON.parse(await readFile(packageJsonPath)); - } catch { - throw new AbortError( - 'Could not find a valid package.json', - 'Please make sure you are running the command in a npm project', - ); - } - - const currentDependencies = { - ...packageJson?.dependencies, - ...packageJson?.devDependencies, - }; - - const currentVersion = currentDependencies['@shopify/hydrogen']; - - if (!currentVersion) { - throw new AbortError( - 'Could not find a valid Hydrogen version in package.json', - 'Please make sure you are running the command in a Hydrogen project', - ); - } - - return {currentVersion, currentDependencies}; -} /** * Fetches the changelog.json file from the Hydrogen repo diff --git a/packages/cli/src/lib/get-version.test.ts b/packages/cli/src/lib/get-version.test.ts new file mode 100644 index 0000000000..e9008debbe --- /dev/null +++ b/packages/cli/src/lib/get-version.test.ts @@ -0,0 +1,132 @@ +import {describe, it, expect} from 'vitest'; +import { + inTemporaryDirectory, + writeFile, + fileExists, +} from '@shopify/cli-kit/node/fs'; +import {createRequire} from 'node:module'; +import {execa} from 'execa'; +import {getHydrogenVersion} from './get-version.js'; +import {PackageJson} from 'type-fest'; +import {joinPath} from '@shopify/cli-kit/node/path'; +import {getSkeletonSourceDir} from './build.js'; + +/** + * Creates a temporary directory with a git repo and a package.json + */ +async function inTemporaryHydrogenRepo( + cb: (tmpDir: string) => Promise, + { + cleanGitRepo, + packageJson, + }: { + cleanGitRepo?: boolean; + packageJson?: PackageJson; + } = {cleanGitRepo: true}, +) { + return inTemporaryDirectory(async (tmpDir) => { + // init the git repo + await execa('git', ['init'], {cwd: tmpDir}); + + if (packageJson) { + const packageJsonPath = joinPath(tmpDir, 'package.json'); + await writeFile(packageJsonPath, JSON.stringify(packageJson)); + expect(await fileExists(packageJsonPath)).toBeTruthy(); + } + + // expect to be a git repo + expect(await fileExists(joinPath(tmpDir, '/.git/config'))).toBeTruthy(); + + if (cleanGitRepo) { + await execa('git', ['add', 'package.json'], {cwd: tmpDir}); + + if (process.env.NODE_ENV === 'test' && process.env.CI) { + await execa('git', ['config', 'user.email', 'test@hydrogen.shop'], { + cwd: tmpDir, + }); + await execa('git', ['config', 'user.name', 'Hydrogen Test'], { + cwd: tmpDir, + }); + } + await execa('git', ['commit', '-m', 'initial commit'], {cwd: tmpDir}); + } + + await cb(tmpDir); + }); +} + +async function createOutdatedSkeletonPackageJson() { + const require = createRequire(import.meta.url); + const packageJson: PackageJson = require(joinPath( + getSkeletonSourceDir(), + 'package.json', + )); + + if (!packageJson) throw new Error('Could not parse package.json'); + if (!packageJson?.dependencies) + throw new Error('Could not parse package.json dependencies'); + if (!packageJson?.devDependencies) + throw new Error('Could not parse package.json devDependencies'); + + // bump the versions to be outdated + packageJson.dependencies['@shopify/hydrogen'] = '^2023.1.6'; + packageJson.dependencies['@remix-run/react'] = '1.12.0'; + packageJson.devDependencies['@shopify/cli-hydrogen'] = '^4.0.8'; + packageJson.devDependencies['@shopify/remix-oxygen'] = '^1.0.3'; + packageJson.devDependencies['@remix-run/dev'] = '1.12.0'; + packageJson.devDependencies['typescript'] = '^4.9.5'; + + return packageJson; +} + +describe('getHydrogenVersion', async () => { + const OUTDATED_HYDROGEN_PACKAGE_JSON = + await createOutdatedSkeletonPackageJson(); + + it('throws if no package.json is found', async () => { + await inTemporaryHydrogenRepo( + async (appPath) => { + await expect(getHydrogenVersion({appPath})).rejects.toThrowError( + 'valid package.json', + ); + }, + {packageJson: undefined}, + ); + }); + + it('throws if no hydrogen version is found in package.json', async () => { + await inTemporaryHydrogenRepo( + async (appPath) => { + await expect(getHydrogenVersion({appPath})).rejects.toThrowError( + 'version in package.json', + ); + }, + { + cleanGitRepo: true, + packageJson: { + name: 'some-name', + dependencies: {}, + }, + }, + ); + }); + + it('returns the current hydrogen version from the package.json', async () => { + await inTemporaryHydrogenRepo( + async (appPath) => { + const hydrogen = await getHydrogenVersion({appPath}); + + expect(hydrogen).toBeDefined(); + expect(hydrogen.currentVersion).toMatch('^2023.1.6'); + expect(hydrogen.currentDependencies).toMatchObject({ + ...OUTDATED_HYDROGEN_PACKAGE_JSON.dependencies, + ...OUTDATED_HYDROGEN_PACKAGE_JSON.devDependencies, + }); + }, + { + cleanGitRepo: true, + packageJson: OUTDATED_HYDROGEN_PACKAGE_JSON, + }, + ); + }); +}); diff --git a/packages/cli/src/lib/get-version.ts b/packages/cli/src/lib/get-version.ts new file mode 100644 index 0000000000..81d4e7db69 --- /dev/null +++ b/packages/cli/src/lib/get-version.ts @@ -0,0 +1,49 @@ +import { AbortError } from "@shopify/cli-kit/node/error"; +import { readFile } from "@shopify/cli-kit/node/fs"; +import { joinPath } from "@shopify/cli-kit/node/path"; +import { PackageJson } from "type-fest"; +import { getProjectPaths } from "./remix-config.js"; + +interface Return { + currentVersion: string; + currentDependencies: Record; +} + +/** + * Gets the current @shopify/hydrogen version from the app's package.json + */ +export async function getHydrogenVersion({appPath}: {appPath: string}): Promise { + const {root} = getProjectPaths(appPath); + const packageJsonPath = joinPath(root, 'package.json'); + + let packageJson: PackageJson | undefined; + + try { + packageJson = JSON.parse(await readFile(packageJsonPath)); + } catch { + throw new AbortError( + 'Could not find a valid package.json', + 'Please make sure you are running the command in a npm project', + ); + } + + const currentDependenciesWithEmptyValues = { + ...packageJson?.dependencies, + ...packageJson?.devDependencies, + }; + + const currentDependencies = Object.fromEntries( + Object.entries(currentDependenciesWithEmptyValues).filter(([_, value]) => value) + ) as Record; + + const currentVersion = currentDependencies['@shopify/hydrogen']; + + if (!currentVersion) { + throw new AbortError( + 'Could not find a valid Hydrogen version in package.json', + 'Please make sure you are running the command in a Hydrogen project', + ); + } + + return {currentVersion, currentDependencies}; +}