mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Scope workspace link preflight to linked worktrees
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
0ed3f56935
commit
d607ca0089
3 changed files with 68 additions and 2 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env -S node --import tsx
|
#!/usr/bin/env -S node --import tsx
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { repoRoot } from "./dev-service-profile.ts";
|
import { repoRoot } from "./dev-service-profile.ts";
|
||||||
|
|
||||||
|
|
@ -43,6 +43,20 @@ function discoverWorkspacePackagePaths(rootDir: string): Map<string, string> {
|
||||||
return packagePaths;
|
return packagePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLinkedGitWorktreeCheckout(rootDir: string) {
|
||||||
|
const gitMetadataPath = path.join(rootDir, ".git");
|
||||||
|
if (!existsSync(gitMetadataPath)) return false;
|
||||||
|
|
||||||
|
const stat = lstatSync(gitMetadataPath);
|
||||||
|
if (!stat.isFile()) return false;
|
||||||
|
|
||||||
|
return readFileSync(gitMetadataPath, "utf8").trimStart().startsWith("gitdir:");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLinkedGitWorktreeCheckout(repoRoot)) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
const workspacePackagePaths = discoverWorkspacePackagePaths(repoRoot);
|
const workspacePackagePaths = discoverWorkspacePackagePaths(repoRoot);
|
||||||
const workspaceDirs = Array.from(
|
const workspaceDirs = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ describe("ensureServerWorkspaceLinksCurrent", () => {
|
||||||
await fs.mkdir(expectedPackageDir, { recursive: true });
|
await fs.mkdir(expectedPackageDir, { recursive: true });
|
||||||
await fs.mkdir(stalePackageDir, { recursive: true });
|
await fs.mkdir(stalePackageDir, { recursive: true });
|
||||||
await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
|
await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
|
||||||
|
await fs.writeFile(path.join(repoRoot, ".git"), "gitdir: /tmp/paperclip-main/.git/worktrees/runtime-links\n", "utf8");
|
||||||
await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
|
await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
path.join(repoRoot, "server", "package.json"),
|
path.join(repoRoot, "server", "package.json"),
|
||||||
|
|
@ -235,6 +236,7 @@ describe("ensureServerWorkspaceLinksCurrent", () => {
|
||||||
await fs.mkdir(path.join(repoRoot, "server"), { recursive: true });
|
await fs.mkdir(path.join(repoRoot, "server"), { recursive: true });
|
||||||
await fs.mkdir(expectedPackageDir, { recursive: true });
|
await fs.mkdir(expectedPackageDir, { recursive: true });
|
||||||
await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
|
await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
|
||||||
|
await fs.writeFile(path.join(repoRoot, ".git"), "gitdir: /tmp/paperclip-main/.git/worktrees/runtime-links-current\n", "utf8");
|
||||||
await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
|
await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
path.join(repoRoot, "server", "package.json"),
|
path.join(repoRoot, "server", "package.json"),
|
||||||
|
|
@ -255,6 +257,45 @@ describe("ensureServerWorkspaceLinksCurrent", () => {
|
||||||
|
|
||||||
await ensureServerWorkspaceLinksCurrent(path.join(repoRoot, "server"));
|
await ensureServerWorkspaceLinksCurrent(path.join(repoRoot, "server"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("skips relinking outside linked git worktrees", async () => {
|
||||||
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-runtime-links-non-worktree-"));
|
||||||
|
const staleRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-runtime-links-non-worktree-stale-"));
|
||||||
|
const serverNodeModulesScopeDir = path.join(repoRoot, "server", "node_modules", "@paperclipai");
|
||||||
|
const expectedPackageDir = path.join(repoRoot, "packages", "db");
|
||||||
|
const stalePackageDir = path.join(staleRoot, "db");
|
||||||
|
|
||||||
|
await fs.mkdir(path.join(repoRoot, ".git"), { recursive: true });
|
||||||
|
await fs.mkdir(path.join(repoRoot, "server"), { recursive: true });
|
||||||
|
await fs.mkdir(expectedPackageDir, { recursive: true });
|
||||||
|
await fs.mkdir(stalePackageDir, { recursive: true });
|
||||||
|
await fs.mkdir(serverNodeModulesScopeDir, { recursive: true });
|
||||||
|
await fs.writeFile(path.join(repoRoot, "pnpm-workspace.yaml"), "packages:\n - packages/*\n - server\n", "utf8");
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(repoRoot, "server", "package.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
name: "@paperclipai/server",
|
||||||
|
dependencies: {
|
||||||
|
"@paperclipai/db": "workspace:*",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(expectedPackageDir, "package.json"),
|
||||||
|
JSON.stringify({ name: "@paperclipai/db" }),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
await fs.writeFile(
|
||||||
|
path.join(stalePackageDir, "package.json"),
|
||||||
|
JSON.stringify({ name: "@paperclipai/db" }),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
await fs.symlink(stalePackageDir, path.join(serverNodeModulesScopeDir, "db"));
|
||||||
|
|
||||||
|
await ensureServerWorkspaceLinksCurrent(path.join(repoRoot, "server"));
|
||||||
|
expect(await fs.realpath(path.join(serverNodeModulesScopeDir, "db"))).toBe(await fs.realpath(stalePackageDir));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("realizeExecutionWorkspace", () => {
|
describe("realizeExecutionWorkspace", () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { spawn, type ChildProcess } from "node:child_process";
|
import { spawn, type ChildProcess } from "node:child_process";
|
||||||
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import { createHash, randomUUID } from "node:crypto";
|
import { createHash, randomUUID } from "node:crypto";
|
||||||
|
|
@ -157,6 +157,16 @@ function findWorkspaceRoot(startCwd: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLinkedGitWorktreeCheckout(rootDir: string) {
|
||||||
|
const gitMetadataPath = path.join(rootDir, ".git");
|
||||||
|
if (!existsSync(gitMetadataPath)) return false;
|
||||||
|
|
||||||
|
const stat = lstatSync(gitMetadataPath);
|
||||||
|
if (!stat.isFile()) return false;
|
||||||
|
|
||||||
|
return readFileSync(gitMetadataPath, "utf8").trimStart().startsWith("gitdir:");
|
||||||
|
}
|
||||||
|
|
||||||
function discoverWorkspacePackagePaths(rootDir: string): Map<string, string> {
|
function discoverWorkspacePackagePaths(rootDir: string): Map<string, string> {
|
||||||
const packagePaths = new Map<string, string>();
|
const packagePaths = new Map<string, string>();
|
||||||
const ignoredDirNames = new Set([".git", ".paperclip", "dist", "node_modules"]);
|
const ignoredDirNames = new Set([".git", ".paperclip", "dist", "node_modules"]);
|
||||||
|
|
@ -228,6 +238,7 @@ export async function ensureServerWorkspaceLinksCurrent(
|
||||||
) {
|
) {
|
||||||
const workspaceRoot = findWorkspaceRoot(startCwd);
|
const workspaceRoot = findWorkspaceRoot(startCwd);
|
||||||
if (!workspaceRoot) return;
|
if (!workspaceRoot) return;
|
||||||
|
if (!isLinkedGitWorktreeCheckout(workspaceRoot)) return;
|
||||||
|
|
||||||
const mismatches = findServerWorkspaceLinkMismatches(workspaceRoot);
|
const mismatches = findServerWorkspaceLinkMismatches(workspaceRoot);
|
||||||
if (mismatches.length === 0) return;
|
if (mismatches.length === 0) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue