Skip to content

feat: add docker support#848

Merged
magyargergo merged 20 commits into
abhigyanpatwari:mainfrom
BRAINIFII:feat/docker
Apr 18, 2026
Merged

feat: add docker support#848
magyargergo merged 20 commits into
abhigyanpatwari:mainfrom
BRAINIFII:feat/docker

Conversation

@BRAINIFII

Copy link
Copy Markdown
Contributor

Summary

Closes #485
Adds Docker support for the GitNexus web UI: a multi-platform Dockerfile, a docker-compose.yaml, a lightweight production static file server, and a GitHub Actions workflow that automatically builds and pushes to GHCR on every version tag and main push.

Motivation / context

Users asked for a way to self-host the GitNexus web UI without running the Vite dev server. Docker provides a portable, reproducible deployment that anyone can run with a single command.

Areas touched

  • gitnexus/ (CLI / core / MCP server)
  • gitnexus-web/ (Vite / React UI)
  • .github/ (workflows, actions)
  • eval/ or other tooling
  • Docs / agent config only (AGENTS.md, CLAUDE.md, .cursor/, llms.txt, etc.)

Scope & constraints

In scope

  • Multi-platform (linux/amd64, linux/arm64) Docker image for the web UI
  • docker-server.mjs — minimal Node.js static file server with SPA fallback, Cache-Control, and required COOP/COEP headers for WASM SharedArrayBuffer
  • docker-compose.yaml with healthcheck, configurable image/container name/port via env vars
  • .env.example and .dockerignore
  • docker.yml GitHub Actions workflow — builds and pushes to GHCR on v* tags and main, with GHA layer caching

Explicitly out of scope / not done here

  • Docker image for the CLI/MCP server (gitnexus serve)
  • Multi-container setup (web UI + backend in one compose)
  • Kubernetes / Helm manifests

Implementation notes

  • The runtime stage uses node:20-bookworm-slim with curl installed for healthcheck support
  • COOP/COEP headers (Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp) are set on every response — required for WASM SharedArrayBuffer which Vite dev server adds automatically but a plain static server does not
  • All Docker action SHAs are pinned to verified commits per repo convention; versions upgraded to Node.js 24-compatible releases (v4/v6/v7)
  • Image is published to ghcr.io/<owner>/gitnexus and authenticated with the built-in GITHUB_TOKEN — no extra secrets required

Testing & verification

  • cd gitnexus && npm test
  • cd gitnexus && npm run test:integration (if core/indexing/MCP paths changed)
  • cd gitnexus && npx tsc --noEmit
  • cd gitnexus-web && npm test (if web changed)
  • cd gitnexus-web && npx tsc -b --noEmit (if web changed)
  • Manual: workflow dispatched via GitHub Actions UI — image built and pushed to GHCR successfully (1m 51s, linux/amd64 + linux/arm64)
  • Manual: docker run --rm -p 4173:4173 ghcr.io/<fork>/gitnexus:<sha> — web UI loads correctly

Risk & rollout

  • No changes to the indexing pipeline, MCP server, or CLI behaviour — purely additive
  • First publish will create a private GHCR package; repo admin should set it to public after the first push
  • Consumers using the image should run docker pull to pick up the new COOP/COEP headers if they were using an older build

Checklist

  • PR body meets repo minimum length (workflow may label short descriptions)
  • If AGENTS.md / overlays changed: headers, scope block, and changelog updated per project conventions
  • No secrets, tokens, or machine-specific paths committed

@github-actions github-actions Bot added the enhancement New feature or request label Apr 15, 2026

@xkonjin xkonjin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

General

Solid addition — Dockerizing the web UI is a clear win for self-hosting, and the multi-platform GHCR workflow is well thought out.

Security

  • COOP/COEP headers: Good catch on Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp for WASM SharedArrayBuffer. Those are easy to miss when moving off Vite.
  • Path traversal in docker-server.mjs: The resolvePath guard (candidate.startsWith(root)) is correct, but consider also rejecting null bytes in the path to defensively harden the check.
  • Cache-Control: The immutable policy on /assets/ is appropriate for Vite-hashed builds.

Bugs / Robustness

  • Missing error handler on createReadStream: In docker-server.mjs, createReadStream(filePath).pipe(res) can throw if the file is truncated or unreadable mid-flight. Pipe errors are swallowed by default. Add:
    const stream = createReadStream(filePath);
    stream.on('error', (err) => {
      res.destroy();
    });
    stream.pipe(res);
  • existsSync after stat race condition: You stat the path, then later call existsSync(filePath). Between those two calls the file could be removed. Prefer reusing the stat result or handling ENOENT on the read stream directly.

Testing

  • No automated tests for docker-server.mjs. Given it's a security-facing boundary, consider a lightweight integration test that hits the server with:
    1. A valid asset
    2. An SPA fallback route
    3. A path traversal attempt (../../../etc/passwd)
    4. A request for a missing file

Minor

  • Node version drift: Dockerfile uses node:20-bookworm-slim but the repo may have Node 22+ elsewhere. Worth pinning to the same major version used in CI to avoid subtle runtime differences.

Overall: LGTM with the stream error handler as a recommended follow-up.

Defensively harden the path traversal guard by returning null
early when the URL contains a null byte, before normalization runs.

Made-with: Cursor
@vercel

vercel Bot commented Apr 16, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

kritik.b added 3 commits April 16, 2026 08:40
Attach an error listener before piping so mid-flight read errors
(truncated file, permission change) cleanly destroy the response
instead of being silently swallowed.

Made-with: Cursor
Eliminates the TOCTOU race between the initial stat call and the
subsequent existsSync check. Reuses the async stat pattern already
in place and removes the now-unused existsSync import.

Made-with: Cursor
Decode the URL before the null-byte check so percent-encoded null
bytes (%00) are also rejected with 400 instead of falling through
to the SPA fallback. Adds 5 node:test integration tests covering
valid assets, SPA fallback, path traversal, null bytes, and 404.

Made-with: Cursor
@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
❌ E2E failure gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
6606 6509 0 97 259s

✅ All 6509 tests passed

