From faa91e066134475e950b8e44941be1a9e1903e01 Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 1 Oct 2026 10:28:54 +0200 Subject: [PATCH] feat(worker): detect company repos by origin remote pattern --- src/ClaudeDo.Data/Git/GitService.cs | 7 ++++ src/ClaudeDo.Worker/Config/WorkerConfig.cs | 4 +++ src/ClaudeDo.Worker/Git/CompanyRepo.cs | 16 +++++++++ .../Git/CompanyRepoTests.cs | 34 +++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 src/ClaudeDo.Worker/Git/CompanyRepo.cs create mode 100644 tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs diff --git a/src/ClaudeDo.Data/Git/GitService.cs b/src/ClaudeDo.Data/Git/GitService.cs index 789b26ca..bacd9855 100644 --- a/src/ClaudeDo.Data/Git/GitService.cs +++ b/src/ClaudeDo.Data/Git/GitService.cs @@ -414,6 +414,13 @@ public sealed class GitService .ToList(); } + /// URL des Remotes , oder null wenn es keins gibt. + public async Task GetRemoteUrlAsync(string repoDir, string remote = "origin", CancellationToken ct = default) + { + var (exitCode, stdout, _) = await RunGitAsync(repoDir, ["remote", "get-url", remote], ct); + return exitCode == 0 && !string.IsNullOrWhiteSpace(stdout) ? stdout.Trim() : null; + } + public async Task IsMidMergeAsync(string repoDir, CancellationToken ct = default) { var (exitCode, stdout, _) = await RunGitAsync(repoDir, ["rev-parse", "--git-dir"], ct); diff --git a/src/ClaudeDo.Worker/Config/WorkerConfig.cs b/src/ClaudeDo.Worker/Config/WorkerConfig.cs index 9d0cc234..3eee5907 100644 --- a/src/ClaudeDo.Worker/Config/WorkerConfig.cs +++ b/src/ClaudeDo.Worker/Config/WorkerConfig.cs @@ -70,6 +70,10 @@ public sealed class WorkerConfig [JsonPropertyName("ticket_api_base_url")] public string? TicketApiBaseUrl { get; set; } + /// Teilstring der origin-URL, an dem ein Firmen-Repo erkannt wird. Nur per Hand editierbar. + [JsonPropertyName("company_remote_pattern")] + public string CompanyRemotePattern { get; set; } = "/_git/"; + public static string DefaultConfigPath => Path.Combine(Paths.AppDataRoot(), "worker.config.json"); diff --git a/src/ClaudeDo.Worker/Git/CompanyRepo.cs b/src/ClaudeDo.Worker/Git/CompanyRepo.cs new file mode 100644 index 00000000..08e0624a --- /dev/null +++ b/src/ClaudeDo.Worker/Git/CompanyRepo.cs @@ -0,0 +1,16 @@ +using ClaudeDo.Data.Git; + +namespace ClaudeDo.Worker.Git; + +/// Erkennt Firmen-Repos am origin-Remote; das Muster kommt aus WorkerConfig.CompanyRemotePattern. +public static class CompanyRepo +{ + /// True wenn das (nicht-leere) Muster enthält, ohne Groß-/Kleinschreibung. + public static bool Matches(string? remoteUrl, string? pattern) => + !string.IsNullOrWhiteSpace(remoteUrl) && !string.IsNullOrWhiteSpace(pattern) + && remoteUrl.Contains(pattern, StringComparison.OrdinalIgnoreCase); + + /// Liest origin live (kein Cache — ein Remote kann sich jederzeit ändern). + public static async Task IsCompanyAsync(GitService git, string workingDir, string? pattern, CancellationToken ct) => + Matches(await git.GetRemoteUrlAsync(workingDir, "origin", ct), pattern); +} diff --git a/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs b/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs new file mode 100644 index 00000000..32b9a4d7 --- /dev/null +++ b/tests/ClaudeDo.Worker.Tests/Git/CompanyRepoTests.cs @@ -0,0 +1,34 @@ +using ClaudeDo.Data.Git; +using ClaudeDo.Worker.Git; +using ClaudeDo.Worker.Tests.Infrastructure; + +namespace ClaudeDo.Worker.Tests.Git; + +public sealed class CompanyRepoTests : IDisposable +{ + private readonly GitRepoFixture _repo = new(); + public void Dispose() => _repo.Dispose(); + + [Theory] + [InlineData("https://srv.local/Coll/Proj/_git/Repo", "/_git/", true)] + [InlineData("https://mika@srv.local/tfs/Coll/Proj/_git/Mein%20Repo.git", "/_git/", true)] + [InlineData("ssh://git@git.kuns.dev:22222/kuns/ClaudeDo.git", "/_git/", false)] + [InlineData(null, "/_git/", false)] + [InlineData("https://srv.local/Coll/Proj/_git/Repo", "", false)] + [InlineData("https://GITHUB.com/acme/repo.git", "github.com/acme", true)] + public void Matches(string? url, string pattern, bool expected) + => Assert.Equal(expected, CompanyRepo.Matches(url, pattern)); + + [Fact] + public async Task IsCompanyAsync_reads_origin() + { + if (!GitRepoFixture.IsGitAvailable()) return; + var git = new GitService(); + Assert.False(await CompanyRepo.IsCompanyAsync(git, _repo.RepoDir, "/_git/", CancellationToken.None)); + + GitRepoFixture.RunGit(_repo.RepoDir, "remote", "add", "origin", "https://srv.local/Coll/Proj/_git/Repo"); + + Assert.True(await CompanyRepo.IsCompanyAsync(git, _repo.RepoDir, "/_git/", CancellationToken.None)); + Assert.Equal("https://srv.local/Coll/Proj/_git/Repo", await git.GetRemoteUrlAsync(_repo.RepoDir)); + } +}