feat: découverte automatique des dépôts git (scan + montrer/cacher)
Scan borné du système de fichiers (racines configurables, défaut home ; profondeur/nombre/timeout bornés ; symlinks non suivis ; exclusions node_modules/dotdirs) qui auto-enregistre les nouveaux dépôts. Insertion atomique ON CONFLICT DO NOTHING (idempotence + anti-résurrection d'un dépôt masqué + anti-course). Scan au démarrage + bouton manuel + re-scan périodique (RepoDiscoveryService, démarré dans runDaemon). Colonne repos.hidden : masquer = conservé en DB mais exclu du dashboard et jamais ré-ajouté ; supprimer = re-découvrable. UI : bouton œil par dépôt + bascule afficher-les-masqués sur le dashboard, section Découverte dans les Réglages (racines + intervalle, allow-list stricte). Robustesse : listAllWorktrees tolère l'échec git par dépôt ; flag --no-discover (escape hatch + hermétisme des acceptations).
This commit is contained in:
42
packages/server/src/core/repo-discovery.ts
Normal file
42
packages/server/src/core/repo-discovery.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
// Planificateur de la découverte auto des repos : scan au démarrage + re-scan périodique.
|
||||
// Calqué sur DiscoveryService (sessions) — start()/stop() avec timer .unref(). Démarré depuis
|
||||
// runDaemon() UNIQUEMENT (jamais buildApp), ce qui isole naturellement les tests vitest du scan.
|
||||
// Lui-même sans état : il lit les racines/l'intervalle dans `settings` et délègue à WorktreeManager.
|
||||
import type { Db } from '../db/index.js';
|
||||
import type { WorktreeManager } from './worktree-manager.js';
|
||||
import { readScanIntervalMin, readScanRoots } from './scan-settings.js';
|
||||
|
||||
export class RepoDiscoveryService {
|
||||
private timer: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly db: Db,
|
||||
private readonly worktrees: WorktreeManager,
|
||||
) {}
|
||||
|
||||
start(): void {
|
||||
if (this.timer) return;
|
||||
void this.refresh(); // scan initial asynchrone : ne bloque pas le boot
|
||||
const intervalMin = readScanIntervalMin(this.db);
|
||||
if (intervalMin > 0) {
|
||||
this.timer = setInterval(() => void this.refresh(), intervalMin * 60_000);
|
||||
this.timer.unref(); // ne maintient pas le process en vie
|
||||
}
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Relit les racines (changement effectif sans redémarrage) et lance un scan. Ne lève jamais. */
|
||||
private async refresh(): Promise<void> {
|
||||
try {
|
||||
await this.worktrees.discoverRepos({ roots: readScanRoots(this.db) });
|
||||
} catch {
|
||||
/* scan tolérant : une erreur ne doit pas tuer le timer */
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user