From 73a930485888a3200b222efaaef36b9137515d1d Mon Sep 17 00:00:00 2001 From: Ryan Bahan Date: Thu, 2 Apr 2026 18:27:06 -0600 Subject: [PATCH] Remove tempy dependency, use Node builtins for temp directories Replace tempy with Node's built-in fs.mkdtemp/mkdtempSync for temp directory creation and cleanup. Add a small temp-dir.ts module that captures the real temp directory path at module load time using async realpath (via top-level await), which resolves both macOS symlinks (/var -> /private/var) and Windows 8.3 short names (RUNNER~1 -> runneradmin). Cleanup uses maxRetries: 2 for Windows robustness. All matching tempy's behavior. Fix three test files that used bare vi.mock('os') to use partial mocks instead, preserving real os.tmpdir() while still allowing specific function mocking (platform, hostname, EOL). Also removes unused tempy dep from e2e and workspace packages. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../cli/services/init/template/npm.test.ts | 5 +- packages/cli-kit/package.json | 1 - packages/cli-kit/src/private/node/temp-dir.ts | 8 ++ .../node/themes/generate-theme-name.test.ts | 5 +- packages/cli-kit/src/public/node/fs.test.ts | 5 +- packages/cli-kit/src/public/node/fs.ts | 12 +- packages/e2e/package.json | 3 +- pnpm-lock.yaml | 117 ------------------ workspace/package.json | 3 +- workspace/src/type-diff.js | 10 +- 10 files changed, 38 insertions(+), 131 deletions(-) create mode 100644 packages/cli-kit/src/private/node/temp-dir.ts diff --git a/packages/app/src/cli/services/init/template/npm.test.ts b/packages/app/src/cli/services/init/template/npm.test.ts index b8eeb21770a..4b5f3923b5a 100644 --- a/packages/app/src/cli/services/init/template/npm.test.ts +++ b/packages/app/src/cli/services/init/template/npm.test.ts @@ -5,7 +5,10 @@ import {inTemporaryDirectory, mkdir, readFile, writeFile} from '@shopify/cli-kit import {joinPath, moduleDirectory, normalizePath} from '@shopify/cli-kit/node/path' import {platform} from 'os' -vi.mock('os') +vi.mock('os', async (importOriginal) => { + const actual = await importOriginal() + return {...actual, platform: vi.fn()} +}) vi.mock('@shopify/cli-kit/node/node-package-manager') vi.mock('@shopify/cli-kit/common/version', () => ({CLI_KIT_VERSION: '1.2.3'})) diff --git a/packages/cli-kit/package.json b/packages/cli-kit/package.json index cedbf26541b..c0f8ada24dd 100644 --- a/packages/cli-kit/package.json +++ b/packages/cli-kit/package.json @@ -156,7 +156,6 @@ "stacktracey": "2.1.8", "strip-ansi": "7.1.0", "supports-hyperlinks": "3.1.0", - "tempy": "3.1.0", "terminal-link": "3.0.0", "ts-error": "1.0.6", "which": "4.0.0", diff --git a/packages/cli-kit/src/private/node/temp-dir.ts b/packages/cli-kit/src/private/node/temp-dir.ts new file mode 100644 index 00000000000..fbbdfeb3b66 --- /dev/null +++ b/packages/cli-kit/src/private/node/temp-dir.ts @@ -0,0 +1,8 @@ +import {realpath} from 'fs/promises' +import {tmpdir} from 'os' + +// Captured at module load time, before test mocks can interfere. +// Async realpath resolves symlinks (e.g. /var -> /private/var on macOS) +// and 8.3 short names on Windows (e.g. RUNNER~1 -> runneradmin), +// matching tempy's temp-dir behavior. +export const systemTempDir = await realpath(tmpdir()) diff --git a/packages/cli-kit/src/private/node/themes/generate-theme-name.test.ts b/packages/cli-kit/src/private/node/themes/generate-theme-name.test.ts index 7c9dfa5cebc..49acc905139 100644 --- a/packages/cli-kit/src/private/node/themes/generate-theme-name.test.ts +++ b/packages/cli-kit/src/private/node/themes/generate-theme-name.test.ts @@ -3,7 +3,10 @@ import {beforeEach, describe, expect, test, vi} from 'vitest' import {hostname} from 'os' import {randomBytes} from 'crypto' -vi.mock('os') +vi.mock('os', async (importOriginal) => { + const actual = await importOriginal() + return {...actual, hostname: vi.fn()} +}) vi.mock('crypto') describe('generateThemeName', () => { diff --git a/packages/cli-kit/src/public/node/fs.test.ts b/packages/cli-kit/src/public/node/fs.test.ts index 60a3a973541..c65237fc338 100644 --- a/packages/cli-kit/src/public/node/fs.test.ts +++ b/packages/cli-kit/src/public/node/fs.test.ts @@ -32,7 +32,10 @@ import * as os from 'os' vi.mock('../common/array.js') vi.mock('fast-glob') -vi.mock('os') +vi.mock('os', async (importOriginal) => { + const actual = await importOriginal() + return {...actual, EOL: actual.EOL} +}) describe('inTemporaryDirectory', () => { test('ties the lifecycle of the temporary directory to the lifecycle of the callback', async () => { diff --git a/packages/cli-kit/src/public/node/fs.ts b/packages/cli-kit/src/public/node/fs.ts index a20f0df445b..4163b73b2d9 100644 --- a/packages/cli-kit/src/public/node/fs.ts +++ b/packages/cli-kit/src/public/node/fs.ts @@ -2,6 +2,7 @@ import {outputContent, outputToken, outputDebug} from './output.js' import {joinPath, normalizePath} from './path.js' import {OverloadParameters} from '../../private/common/ts/overloaded-parameters.js' import {getRandomName, RandomNameFamily} from '../common/string.js' +import {systemTempDir} from '../../private/node/temp-dir.js' import { copy as fsCopy, ensureFile as fsEnsureFile, @@ -13,7 +14,6 @@ import { // @ts-ignore } from 'fs-extra/esm' -import {temporaryDirectory, temporaryDirectoryTask} from 'tempy' import {sep, join} from 'pathe' import {findUp as internalFindUp, findUpSync as internalFindUpSync} from 'find-up' import {minimatch} from 'minimatch' @@ -29,6 +29,7 @@ import { constants as fsConstants, existsSync as fsFileExistsSync, unlinkSync as fsUnlinkSync, + mkdtempSync as fsMkdtempSync, accessSync, ReadStream, WriteStream, @@ -75,7 +76,12 @@ export function stripUpPath(path: string, strip: number): string { * @param callback - The callback that receives the temporary directory. */ export async function inTemporaryDirectory(callback: (tmpDir: string) => T | Promise): Promise { - return temporaryDirectoryTask(callback) + const tmpDir = await fsMkdtemp(join(systemTempDir, 'tmp-')) + try { + return await callback(tmpDir) + } finally { + await fsRm(tmpDir, {recursive: true, force: true, maxRetries: 2}) + } } /** @@ -83,7 +89,7 @@ export async function inTemporaryDirectory(callback: (tmpDir: string) => T | * @returns - The path to the temporary directory. */ export function tempDirectory(): string { - return temporaryDirectory() + return fsMkdtempSync(join(systemTempDir, 'tmp-')) } /** diff --git a/packages/e2e/package.json b/packages/e2e/package.json index 8b9d5de63e8..fa0b9c0d312 100644 --- a/packages/e2e/package.json +++ b/packages/e2e/package.json @@ -33,8 +33,7 @@ "@types/node": "18.19.70", "execa": "^7.2.0", "node-pty": "^1.0.0", - "strip-ansi": "^7.1.0", - "tempy": "^1.0.1" + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=20.10.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b76d2f5954e..dca13818234 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -462,9 +462,6 @@ importers: supports-hyperlinks: specifier: 3.1.0 version: 3.1.0 - tempy: - specifier: 3.1.0 - version: 3.1.0 terminal-link: specifier: 3.0.0 version: 3.0.0 @@ -551,9 +548,6 @@ importers: strip-ansi: specifier: ^7.1.0 version: 7.1.0 - tempy: - specifier: ^1.0.1 - version: 1.0.1 packages/eslint-plugin-cli: dependencies: @@ -778,9 +772,6 @@ importers: simple-git: specifier: 3.32.3 version: 3.32.3 - tempy: - specifier: 3.0.0 - version: 3.0.0 packages: @@ -4254,10 +4245,6 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -4703,10 +4690,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - clean-stack@3.0.1: resolution: {integrity: sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==} engines: {node: '>=10'} @@ -4949,10 +4932,6 @@ packages: crossws@0.3.5: resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} - crypto-random-string@2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} @@ -5094,10 +5073,6 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - del@6.1.1: - resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} - engines: {node: '>=10'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -6332,14 +6307,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-cwd@2.2.0: - resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} - engines: {node: '>=6'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -7302,10 +7269,6 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -7819,11 +7782,6 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rimraf@6.1.3: resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} engines: {node: 20 || >=22} @@ -8262,22 +8220,10 @@ packages: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} - temp-dir@3.0.0: - resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} - engines: {node: '>=14.16'} - - tempy@1.0.1: - resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==} - engines: {node: '>=10'} - tempy@3.0.0: resolution: {integrity: sha512-B2I9X7+o2wOaW4r/CWMkpOO9mdiTRCxXNgob6iGvPmfPWgH/KyUD6Uy5crtWBxIBe3YrNZKR2lSzv1JJKWD4vA==} engines: {node: '>=14.16'} - tempy@3.1.0: - resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==} - engines: {node: '>=14.16'} - term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -8453,10 +8399,6 @@ packages: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} - type-fest@0.16.0: - resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -8582,10 +8524,6 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} - unique-string@2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} @@ -13641,11 +13579,6 @@ snapshots: agent-base@7.1.4: {} - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -14180,8 +14113,6 @@ snapshots: ci-info@3.9.0: {} - clean-stack@2.2.0: {} - clean-stack@3.0.1: dependencies: escape-string-regexp: 4.0.0 @@ -14429,8 +14360,6 @@ snapshots: dependencies: uncrypto: 0.1.3 - crypto-random-string@2.0.0: {} - crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 @@ -14560,17 +14489,6 @@ snapshots: defu@6.1.4: {} - del@6.1.1: - dependencies: - globby: 11.1.0 - graceful-fs: 4.2.11 - is-glob: 4.0.3 - is-path-cwd: 2.2.0 - is-path-inside: 3.0.3 - p-map: 4.0.0 - rimraf: 3.0.2 - slash: 3.0.0 - delayed-stream@1.0.0: {} dependency-graph@0.11.0: {} @@ -16041,10 +15959,6 @@ snapshots: is-number@7.0.0: {} - is-path-cwd@2.2.0: {} - - is-path-inside@3.0.3: {} - is-plain-obj@1.1.0: {} is-plain-obj@4.1.0: {} @@ -17095,10 +17009,6 @@ snapshots: p-map@2.1.0: {} - p-map@4.0.0: - dependencies: - aggregate-error: 3.1.0 - p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -17643,10 +17553,6 @@ snapshots: rfdc@1.4.1: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rimraf@6.1.3: dependencies: glob: 13.0.6 @@ -18172,16 +18078,6 @@ snapshots: temp-dir@2.0.0: {} - temp-dir@3.0.0: {} - - tempy@1.0.1: - dependencies: - del: 6.1.1 - is-stream: 2.0.1 - temp-dir: 2.0.0 - type-fest: 0.16.0 - unique-string: 2.0.0 - tempy@3.0.0: dependencies: is-stream: 3.0.0 @@ -18189,13 +18085,6 @@ snapshots: type-fest: 2.19.0 unique-string: 3.0.0 - tempy@3.1.0: - dependencies: - is-stream: 3.0.0 - temp-dir: 3.0.0 - type-fest: 2.19.0 - unique-string: 3.0.0 - term-size@2.2.1: {} terminal-link@3.0.0: @@ -18358,8 +18247,6 @@ snapshots: type-fest@0.13.1: {} - type-fest@0.16.0: {} - type-fest@0.21.3: {} type-fest@0.6.0: {} @@ -18476,10 +18363,6 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} - unique-string@2.0.0: - dependencies: - crypto-random-string: 2.0.0 - unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 diff --git a/workspace/package.json b/workspace/package.json index ad64174f312..f37f031c5f6 100644 --- a/workspace/package.json +++ b/workspace/package.json @@ -17,7 +17,6 @@ "devDependencies": { "@actions/core": "^1.10.0", "git-diff": "^2.0.6", - "simple-git": "3.32.3", - "tempy": "3.0.0" + "simple-git": "3.32.3" } } diff --git a/workspace/src/type-diff.js b/workspace/src/type-diff.js index c9c057f7d28..e33c879f572 100644 --- a/workspace/src/type-diff.js +++ b/workspace/src/type-diff.js @@ -4,7 +4,8 @@ import * as path from 'pathe' import * as url from 'url' import {execa} from 'execa' import fg from 'fast-glob' -import {temporaryDirectoryTask} from 'tempy' +import {mkdtemp, rm} from 'fs/promises' +import os from 'os' import git from 'simple-git' import {setOutput} from '@actions/core' import {promises as fs, existsSync} from 'fs' @@ -95,7 +96,8 @@ ${ ) } -await temporaryDirectoryTask(async (tmpDir) => { +const tmpDir = await mkdtemp(path.join(os.tmpdir(), 'type-diff-')) +try { const baselineDirectory = await cloneCLIRepository(tmpDir) const currentDirectory = path.join(url.fileURLToPath(new URL('.', import.meta.url)), '../..') const baselineFiles = await build(baselineDirectory, {name: 'baseline'}) @@ -106,4 +108,6 @@ await temporaryDirectoryTask(async (tmpDir) => { baselineFiles, currentFiles, }) -}) +} finally { + await rm(tmpDir, {recursive: true, force: true, maxRetries: 2}) +}