mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
fix(server): resolve clone/upload/mapping roots from GITNEXUS_HOME
CLONE_ROOT (git-clone.ts), UPLOAD_ROOT (upload-paths.ts), and the server-mapping file (embeddings/server-mapping.ts) computed their root from os.homedir() directly, ignoring GITNEXUS_HOME. The Docker image sets GITNEXUS_HOME=/data/gitnexus (the persistent volume that also holds the registry and indexes), so cloned/uploaded repos and their .gitnexus indexes instead landed in the container's ephemeral ~/.gitnexus and were lost on container recreation, while registry.json (which honors GITNEXUS_HOME) kept pointing at the now-dead path. That also defeated incremental re-analysis: a recreated container re-clones from scratch and full-rebuilds instead of git pull + incremental update. Source all three from the existing getGlobalDir() helper — the same GITNEXUS_HOME-aware primitive the registry and groups already use. Behavior is unchanged when GITNEXUS_HOME is unset (CLI / local installs): it falls back to ~/.gitnexus exactly as before. No signatures change and no UPLOAD_ROOT consumers are touched. Adds test/unit/gitnexus-home-roots.test.ts covering both the GITNEXUS_HOME-set and unset paths for all three roots. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b895a20415
commit
7a322fb976
4 changed files with 98 additions and 11 deletions
|
|
@ -1,15 +1,17 @@
|
|||
/**
|
||||
* Server Mapping Configuration
|
||||
*
|
||||
* Reads ~/.gitnexus/server-mapping.json to map repo names to service names.
|
||||
* Reads getGlobalDir()/server-mapping.json to map repo names to service names.
|
||||
* Used in embedding text to enrich metadata with microservice context.
|
||||
*/
|
||||
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { getGlobalDir } from '../../storage/repo-manager.js';
|
||||
|
||||
const MAPPING_FILE = path.join(os.homedir(), '.gitnexus', 'server-mapping.json');
|
||||
// Sourced from getGlobalDir() so it honors GITNEXUS_HOME (the Docker image sets
|
||||
// GITNEXUS_HOME=/data/gitnexus); falls back to ~/.gitnexus when unset.
|
||||
const MAPPING_FILE = path.join(getGlobalDir(), 'server-mapping.json');
|
||||
|
||||
let cachedMapping: Record<string, string> | null = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,29 @@
|
|||
/**
|
||||
* Git Clone Utility
|
||||
*
|
||||
* Shallow-clones repositories into ~/.gitnexus/repos/{name}/.
|
||||
* Shallow-clones repositories into the clone root (getGlobalDir()/repos/{name}/).
|
||||
* If already cloned, does git pull instead.
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import fs from 'fs/promises';
|
||||
import { isIP } from 'net';
|
||||
import { logger } from '../core/logger.js';
|
||||
import { parseRepoNameFromUrl } from '../storage/git.js';
|
||||
import { getGlobalDir } from '../storage/repo-manager.js';
|
||||
|
||||
/** Root directory for all cloned repositories. Targets must resolve inside this. */
|
||||
const CLONE_ROOT = path.resolve(path.join(os.homedir(), '.gitnexus', 'repos'));
|
||||
/**
|
||||
* Root directory for all cloned repositories. Targets must resolve inside this.
|
||||
*
|
||||
* Sourced from getGlobalDir() so it honors GITNEXUS_HOME — the Docker image sets
|
||||
* GITNEXUS_HOME=/data/gitnexus, the persistent volume that also holds the
|
||||
* registry and indexes. Without this, clones landed in the container's
|
||||
* ephemeral ~/.gitnexus/repos and were lost on container recreation while the
|
||||
* registry still pointed at the dead path. Falls back to ~/.gitnexus when the
|
||||
* env var is unset (CLI / local installs), matching the prior behavior exactly.
|
||||
*/
|
||||
const CLONE_ROOT = path.resolve(path.join(getGlobalDir(), 'repos'));
|
||||
|
||||
// A valid git repository name is filesystem-safe: alphanumerics plus `. _ -`.
|
||||
// Rejecting anything else (including `..`, `/`, `\`, shell metacharacters)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Upload working-directory paths.
|
||||
*
|
||||
* Browser folder uploads are written into ~/.gitnexus/uploads/{name}/ — a
|
||||
* Browser folder uploads are written into getGlobalDir()/uploads/{name}/ — a
|
||||
* sibling of the clone root (git-clone.ts CLONE_ROOT) — so an uploaded repo
|
||||
* persists and behaves like a cloned one (the graph UI's /api/file reads its
|
||||
* files after analysis, and DELETE /api/repo removes it). Staging happens in
|
||||
|
|
@ -12,12 +12,18 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { sanitizeRepoName } from '../storage/git.js';
|
||||
import { REPO_NAME_PATTERN } from './git-clone.js';
|
||||
import { getGlobalDir } from '../storage/repo-manager.js';
|
||||
|
||||
/** Root directory for all uploaded repositories. Targets must resolve inside this. */
|
||||
export const UPLOAD_ROOT = path.resolve(path.join(os.homedir(), '.gitnexus', 'uploads'));
|
||||
/**
|
||||
* Root directory for all uploaded repositories. Targets must resolve inside this.
|
||||
*
|
||||
* Sourced from getGlobalDir() so it honors GITNEXUS_HOME and stays a sibling of
|
||||
* the clone root on the same (in Docker, persistent) volume. Falls back to
|
||||
* ~/.gitnexus when the env var is unset.
|
||||
*/
|
||||
export const UPLOAD_ROOT = path.resolve(path.join(getGlobalDir(), 'uploads'));
|
||||
|
||||
/** Prefix for per-upload staging directories created under UPLOAD_ROOT. */
|
||||
export const STAGING_PREFIX = '.staging-';
|
||||
|
|
|
|||
70
gitnexus/test/unit/gitnexus-home-roots.test.ts
Normal file
70
gitnexus/test/unit/gitnexus-home-roots.test.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { describe, it, expect, afterEach, vi } from 'vitest';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import fs from 'node:fs/promises';
|
||||
|
||||
/**
|
||||
* Regression guard: the clone root (git-clone.ts), upload root (upload-paths.ts),
|
||||
* and server-mapping file (embeddings/server-mapping.ts) must follow
|
||||
* GITNEXUS_HOME, not the bare home directory.
|
||||
*
|
||||
* The Docker image sets GITNEXUS_HOME=/data/gitnexus — the persistent volume
|
||||
* that also holds the registry and indexes. Before this fix these three roots
|
||||
* used os.homedir() directly, so clones/uploads landed in the container's
|
||||
* ephemeral ~/.gitnexus and were lost on container recreation while the
|
||||
* registry (which honors GITNEXUS_HOME) still pointed at the dead path.
|
||||
*
|
||||
* The roots are module-level constants resolved at import time from
|
||||
* getGlobalDir(), so each case sets the env var, resets the module registry,
|
||||
* and re-imports under the new value.
|
||||
*/
|
||||
describe('GITNEXUS_HOME path roots', () => {
|
||||
const savedHome = process.env.GITNEXUS_HOME;
|
||||
const customHome = path.join(os.tmpdir(), 'gitnexus-home-roots-test');
|
||||
|
||||
afterEach(async () => {
|
||||
if (savedHome === undefined) delete process.env.GITNEXUS_HOME;
|
||||
else process.env.GITNEXUS_HOME = savedHome;
|
||||
vi.resetModules();
|
||||
await fs.rm(customHome, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('clone root follows GITNEXUS_HOME', async () => {
|
||||
process.env.GITNEXUS_HOME = customHome;
|
||||
vi.resetModules();
|
||||
const { getCloneDir } = await import('../../src/server/git-clone.js');
|
||||
expect(getCloneDir('my-repo')).toBe(path.resolve(customHome, 'repos', 'my-repo'));
|
||||
});
|
||||
|
||||
it('upload root follows GITNEXUS_HOME', async () => {
|
||||
process.env.GITNEXUS_HOME = customHome;
|
||||
vi.resetModules();
|
||||
const { UPLOAD_ROOT, getUploadDir } = await import('../../src/server/upload-paths.js');
|
||||
expect(UPLOAD_ROOT).toBe(path.resolve(customHome, 'uploads'));
|
||||
expect(getUploadDir('my-repo')).toBe(path.resolve(customHome, 'uploads', 'my-repo'));
|
||||
});
|
||||
|
||||
it('server-mapping file is read from GITNEXUS_HOME', async () => {
|
||||
process.env.GITNEXUS_HOME = customHome;
|
||||
await fs.mkdir(customHome, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(customHome, 'server-mapping.json'),
|
||||
JSON.stringify({ 'my-repo': 'payments-service' }),
|
||||
'utf-8',
|
||||
);
|
||||
vi.resetModules();
|
||||
const { readServerMapping } = await import('../../src/core/embeddings/server-mapping.js');
|
||||
expect(await readServerMapping('my-repo')).toBe('payments-service');
|
||||
});
|
||||
|
||||
it('falls back to ~/.gitnexus when GITNEXUS_HOME is unset', async () => {
|
||||
delete process.env.GITNEXUS_HOME;
|
||||
vi.resetModules();
|
||||
const { getCloneDir } = await import('../../src/server/git-clone.js');
|
||||
const { UPLOAD_ROOT } = await import('../../src/server/upload-paths.js');
|
||||
expect(getCloneDir('my-repo')).toBe(
|
||||
path.resolve(os.homedir(), '.gitnexus', 'repos', 'my-repo'),
|
||||
);
|
||||
expect(UPLOAD_ROOT).toBe(path.resolve(os.homedir(), '.gitnexus', 'uploads'));
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue