diff options
| author | Martin Fischer <martin@push-f.com> | 2026-01-17 13:07:17 +0100 |
|---|---|---|
| committer | Martin Fischer <martin@push-f.com> | 2026-01-17 19:40:49 +0100 |
| commit | 6bff1a8e74e4eba3fae3ac3d0d6a3bd28a00ef3c (patch) | |
| tree | 203ea8c97dd5111554c824f9e2fbd358bccb581f | |
| parent | 7750ec9ab103d3687553a6bbcb25d676b985edcd (diff) | |
break: take --repos-dir instead of --repos
Because I want to be able to configure which repos are exported
in the repo configs, which is implemented in the next commit.
| -rw-r--r-- | main.go | 49 | ||||
| -rw-r--r-- | main_test.go | 11 | ||||
| -rw-r--r-- | service.nix | 6 |
3 files changed, 45 insertions, 21 deletions
@@ -10,6 +10,7 @@ import ( "net/http" "os" "os/exec" + "path" "regexp" "slices" "strings" @@ -21,7 +22,7 @@ import ( type Args struct { Port int `help:"Port to listen on" short:"p" default:"8888" env:"PORT"` - Repos StringMap `placeholder:"{\"name\": \"path\", ...}" required:"" help:"Repositories to be grepped" env:"REPOS"` + ReposDir string `required:"" help:"Directory containing repositories to be grepped" env:"REPOS_DIR"` Patterns StringMap `placeholder:"{\"name\": \"regex\", ...}" required:"" help:"Patterns to be grepped for" env:"PATTERNS"` } @@ -40,32 +41,29 @@ func main() { var args Args ctx := kong.Parse(&args, kong.Description( - "Prometheus metrics for how many lines in certain git repos match certain regular expressions.\n\n"+ + "Prometheus metrics for how many lines in git repos match certain regular expressions.\n\n"+ "Only the HEAD of the repositories will be grepped.", )) - te, err := newGitGrepExporter(args.Repos, args.Patterns, prometheus.DefaultRegisterer) + te, err := newGitGrepExporter(args.ReposDir, args.Patterns, prometheus.DefaultRegisterer) ctx.FatalIfErrorf(err) prometheus.MustRegister(te) http.Handle("/metrics", promhttp.Handler()) - slog.Info("starting", "repos", args.Repos, "patterns", args.Patterns) + slog.Info("starting", "reposDir", args.ReposDir, "patterns", args.Patterns) slog.Info(fmt.Sprintf("serving metrics at http://localhost:%d/metrics", args.Port)) http.ListenAndServe(fmt.Sprintf(":%d", args.Port), nil) } type GitGrepExporter struct { - repos map[string]string + reposDir string descs map[string]*prometheus.Desc regexes map[string]*regexp.Regexp combinedPattern string } -func newGitGrepExporter(repos map[string]string, patterns map[string]string, reg prometheus.Registerer) (*GitGrepExporter, error) { - if len(repos) == 0 { - return nil, fmt.Errorf("no repositories specified") - } +func newGitGrepExporter(reposDir string, patterns map[string]string, reg prometheus.Registerer) (*GitGrepExporter, error) { if len(patterns) == 0 { return nil, fmt.Errorf("no patterns specified") } @@ -83,7 +81,7 @@ func newGitGrepExporter(repos map[string]string, patterns map[string]string, reg regexes[name] = regexp.MustCompile(pattern) } - return &GitGrepExporter{repos, descs, regexes, strings.Join(slices.Collect(maps.Values(patterns)), "|")}, nil + return &GitGrepExporter{reposDir, descs, regexes, strings.Join(slices.Collect(maps.Values(patterns)), "|")}, nil } func (e *GitGrepExporter) Describe(ch chan<- *prometheus.Desc) { @@ -93,15 +91,34 @@ func (e *GitGrepExporter) Describe(ch chan<- *prometheus.Desc) { } func (g *GitGrepExporter) Collect(ch chan<- prometheus.Metric) { - for name, path := range g.repos { - slog.Info("starting grep", "path", path) - g.collectCountsForRepo(name, ch) - slog.Info("finished grep", "path", path) + slog.Info("listing directory", "path", g.reposDir) + entries, err := os.ReadDir(g.reposDir) + if err != nil { + slog.Error("failed to read repos directory", "path", g.reposDir, "error", err) + for _, desc := range g.descs { + ch <- prometheus.NewInvalidMetric(desc, err) + } + return + } + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + repoPath := path.Join(g.reposDir, entry.Name()) + logger := slog.With("path", repoPath) + + logger.Info("starting grep") + g.collectCountsForRepo(entry.Name(), repoPath, ch) + if err != nil { + logger.Error("grep failed", "error", err) + continue + } + logger.Info("finished grep") } } -func (g *GitGrepExporter) collectCountsForRepo(repo string, ch chan<- prometheus.Metric) { - path := g.repos[repo] +func (g *GitGrepExporter) collectCountsForRepo(repo string, path string, ch chan<- prometheus.Metric) { output, err := run("git", "-C", path, "grep", "-hE", g.combinedPattern, "HEAD") if err != nil { diff --git a/main_test.go b/main_test.go index 9fdecca..45a3ba5 100644 --- a/main_test.go +++ b/main_test.go @@ -2,6 +2,8 @@ package main import ( "log" + "os" + "path" "strings" "testing" @@ -11,10 +13,15 @@ import ( ) func TestGrepRepo(t *testing.T) { - repos := map[string]string{"project1": "/srv/repos/project1"} + reposDir := t.TempDir() + err := os.Mkdir(path.Join(reposDir, "project1"), 0o755) + if err != nil { + t.Fatal(err) + } + patterns := map[string]string{"match": "\\bMATCH\\b", "match_start": "^MATCH"} reg := prometheus.NewRegistry() - te, err := newGitGrepExporter(repos, patterns, reg) + te, err := newGitGrepExporter(reposDir, patterns, reg) if err != nil { t.Fatal(err) } diff --git a/service.nix b/service.nix index bc0bbdc..ba53972 100644 --- a/service.nix +++ b/service.nix @@ -16,8 +16,8 @@ in type = lib.types.attrsOf lib.types.str; }; - repos = lib.mkOption { - type = lib.types.attrsOf lib.types.str; + reposDir = lib.mkOption { + type = lib.types.str; }; extraGroups = lib.mkOption { @@ -35,7 +35,7 @@ in }; environment = { PORT = toString cfg.port; - REPOS = builtins.toJSON cfg.repos; + REPOS_DIR = cfg.reposDir; PATTERNS = builtins.toJSON cfg.patterns; }; |
