Skip to content

Latest commit

 

History

History
326 lines (266 loc) · 8.88 KB

File metadata and controls

326 lines (266 loc) · 8.88 KB

GitLab Multi-Repo Search Tool – Projektspezifikation

Kontext & Ziel

Dieses Tool soll Entwicklern in einem Produkt-Team ermöglichen, schnell herauszufinden, in welchen von bis zu 140 GitLab-Projekt-Repos eine bestimmte Komponente, Methode oder Anforderung umgesetzt wird – ohne manuell durch GitLab zu navigieren.

Zwei Suchmodi:

  1. Syntaktische Suche – Wo wird ComponentX oder methodY konkret verwendet? (exakte/strukturelle Suche)
  2. Semantische Suche – Wo wird eine bestimmte Anforderung oder User Story umgesetzt? (KI-basierte Ähnlichkeitssuche)

Tech Stack

Bereich Technologie
Backend Node.js + TypeScript + Fastify
Frontend React + TypeScript + Vite
Embeddings Ollama (nomic-embed-text) – läuft lokal
Vektordatenbank Qdrant (Docker)
GitLab API REST API mit axios
Echtzeit-Streaming Server-Sent Events (SSE)
Infrastruktur Docker Compose
Monorepo pnpm Workspaces

Projektstruktur

gitlab-search-tool/
├── apps/
│   ├── frontend/               # React + Vite App
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   ├── SearchBar.tsx
│   │   │   │   ├── ResultList.tsx
│   │   │   │   └── ResultItem.tsx
│   │   │   ├── hooks/
│   │   │   │   └── useSearch.ts
│   │   │   └── App.tsx
│   │   └── package.json
│   │
│   └── backend/                # Fastify API
│       ├── src/
│       │   ├── routes/
│       │   │   ├── search.syntax.ts
│       │   │   ├── search.semantic.ts
│       │   │   └── webhook.ts
│       │   ├── services/
│       │   │   ├── gitlab.service.ts
│       │   │   ├── qdrant.service.ts
│       │   │   ├── ollama.service.ts
│       │   │   └── indexer.service.ts
│       │   └── index.ts
│       └── package.json
│
├── packages/
│   └── shared/                 # Gemeinsame TypeScript Types
│       ├── src/
│       │   └── types.ts
│       └── package.json
│
├── scripts/
│   └── index-all.ts            # Einmaliges initiales Indexing aller Repos
│
├── docker-compose.yml
├── .env.example
└── pnpm-workspace.yaml

Umgebungsvariablen (.env)

# GitLab
GITLAB_BASE_URL=https://your-gitlab.company.com
GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
GITLAB_GROUP_ID=123

# Ollama
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_EMBED_MODEL=nomic-embed-text

# Qdrant
QDRANT_URL=http://localhost:6333
QDRANT_COLLECTION=gitlab-code

# Backend
PORT=3001

# File extensions to index (comma-separated)
INDEX_EXTENSIONS=.ts,.tsx,.js,.jsx

Docker Compose

services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage

  ollama:
    image: ollama/ollama:latest
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama

volumes:
  qdrant_data:
  ollama_data:

API Endpunkte

Syntaktische Suche

GET /api/search/syntax?q=MyComponent
  • Durchsucht alle 140 Repos parallel über die GitLab Search API
  • Streamt Treffer per SSE ins Frontend sobald sie reinkommen
  • Response-Format: text/event-stream

SSE Event Format:

{
  "repo": "project-customer-abc",
  "repoUrl": "https://gitlab.../project-customer-abc",
  "file": "src/components/Dashboard.tsx",
  "line": 42,
  "preview": "import { MyComponent } from '@company/standard-app'",
  "matchType": "import"
}

Semantische Suche

GET /api/search/semantic?q=SSO+Authentifizierung+umsetzen&limit=20
  • Embedded die Query mit Ollama
  • Sucht in Qdrant nach ähnlichen Code-Chunks
  • Gibt Top-N Treffer mit Score zurück

Response Format:

{
  "results": [
    {
      "repo": "project-customer-xyz",
      "file": "src/auth/SSOHandler.tsx",
      "score": 0.89,
      "preview": "// Handles SAML-based SSO login flow...",
      "repoUrl": "https://gitlab.../..."
    }
  ]
}

Webhook (für inkrementellen Index-Update)

POST /api/webhook/gitlab
  • Empfängt Push-Events vom GitLab System Hook
  • Re-embedded nur geänderte/neue Dateien
  • Löscht Embeddings von gelöschten Dateien aus Qdrant

Services – Kernlogik

gitlab.service.ts

// Wichtige Methoden:
getGroupProjects(groupId: string): Promise<GitLabProject[]>
searchInRepo(projectId: number, query: string): Promise<SearchResult[]>
searchAllRepos(query: string): AsyncGenerator<SearchResult>   // für SSE
getFileContent(projectId: number, filePath: string): Promise<string>
getChangedFiles(projectId: number, commitBefore: string, commitAfter: string): Promise<string[]>

