feat(FN-3406): add shell context hook plumbing for dashboard

Adds a new `useShellContext` hook with tests to the dashboard, integrated into `App.tsx` and `Header.tsx` to provide shell context plumbing throughout the UI.

Fusion-Task-Id: FN-3406
This commit is contained in:
Fusion
2026-05-07 22:00:41 -07:00
committed by gsxdsm
parent ecbf1f829c
commit 9e04467b57
23 changed files with 785 additions and 23 deletions

View File

@@ -1,18 +1,84 @@
#!/usr/bin/env node
import { readFileSync, readdirSync, writeFileSync, mkdirSync, renameSync, mkdtempSync, rmSync, realpathSync } from "node:fs";
import { readFileSync, readdirSync, writeFileSync, mkdirSync, renameSync, mkdtempSync, rmSync, realpathSync, globSync } from "node:fs";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import { createHash } from "node:crypto";
import { cpus, tmpdir } from "node:os";
import { createRequire } from "node:module";
import { ensureTestArtifacts } from "./ensure-test-artifacts.mjs";
import fg from "fast-glob";
import { parse as parseYaml } from "yaml";
const currentFilePath = fileURLToPath(import.meta.url);
const scriptDir = path.dirname(currentFilePath);
const checkIsolationScript = path.join(scriptDir, "check-test-isolation.mjs");
const require = createRequire(import.meta.url);
function fastGlobSync(patterns, options) {
const patternList = Array.isArray(patterns) ? patterns : [patterns];
const matches = new Set();
for (const pattern of patternList) {
if (typeof pattern !== "string" || pattern.length === 0) continue;
const isNegated = pattern.startsWith("!");
const body = isNegated ? pattern.slice(1) : pattern;
const resolved = globSync(body, {
cwd: options?.cwd,
absolute: options?.absolute,
dot: options?.dot,
nodir: options?.onlyFiles,
});
for (const entry of resolved) {
if (isNegated) {
matches.delete(entry);
} else {
matches.add(entry);
}
}
}
return [...matches];
}
let fgSync = fastGlobSync;
try {
const loaded = require("fast-glob");
if (typeof loaded?.sync === "function") {
fgSync = loaded.sync;
}
} catch {
// Fallback to node:fs globSync when fast-glob is not installed.
}
function parseWorkspacePackagesFromYaml(rawYaml) {
const lines = rawYaml.split(/\r?\n/);
const packages = [];
let inPackages = false;
for (const line of lines) {
const trimmed = line.trim();
if (!inPackages) {
if (trimmed === "packages:") {
inPackages = true;
}
continue;
}
if (!trimmed) continue;
if (!trimmed.startsWith("-")) {
if (!line.startsWith(" ") && !line.startsWith("\t")) {
break;
}
continue;
}
const value = trimmed.slice(1).trim().replace(/^['"]|['"]$/g, "");
if (value) packages.push(value);
}
return packages;
}
const rootDir = process.env.FUSION_PROJECT_DIR
? path.resolve(process.env.FUSION_PROJECT_DIR)
@@ -125,10 +191,7 @@ function getBaseBranch() {
function readWorkspacePatterns(projectRoot = rootDir) {
try {
const workspacePath = path.join(projectRoot, "pnpm-workspace.yaml");
const parsed = parseYaml(readFileSync(workspacePath, "utf8"));
return Array.isArray(parsed?.packages)
? parsed.packages.filter((pattern) => typeof pattern === "string")
: [];
return parseWorkspacePackagesFromYaml(readFileSync(workspacePath, "utf8"));
} catch {
return ["packages/*"];
}
@@ -139,7 +202,7 @@ function expandWorkspacePattern(projectRoot, pattern) {
return [];
}
return fg.sync(workspacePatternToPackageJsonGlob(pattern), {
return fgSync(workspacePatternToPackageJsonGlob(pattern), {
absolute: true,
cwd: projectRoot,
dot: false,
@@ -153,7 +216,7 @@ function expandWorkspacePatterns(projectRoot, patterns) {
return patterns.flatMap((pattern) => expandWorkspacePattern(projectRoot, pattern));
}
return fg.sync(patterns.map(workspacePatternToPackageJsonGlob), {
return fgSync(patterns.map(workspacePatternToPackageJsonGlob), {
absolute: true,
cwd: projectRoot,
dot: false,