97 test(s) skipped — expand for details
  • Swift MethodExtractor > isTypeDeclaration > recognizes class_declaration
  • Swift MethodExtractor > isTypeDeclaration > recognizes protocol_declaration
  • Swift MethodExtractor > isTypeDeclaration > rejects import_declaration
  • Swift MethodExtractor > visibility > extracts public method
  • Swift MethodExtractor > visibility > extracts private method
  • Swift MethodExtractor > visibility > defaults to internal when no modifier
  • Swift MethodExtractor > protocol methods > marks protocol method as abstract
  • Swift MethodExtractor > static and class methods > detects static func as isStatic
  • Swift MethodExtractor > static and class methods > detects class func as isStatic
  • Swift MethodExtractor > parameters > extracts parameters with types and default values
  • Swift MethodExtractor > return type > extracts return type from -> annotation
  • Swift MethodExtractor > annotations > extracts @objc attribute
  • Swift MethodExtractor > isFinal > detects final func
  • Swift MethodExtractor > isFinal > is false when not final
  • Swift MethodExtractor > isAsync > detects async func
  • Swift MethodExtractor > isOverride > detects override method
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift explicit init inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference does not bind plain functions
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call
  • Swift method enrichment > detects Animal protocol and Dog class
  • Swift method enrichment > emits IMPLEMENTS edge Dog -> Animal
  • Swift method enrichment > emits HAS_METHOD edges for Dog methods
  • Swift method enrichment > marks protocol Animal.speak as isAbstract
  • Swift method enrichment > marks Dog.speak as NOT isAbstract
  • Swift method enrichment > marks breathe as isFinal
  • Swift method enrichment > marks classify as isStatic
  • Swift method enrichment > captures @objc annotation on breathe
  • Swift method enrichment > populates parameterTypes for classify(_ name: String)
  • Swift method enrichment > records parameterCount for classify
  • Swift method enrichment > records returnType for speak
  • Swift method enrichment > resolves dog.speak() CALLS edge
  • Swift method enrichment > resolves Dog.classify("dog") CALLS edge
  • Swift abstract dispatch > detects Repository protocol and SqlRepository class
  • Swift abstract dispatch > emits IMPLEMENTS edge SqlRepository -> Repository
  • Swift abstract dispatch > emits HAS_METHOD edges for Repository.find and Repository.save
  • Swift abstract dispatch > emits HAS_METHOD edges for SqlRepository.find and SqlRepository.save
  • Swift abstract dispatch > marks base Repository.find as isAbstract
  • Swift abstract dispatch > marks base Repository.save as isAbstract
  • Swift abstract dispatch > marks concrete SqlRepository.find as NOT isAbstract
  • Swift abstract dispatch > resolves repo.find(id: 42) CALLS edge
  • Swift abstract dispatch > resolves repo.save(entity: user) CALLS edge
  • Swift abstract dispatch > populates parameterTypes for Repository.find
  • Swift abstract dispatch > populates parameterTypes for Repository.save
  • Swift abstract dispatch > records returnType for SqlRepository.find
  • Swift abstract dispatch > emits METHOD_IMPLEMENTS edges from SqlRepository methods → Repository protocol methods
  • Swift overloaded method disambiguation > detects 2 distinct find Method nodes on SqlRepository
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edges for both find overloads
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edge for save
  • Swift overloaded method disambiguation > emits exactly 3 METHOD_IMPLEMENTS edges total
  • Swift Child extends Parent — inherited method resolution (SM-9) > detects Parent and Child classes
  • Swift Child extends Parent — inherited method resolution (SM-9) > resolves c.parentMethod() to Parent.parentMethod via first-wins MRO walk

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 73.22% 17852/24380 73.22% = 0.0 🟢 ██████████████░░░░░░
Branches 62.24% 11340/18218 62.24% = 0.0 🟢 ████████████░░░░░░░░
Functions 77.82% 1688/2169 77.82% = 0.0 🟢 ███████████████░░░░░
Lines 75.78% 16185/21356 75.78% = 0.0 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude Act as a senior reviewer for GitNexus. Your job is to determine whether this PR is production-ready for this repo, not to give a generic code review.

You are reviewing a PR in the GitNexus monorepo:

  • gitnexus/ → CLI + MCP
  • gitnexus-web/
  • gitnexus-shared/

Your task has 2 phases, in this exact order:

PHASE 1 — DEFINE THE BAR
Before reviewing the diff, establish a concise repo-specific definition of “production-ready” for GitNexus, based only on the repo docs and the affected area.
Keep this definition practical and reviewable. Do not invent standards that are not grounded in the repo.

PHASE 2 — REVIEW THE PR AGAINST THAT BAR
Review the actual diff only after defining the bar.
Stay tightly scoped to the changed code and its direct consequences.


CONTEXT TO LOAD FIRST
Read these from origin/main before reviewing:

  • AGENTS.md
  • GUARDRAILS.md
  • CONTRIBUTING.md
  • TESTING.md
  • ARCHITECTURE.md

Additional context:


PRIMARY OBJECTIVE
Decide whether this PR is safe, correct, maintainable, and operationally acceptable to merge into production for GitNexus.

Do not optimize for completeness at the expense of signal.
Do not pad the review.
Do not propose unrelated refactors.
Do not restate the PR description unless needed for verification.


REVIEW RULES

  • Every finding must be grounded in specific evidence from the diff or directly relevant surrounding code.
  • Every finding must include path:line.
  • If you make a behavioral claim, cite the code that proves it.
  • If you make a performance claim, explain the mechanism.
  • If something cannot be verified from the diff alone, explicitly say so.
  • Distinguish clearly between:
    • verified issue
    • plausible risk
    • unverified concern
  • Avoid vague wording like “might be better” or “could be improved” unless you explain exactly why.
  • Keep the review focused on this PR’s scope only.

For each finding, assign one severity:

  • BLOCKING → must be fixed before merge
  • NON-BLOCKING → valid issue, but merge may still be acceptable
  • NIT → stylistic/minor, not merge-relevant

REPO-SPECIFIC REVIEW CHECKLIST
Use these exact headings.

1. Correctness & functional completeness

