Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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/
5 changes: 4 additions & 1 deletion 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 @@ -41,7 +42,9 @@
"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": "node scripts/stage-compose.mjs",
"postpack": "rm -rf docker"
},
"dependencies": {
"chalk": "^5.0.0",
Expand Down
40 changes: 40 additions & 0 deletions cli/scripts/stage-compose.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/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`, removed by `postpack`. `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 });
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