forked from farhoodlabs/paperclip
test: isolate CLI company import e2e state (#4560)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies, and its CLI import/export path is part of how operators move company state safely between environments. > - The `paperclipai company import/export` e2e test is supposed to validate that portability flow inside a hermetic harness, not against a developer's live Paperclip home. > - This regression showed nested CLI subprocesses could silently fall back to ambient `PAPERCLIP_*` state and mutate a real local instance by creating extra companies such as `CLI-1-Roundtrip-Test`. > - The first job was to pin the test subprocesses to isolated config, home, instance, auth, and context paths, and to add a regression assertion that proves the nested CLI writes stay inside the test-owned state. > - Once the PR was up, CI and Greptile exposed two follow-on issues that were blocking merge: plugin SDK typecheck bootstrap was racing across packages in fresh CI, and the new lock helper needed one more fix to release its lock on failure. > - This pull request therefore ends up doing two tightly related things: fixing the original CLI isolation leak, and hardening the supporting typecheck/bootstrap path enough for the fix to verify cleanly in CI. > - The benefit is that the portability e2e test is now actually isolated, and the PR verification path is stable enough to catch regressions instead of introducing its own nondeterministic failures. ## What Changed - Hardened `cli/src/__tests__/company-import-export-e2e.test.ts` so nested CLI subprocesses re-seed isolated `PAPERCLIP_CONFIG`, `PAPERCLIP_HOME`, `PAPERCLIP_INSTANCE_ID`, `PAPERCLIP_CONTEXT`, `PAPERCLIP_AUTH_STORE`, and throwaway `HOME` values instead of falling back to ambient machine state. - Added a regression assertion around `paperclipai context set --json`, then cleared the temporary `context.json` so the isolation check and the later export/import flow stay independent. - Passed the same isolated `HOME` into the server subprocess so both sides of the e2e harness are symmetric. - Introduced locking in `scripts/ensure-plugin-build-deps.mjs` and switched the server/plugin example `typecheck` scripts to use that helper instead of launching concurrent raw `@paperclipai/plugin-sdk` builds. - Fixed the helper failure path so it releases the lock before exiting non-zero, which prevents stale-lock timeouts during parallel typecheck runs. ## Verification - `pnpm vitest run cli/src/__tests__/company-import-export-e2e.test.ts --project paperclipai` - `pnpm --filter paperclipai typecheck` - `pnpm -r typecheck` - PR checks now pass on the current head, including `policy`, `verify`, `e2e`, `security/snyk`, and `Greptile Review`. ## Risks - Low risk. The product-facing behavior change is scoped to test harness code in the CLI e2e suite. - The CI stabilization changes only affect bootstrap/typecheck helper paths for the server and plugin/example packages, but they do touch shared verification plumbing; the main risk is changing how fresh build artifacts are prepared in local/CI typecheck runs. ## Model Used - Anthropic Claude via Paperclip `claude_local`, model `claude-opus-4-7`, high-effort local coding agent, used for the initial implementation and first peer-reviewed verification. - OpenAI Codex via Paperclip `codex_local`, model `gpt-5.4`, high reasoning-effort local coding agent with tool use, used for CI triage, Greptile follow-up fixes, verification, and PR maintenance. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import net from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -104,20 +104,50 @@ function writeTestConfig(configPath: string, tempRoot: string, port: number, con
|
||||
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
||||
}
|
||||
|
||||
function createServerEnv(configPath: string, port: number, connectionString: string) {
|
||||
interface TestPaperclipEnv {
|
||||
configPath: string;
|
||||
paperclipHome: string;
|
||||
instanceId: string;
|
||||
shellHome?: string;
|
||||
}
|
||||
|
||||
function createBasePaperclipEnv(options: TestPaperclipEnv) {
|
||||
const env = { ...process.env };
|
||||
for (const key of Object.keys(env)) {
|
||||
if (key.startsWith("PAPERCLIP_")) {
|
||||
delete env[key];
|
||||
}
|
||||
}
|
||||
|
||||
env.PAPERCLIP_CONFIG = options.configPath;
|
||||
env.PAPERCLIP_HOME = options.paperclipHome;
|
||||
env.PAPERCLIP_INSTANCE_ID = options.instanceId;
|
||||
env.PAPERCLIP_CONTEXT = path.join(options.paperclipHome, "context.json");
|
||||
env.PAPERCLIP_AUTH_STORE = path.join(options.paperclipHome, "auth.json");
|
||||
if (options.shellHome) {
|
||||
env.HOME = options.shellHome;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
function createServerEnv(
|
||||
configPath: string,
|
||||
port: number,
|
||||
connectionString: string,
|
||||
options: Omit<TestPaperclipEnv, "configPath">,
|
||||
) {
|
||||
const env = createBasePaperclipEnv({
|
||||
configPath,
|
||||
...options,
|
||||
});
|
||||
|
||||
delete env.DATABASE_URL;
|
||||
delete env.PORT;
|
||||
delete env.HOST;
|
||||
delete env.SERVE_UI;
|
||||
delete env.HEARTBEAT_SCHEDULER_ENABLED;
|
||||
|
||||
env.PAPERCLIP_CONFIG = configPath;
|
||||
env.DATABASE_URL = connectionString;
|
||||
env.HOST = "127.0.0.1";
|
||||
env.PORT = String(port);
|
||||
@@ -130,13 +160,8 @@ function createServerEnv(configPath: string, port: number, connectionString: str
|
||||
return env;
|
||||
}
|
||||
|
||||
function createCliEnv() {
|
||||
const env = { ...process.env };
|
||||
for (const key of Object.keys(env)) {
|
||||
if (key.startsWith("PAPERCLIP_")) {
|
||||
delete env[key];
|
||||
}
|
||||
}
|
||||
function createCliEnv(options: TestPaperclipEnv) {
|
||||
const env = createBasePaperclipEnv(options);
|
||||
delete env.DATABASE_URL;
|
||||
delete env.PORT;
|
||||
delete env.HOST;
|
||||
@@ -183,14 +208,25 @@ async function api<T>(baseUrl: string, pathname: string, init?: RequestInit): Pr
|
||||
return text ? JSON.parse(text) as T : (null as T);
|
||||
}
|
||||
|
||||
async function runCliJson<T>(args: string[], opts: { apiBase: string; configPath: string }) {
|
||||
async function runCliJson<T>(
|
||||
args: string[],
|
||||
opts: TestPaperclipEnv & { apiBase?: string; includeConfigArg?: boolean },
|
||||
) {
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
||||
const cliArgs = ["--silent", "paperclipai", ...args];
|
||||
if (opts.apiBase) {
|
||||
cliArgs.push("--api-base", opts.apiBase);
|
||||
}
|
||||
if (opts.includeConfigArg !== false) {
|
||||
cliArgs.push("--config", opts.configPath);
|
||||
}
|
||||
cliArgs.push("--json");
|
||||
const result = await execFileAsync(
|
||||
"pnpm",
|
||||
["--silent", "paperclipai", ...args, "--api-base", opts.apiBase, "--config", opts.configPath, "--json"],
|
||||
cliArgs,
|
||||
{
|
||||
cwd: repoRoot,
|
||||
env: createCliEnv(),
|
||||
env: createCliEnv(opts),
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
},
|
||||
);
|
||||
@@ -235,6 +271,9 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
let configPath = "";
|
||||
let exportDir = "";
|
||||
let apiBase = "";
|
||||
let paperclipHome = "";
|
||||
let cliShellHome = "";
|
||||
let paperclipInstanceId = "";
|
||||
let serverProcess: ServerProcess | null = null;
|
||||
let tempDb: Awaited<ReturnType<typeof startEmbeddedPostgresTestDatabase>> | null = null;
|
||||
|
||||
@@ -242,6 +281,11 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
tempRoot = mkdtempSync(path.join(os.tmpdir(), "paperclip-company-cli-e2e-"));
|
||||
configPath = path.join(tempRoot, "config", "config.json");
|
||||
exportDir = path.join(tempRoot, "exported-company");
|
||||
paperclipHome = path.join(tempRoot, "paperclip-home");
|
||||
cliShellHome = path.join(tempRoot, "shell-home");
|
||||
paperclipInstanceId = "company-cli-e2e";
|
||||
mkdirSync(paperclipHome, { recursive: true });
|
||||
mkdirSync(cliShellHome, { recursive: true });
|
||||
|
||||
tempDb = await startEmbeddedPostgresTestDatabase("paperclip-company-cli-db-");
|
||||
|
||||
@@ -256,7 +300,11 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
["paperclipai", "run", "--config", configPath],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
env: createServerEnv(configPath, port, tempDb.connectionString),
|
||||
env: createServerEnv(configPath, port, tempDb.connectionString, {
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
}),
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
},
|
||||
);
|
||||
@@ -282,6 +330,31 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
it("exports a company package and imports it into new and existing companies", async () => {
|
||||
expect(serverProcess).not.toBeNull();
|
||||
|
||||
const cliContext = await runCliJson<{
|
||||
contextPath: string;
|
||||
profileName: string;
|
||||
profile: { apiBase?: string };
|
||||
}>(
|
||||
["context", "set", "--profile", "isolation-check", "--api-base", "https://example.test"],
|
||||
{
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
includeConfigArg: false,
|
||||
},
|
||||
);
|
||||
|
||||
const expectedContextPath = path.join(paperclipHome, "context.json");
|
||||
const leakedContextPath = path.join(cliShellHome, ".paperclip", "context.json");
|
||||
expect(cliContext.contextPath).toBe(expectedContextPath);
|
||||
expect(cliContext.profileName).toBe("isolation-check");
|
||||
expect(cliContext.profile.apiBase).toBe("https://example.test");
|
||||
expect(existsSync(expectedContextPath)).toBe(true);
|
||||
expect(existsSync(leakedContextPath)).toBe(false);
|
||||
rmSync(expectedContextPath, { force: true });
|
||||
expect(existsSync(expectedContextPath)).toBe(false);
|
||||
|
||||
const sourceCompany = await api<{ id: string; name: string; issuePrefix: string }>(apiBase, "/api/companies", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
@@ -355,7 +428,13 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
"--include",
|
||||
"company,agents,projects,issues",
|
||||
],
|
||||
{ apiBase, configPath },
|
||||
{
|
||||
apiBase,
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
},
|
||||
);
|
||||
|
||||
expect(exportResult.ok).toBe(true);
|
||||
@@ -379,7 +458,13 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
"company,agents,projects,issues",
|
||||
"--yes",
|
||||
],
|
||||
{ apiBase, configPath },
|
||||
{
|
||||
apiBase,
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
},
|
||||
);
|
||||
|
||||
expect(importedNew.company.action).toBe("created");
|
||||
@@ -427,7 +512,13 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
"rename",
|
||||
"--dry-run",
|
||||
],
|
||||
{ apiBase, configPath },
|
||||
{
|
||||
apiBase,
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
},
|
||||
);
|
||||
|
||||
expect(previewExisting.errors).toEqual([]);
|
||||
@@ -454,7 +545,13 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
"rename",
|
||||
"--yes",
|
||||
],
|
||||
{ apiBase, configPath },
|
||||
{
|
||||
apiBase,
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
},
|
||||
);
|
||||
|
||||
expect(importedExisting.company.action).toBe("unchanged");
|
||||
@@ -501,7 +598,13 @@ describeEmbeddedPostgres("paperclipai company import/export e2e", () => {
|
||||
"company,agents,projects,issues",
|
||||
"--yes",
|
||||
],
|
||||
{ apiBase, configPath },
|
||||
{
|
||||
apiBase,
|
||||
configPath,
|
||||
paperclipHome,
|
||||
instanceId: paperclipInstanceId,
|
||||
shellHome: cliShellHome,
|
||||
},
|
||||
);
|
||||
|
||||
expect(importedFromZip.company.action).toBe("created");
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"private": true,
|
||||
"description": "A Paperclip plugin",
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "node ./esbuild.config.mjs",
|
||||
"build:rollup": "rollup -c",
|
||||
"dev": "node ./esbuild.config.mjs --watch",
|
||||
"dev:ui": "paperclip-plugin-dev-server --root . --ui-dir dist/ui --port 4177",
|
||||
"test": "vitest run --config ./vitest.config.ts",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"paperclipPlugin": {
|
||||
"manifest": "./dist/manifest.js",
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"ui": "./dist/ui/"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "tsc && node ./scripts/build-ui.mjs",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"ui": "./dist/ui/"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paperclipai/plugin-sdk": "workspace:*"
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
"ui": "./dist/ui/"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "tsc && node ./scripts/build-ui.mjs",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paperclipai/plugin-sdk": "workspace:*",
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"private": true,
|
||||
"description": "First-party smoke plugin for orchestration-grade Paperclip plugin APIs",
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "node ./esbuild.config.mjs",
|
||||
"build:rollup": "rollup -c",
|
||||
"dev": "node ./esbuild.config.mjs --watch",
|
||||
"dev:ui": "paperclip-plugin-dev-server --root . --ui-dir dist/ui --port 4177",
|
||||
"test": "vitest run --config ./vitest.config.ts",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"paperclipPlugin": {
|
||||
"manifest": "./dist/manifest.js",
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"worker": "./dist/worker.js"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"prebuild": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit",
|
||||
"test": "vitest run --config vitest.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
],
|
||||
"scripts": {
|
||||
"postinstall": "node ../../../../scripts/link-plugin-dev-sdk.mjs",
|
||||
"prebuild": "pnpm -C ../../../.. --filter @paperclipai/plugin-sdk build",
|
||||
"prebuild": "pnpm -C ../../../.. --filter @paperclipai/plugin-sdk ensure-build-deps",
|
||||
"build": "rm -rf dist && tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm -C ../../../.. --filter @paperclipai/plugin-sdk build && tsc --noEmit",
|
||||
"typecheck": "pnpm -C ../../../.. --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit",
|
||||
"test": "vitest run --config vitest.config.ts",
|
||||
"prepack": "rm -f package.dev.json && cp package.json package.dev.json && node ../../../../scripts/generate-plugin-package-json.mjs",
|
||||
"postpack": "if [ -f package.dev.json ]; then mv package.dev.json package.json; fi"
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
"scripts": {
|
||||
"build": "pnpm --filter @paperclipai/shared build && tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"ensure-build-deps": "node ../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"typecheck": "pnpm --filter @paperclipai/shared build && tsc --noEmit",
|
||||
"dev:server": "tsx src/dev-cli.ts"
|
||||
},
|
||||
|
||||
@@ -8,6 +8,9 @@ import { fileURLToPath } from "node:url";
|
||||
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const rootDir = path.resolve(scriptDir, "..");
|
||||
const tscCliPath = path.join(rootDir, "node_modules", "typescript", "bin", "tsc");
|
||||
const lockDir = path.join(rootDir, "node_modules", ".cache", "paperclip-plugin-build-deps.lock");
|
||||
const lockTimeoutMs = 60_000;
|
||||
const lockPollMs = 100;
|
||||
|
||||
const buildTargets = [
|
||||
{
|
||||
@@ -26,21 +29,77 @@ if (!fs.existsSync(tscCliPath)) {
|
||||
throw new Error(`TypeScript CLI not found at ${tscCliPath}`);
|
||||
}
|
||||
|
||||
for (const target of buildTargets) {
|
||||
if (fs.existsSync(target.output)) {
|
||||
continue;
|
||||
function allOutputsExist() {
|
||||
return buildTargets.every((target) => fs.existsSync(target.output));
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
||||
}
|
||||
|
||||
function waitForLockRelease() {
|
||||
const startedAt = Date.now();
|
||||
while (Date.now() - startedAt < lockTimeoutMs) {
|
||||
if (!fs.existsSync(lockDir)) {
|
||||
return;
|
||||
}
|
||||
if (allOutputsExist()) {
|
||||
return;
|
||||
}
|
||||
sleep(lockPollMs);
|
||||
}
|
||||
|
||||
const result = spawnSync(process.execPath, [tscCliPath, "-p", target.tsconfig], {
|
||||
cwd: rootDir,
|
||||
stdio: "inherit",
|
||||
});
|
||||
throw new Error(`Timed out waiting for plugin build dependency lock at ${lockDir}`);
|
||||
}
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
if (allOutputsExist()) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(lockDir), { recursive: true });
|
||||
|
||||
let holdsLock = false;
|
||||
let exitCode = 0;
|
||||
try {
|
||||
try {
|
||||
fs.mkdirSync(lockDir);
|
||||
holdsLock = true;
|
||||
} catch (error) {
|
||||
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
||||
waitForLockRelease();
|
||||
if (!allOutputsExist()) {
|
||||
throw new Error("Plugin build dependency lock released before all outputs were created");
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
for (const target of buildTargets) {
|
||||
if (fs.existsSync(target.output)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const result = spawnSync(process.execPath, [tscCliPath, "-p", target.tsconfig], {
|
||||
cwd: rootDir,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
exitCode = result.status ?? 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (holdsLock) {
|
||||
fs.rmSync(lockDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (exitCode !== 0) {
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@
|
||||
"postpack": "rm -rf ui-dist",
|
||||
"clean": "rm -rf dist",
|
||||
"start": "node dist/index.js",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.888.0",
|
||||
|
||||
Reference in New Issue
Block a user