Check:

  • Does the implementation actually satisfy the PR claim?
    • ManifestExtractor is truly invoked
    • config.links produces non-zero cross-links where expected
  • Resolver contracts are preserved:
    • resolveSymbol remains exact-match
    • label-scoped Cypher remains correct per contract type
    • flag any regression toward fuzzy or unscoped matching
  • Graph schema integrity is preserved:
    • no silent changes to node labels
    • no silent changes to edge types
    • no silent changes to ID generation (generateId)
  • Call out any missing wiring, partial integration, dead branch, or mismatch between tests and runtime behavior

2. Code clarity & clean code

Check:

  • naming quality
  • local cohesion
  • dead code
  • unnecessary abstraction
  • hidden control flow
  • confusing indirection
  • adherence to repo conventions:
    • direct imports from gitnexus-shared
    • no barrel re-export regressions
    • no // removed comments
    • no unused re-exports
  • no drive-by refactors outside stated scope per CONTRIBUTING.md and GUARDRAILS.md § Scope

3. Test coverage & change safety

Evaluate against TESTING.md:

  • Are there unit tests under gitnexus/test/unit/ covering the newly wired path?
  • Is there a regression guard for 0-link → N-link behavior?
  • Are assertions meaningful rather than tautological?
  • Are fixtures realistic for manifest inputs?
  • If memoization/cache was introduced, is there a test proving hit/miss behavior and correctness?
  • Is there evidence the expected validation path would pass for staged gitnexus/ files?
    • tsc --noEmit
    • vitest run --project default
      If not verifiable, say exactly what is missing.

4. Performance

Inspect for:

  • hot-path overhead in ingestion/group sync
  • excess allocations per manifest entry
  • redundant Cypher round-trips
  • missed batching or missed parallelism (Promise.all) where it materially matters
  • O(n²) or repeated lookup patterns on large repos
  • memoization tradeoffs:
    • correctness
    • invalidation
    • bounded vs unbounded memory growth
      Do not speculate casually; explain the mechanism and likely impact.

5. Operational risk

Check:

  • Windows/cross-platform safety:
    • stream lifecycle
    • FD/file handle lifecycle
    • path separator assumptions
    • anything resembling prior ENOTEMPTY-style lifecycle regressions
  • LadybugDB single-writer invariant is preserved
  • Embeddings preservation:
    • no silent breakage of --embeddings
    • .gitnexus/meta.json.stats.embeddings not silently zeroed by changed paths
  • MCP contracts remain compatible:
    • group_*
    • query
    • context
    • impact
    • detect_changes
    • rename
    • cypher
      Flag any schema or contract break without migration note
  • staleness behavior still triggers correctly (gitnexus/src/mcp/staleness.ts)
  • rollback safety:
    • can this PR be reverted safely without re-analyze?
    • if not, explain why

6. Maintainability

Check:

  • Does the change respect Pipeline Phase DAG boundaries?
  • Is cross-phase coupling being introduced?
  • Are manifest-extractor entry points discoverable from group/sync.ts?
  • Would the next contributor know where to extend this?
  • Is shared logic placed appropriately:
    • in gitnexus-shared/
    • or gitnexus/src/core/**/utils.ts
    • and not duplicated between CLI and web bridge (server/api.ts)?

NON-NEGOTIABLES TO VERIFY
From GUARDRAILS.md / CONTRIBUTING.md:

  • no secrets, tokens, or machine-specific paths committed
  • no blind find-and-replace renames
  • if a symbol rename occurred, rename MCP tool or equivalent careful review was used
  • upstream impact was considered for modified shared symbols
  • no HIGH/CRITICAL impact left unaddressed
  • PR title follows conventional commit style + [area] Short description
  • PolyForm Noncommercial license headers/metadata untouched

OUTPUT FORMAT

Production-ready bar for this PR

Give a concise repo-specific definition in 5–10 bullets.

Findings

For each finding, use this structure exactly:

[BLOCKING|NON-BLOCKING|NIT] Short title

  • Evidence: path:line, path:line
  • Why it matters: ...
  • Recommendation: ...
  • Confidence: High / Medium / Low

Only include findings that are real and supported.
If there are no findings in a section, say:

  • No issues found in this area based on the diff and reviewed context.

What I could not fully verify

List any behavior you could not prove from the diff, tests, or available context.

Final verdict

Choose exactly one:

  • ✅ Ready to merge — no blocking issues.
  • ⚠️ Merge after addressing blocking items.
  • ❌ Not ready — fundamental concerns.

If verdict is not ready, list the blocking items in priority order.


IMPORTANT REVIEW BEHAVIOR

  • Be strict on correctness and contracts.
  • Be conservative about operational risk.
  • Be explicit about uncertainty.
  • Do not suggest unrelated refactors.
  • Do not turn nits into blockers.
  • Do not reward intent; review actual implementation.

@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 5m 12s —— View job


Production-Readiness Review — PR #848 feat: add docker support

Tasks

  • Read repo docs (AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md)
  • Read changed files (docker-server.mjs, docker-server.test.mjs, Dockerfile, docker-compose.yaml, docker.yml, vite.config.ts)
  • Phase 1: Define production-ready bar
  • Phase 2: Review diff against the bar

Production-ready bar for this PR

Grounded in AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, and TESTING.md:

  • CI gate passes: quality/typecheck, quality/format (Prettier), and all unit/integration tests green.
  • No secrets or real-namespace defaults committed: .env.example uses placeholders only; Docker image references must not hardcode a contributor's personal fork namespace in a file meant for end users.
  • Security boundary correct: docker-server.mjs is a public-facing HTTP server — path-traversal guards must be logically sound, not just empirically working in the current filesystem layout.
  • Tests run in CI: New test files must be discovered and executed by the CI test runner (vitest for this repo), not silently dropped.
  • Conventional-commit PR title: feat: add docker support — satisfies the <type>[(<scope>)]: <subject> format enforced by pr-labeler.yml.
  • No drive-by refactors: Changes limited to Docker scaffolding + necessary vite.config.ts alias.
  • PolyForm Noncommercial license headers untouched: n/a for these new infra files; verified no license metadata modified.
  • Workflow concurrency block follows CONTRIBUTING.md convention: tag and main-branch policies must be correct.

1. Correctness & functional completeness

PR claim verified:

  • docker-server.mjs is a working SPA-serving Node.js static server with correct COOP/COEP headers. ✓
  • Vite build is wired properly — gitnexus/package.json COPY in Dockerfile:14 exists to feed vite.config.ts:8 (_require('../gitnexus/package.json') reads the engines field). ✓
  • vite.config.ts adds a gitnexus-shared alias (../gitnexus-shared/src/index.ts) required so npm ci inside Docker (no workspace symlinks) can resolve the shared package at build time. ✓
  • COOP/COEP headers set on every response — confirmed in docker-server.mjs:67-68. ✓

No missing wiring, dead branches, or partial integration found.


2. Code clarity & clean code

  • No // removed comments, no unused re-exports, no drive-by refactors outside stated scope.
  • resolvePath is cohesive and readable.
  • No barrel re-export regressions (new files are root-level .mjs, outside the package graph).
  • No concerns here beyond the security finding below.

3. Test coverage & change safety

NON-BLOCKING — docker-server.test.mjs uses node:test, not Vitest — tests may not run in CI

  • Evidence: docker-server.test.mjs:7import { after, before, it } from 'node:test'; import assert from 'node:assert/strict'. The project test runner is Vitest (cd gitnexus && npm testvitest run test/unit; cd gitnexus-web && npm testvitest run). Neither package's Vitest config covers a root-level .mjs file using the Node test runner.
  • Why it matters: These 5 tests cover the security-facing HTTP boundary (path traversal, null bytes, SPA fallback, COOP/COEP headers). If they don't run in CI, regressions to this component won't be caught automatically.
  • Recommendation: Either move the test into gitnexus-web/ and rewrite it using Vitest (preferred — matches project convention), or add an explicit node --test docker-server.test.mjs step to CI. Confirm by checking whether the 6354-test total in CI includes or excludes these 5 tests.
  • Confidence: High (framework mismatch is clear from the import; whether Vitest auto-discovers root .mjs files depends on config not shown in the diff).

4. Performance

No hot-path, ingestion, or MCP code touched. Static file server is I/O-bound by definition. No performance concerns.


5. Operational risk

BLOCKING — CI gate failing on Prettier format check

  • Evidence: CI job quality / format (job ID 71582086210) → Run npx prettier --check .failure. All other jobs pass: quality/typecheck ✓, quality/typecheck-web ✓, all test suites ✓. The CI summary comment on this PR incorrectly labels the failure as "Typecheck" — the actual failing stage is formatting. Latest commit 1019ee2 is titled "style: fix prettier formatting in docker-server files" but CI has not re-run since then.
  • Why it matters: The CI gate (CI Gate job) is explicitly blocked. Per CONTRIBUTING.md, checks must pass before merge.
  • Recommendation: Trigger a new CI run on the current HEAD (1019ee2). If Prettier now passes, this blocker is resolved.
  • Confidence: High (job-level CI data is authoritative; the fix commit exists but needs verification).

NON-BLOCKING — resolvePath path-separator bypass

  • Evidence: docker-server.mjs:33if (!candidate.startsWith(root)) return null. With root = '/app/dist', a path that resolves to /app/dist_evil/secret satisfies startsWith('/app/dist') and passes the guard. Concrete example: URL /../dist_evil/secretnormalize('../dist_evil/secret')join('/app/dist', '../dist_evil/secret') = /app/dist_evil/secret → passes guard. The existing test at docker-server.test.mjs:93 only tests /../../../etc/passwd (which correctly returns 400) and does not cover this case.
  • Why it matters: In the current Docker image the filesystem contains only /app/dist/ and /app/docker-server.mjs, so no traversal target exists in practice. However, the guard is logically incorrect and becomes exploitable if the image is extended or volume-mounted with sibling directories.
  • Recommendation: Change line 33 to if (!candidate.startsWith(root + '/') && candidate !== root) return null and add a test case for /../dist_evil/foo → 400.
  • Confidence: Medium (verified logic defect; zero practical impact in current Docker image).

