mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
A3: keep a project's DATABASE_URL away from a PGLite brain
With a PGLite config, buildGbrainEnv returned the caller env unchanged, so a project's .env DATABASE_URL (autoloaded by Bun) reached gbrain: a false broken-db at best, gbrain talking to the app's own Postgres at worst. Inherited DATABASE_URL and GBRAIN_DATABASE_URL are now blanked for PGLite configs (GSTACK_RESPECT_ENV_DATABASE_URL=1 still keeps them), and every gbrain child runs from gbrain's config dir instead of the user's project, so it cannot reload the project's .env / .env.local. The old test that pinned pass-through for an engine:pglite config is migrated: pass-through now applies only to configs that name neither a database_url nor PGLite. Co-authored-by: maxpetrusenkoagent <max.petrusenko.agent@gmail.com>
This commit is contained in:
1 parent
7e2edf2a3a
commit
f20cd9a3a3
4 files changed
+109
-10
No files matched your search
+33
-6
@@ -35,11 +35,13 @@
|
||||
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { homedir } from "os";
|
||||
import { homedir, tmpdir } from "os";
|
||||
import { spawnSync, spawn, execFileSync, type SpawnSyncReturns, type ChildProcess, type SpawnOptions } from "child_process";
|
||||
|
||||
interface GbrainConfig {
|
||||
database_url?: string;
|
||||
engine?: string;
|
||||
database_path?: string;
|
||||
}
|
||||
|
||||
export interface BuildGbrainEnvOptions {
|
||||
@@ -95,9 +97,15 @@ export function gbrainConfigDir(env: NodeJS.ProcessEnv = process.env): string {
|
||||
* unchanged when:
|
||||
* - `GSTACK_RESPECT_ENV_DATABASE_URL=1` (intentional opt-out),
|
||||
* - the config file is missing or unparseable,
|
||||
* - the config has no `database_url`,
|
||||
* - the config has no `database_url` and is not a PGLite config,
|
||||
* - the caller already set DATABASE_URL to the same value.
|
||||
*
|
||||
* A PGLite config has no URL to seed, so an inherited DATABASE_URL /
|
||||
* GBRAIN_DATABASE_URL (Bun autoloads a project's `.env`) would make gbrain
|
||||
* connect to the app's own database instead of its file brain (#1917). Both
|
||||
* are blanked, not deleted: gbrain treats an empty value as unset, and an
|
||||
* existing (empty) variable is never refilled by a child's dotenv autoload.
|
||||
*
|
||||
* GBRAIN_PREPARE is never set here (#1965): gbrain auto-disables prepared
|
||||
* statements on transaction-mode poolers itself, and forcing them on breaks
|
||||
* every write with "prepared statement does not exist". A caller-set
|
||||
@@ -122,7 +130,13 @@ export function buildGbrainEnv(opts: BuildGbrainEnvOptions = {}): NodeJS.Process
|
||||
} catch {
|
||||
return out;
|
||||
}
|
||||
if (!cfg.database_url) return out;
|
||||
if (!cfg.database_url) {
|
||||
if (cfg.engine === "pglite" || cfg.database_path) {
|
||||
out.DATABASE_URL = "";
|
||||
out.GBRAIN_DATABASE_URL = "";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const hadCaller = baseEnv.DATABASE_URL !== undefined;
|
||||
const alreadyMatch = baseEnv.DATABASE_URL === cfg.database_url;
|
||||
@@ -249,6 +263,19 @@ export function gbrainInvocation(args: string[]): { cmd: string; argv: string[];
|
||||
: { cmd: "gbrain", argv: args, shell: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Working directory for a gbrain child (#1917). gbrain (a Bun program) and
|
||||
* its own config loader read `.env` / `.env.local` from the working
|
||||
* directory, so a child started inside the user's project can pick the
|
||||
* project's DATABASE_URL back up. Children run from gbrain's config
|
||||
* directory (or the OS temp dir before init) unless the caller names a cwd.
|
||||
*/
|
||||
export function gbrainChildCwd(cwd?: string, env: NodeJS.ProcessEnv = process.env): string {
|
||||
if (cwd) return cwd;
|
||||
const dir = gbrainConfigDir(env);
|
||||
return existsSync(dir) ? dir : tmpdir();
|
||||
}
|
||||
|
||||
export interface SpawnGbrainOptions {
|
||||
/** Timeout in milliseconds. Defaults to 30s. */
|
||||
timeout?: number;
|
||||
@@ -276,7 +303,7 @@ export function spawnGbrain(args: string[], opts: SpawnGbrainOptions = {}): Spaw
|
||||
return spawnSync(inv.cmd, inv.argv, {
|
||||
encoding: "utf-8",
|
||||
timeout: opts.timeout ?? 30_000,
|
||||
cwd: opts.cwd,
|
||||
cwd: gbrainChildCwd(opts.cwd, opts.baseEnv),
|
||||
stdio: opts.stdio || ["ignore", "pipe", "pipe"],
|
||||
env: buildGbrainEnv({ baseEnv: opts.baseEnv, announce: opts.announce }),
|
||||
shell: inv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
@@ -311,7 +338,7 @@ export function spawnGbrainAsync(
|
||||
const inv = gbrainInvocation(args);
|
||||
return spawn(inv.cmd, inv.argv, {
|
||||
stdio: opts.stdio || ["ignore", "pipe", "pipe"],
|
||||
cwd: opts.cwd,
|
||||
cwd: gbrainChildCwd(opts.cwd, opts.baseEnv),
|
||||
env: buildGbrainEnv({ baseEnv: opts.baseEnv, announce: false }),
|
||||
shell: inv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
});
|
||||
@@ -326,7 +353,7 @@ export function execGbrainText(args: string[], opts: SpawnGbrainOptions = {}): s
|
||||
return execFileSync(inv.cmd, inv.argv, {
|
||||
encoding: "utf-8",
|
||||
timeout: opts.timeout ?? 30_000,
|
||||
cwd: opts.cwd,
|
||||
cwd: gbrainChildCwd(opts.cwd, opts.baseEnv),
|
||||
stdio: opts.stdio || ["ignore", "pipe", "pipe"],
|
||||
env: buildGbrainEnv({ baseEnv: opts.baseEnv, announce: opts.announce }),
|
||||
shell: inv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
import { atomicWriteSync } from "./fs-atomic";
|
||||
import { homedir } from "os";
|
||||
import { dirname, join } from "path";
|
||||
import { buildGbrainEnv, gbrainConfigDir, isExecTimeout, NEEDS_SHELL_ON_WINDOWS } from "./gbrain-exec";
|
||||
import { buildGbrainEnv, gbrainChildCwd, gbrainConfigDir, isExecTimeout, NEEDS_SHELL_ON_WINDOWS } from "./gbrain-exec";
|
||||
import { resolveStateRoot } from "./state-root";
|
||||
|
||||
export type LocalEngineStatus =
|
||||
@@ -458,6 +458,7 @@ function freshClassify(env?: NodeJS.ProcessEnv): LocalEngineStatus {
|
||||
timeout: probeTimeoutMs(env),
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: buildGbrainEnv({ baseEnv: env ?? process.env }),
|
||||
cwd: gbrainChildCwd(undefined, env ?? process.env),
|
||||
shell: NEEDS_SHELL_ON_WINDOWS, // #1731: gbrain is a .cmd shim on Windows
|
||||
});
|
||||
return "ok";
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { execFileSync, spawnSync } from "child_process";
|
||||
import { realpathSync } from "fs";
|
||||
import { withErrorContext } from "./gstack-memory-helpers";
|
||||
import { execGbrainJson, gbrainInvocation } from "./gbrain-exec";
|
||||
import { execGbrainJson, gbrainChildCwd, gbrainInvocation } from "./gbrain-exec";
|
||||
import {
|
||||
detectAutopilot,
|
||||
decideSourceRemove,
|
||||
@@ -123,6 +123,7 @@ export function probeSource(id: string, env?: NodeJS.ProcessEnv): SourceState {
|
||||
timeout: 30_000,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env,
|
||||
cwd: gbrainChildCwd(undefined, env),
|
||||
shell: inv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -230,6 +231,7 @@ export async function ensureSourceRegistered(
|
||||
encoding: "utf-8",
|
||||
timeout: 30_000,
|
||||
env,
|
||||
cwd: gbrainChildCwd(undefined, env),
|
||||
shell: rmInv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
});
|
||||
if (rm.status !== 0) {
|
||||
@@ -246,6 +248,7 @@ export async function ensureSourceRegistered(
|
||||
encoding: "utf-8",
|
||||
timeout: 30_000,
|
||||
env,
|
||||
cwd: gbrainChildCwd(undefined, env),
|
||||
shell: addInv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
});
|
||||
if (add.status !== 0) {
|
||||
@@ -273,6 +276,7 @@ export function sourcePageCount(id: string, env?: NodeJS.ProcessEnv): number | n
|
||||
timeout: 30_000,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env,
|
||||
cwd: gbrainChildCwd(undefined, env),
|
||||
shell: inv.shell, // #1731: gbrain is a .cmd shim on Windows (+#2471 quoting)
|
||||
});
|
||||
} catch {
|
||||
|
||||
Reference in new issue
Block a user