Skip to content

Commit bdf9445

Browse files
committed
fix: track the wav temp dir for cleanup and finish the mkdtemp sweep
The wav helper pushed the file path into `dirs`, so `afterEach` removed `tone.wav` and left the directory it had just made — four per suite run. Push the directory and derive the file path from it. Measured: the old code leaks 4 directories per run, the new code leaks 0. Three sites still built a predictable name and then created it. CodeQL never flagged them — its dataflow reaches the template preview writes through a `readdir` walk and does not connect them back to the `tmpdir()` root — so the alert list was narrower than the pattern, and closing only the alerts would turn the rule green while the shape survived where nothing would re-flag it. `generate-template-previews.ts` is the near-twin of the file this change started from, and the other two are producer dev entry points. All three use the path only through the variable, so the random suffix changes nothing. Catalog previews now call the existing `createCatalogPreviewTempDir` instead of repeating its body. That test was in no runner, so it pinned uniqueness and mode 0700 on a function nothing called; adding it to `test:scripts` alongside a real caller makes it load-bearing. The rationale for the primitive moves to the helper, which is now the only place it lives.
1 parent d82fa5f commit bdf9445

7 files changed

Lines changed: 19 additions & 17 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"player:perf": "bun run --filter @hyperframes/player perf",
4949
"format:check": "oxfmt --check .",
5050
"knip": "knip",
51-
"test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/publish-workflow.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs && vitest run scripts/catalog/",
51+
"test:scripts": "node --import tsx --test scripts/check-tracked-artifacts.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/publish-workflow.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/catalog-preview-temp.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs && vitest run scripts/catalog/",
5252
"typecheck:scripts": "tsc --noEmit -p scripts/tsconfig.json",
5353
"test:skills": "node --test 'skills/**/*.test.mjs'",
5454
"generate:previews": "tsx scripts/generate-template-previews.ts",

packages/cli/src/whisper/normalize.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,10 @@ describe("detectSpeechOnset", () => {
567567
const amplitude = energyFn(t);
568568
buf.writeInt16LE(Math.round(amplitude * 32767), 44 + i * 2);
569569
}
570-
const path = join(mkdtempSync(join(tmpdir(), "hf-wav-test-")), "tone.wav");
570+
const dir = mkdtempSync(join(tmpdir(), "hf-wav-test-"));
571+
dirs.push(dir);
572+
const path = join(dir, "tone.wav");
571573
writeFileSync(path, buf);
572-
dirs.push(path);
573574
return path;
574575
}
575576

packages/producer/src/benchmark.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
writeFileSync,
2626
existsSync,
2727
mkdirSync,
28+
mkdtempSync,
2829
cpSync,
2930
rmSync,
3031
} from "node:fs";
@@ -204,8 +205,7 @@ async function runBenchmark(): Promise<void> {
204205
console.log(` Run ${r + 1}/${runs}...`);
205206

206207
// Copy src to temp dir for isolation
207-
const tmpRoot = join(tmpdir(), `benchmark-${fixture.id}-${Date.now()}`);
208-
mkdirSync(tmpRoot, { recursive: true });
208+
const tmpRoot = mkdtempSync(join(tmpdir(), `benchmark-${fixture.id}-`));
209209
cpSync(join(fixture.dir, "src"), join(tmpRoot, "src"), { recursive: true });
210210

211211
const projectDir = join(tmpRoot, "src");

packages/producer/src/transparency-test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121

2222
import { strict as assert } from "node:assert";
23-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "node:fs";
23+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
2424
import { tmpdir } from "node:os";
2525
import { dirname, join, resolve } from "node:path";
2626
import { fileURLToPath } from "node:url";
@@ -309,8 +309,7 @@ async function main(): Promise<void> {
309309
if (!existsSync(SHADER_FIXTURE_SRC) || !existsSync(SHADER_GOLDEN)) {
310310
throw new Error(`Shader fixture or golden missing: ${SHADER_FIXTURE_DIR}`);
311311
}
312-
const workRoot = join(tmpdir(), `hf-transparency-${process.pid}-${Date.now()}`);
313-
mkdirSync(workRoot, { recursive: true });
312+
const workRoot = mkdtempSync(join(tmpdir(), "hf-transparency-"));
314313
const keepWork = process.env.KEEP_TEMP === "1";
315314
console.log(`work dir: ${workRoot}${keepWork ? " (KEEP_TEMP=1)" : ""}`);
316315

scripts/catalog-preview-temp.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ import { mkdtempSync } from "node:fs";
22
import { tmpdir } from "node:os";
33
import { join } from "node:path";
44

5-
/** Atomically allocate an owner-only preview directory under the OS temp root. */
5+
/**
6+
* Atomically allocate an owner-only preview directory under the OS temp root.
7+
*
8+
* `mkdtemp` rather than a name built from `Date.now()`: it picks the random
9+
* suffix and creates the directory 0700 in one syscall, so nothing can
10+
* pre-create or symlink the path between choosing the name and making it.
11+
*/
612
export function createCatalogPreviewTempDir(itemName: string): string {
713
return mkdtempSync(join(tmpdir(), `hf-catalog-${itemName}-`));
814
}

scripts/generate-catalog-previews.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,15 @@ import {
2424
readFileSync,
2525
existsSync,
2626
mkdirSync,
27-
mkdtempSync,
2827
cpSync,
2928
rmSync,
3029
writeFileSync,
3130
statSync,
3231
} from "node:fs";
3332
import { execFileSync } from "node:child_process";
3433
import { join, resolve, dirname } from "node:path";
35-
import { tmpdir } from "node:os";
3634
import { fileURLToPath } from "node:url";
35+
import { createCatalogPreviewTempDir } from "./catalog-preview-temp.js";
3736
// Import from source — bun workspace linking doesn't resolve for scripts outside packages/.
3837
import {
3938
captureFrame,
@@ -171,10 +170,7 @@ export async function prepareProjectDir(
171170
item: CatalogItem,
172171
options: PrepareOptions = {},
173172
): Promise<string> {
174-
// mkdtemp rather than a name built from `Date.now()`: it picks the random
175-
// suffix and creates the directory 0700 in one syscall, so nothing can
176-
// pre-create or symlink the path between choosing it and making it.
177-
const tmpDir = mkdtempSync(join(tmpdir(), `hf-catalog-${item.name}-`));
173+
const tmpDir = createCatalogPreviewTempDir(item.name);
178174
cpSync(item.sourceDir, tmpDir, { recursive: true });
179175
mirrorRegistryTargets(tmpDir);
180176

scripts/generate-template-previews.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
writeFileSync,
2222
existsSync,
2323
mkdirSync,
24+
mkdtempSync,
2425
cpSync,
2526
rmSync,
2627
} from "node:fs";
@@ -126,8 +127,7 @@ function discoverTemplates(only: string | null): string[] {
126127
}
127128

128129
function prepareTemplateDir(templateId: string): string {
129-
const tmpDir = join(tmpdir(), `hf-preview-${templateId}-${Date.now()}`);
130-
mkdirSync(tmpDir, { recursive: true });
130+
const tmpDir = mkdtempSync(join(tmpdir(), `hf-preview-${templateId}-`));
131131
const src = resolveTemplateDir(templateId);
132132
if (!src) throw new Error(`Template directory not found for "${templateId}"`);
133133
cpSync(src, tmpDir, { recursive: true });

0 commit comments

Comments
 (0)