NON-BLOCKING — docker-compose.yaml defaults to fork-author GHCR namespace

  • Evidence: docker-compose.yaml:3image: ${IMAGE_NAME:-ghcr.io/abhigyanpatwari/gitnexus:latest}. When this file lands on main of the upstream repo, end users running docker compose up without setting IMAGE_NAME will pull from the PR author's personal fork registry, not the upstream project's registry.
  • Why it matters: Correctness for upstream consumers; also a supply-chain hygiene concern (dependency on a third-party registry that could become stale or be removed).
  • Recommendation: Change the default to the upstream org's image reference (e.g., ghcr.io/abhigyanpatwari/gitnexus:latest is only correct if the upstream and fork share the same org) or make it a required variable with no default, documenting the expected value. The docker.yml workflow publishes to ghcr.io/${{ github.repository }}, so the default should be ghcr.io/<upstream-org>/gitnexus:latest once the image is made public.
  • Confidence: High (the value is clearly the fork author's namespace, not a generic placeholder).

Rollback safety

Purely additive PR (new files only, plus one alias in vite.config.ts). Can be reverted without re-analyze. ✓

LadybugDB / MCP contracts / embeddings

Not touched. ✓


6. Maintainability

NIT — BUILDPLATFORM build-arg is a dead expression

  • Evidence: docker.yml:71BUILDPLATFORM=${{ runner.os == 'Linux' && 'linux/amd64' || 'linux/amd64' }}. Both branches of the ternary produce linux/amd64. Docker Buildx automatically populates BUILDPLATFORM when declared as ARG in the Dockerfile; this explicit override is redundant and was clearly intended to be conditional.
  • Why it matters: Misleading to the next contributor; on a non-Linux runner it would also hardcode linux/amd64.
  • Recommendation: Remove the build-args block entirely — Docker Buildx sets BUILDPLATFORM from the native builder host automatically.
  • Confidence: High.

NIT — Node 20 in Dockerfile vs CI Node version

  • Evidence: Dockerfile:4FROM node:20-bookworm-slim. The repo may pin to a different Node major in CI (AGENTS.md notes Node 22+ as possible elsewhere).
  • Why it matters: Subtle runtime differences (e.g., node:test runner changes between majors) though minimal for a static file server.
  • Recommendation: Align with the Node version in gitnexus/package.json engines.node.
  • Confidence: Low (cannot verify CI Node version without checking ci.yml setup-node step).

Non-negotiables

Check Result
No secrets / tokens committed .env.example contains only placeholders
No blind find-and-replace renames ✓ Additive-only change
No HIGH/CRITICAL impact on shared symbols ✓ New files only; vite.config.ts alias is additive
PR title follows conventional-commit format feat: add docker support
PolyForm Noncommercial license untouched
GUARDRAILS.md escalation triggers None triggered

What I could not fully verify

  1. Whether docker-server.test.mjs runs in CI — the Vitest config files for gitnexus/ and gitnexus-web/ were not in the diff; I cannot confirm whether either picks up root-level .mjs files.
  2. Whether the Prettier fix in 1019ee2 fully resolves the format failure — CI has not re-run on HEAD.
  3. Node version in CI setup-node stepci.yml was not modified in this PR and I didn't read the full setup-node configuration.
  4. Whether ghcr.io/abhigyanpatwari/gitnexus is the canonical upstream image location — depends on whether the fork and upstream share the same org name.

Final verdict

⚠️ Merge after addressing blocking items.

Blocking items (priority order):

  1. Trigger a new CI run to verify the Prettier fix in 1019ee2 clears the quality / format gate. No code change needed if the fix is correct — just re-run CI.
  2. Confirm docker-server.test.mjs runs in CI — if it doesn't, either move to Vitest or add an explicit CI step. Given that this file covers the path-traversal guard, silent test exclusion is not acceptable for a security boundary.

The resolvePath separator bug and the docker-compose.yaml namespace issue are worth addressing before landing but are not merge blockers given the Docker image's filesystem containment and the fact that IMAGE_NAME is overridable.

xkonjin

This comment was marked as spam.

@magyargergo

Copy link
Copy Markdown
Collaborator

@BRAINIFII Can you please look at these findings? 🙏

@BRAINIFII

Copy link
Copy Markdown
Contributor Author

@BRAINIFII Can you please look at these findings? 🙏

Sure, i'll check it out

kritik.b added 2 commits April 16, 2026 14:13
…mage namespace

- Add `node --test docker-server.test.mjs` step to ci-tests.yml so the
  path-traversal guard tests run in every CI pass instead of being silently skipped.
- Fix resolvePath containment check: `startsWith(root)` would allow sibling
  directories like `/app/dist-evil/`; now guards with `root + sep` or exact match.
- Update docker-compose.yaml default image from `abhigyanpatwari` namespace to
  `brainifii` to match what docker.yml publishes to GHCR.
- Modify Dockerfile and Dockerfile.test to include options for apt-get to bypass validity checks during updates.
- Set ownership of the /app directory to the 'node' user in the runtime stage for improved security and proper permission handling.
Comment thread Dockerfile Outdated
Comment thread Dockerfile Outdated
- Update Dockerfile to use Alpine-based Node.js images for both builder and runtime stages, reducing image size and improving performance.
- Replace apt-get commands with apk for package installation in the runtime stage.
Comment thread Dockerfile Outdated
kritik.b and others added 2 commits April 16, 2026 17:42
@magyargergo

Copy link
Copy Markdown
Collaborator

@claude Act as a senior reviewer for GitNexus. Your job is to determine whether this PR is production-ready for this repo, not to give a generic code review.

You are reviewing a PR in the GitNexus monorepo:

  • gitnexus/ → CLI + MCP
  • gitnexus-web/
  • gitnexus-shared/

Your task has 2 phases, in this exact order:

PHASE 1 — DEFINE THE BAR
Before reviewing the diff, establish a concise repo-specific definition of “production-ready” for GitNexus, based only on the repo docs and the affected area.
Keep this definition practical and reviewable. Do not invent standards that are not grounded in the repo.

PHASE 2 — REVIEW THE PR AGAINST THAT BAR
Review the actual diff only after defining the bar.
Stay tightly scoped to the changed code and its direct consequences.


CONTEXT TO LOAD FIRST
Read these before reviewing:

  • AGENTS.md
  • GUARDRAILS.md
  • CONTRIBUTING.md
  • TESTING.md
  • ARCHITECTURE.md

Additional context:


PRIMARY OBJECTIVE
Decide whether this PR is safe, correct, maintainable, and operationally acceptable to merge into production for GitNexus.

Do not optimize for completeness at the expense of signal.
Do not pad the review.
Do not propose unrelated refactors.
Do not restate the PR description unless needed for verification.


REVIEW RULES

  • Every finding must be grounded in specific evidence from the diff or directly relevant surrounding code.
  • Every finding must include path:line.
  • If you make a behavioral claim, cite the code that proves it.
  • If you make a performance claim, explain the mechanism.
  • If something cannot be verified from the diff alone, explicitly say so.
  • Distinguish clearly between:
    • verified issue
    • plausible risk
    • unverified concern
  • Avoid vague wording like “might be better” or “could be improved” unless you explain exactly why.
  • Keep the review focused on this PR’s scope only.

For each finding, assign one severity:

  • BLOCKING → must be fixed before merge
  • NON-BLOCKING → valid issue, but merge may still be acceptable
  • NIT → stylistic/minor, not merge-relevant

REPO-SPECIFIC REVIEW CHECKLIST
Use these exact headings.

1. Correctness & functional completeness

Check:

  • Does the implementation actually satisfy the PR claim?
    • ManifestExtractor is truly invoked
    • config.links produces non-zero cross-links where expected
  • Resolver contracts are preserved:
    • resolveSymbol remains exact-match
    • label-scoped Cypher remains correct per contract type
    • flag any regression toward fuzzy or unscoped matching
  • Graph schema integrity is preserved:
    • no silent changes to node labels
    • no silent changes to edge types
    • no silent changes to ID generation (generateId)
  • Call out any missing wiring, partial integration, dead branch, or mismatch between tests and runtime behavior

2. Code clarity & clean code

Check:

  • naming quality
  • local cohesion
  • dead code
  • unnecessary abstraction
  • hidden control flow
  • confusing indirection
  • adherence to repo conventions:
    • direct imports from gitnexus-shared
    • no barrel re-export regressions
    • no // removed comments
    • no unused re-exports
  • no drive-by refactors outside stated scope per CONTRIBUTING.md and GUARDRAILS.md § Scope

3. Test coverage & change safety

Evaluate against TESTING.md:

  • Are there unit tests under gitnexus/test/unit/ covering the newly wired path?
  • Is there a regression guard for 0-link → N-link behavior?
  • Are assertions meaningful rather than tautological?
  • Are fixtures realistic for manifest inputs?
  • If memoization/cache was introduced, is there a test proving hit/miss behavior and correctness?
  • Is there evidence the expected validation path would pass for staged gitnexus/ files?
    • tsc --noEmit
    • vitest run --project default
      If not verifiable, say exactly what is missing.

4. Performance

Inspect for:

  • hot-path overhead in ingestion/group sync
  • excess allocations per manifest entry
  • redundant Cypher round-trips
  • missed batching or missed parallelism (Promise.all) where it materially matters
  • O(n²) or repeated lookup patterns on large repos
  • memoization tradeoffs:
    • correctness
    • invalidation
    • bounded vs unbounded memory growth
      Do not speculate casually; explain the mechanism and likely impact.

5. Operational risk

Check:

  • Windows/cross-platform safety:
    • stream lifecycle
    • FD/file handle lifecycle
    • path separator assumptions
    • anything resembling prior ENOTEMPTY-style lifecycle regressions
  • LadybugDB single-writer invariant is preserved
  • Embeddings preservation:
    • no silent breakage of --embeddings
    • .gitnexus/meta.json.stats.embeddings not silently zeroed by changed paths
  • MCP contracts remain compatible:
    • group_*
    • query
    • context
    • impact
    • detect_changes
    • rename
    • cypher
      Flag any schema or contract break without migration note
  • staleness behavior still triggers correctly (gitnexus/src/mcp/staleness.ts)
  • rollback safety:
    • can this PR be reverted safely without re-analyze?
    • if not, explain why

6. Maintainability

Check:

  • Does the change respect Pipeline Phase DAG boundaries?
  • Is cross-phase coupling being introduced?
  • Are manifest-extractor entry points discoverable from group/sync.ts?
  • Would the next contributor know where to extend this?
  • Is shared logic placed appropriately:
    • in gitnexus-shared/
    • or gitnexus/src/core/**/utils.ts
    • and not duplicated between CLI and web bridge (server/api.ts)?

NON-NEGOTIABLES TO VERIFY
From GUARDRAILS.md / CONTRIBUTING.md:

  • no secrets, tokens, or machine-specific paths committed
  • no blind find-and-replace renames
  • if a symbol rename occurred, rename MCP tool or equivalent careful review was used
  • upstream impact was considered for modified shared symbols
  • no HIGH/CRITICAL impact left unaddressed
  • PR title follows conventional commit style + [area] Short description
  • PolyForm Noncommercial license headers/metadata untouched

OUTPUT FORMAT

Production-ready bar for this PR

Give a concise repo-specific definition in 5–10 bullets.

Findings

For each finding, use this structure exactly:

[BLOCKING|NON-BLOCKING|NIT] Short title

  • Evidence: path:line, path:line
  • Why it matters: ...
  • Recommendation: ...
  • Confidence: High / Medium / Low

Only include findings that are real and supported.
If there are no findings in a section, say:

  • No issues found in this area based on the diff and reviewed context.

What I could not fully verify

List any behavior you could not prove from the diff, tests, or available context.

Final verdict

Choose exactly one:

  • ✅ Ready to merge — no blocking issues.
  • ⚠️ Merge after addressing blocking items.
  • ❌ Not ready — fundamental concerns.

If verdict is not ready, list the blocking items in priority order.


IMPORTANT REVIEW BEHAVIOR

  • Be strict on correctness and contracts.
  • Be conservative about operational risk.
  • Be explicit about uncertainty.
  • Do not suggest unrelated refactors.
  • Do not turn nits into blockers.
  • Do not reward intent; review actual implementation.

@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 2m 2s —— View job


Production-Readiness Review — PR #848 feat: add docker support

Tasks

  • Load repo docs (AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md)
  • Read changed files (Dockerfile, docker-server.mjs, docker-server.test.mjs, docker-compose.yaml, docker.yml, vite.config.ts, ci-tests.yml)
  • Phase 1: Define production-ready bar
  • Phase 2: Review diff against the bar

Production-ready bar for this PR

Grounded in AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, and TESTING.md:

  • CI gate passes: quality/typecheck, quality/format (Prettier/ESLint), and all unit/integration tests green.
  • Tests run in CI: docker-server.test.mjs must be explicitly discovered and executed by CI, not silently dropped (it uses node:test, not Vitest).
  • Docker image internally consistent: builder and runtime stages pin the same Node.js major version; no silent divergence.
  • No secrets or real fork-namespace defaults committed: docker-compose.yaml image default must reference a canonical upstream address or a documented placeholder, not a personal fork registry.
  • Path-traversal guard logically sound: resolvePath must reject all escape attempts including separator bypass, not just the common ../etc/passwd form.
  • Workflow concurrency follows CONTRIBUTING.md convention: tag and main-push policies correct; cancel-in-progress event-aware.
  • Additive-only change: no regressions to CLI/MCP/indexing pipeline.
  • PR title follows conventional-commit format per pr-labeler.yml.
  • PolyForm Noncommercial license headers untouched.

1. Correctness & functional completeness

The core implementation is sound:

  • SPA serving, COOP/COEP headers, and Cache-Control: immutable for /assets/ are all correct.
  • vite.config.ts:19 adds the gitnexus-shared alias pointing to ../gitnexus-shared/src/index.ts — correctly resolves the shared package in Docker where workspace symlinks don't exist.
  • resolvePath guard at docker-server.mjs:33 now uses root + sep (platform-aware separator): if (candidate !== root && !candidate.startsWith(root + sep)) return nullthe separator bypass bug from the previous review is fixed. ✓
  • Stream error handler at docker-server.mjs:71: stream.on('error', () => res.destroy())the missing error handler from the previous review is fixed. ✓

One verified correctness issue remains:

BLOCKING — Runtime stage still uses node:20-alpine, not node:22-alpine

  • Evidence: Dockerfile:4FROM --platform=$BUILDPLATFORM node:22-alpine AS builder; Dockerfile:21FROM node:20-alpine AS runtime
  • Why it matters: The reviewer explicitly requested node:22-alpine for both stages. @BRAINIFII replied "Updated with node:22-alpine" for both comments, but the runtime stage was not updated. The published image will run docker-server.mjs on Node 20 while being built on Node 22 — a silent divergence that violates the change request and can mask Node-version-specific behavior differences.
  • Recommendation: Change Dockerfile:21 from FROM node:20-alpine AS runtime to FROM node:22-alpine AS runtime. Fix this →
  • Confidence: High (verified in current Dockerfile on this branch).

2. Code clarity & clean code

  • No // removed comments, no drive-by refactors, no barrel re-export regressions.
  • resolvePath is cohesive and well-named.
  • New files are root-level .mjs, completely outside the package graph — no import convention concerns.

No issues found in this area based on the diff and reviewed context.


3. Test coverage & change safety

Previous blocker resolved: ci-tests.yml:44-45 now adds an explicit step node --test docker-server.test.mjs at the repo root. The test file uses node:test (not Vitest), but CI now wires it correctly. ✓

Test coverage is meaningful:

  • Valid asset + immutable cache header (docker-server.test.mjs:78)
  • SPA fallback for unknown route (docker-server.test.mjs:86)
  • Path traversal → 400 (docker-server.test.mjs:93)
  • Null byte → 400 (docker-server.test.mjs:98)
  • Missing index.html → 404 (docker-server.test.mjs:103)

No issues found in this area based on the diff and reviewed context.


4. Performance

No ingestion, MCP, or pipeline code touched. Not applicable.


5. Operational risk

  • LadybugDB, MCP contracts, embeddings: not touched. ✓
  • Rollback safety: purely additive (new files + one alias in vite.config.ts). Safe to revert without re-analyze. ✓

NON-BLOCKING — docker-compose.yaml defaults to PR author's fork namespace

  • Evidence: docker-compose.yaml:3image: ${IMAGE_NAME:-ghcr.io/brainifii/gitnexus:latest}. The .env.example uses ghcr.io/abhigyanpatwari/gitnexus:latest (the upstream org), but the compose default hardcodes the fork author's registry.
  • Why it matters: End users running docker compose up without setting IMAGE_NAME will pull from a contributor's personal fork registry, not the upstream project. This is a supply-chain hygiene concern and will break for consumers if the fork is deleted or made private.
  • Recommendation: Change the default to match the upstream org reference, e.g. ghcr.io/abhigyanpatwari/gitnexus:latest, consistent with .env.example. Fix this →
  • Confidence: High.

6. Maintainability

NIT — BUILDPLATFORM build-arg is a dead expression

  • Evidence: docker.yml:71BUILDPLATFORM=${{ runner.os == 'Linux' && 'linux/amd64' || 'linux/amd64' }}. Both branches of the ternary evaluate to linux/amd64. Docker Buildx sets BUILDPLATFORM automatically from the native builder host when declared as ARG in the Dockerfile — this explicit override is redundant.
  • Recommendation: Remove the build-args block entirely.
  • Confidence: High.

NIT — No pull_request trigger on docker.yml

  • Evidence: docker.yml:3-11 — workflow only triggers on tag/main push and workflow_dispatch.
  • Why it matters: PRs that modify Docker files get no build validation until merge. Adding a pull_request trigger with push: false (or a dry-run build without push) would catch Dockerfile errors at PR time.
  • Recommendation: Add a pull_request trigger scoped to Docker-relevant paths. Not merge-blocking.
  • Confidence: High.

Non-negotiables

Check Result
No secrets / tokens committed .env.example uses real image references (not credentials); no API keys or tokens
No blind find-and-replace renames ✓ Additive-only
No HIGH/CRITICAL impact on shared symbols ✓ New files only; vite.config.ts alias is additive
PR title follows conventional-commit format feat: add docker support
PolyForm Noncommercial license untouched
Concurrency convention per CONTRIBUTING.md cancel-in-progress: ${{ github.ref == 'refs/heads/main' }} is event-aware and correct

What I could not fully verify

  1. Whether CI currently passes on HEAD — the latest commit fixes Prettier; CI has not re-run during this review. Based on the fix commit message and the clean implementation, Prettier should now pass.
  2. Whether docker.yml has been manually dispatched and successfully built on this branch's current HEAD — the PR body claims a successful manual dispatch, but Node version mismatch was introduced after that test.

Final verdict

⚠️ Merge after addressing blocking items.

Blocking items (priority order):

  1. Fix runtime stage Node version (Dockerfile:21): change node:20-alpinenode:22-alpine to match the builder stage and fulfill the explicitly requested change. Fix this →

All other items from the previous review cycle have been resolved:

  • ✅ Prettier format issue: addressed by style fix commit
  • ✅ Test discoverability: ci-tests.yml now explicitly runs node --test docker-server.test.mjs
  • ✅ Path traversal separator bug: fixed (root + sep)
  • ✅ Missing stream error handler: fixed (stream.on('error', () => res.destroy()))

@magyargergo

Copy link
Copy Markdown
Collaborator

@BRAINIFII can you please resolve the node version conflicts between stages in docker?

@BRAINIFII

Copy link
Copy Markdown
Contributor Author

@BRAINIFII can you please resolve the node version conflicts between stages in docker?

Oops missed it, on it

@magyargergo

Copy link
Copy Markdown
Collaborator

Amazing work! Once the CI is green I'll merge your changes! Thank you for your contribution! 🚀

@magyargergo

Copy link
Copy Markdown
Collaborator

Ok, the e2e tests failures are not releated to your changes! I'll look into them.

@magyargergo magyargergo merged commit 040bb7a into abhigyanpatwari:main Apr 18, 2026
10 of 13 checks passed
magyargergo added a commit that referenced this pull request Apr 18, 2026
Bumps gitnexus to v1.6.2 and adds the matching CHANGELOG entry.

Highlights since v1.6.1 (61 commits):
  - Docker support (#848)
  - Language-agnostic heritage / call / variable extractors
    (config+factory pattern, #877 #878 #890)
  - AST-aware embedding chunking (#889)
  - jQuery / axios HTTP consumer detection (#887)
  - SemanticModel wired as first-class resolution input, SM-20 (#885)
  - ImportSemantics split into per-strategy hooks (#886)
  - Python dotted-import fix (#899); worker warnings non-terminal
    (#900 / #261); global-install ENOTEMPTY fixes (#843 #846);
    embeddings staleness fix (#831)

See gitnexus/CHANGELOG.md for the full list.

After merge, tag `v1.6.2` triggers publish.yml which runs CI,
verifies tag↔package.json match, publishes to npm with provenance,
and creates the GitHub Release using the extracted CHANGELOG body.
@magyargergo magyargergo mentioned this pull request Apr 18, 2026
5 tasks
magyargergo added a commit that referenced this pull request Apr 18, 2026
Bumps gitnexus to v1.6.2 and adds the matching CHANGELOG entry.

Highlights since v1.6.1 (61 commits):
  - Docker support (#848)
  - Language-agnostic heritage / call / variable extractors
    (config+factory pattern, #877 #878 #890)
  - AST-aware embedding chunking (#889)
  - jQuery / axios HTTP consumer detection (#887)
  - SemanticModel wired as first-class resolution input, SM-20 (#885)
  - ImportSemantics split into per-strategy hooks (#886)
  - Python dotted-import fix (#899); worker warnings non-terminal
    (#900 / #261); global-install ENOTEMPTY fixes (#843 #846);
    embeddings staleness fix (#831)

See gitnexus/CHANGELOG.md for the full list.

After merge, tag `v1.6.2` triggers publish.yml which runs CI,
verifies tag↔package.json match, publishes to npm with provenance,
and creates the GitHub Release using the extracted CHANGELOG body.
github714801013 pushed a commit to github714801013/GitNexus that referenced this pull request Apr 28, 2026
* feat: add docker support

* feat: move docker files to root

* feat: add docker build and push workflow

* fix: pin docker action SHAs to verified commits

Made-with: Cursor

* fix: remove redundant --platform=$TARGETPLATFORM from runtime stage

Made-with: Cursor

* fix: upgrade docker actions to Node.js 24-compatible versions

Made-with: Cursor

* docs: updated readme

* fix: update docker references

* fix(docker-server): reject null bytes in resolvePath

Defensively harden the path traversal guard by returning null
early when the URL contains a null byte, before normalization runs.

Made-with: Cursor

* fix(docker-server): handle createReadStream errors

Attach an error listener before piping so mid-flight read errors
(truncated file, permission change) cleanly destroy the response
instead of being silently swallowed.

Made-with: Cursor

* fix(docker-server): replace existsSync with async stat

Eliminates the TOCTOU race between the initial stat call and the
subsequent existsSync check. Reuses the async stat pattern already
in place and removes the now-unused existsSync import.

Made-with: Cursor

* test(docker-server): add integration tests; fix %00 null-byte bypass

Decode the URL before the null-byte check so percent-encoded null
bytes (%00) are also rejected with 400 instead of falling through
to the SPA fallback. Adds 5 node:test integration tests covering
valid assets, SPA fallback, path traversal, null bytes, and 404.

Made-with: Cursor

* style: fix prettier formatting in docker-server files

Made-with: Cursor

* fix(docker): wire tests into CI, fix resolvePath separator, correct image namespace

- Add `node --test docker-server.test.mjs` step to ci-tests.yml so the
  path-traversal guard tests run in every CI pass instead of being silently skipped.
- Fix resolvePath containment check: `startsWith(root)` would allow sibling
  directories like `/app/dist-evil/`; now guards with `root + sep` or exact match.
- Update docker-compose.yaml default image from `abhigyanpatwari` namespace to
  `brainifii` to match what docker.yml publishes to GHCR.

* fix(docker): update apt-get commands and set user permissions

- Modify Dockerfile and Dockerfile.test to include options for apt-get to bypass validity checks during updates.
- Set ownership of the /app directory to the 'node' user in the runtime stage for improved security and proper permission handling.

* fix(docker): switch to Alpine base images for smaller footprint

- Update Dockerfile to use Alpine-based Node.js images for both builder and runtime stages, reducing image size and improving performance.
- Replace apt-get commands with apk for package installation in the runtime stage.

* fix(docker): update Node.js version in Dockerfile

- Change base image from node:20-alpine to node:22-alpine

* fix(docker): update Node.js version in Dockerfile to 22-alpine for runtime

---------

Co-authored-by: kritik.b <kritik.b@media.net>
github714801013 pushed a commit to github714801013/GitNexus that referenced this pull request Apr 28, 2026
Bumps gitnexus to v1.6.2 and adds the matching CHANGELOG entry.

Highlights since v1.6.1 (61 commits):
  - Docker support (abhigyanpatwari#848)
  - Language-agnostic heritage / call / variable extractors
    (config+factory pattern, abhigyanpatwari#877 abhigyanpatwari#878 abhigyanpatwari#890)
  - AST-aware embedding chunking (abhigyanpatwari#889)
  - jQuery / axios HTTP consumer detection (abhigyanpatwari#887)
  - SemanticModel wired as first-class resolution input, SM-20 (abhigyanpatwari#885)
  - ImportSemantics split into per-strategy hooks (abhigyanpatwari#886)
  - Python dotted-import fix (abhigyanpatwari#899); worker warnings non-terminal
    (abhigyanpatwari#900 / abhigyanpatwari#261); global-install ENOTEMPTY fixes (abhigyanpatwari#843 abhigyanpatwari#846);
    embeddings staleness fix (abhigyanpatwari#831)

See gitnexus/CHANGELOG.md for the full list.

After merge, tag `v1.6.2` triggers publish.yml which runs CI,
verifies tag↔package.json match, publishes to npm with provenance,
and creates the GitHub Release using the extracted CHANGELOG body.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run in Docker

3 participants