From 86eaeb4b2a9d8068586837ca639222745ddc57ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 00:27:38 +0100 Subject: [PATCH] fix: use configuration-revision for current_rev in flake info metric The nixos_flake_info metric's current_rev label was incorrectly showing the nixpkgs input revision (from /run/current-system/nixos-version) instead of the flake's own revision. Now reads from /run/current-system/configuration-revision which contains the flake's self.rev when system.configurationRevision is set in the NixOS configuration. Co-Authored-By: Claude Opus 4.5 --- collector/flake.go | 21 ++++------- collector/flake_test.go | 77 ++--------------------------------------- main.go | 2 +- 3 files changed, 11 insertions(+), 89 deletions(-) diff --git a/collector/flake.go b/collector/flake.go index 825f277..c249aa8 100644 --- a/collector/flake.go +++ b/collector/flake.go @@ -7,7 +7,6 @@ import ( "log/slog" "os" "os/exec" - "regexp" "strings" "sync" "time" @@ -15,11 +14,7 @@ import ( "github.com/prometheus/client_golang/prometheus" ) -const nixosVersionPath = "/run/current-system/nixos-version" - -// revisionPattern extracts the git hash from nixos-version. -// Formats: "25.11.20260203.e576e3c" or "1994-294a625" -var revisionPattern = regexp.MustCompile(`[.-]([a-f0-9]{7,40})$`) +const configRevisionPath = "/run/current-system/configuration-revision" type FlakeCollector struct { flakeURL string @@ -215,18 +210,16 @@ func fetchFlakeMetadata(flakeURL string) (*flakeMetadata, error) { } func getCurrentSystemRevision() (string, error) { - data, err := os.ReadFile(nixosVersionPath) + data, err := os.ReadFile(configRevisionPath) if err != nil { + if os.IsNotExist(err) { + // configuration-revision doesn't exist; user hasn't set system.configurationRevision + return "", nil + } return "", err } - version := strings.TrimSpace(string(data)) - matches := revisionPattern.FindStringSubmatch(version) - if matches == nil { - return "", nil - } - - rev := matches[1] + rev := strings.TrimSpace(string(data)) if len(rev) > 7 { rev = rev[:7] } diff --git a/collector/flake_test.go b/collector/flake_test.go index 219dcc4..ceb97d0 100644 --- a/collector/flake_test.go +++ b/collector/flake_test.go @@ -3,40 +3,13 @@ package collector import ( "encoding/json" "os" - "path/filepath" "testing" ) -func TestRevisionPattern(t *testing.T) { - tests := []struct { - version string - wantRev string - }{ - {"25.11.20260203.e576e3c", "e576e3c"}, - {"1994-294a625", "294a625"}, - {"25.05.20250101.abcdef1234567890", "abcdef1234567890"}, - {"no-revision-here", ""}, - {"", ""}, - } - - for _, tt := range tests { - t.Run(tt.version, func(t *testing.T) { - matches := revisionPattern.FindStringSubmatch(tt.version) - var got string - if matches != nil { - got = matches[1] - } - if got != tt.wantRev { - t.Errorf("revisionPattern.FindStringSubmatch(%q) = %q, want %q", tt.version, got, tt.wantRev) - } - }) - } -} - func TestGetCurrentSystemRevision(t *testing.T) { - // Skip if not on NixOS - if _, err := os.Stat(nixosVersionPath); os.IsNotExist(err) { - t.Skip("not running on NixOS") + // Skip if not on NixOS with system.configurationRevision set + if _, err := os.Stat(configRevisionPath); os.IsNotExist(err) { + t.Skip("not running on NixOS with system.configurationRevision set") } rev, err := getCurrentSystemRevision() @@ -48,50 +21,6 @@ func TestGetCurrentSystemRevision(t *testing.T) { t.Logf("current system revision: %s", rev) } -func TestGetCurrentSystemRevisionFromFile(t *testing.T) { - // Create a temp file to simulate /run/current-system/nixos-version - dir := t.TempDir() - versionPath := filepath.Join(dir, "nixos-version") - - tests := []struct { - content string - wantRev string - }{ - {"25.11.20260203.e576e3c\n", "e576e3c"}, - {"1994-294a625\n", "294a625"}, - {"25.05.20250101.abcdef1234567890\n", "abcdef1"}, - {"no-hash", ""}, - } - - for _, tt := range tests { - t.Run(tt.content, func(t *testing.T) { - if err := os.WriteFile(versionPath, []byte(tt.content), 0644); err != nil { - t.Fatal(err) - } - - // We can't easily test the actual function without modifying the constant, - // so we test the pattern extraction logic directly - version := tt.content - if len(version) > 0 && version[len(version)-1] == '\n' { - version = version[:len(version)-1] - } - - matches := revisionPattern.FindStringSubmatch(version) - var rev string - if matches != nil { - rev = matches[1] - if len(rev) > 7 { - rev = rev[:7] - } - } - - if rev != tt.wantRev { - t.Errorf("got revision %q, want %q", rev, tt.wantRev) - } - }) - } -} - func TestFlakeLocksUnmarshal(t *testing.T) { jsonData := `{ "revision": "abc1234567890", diff --git a/main.go b/main.go index 97c9b7a..eac8a4b 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -const version = "0.2.0" +const version = "0.2.1" func main() { cfg, err := config.Parse()