Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,6 @@ videos/

# Per-install secrets for the full-stack compose (generated by the CLI, #970)
docker/.env

# Staged by cli/scripts/stage-compose.mjs at prepack; never committed (#1315)
cli/docker/
6 changes: 4 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"files": [
"dist",
"docker",
"README.md",
"LICENSE"
],
Expand All @@ -37,11 +38,12 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"prepublishOnly": "npm run build",
"test": "SKIP_INTEGRATION=1 vitest run",
"test:integration": "vitest run src/__tests__/integration",
"test:watch": "vitest",
"test:coverage": "SKIP_INTEGRATION=1 vitest run --coverage"
"test:coverage": "SKIP_INTEGRATION=1 vitest run --coverage",
"prepack": "npm run build && node scripts/stage-compose.mjs",
"postpack": "node scripts/stage-compose.mjs --clean"
},
"dependencies": {
"chalk": "^5.0.0",
Expand Down
54 changes: 54 additions & 0 deletions cli/scripts/stage-compose.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env node
/**
* Copy the compose files the standalone CLI needs into the package directory,
* so `npm pack` can include them.
*
* npm's `files` is resolved relative to the PACKAGE root, and `docker/` lives
* at the REPO root — one level up. Listing "docker" in `files` therefore does
* nothing at all, silently: `npm pack` succeeds, ships no compose file, and a
* manifest-only test asserting `files.includes("docker")` passes while proving
* nothing. That is how #1315's second half would have shipped twice.
*
* Run by `prepack` (after the build — a package without dist/ is as broken as
* one without compose files, and prepack is the hook that guarantees BOTH for
* anyone who packs, not just for the release workflow which happens to build
* first). `--clean`, from `postpack`, removes the staged copy.
*
* Both paths are computed from import.meta.url, never from cwd. A bare
* `rm -rf docker` in postpack would be relative to whatever directory npm
* happened to run it from — and one level up is the repo's real docker/.
* `cli/docker/` is gitignored.
*/
import { mkdirSync, copyFileSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

const cliDir = dirname(dirname(fileURLToPath(import.meta.url)));
const repoDocker = join(dirname(cliDir), "docker");
const dest = join(cliDir, "docker");

// Only what a standalone install can actually use. prod-full pulls
// ghcr.io/... rather than building from source, which a standalone user has
// none of; the dev compose files reference build contexts that will not exist.
const FILES = [
"docker-compose.prod-full.yml",
"docker-compose.prod.yml",
"neo4j/init.cypher",
"postgres/init.sql",
];

rmSync(dest, { recursive: true, force: true });

if (process.argv.includes("--clean")) {
console.error("removed staged cli/docker/");
process.exit(0);
}

for (const rel of FILES) {
const to = join(dest, rel);
mkdirSync(dirname(to), { recursive: true });
copyFileSync(join(repoDocker, rel), to);
}
// stderr, not stdout: prepack output is interleaved with `npm pack --json`,
// and a stray line there makes the JSON unparseable for every consumer.
console.error(`staged ${FILES.length} compose assets into cli/docker/`);
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const mockConfig = {
};

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { projectConfig: "/project/neoboard.config.json" },
readProjectConfig: vi.fn(() => ({ ...mockConfig })),
writeProjectConfig: vi.fn(),
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/db/dump.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("node:child_process", () => ({
}));

vi.mock("../../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { root: "/project" },
readProjectConfig: vi.fn(() => ({
ports: { postgres: 5432 },
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/db/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ vi.mock("../../../lib/exec.js", () => ({
}));

vi.mock("../../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
journalPath: "/project/app/drizzle/migrations/meta/_journal.json",
appDir: "/project/app",
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/db/reset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vi.mock("../../../lib/exec.js", () => ({
}));

vi.mock("../../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { envFile: "/project/app/.env.local" },
readProjectConfig: vi.fn(() => ({
postgres: { user: "neoboard", password: "neoboard", database: "neoboard" },
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/db/seed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ vi.mock("../../../lib/exec.js", () => ({
}));

vi.mock("../../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { root: "/project" },
getMode: vi.fn(() => "docker"),
readProjectConfig: vi.fn(() => ({
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/demo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ vi.mock("../../lib/prompt.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { root: "/repo" },
getMode: vi.fn(() => "local"),
readProjectConfig: vi.fn(() => ({ ports: { app: 3000 } })),
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ vi.mock("../../lib/exec.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { appDir: "/project/app" },
getMode: vi.fn(() => "local"),
readProjectConfig: vi.fn(() => ({
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/doctor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ vi.mock("../../lib/ports.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
root: "/project",
appDir: "/project/app",
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vi.mock("node:crypto", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
envFile: "/project/app/.env.local",
envExample: "/project/.env.example",
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ vi.mock("../../lib/exec.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
root: "/project",
appDir: "/project/app",
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ vi.mock("../../lib/docker.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { root: "/project" },
}));

Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/plugin-hints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { pathToFileURL } from "node:url";
vi.mock("../../lib/exec.js", () => ({ run: vi.fn(), runFile: vi.fn() }));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
findProjectRoot: vi.fn(() => "/project"),
}));

Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
findProjectRoot: vi.fn(() => "/project"),
}));

Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/start.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ vi.mock("../../lib/health.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
readProjectConfig: vi.fn(() => ({
ports: { app: 3000, postgres: 5432, neo4j_http: 7474, neo4j_bolt: 7687 },
})),
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/commands/status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ vi.mock("../../lib/exec.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
journalPath: "/project/app/drizzle/migrations/meta/_journal.json",
root: "/project",
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/lib/bootstrap-status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";

vi.mock("../../lib/exec.js", () => ({ runOrNull: vi.fn() }));
vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
readProjectConfig: vi.fn(() => ({ ports: { app: 3000 } })),
}));

Expand Down
18 changes: 11 additions & 7 deletions cli/src/__tests__/lib/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,24 @@ describe("findProjectRoot", () => {
expect(findProjectRoot("/a/b/c")).toBe("/a");
});

it("throws when no project root found", () => {
it("returns null when no project root is found", () => {
// Was `toThrow`. Changed deliberately in #1315: under `npx`, the CLI lives
// in an npm cache directory with no monorepo above it, and throwing this
// deep in a path helper surfaced as an unrelated-looking crash. Absence is
// a normal state now; only the caller knows whether it is a problem.
mockExistsSync.mockReturnValue(false);
expect(() => findProjectRoot("/nowhere")).toThrow(
"Could not find NeoBoard project root",
);
expect(findProjectRoot("/nowhere")).toBeNull();
});

it("terminates instead of looping when run from a Windows drive root (#991)", () => {
// dirname("C:\\") === "C:\\" — the old `while (dir !== "/")` loop
// never terminated. The fixed loop stops when dirname stops changing.
//
// Returning null still proves termination: a non-terminating loop would
// hang the test rather than return anything. The assertion changed with
// #1315; what it protects did not.
mockExistsSync.mockReturnValue(false);
expect(() => findProjectRoot("C:\\")).toThrow(
"Could not find NeoBoard project root",
);
expect(findProjectRoot("C:\\")).toBeNull();
});
});

Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/lib/credential-probe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vi.mock("../../lib/exec.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
readProjectConfig: vi.fn(() => ({
ports: { app: 3000, postgres: 5432, neo4j_http: 7474, neo4j_bolt: 7687 },
postgres: { user: "neoboard", password: "neoboard", database: "neoboard" },
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/lib/docker-env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ vi.mock("node:crypto", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: { root: "/project" },
getMode: vi.fn(() => "docker"),
}));
Expand Down
1 change: 1 addition & 0 deletions cli/src/__tests__/lib/docker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ vi.mock("../../lib/exec.js", () => ({
}));

vi.mock("../../lib/config.js", () => ({
assertCheckout: vi.fn(),
paths: {
root: "/project",
dockerDir: "/project/docker",
Expand Down
Loading
Loading