diff --git a/.env.example b/.env.example index 8af9dee79..445ae37a7 100644 --- a/.env.example +++ b/.env.example @@ -17,3 +17,9 @@ WEB_HOST_PORT=4173 # Optional read-only mount, exposed to the server as /workspace. # Override with the directory that contains the repos you want to index. WORKSPACE_DIR=./ + +# Azure DevOps Server Integration (passed to the server container) +# Prefer https:// — the PAT rides in an Authorization header, so cleartext +# http:// exposes it on the wire (still supported for internal-only instances). +# AZURE_DEVOPS_URL=https://azuredevops.example.com +# AZURE_DEVOPS_PAT=your-pat-here diff --git a/.gitleaks.toml b/.gitleaks.toml index 769b7eee9..cecf77d5a 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -3,10 +3,17 @@ title = "GitNexus" [extend] useDefault = true -# Fake embedding API keys in unit tests (current probe + historical placeholder). +# Fake credentials in unit tests — none are real secrets: +# - embedding API keys in the http-embedder tests (regexes below) +# - synthetic GitHub PAT fixtures in the git-clone PAT-injection tests +# (e.g. ghp_secret123, ghp_uniqueRawSecret_98765) — allowlisted by path +# so the exception is bounded to that one test file. [allowlist] -description = "fake embedding API keys in http-embedder unit tests" +description = "fake credentials in unit tests (no real secrets)" regexes = [ '''secret-key-12345''', '''test-api-key-redaction-check''', ] +paths = [ + '''gitnexus/test/unit/git-clone\.test\.ts''', +] diff --git a/gitnexus-web/src/components/AnalyzeOnboarding.tsx b/gitnexus-web/src/components/AnalyzeOnboarding.tsx index 53260e388..b970ee842 100644 --- a/gitnexus-web/src/components/AnalyzeOnboarding.tsx +++ b/gitnexus-web/src/components/AnalyzeOnboarding.tsx @@ -3,7 +3,7 @@ * * The "empty state" card rendered inside DropZone's Crossfade when the server * is connected but zero repos are indexed. Replaces the generic error message - * with a first-class GitHub URL input flow. + * with a first-class repository URL input flow. * * Rendering context: * DropZone (Crossfade, phase="analyze") @@ -15,7 +15,7 @@ * the app to the graph explorer. */ -import { Sparkles, Github } from '@/lib/lucide-icons'; +import { Sparkles, GitBranch } from '@/lib/lucide-icons'; import { RepoAnalyzer } from './RepoAnalyzer'; import { useTranslation } from 'react-i18next'; @@ -46,7 +46,7 @@ export const AnalyzeOnboarding = ({ onComplete }: AnalyzeOnboardingProps) => { {/* Icon */}
- +

diff --git a/gitnexus-web/src/components/RepoAnalyzer.tsx b/gitnexus-web/src/components/RepoAnalyzer.tsx index 613284830..fd08d197d 100644 --- a/gitnexus-web/src/components/RepoAnalyzer.tsx +++ b/gitnexus-web/src/components/RepoAnalyzer.tsx @@ -10,12 +10,14 @@ import { useState, useRef, useEffect, useId } from 'react'; import { Github, Gitlab, + AzureDevops, FolderOpen, Loader2, Check, ArrowRight, AlertCircle, Sparkles, + Key, } from '@/lib/lucide-icons'; import { startAnalyze, @@ -30,10 +32,14 @@ import { useTranslation } from 'react-i18next'; // ── Helpers ────────────────────────────────────────────────────────────────── -type InputMode = 'github' | 'gitlab' | 'local'; +type InputMode = 'github' | 'gitlab' | 'azure' | 'local'; const GITHUB_RE = /^https?:\/\/(www\.)?github\.com\/[^/\s]+\/[^/\s]+/i; const GITLAB_RE = /^https?:\/\/[^/\s]+\/[^/\s]+\/[^/\s]+(\/.*)?$/i; +// One-or-more path segments before `/_git/`, so the legacy single-project +// cloud form (myorg.visualstudio.com/project/_git/repo) is accepted too — +// the backend already supports it (isAzureDevOpsUrl / extractRepoName). +const AZURE_RE = /^https?:\/\/[^/\s]+\/(?:[^/\s]+\/)+_git\/[^/\s]+/i; const IS_WINDOWS = navigator.userAgent.toLowerCase().includes('win'); function isValidGithubUrl(value: string): boolean { @@ -44,6 +50,10 @@ function isValidGitlabUrl(value: string): boolean { return GITLAB_RE.test(value.trim()); } +function isValidAzureUrl(value: string): boolean { + return AZURE_RE.test(value.trim()); +} + // ── Mode tabs ──────────────────────────────────────────────────────────────── function ModeTabs({ mode, onChange }: { mode: InputMode; onChange: (m: InputMode) => void }) { @@ -81,6 +91,19 @@ function ModeTabs({ mode, onChange }: { mode: InputMode; onChange: (m: InputMode {t('repoAnalyzer.gitlabUrl')} +