indexer.service.ts

// Wichtige Methoden:
indexAllRepos(): Promise<void>           // initialer Bulk-Index
indexRepo(projectId: number): Promise<void>
indexFile(projectId: number, filePath: string): Promise<void>
deleteFileFromIndex(projectId: number, filePath: string): Promise<void>
chunkFile(content: string, filePath: string): Chunk[]

Chunking-Strategie: Dateien werden in Chunks von ~500 Tokens aufgeteilt, mit 50 Token Overlap. Jeder Chunk speichert als Payload: { repo, repoId, filePath, startLine, endLine, language }.

ollama.service.ts

embed(text: string): Promise<number[]>
embedBatch(texts: string[]): Promise<number[][]>

qdrant.service.ts

ensureCollection(): Promise<void>
upsertVectors(points: QdrantPoint[]): Promise<void>
search(queryVector: number[], limit: number): Promise<ScoredPoint[]>
deleteByFilter(filter: QdrantFilter): Promise<void>

Initiales Indexing Script

pnpm run index:all

Das Script scripts/index-all.ts:

  1. Holt alle Projekte der GitLab-Gruppe
  2. Iteriert über alle relevanten Dateien (gefiltert nach INDEX_EXTENSIONS)
  3. Chunked und embedded jede Datei
  4. Schreibt alles in Qdrant
  5. Zeigt Fortschritt mit einer Progress-Bar (z.B. cli-progress)

Hinweis: Dieser Prozess kann beim ersten Mal mehrere Stunden dauern, je nach Größe der Repos. Empfehlung: Über Nacht laufen lassen.


Frontend – Kernkomponenten

SearchBar.tsx

  • Einzelnes Suchfeld
  • Toggle zwischen "Syntaktisch" und "Semantisch"
  • Debounced Input (300ms)

ResultList.tsx

  • Zeigt Ergebnisse gruppiert nach Repo
  • Bei syntaktischer Suche: Ergebnisse erscheinen live per SSE
  • Bei semantischer Suche: Ergebnisse erscheinen nach Abschluss, sortiert nach Score

ResultItem.tsx

  • Repo-Name mit Link zu GitLab
  • Dateipfad mit Link zur Datei
  • Code-Preview mit Syntax-Highlighting (prism-react-renderer oder shiki)
  • Bei semantischer Suche: Ähnlichkeits-Score als Badge

Shared Types (packages/shared/src/types.ts)

export interface SearchResult {
  repo: string
  repoId: number
  repoUrl: string
  file: string
  fileUrl: string
  line?: number
  preview: string
  matchType: 'syntax' | 'semantic'
  score?: number  // nur bei semantic
}

export interface SearchRequest {
  query: string
  mode: 'syntax' | 'semantic'
  limit?: number
}

export interface IndexStatus {
  totalRepos: number
  indexedRepos: number
  totalChunks: number
  lastUpdated: string
}

GitLab System Hook einrichten

Damit der Index bei jedem Push automatisch aktualisiert wird, muss in der selbst-gehosteten GitLab-Instanz ein System Hook eingerichtet werden:

Admin Area → System Hooks → URL: http://your-backend:3001/api/webhook/gitlab
Push events: ✓
Secret Token: (optional, empfohlen)

Setup-Reihenfolge für Claude Code

  1. Monorepo initialisieren mit pnpm Workspaces
  2. packages/shared aufsetzen mit Types
  3. Docker Compose aufsetzen und starten (docker compose up -d)
  4. Ollama Model pullen (ollama pull nomic-embed-text)
  5. Backend aufsetzen:
    • Fastify Grundstruktur
    • GitLab Service (erst nur getGroupProjects + searchInRepo)
    • Syntaktische Such-Route mit SSE
  6. Frontend aufsetzen:
    • Vite React Grundstruktur
    • SearchBar + ResultList Komponenten
    • SSE Hook für live Ergebnisse
  7. Syntaktische Suche end-to-end testen
  8. Ollama + Qdrant Services implementieren
  9. Indexing Script bauen und initialen Index erstellen
  10. Semantische Such-Route implementieren
  11. Webhook Handler implementieren

Offene Entscheidungen / Diskussionspunkte

  • Rate Limiting: Wie viele parallele GitLab API Calls sind auf eurer Instanz okay? Startpunkt: 20 parallele Requests, anpassbar per Env-Variable.
  • Embedding-Modell: nomic-embed-text ist ein guter Start. Falls die semantische Qualität nicht reicht, kann auf mxbai-embed-large gewechselt werden.
  • Code-only oder auch Kommentare/Commit-Messages? Aktuell: nur Code-Dateien. Könnte später erweitert werden.
  • Authentifizierung der Web-App: Erstmal kein Auth (internes Tool). Kann später per GitLab OAuth ergänzt werden.