Merge pull request #43 from gogrlx/cd/update-deps-go1.26.1

chore: update Go to 1.26.1, fix formatting, add parse tests
This commit is contained in:
2026-03-08 14:28:48 -04:00
committed by GitHub
14 changed files with 358 additions and 353 deletions

View File

@@ -23,7 +23,7 @@ func TestSplitNameVersion(t *testing.T) {
{"my-pkg-name-0.1-r0", "my-pkg-name", "0.1-r0"},
{"a-b-c-3.0", "a-b-c", "3.0"},
{"single", "single", ""},
{"-1.0", "-1.0", ""}, // no digit follows last hyphen at position > 0
{"-1.0", "-1.0", ""}, // no digit follows last hyphen at position > 0
{"pkg-0", "pkg", "0"},
}
for _, tt := range tests {
@@ -289,10 +289,10 @@ func TestParseInfoEdgeCases(t *testing.T) {
func TestParseInfoNameVersion(t *testing.T) {
tests := []struct {
name string
input string
wantN string
wantV string
name string
input string
wantN string
wantV string
}{
{
name: "standard",
@@ -435,8 +435,8 @@ func TestParseUpgradeSimulation(t *testing.T) {
wantLen: 0,
},
{
name: "single upgrade",
input: "(1/1) Upgrading curl (8.5.0-r0 -> 8.6.0-r0)\n",
name: "single upgrade",
input: "(1/1) Upgrading curl (8.5.0-r0 -> 8.6.0-r0)\n",
wantLen: 1,
wantPkgs: []snack.Package{
{Name: "curl", Version: "8.6.0-r0", Installed: true},
@@ -462,8 +462,8 @@ OK: 123 MiB in 45 packages
wantLen: 0,
},
{
name: "upgrade without version parens",
input: "(1/1) Upgrading busybox\n",
name: "upgrade without version parens",
input: "(1/1) Upgrading busybox\n",
wantLen: 1,
wantPkgs: []snack.Package{
{Name: "busybox", Version: "", Installed: true},

View File

@@ -17,14 +17,14 @@ func normalizeName(name string) string {
// - package-aarch64
func parseArchNormalize(name string) (string, string) {
knownArchs := map[string]bool{
"x86_64": true,
"x86": true,
"aarch64": true,
"armhf": true,
"armv7": true,
"ppc64le": true,
"s390x": true,
"riscv64": true,
"x86_64": true,
"x86": true,
"aarch64": true,
"armhf": true,
"armv7": true,
"ppc64le": true,
"s390x": true,
"riscv64": true,
"loongarch64": true,
}

71
apk/normalize_test.go Normal file
View File

@@ -0,0 +1,71 @@
package apk
import (
"testing"
)
func TestNormalizeName(t *testing.T) {
tests := []struct {
input string
want string
}{
{"curl", "curl"},
{"curl-x86_64", "curl"},
{"openssl-aarch64", "openssl"},
{"musl-armhf", "musl"},
{"busybox-armv7", "busybox"},
{"lib-ssl-dev-x86", "lib-ssl-dev"},
{"zlib-ppc64le", "zlib"},
{"kernel-s390x", "kernel"},
{"toolchain-riscv64", "toolchain"},
{"app-loongarch64", "app"},
// No arch suffix — unchanged
{"python", "python"},
{"go", "go"},
{"", ""},
// Suffix that isn't an arch — unchanged
{"my-pkg-foo", "my-pkg-foo"},
{"libfoo-1.0", "libfoo-1.0"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := normalizeName(tt.input)
if got != tt.want {
t.Errorf("normalizeName(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestParseArchNormalize(t *testing.T) {
tests := []struct {
name string
input string
wantName string
wantArch string
}{
{"x86_64", "curl-x86_64", "curl", "x86_64"},
{"x86", "musl-x86", "musl", "x86"},
{"aarch64", "openssl-aarch64", "openssl", "aarch64"},
{"armhf", "busybox-armhf", "busybox", "armhf"},
{"armv7", "lib-armv7", "lib", "armv7"},
{"ppc64le", "app-ppc64le", "app", "ppc64le"},
{"s390x", "pkg-s390x", "pkg", "s390x"},
{"riscv64", "tool-riscv64", "tool", "riscv64"},
{"loongarch64", "gcc-loongarch64", "gcc", "loongarch64"},
{"no arch", "curl", "curl", ""},
{"unknown suffix", "pkg-foobar", "pkg-foobar", ""},
{"empty", "", "", ""},
{"hyphen but not arch", "lib-ssl-dev", "lib-ssl-dev", ""},
{"multi hyphen with arch", "lib-ssl-dev-x86_64", "lib-ssl-dev", "x86_64"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotName, gotArch := parseArchNormalize(tt.input)
if gotName != tt.wantName || gotArch != tt.wantArch {
t.Errorf("parseArchNormalize(%q) = (%q, %q), want (%q, %q)",
tt.input, gotName, gotArch, tt.wantName, tt.wantArch)
}
})
}
}

View File

@@ -191,7 +191,6 @@ func listRepos(_ context.Context) ([]snack.Repository, error) {
return repos, nil
}
func addRepo(ctx context.Context, repo snack.Repository) error {
repoLine := repo.URL
if repo.Type != "" {

View File

@@ -14,8 +14,8 @@ func TestParseList_EdgeCases(t *testing.T) {
}{
{"empty", "", 0},
{"whitespace_only", " \n \n ", 0},
{"single_tab_field", "bash", 0}, // needs at least 2 tab-separated fields
{"no_description", "bash\t5.2-1", 1}, // 2 fields is OK
{"single_tab_field", "bash", 0}, // needs at least 2 tab-separated fields
{"no_description", "bash\t5.2-1", 1}, // 2 fields is OK
{"with_description", "bash\t5.2-1\tGNU Bourne Again SHell", 1},
{"blank_lines_mixed", "\nbash\t5.2-1\n\ncurl\t7.88\n\n", 2},
{"trailing_newline", "bash\t5.2-1\n", 1},

View File

@@ -378,5 +378,3 @@ func upgradePackages(ctx context.Context, pkgs []snack.Target, opts ...snack.Opt
allOpts := append([]snack.Option{snack.WithReinstall()}, opts...)
return install(ctx, pkgs, allOpts...)
}

View File

@@ -115,4 +115,3 @@ func rpcInfo(ctx context.Context, pkg string) (*snack.Package, error) {
p := resp.Results[0].toPackage()
return &p, nil
}

View File

@@ -235,10 +235,10 @@ func clean(ctx context.Context) error {
// brewInfoJSON represents the JSON output from `brew info --json=v2`.
type brewInfoJSON struct {
Formulae []struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Desc string `json:"desc"`
Versions struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Desc string `json:"desc"`
Versions struct {
Stable string `json:"stable"`
} `json:"versions"`
Installed []struct {
@@ -246,24 +246,24 @@ type brewInfoJSON struct {
} `json:"installed"`
} `json:"formulae"`
Casks []struct {
Token string `json:"token"`
Token string `json:"token"`
Name []string `json:"name"`
Desc string `json:"desc"`
Version string `json:"version"`
Desc string `json:"desc"`
Version string `json:"version"`
} `json:"casks"`
}
// brewOutdatedJSON represents the JSON output from `brew outdated --json=v2`.
type brewOutdatedJSON struct {
Formulae []struct {
Name string `json:"name"`
Name string `json:"name"`
InstalledVersions []string `json:"installed_versions"`
CurrentVersion string `json:"current_version"`
CurrentVersion string `json:"current_version"`
} `json:"formulae"`
Casks []struct {
Name string `json:"name"`
Name string `json:"name"`
InstalledVersions string `json:"installed_versions"`
CurrentVersion string `json:"current_version"`
CurrentVersion string `json:"current_version"`
} `json:"casks"`
}

View File

@@ -4,16 +4,16 @@ package snack
// Useful for grlx to determine what operations are available before
// attempting them.
type Capabilities struct {
VersionQuery bool
Hold bool
Clean bool
FileOwnership bool
RepoManagement bool
KeyManagement bool
Groups bool
NameNormalize bool
DryRun bool
PackageUpgrade bool
VersionQuery bool
Hold bool
Clean bool
FileOwnership bool
RepoManagement bool
KeyManagement bool
Groups bool
NameNormalize bool
DryRun bool
PackageUpgrade bool
}
// GetCapabilities probes a Manager for all optional interface support.
@@ -29,15 +29,15 @@ func GetCapabilities(m Manager) Capabilities {
_, dr := m.(DryRunner)
_, pu := m.(PackageUpgrader)
return Capabilities{
VersionQuery: vq,
Hold: h,
Clean: c,
FileOwnership: fo,
RepoManagement: rm,
KeyManagement: km,
Groups: g,
NameNormalize: nn,
DryRun: dr,
PackageUpgrade: pu,
VersionQuery: vq,
Hold: h,
Clean: c,
FileOwnership: fo,
RepoManagement: rm,
KeyManagement: km,
Groups: g,
NameNormalize: nn,
DryRun: dr,
PackageUpgrade: pu,
}
}

View File

@@ -18,15 +18,15 @@ func (m *mockManager) Remove(context.Context, []snack.Target, ...snack.Option) (
return snack.RemoveResult{}, nil
}
func (m *mockManager) Purge(context.Context, []snack.Target, ...snack.Option) error { return nil }
func (m *mockManager) Upgrade(context.Context, ...snack.Option) error { return nil }
func (m *mockManager) Update(context.Context) error { return nil }
func (m *mockManager) List(context.Context) ([]snack.Package, error) { return nil, nil }
func (m *mockManager) Search(context.Context, string) ([]snack.Package, error) { return nil, nil }
func (m *mockManager) Info(context.Context, string) (*snack.Package, error) { return nil, nil }
func (m *mockManager) IsInstalled(context.Context, string) (bool, error) { return false, nil }
func (m *mockManager) Version(context.Context, string) (string, error) { return "", nil }
func (m *mockManager) Available() bool { return true }
func (m *mockManager) Name() string { return "mock" }
func (m *mockManager) Upgrade(context.Context, ...snack.Option) error { return nil }
func (m *mockManager) Update(context.Context) error { return nil }
func (m *mockManager) List(context.Context) ([]snack.Package, error) { return nil, nil }
func (m *mockManager) Search(context.Context, string) ([]snack.Package, error) { return nil, nil }
func (m *mockManager) Info(context.Context, string) (*snack.Package, error) { return nil, nil }
func (m *mockManager) IsInstalled(context.Context, string) (bool, error) { return false, nil }
func (m *mockManager) Version(context.Context, string) (string, error) { return "", nil }
func (m *mockManager) Available() bool { return true }
func (m *mockManager) Name() string { return "mock" }
// fullMockManager implements Manager plus all optional interfaces.
type fullMockManager struct {

View File

@@ -8,16 +8,16 @@ import (
// Compile-time interface assertions — DNF implements all optional interfaces.
var (
_ snack.Manager = (*DNF)(nil)
_ snack.VersionQuerier = (*DNF)(nil)
_ snack.Holder = (*DNF)(nil)
_ snack.Cleaner = (*DNF)(nil)
_ snack.FileOwner = (*DNF)(nil)
_ snack.RepoManager = (*DNF)(nil)
_ snack.KeyManager = (*DNF)(nil)
_ snack.Grouper = (*DNF)(nil)
_ snack.NameNormalizer = (*DNF)(nil)
_ snack.DryRunner = (*DNF)(nil)
_ snack.Manager = (*DNF)(nil)
_ snack.VersionQuerier = (*DNF)(nil)
_ snack.Holder = (*DNF)(nil)
_ snack.Cleaner = (*DNF)(nil)
_ snack.FileOwner = (*DNF)(nil)
_ snack.RepoManager = (*DNF)(nil)
_ snack.KeyManager = (*DNF)(nil)
_ snack.Grouper = (*DNF)(nil)
_ snack.NameNormalizer = (*DNF)(nil)
_ snack.DryRunner = (*DNF)(nil)
_ snack.PackageUpgrader = (*DNF)(nil)
)

27
go.mod
View File

@@ -1,10 +1,9 @@
module github.com/gogrlx/snack
go 1.26.0
go 1.26.1
require (
github.com/charmbracelet/fang v0.4.4
github.com/go-git/go-git/v5 v5.17.0
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.40.0
@@ -15,48 +14,39 @@ require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.4.2 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260303162955-0b88c25f3fff // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260304213900-0e78e2954235 // indirect
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260305213658-fe36e8c10185 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v28.5.1+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.8.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-runewidth v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
@@ -72,19 +62,15 @@ require (
github.com/muesli/roff v0.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
@@ -96,11 +82,10 @@ require (
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

78
go.sum
View File

@@ -6,15 +6,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aymanbagabas/go-udiff v0.4.0 h1:TKnLPh7IbnizJIBKFWa9mKayRUBQ9Kh1BPCk6w2PnYM=
github.com/aymanbagabas/go-udiff v0.4.0/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
@@ -29,8 +22,8 @@ github.com/charmbracelet/ultraviolet v0.0.0-20260303162955-0b88c25f3fff h1:uY7A6
github.com/charmbracelet/ultraviolet v0.0.0-20260303162955-0b88c25f3fff/go.mod h1:E6/0abq9uG2SnM8IbLB9Y5SW09uIgfaFETk8aRzgXUQ=
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260304213900-0e78e2954235 h1:G96IHDV9QdhxyJZN/UBk6RiVsyejQBrKl6XxP5rvydE=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260304213900-0e78e2954235/go.mod h1:nsExn0DGyX0lh9LwLHTn2Gg+hafdzfSXnC+QmEJTZFY=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260305213658-fe36e8c10185 h1:/192monmpmRICpSPrFRzkIO+xfhioV6/nwrQdkDTj10=
github.com/charmbracelet/x/exp/charmtone v0.0.0-20260305213658-fe36e8c10185/go.mod h1:nsExn0DGyX0lh9LwLHTn2Gg+hafdzfSXnC+QmEJTZFY=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
@@ -43,8 +36,6 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
@@ -58,8 +49,6 @@ github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -73,22 +62,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -96,8 +71,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -107,17 +80,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnV
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
@@ -126,8 +92,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
@@ -156,14 +122,10 @@ github.com/muesli/mango-pflag v0.2.0 h1:QViokgKDZQCzKhYe1zH8D+UlPJzBSGoP9yx0hBG0
github.com/muesli/mango-pflag v0.2.0/go.mod h1:X9LT1p/pbGA1wjvEbtwnixujKErkP0jVmrxwrw3fL0Y=
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -175,15 +137,10 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -192,8 +149,6 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
@@ -203,8 +158,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
@@ -228,38 +181,28 @@ go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTq
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
@@ -270,13 +213,8 @@ google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -4,182 +4,25 @@ import (
"testing"
)
func TestParseList(t *testing.T) {
input := "bash\t5.1.8-6.el9\tThe GNU Bourne Again shell\ncurl\t7.76.1-23.el9\tA utility for getting files from remote servers\n"
pkgs := parseList(input)
if len(pkgs) != 2 {
t.Fatalf("expected 2 packages, got %d", len(pkgs))
}
if pkgs[0].Name != "bash" || pkgs[0].Version != "5.1.8-6.el9" {
t.Errorf("unexpected pkg[0]: %+v", pkgs[0])
}
if pkgs[0].Description != "The GNU Bourne Again shell" {
t.Errorf("unexpected description: %q", pkgs[0].Description)
}
if !pkgs[0].Installed {
t.Error("expected Installed=true")
}
}
func TestParseInfo(t *testing.T) {
input := `Name : bash
Version : 5.1.8
Release : 6.el9
Architecture: x86_64
Install Date: Mon 01 Jan 2024 12:00:00 AM UTC
Group : System Environment/Shells
Size : 7896043
License : GPLv3+
Signature : RSA/SHA256, Mon 01 Jan 2024 12:00:00 AM UTC, Key ID abc123
Source RPM : bash-5.1.8-6.el9.src.rpm
Build Date : Mon 01 Jan 2024 12:00:00 AM UTC
Build Host : builder.example.com
Packager : CentOS Buildsys <bugs@centos.org>
Vendor : CentOS
URL : https://www.gnu.org/software/bash
Summary : The GNU Bourne Again shell
Description :
The GNU Bourne Again shell (Bash) is a shell or command language
interpreter that is compatible with the Bourne shell (sh).
`
p := parseInfo(input)
if p == nil {
t.Fatal("expected package, got nil")
}
if p.Name != "bash" {
t.Errorf("Name = %q, want bash", p.Name)
}
if p.Version != "5.1.8-6.el9" {
t.Errorf("Version = %q, want 5.1.8-6.el9", p.Version)
}
if p.Arch != "x86_64" {
t.Errorf("Arch = %q, want x86_64", p.Arch)
}
if p.Description != "The GNU Bourne Again shell" {
t.Errorf("Description = %q, want 'The GNU Bourne Again shell'", p.Description)
}
}
func TestNormalizeName(t *testing.T) {
tests := []struct {
input, want string
input string
want string
}{
{"nginx", "nginx"},
{"nginx.x86_64", "nginx"},
{"curl.aarch64", "curl"},
{"bash.noarch", "bash"},
{"python3", "python3"},
}
for _, tt := range tests {
got := normalizeName(tt.input)
if got != tt.want {
t.Errorf("normalizeName(%q) = %q, want %q", tt.input, got, tt.want)
}
}
}
func TestParseArchSuffix(t *testing.T) {
tests := []struct {
input, wantName, wantArch string
}{
{"nginx.x86_64", "nginx", "x86_64"},
{"bash", "bash", ""},
{"glibc.i686", "glibc", "i686"},
}
for _, tt := range tests {
name, arch := parseArchSuffix(tt.input)
if name != tt.wantName || arch != tt.wantArch {
t.Errorf("parseArchSuffix(%q) = (%q, %q), want (%q, %q)", tt.input, name, arch, tt.wantName, tt.wantArch)
}
}
}
// --- Edge case tests ---
func TestParseListEmpty(t *testing.T) {
pkgs := parseList("")
if len(pkgs) != 0 {
t.Errorf("expected 0 packages from empty input, got %d", len(pkgs))
}
}
func TestParseListSinglePackage(t *testing.T) {
input := "curl\t7.76.1-23.el9\tA utility\n"
pkgs := parseList(input)
if len(pkgs) != 1 {
t.Fatalf("expected 1 package, got %d", len(pkgs))
}
if pkgs[0].Name != "curl" {
t.Errorf("Name = %q, want curl", pkgs[0].Name)
}
}
func TestParseListNoDescription(t *testing.T) {
input := "bash\t5.1.8-6.el9\n"
pkgs := parseList(input)
if len(pkgs) != 1 {
t.Fatalf("expected 1 package, got %d", len(pkgs))
}
if pkgs[0].Description != "" {
t.Errorf("Description = %q, want empty", pkgs[0].Description)
}
}
func TestParseListMalformedLines(t *testing.T) {
input := "bash\t5.1.8-6.el9\tShell\nno-tab-here\ncurl\t7.76.1\tHTTP tool\n"
pkgs := parseList(input)
if len(pkgs) != 2 {
t.Fatalf("expected 2 packages (skip malformed), got %d", len(pkgs))
}
}
func TestParseInfoEmpty(t *testing.T) {
p := parseInfo("")
if p != nil {
t.Errorf("expected nil from empty input, got %+v", p)
}
}
func TestParseInfoNoName(t *testing.T) {
input := `Version : 1.0
Architecture: x86_64
`
p := parseInfo(input)
if p != nil {
t.Errorf("expected nil when no Name field, got %+v", p)
}
}
func TestParseInfoArchField(t *testing.T) {
// Test both "Architecture" and "Arch" key forms
input := `Name : test
Version : 1.0
Release : 1.el9
Arch : aarch64
Summary : Test package
`
p := parseInfo(input)
if p == nil {
t.Fatal("expected non-nil package")
}
if p.Arch != "aarch64" {
t.Errorf("Arch = %q, want aarch64", p.Arch)
}
}
func TestNormalizeNameEdgeCases(t *testing.T) {
tests := []struct {
input, want string
}{
{"curl.noarch", "curl"},
{"kernel.aarch64", "kernel"},
{"bash.i686", "bash"},
{"glibc.i386", "glibc"},
{"libfoo.armv7hl", "libfoo"},
{"module.ppc64le", "module"},
{"app.s390x", "app"},
{"source.src", "source"},
{"nodot", "nodot"},
{"", ""},
{"pkg.unknown.ext", "pkg.unknown.ext"},
{"name.with.dots.x86_64", "name.with.dots"},
{"python3.11", "python3.11"},
{"glibc.s390x", "glibc"},
{"kernel.src", "kernel"},
{".x86_64", ""},
{"pkg.ppc64le", "pkg"},
{"pkg.armv7hl", "pkg"},
{"pkg.i386", "pkg"},
{"pkg.unknown", "pkg.unknown"},
{"multi.dot.x86_64", "multi.dot"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
@@ -191,30 +34,202 @@ func TestNormalizeNameEdgeCases(t *testing.T) {
}
}
func TestParseArchSuffixEdgeCases(t *testing.T) {
func TestParseArchSuffix(t *testing.T) {
tests := []struct {
input, wantName, wantArch string
name string
input string
wantName string
wantArch string
}{
{"", "", ""},
{"pkg.i386", "pkg", "i386"},
{"pkg.ppc64le", "pkg", "ppc64le"},
{"pkg.s390x", "pkg", "s390x"},
{"pkg.armv7hl", "pkg", "armv7hl"},
{"pkg.src", "pkg", "src"},
{"pkg.aarch64", "pkg", "aarch64"},
{"pkg.noarch", "pkg", "noarch"},
{"pkg.unknown", "pkg.unknown", ""},
{"name.with.many.dots.noarch", "name.with.many.dots", "noarch"},
{".noarch", "", "noarch"},
{"pkg.x86_64.extra", "pkg.x86_64.extra", ""},
{"x86_64", "nginx.x86_64", "nginx", "x86_64"},
{"noarch", "bash.noarch", "bash", "noarch"},
{"aarch64", "kernel.aarch64", "kernel", "aarch64"},
{"i686", "glibc.i686", "glibc", "i686"},
{"i386", "compat.i386", "compat", "i386"},
{"armv7hl", "lib.armv7hl", "lib", "armv7hl"},
{"ppc64le", "app.ppc64le", "app", "ppc64le"},
{"s390x", "z.s390x", "z", "s390x"},
{"src", "pkg.src", "pkg", "src"},
{"no dot", "curl", "curl", ""},
{"unknown arch", "pkg.foobar", "pkg.foobar", ""},
{"empty", "", "", ""},
{"multiple dots", "a.b.x86_64", "a.b", "x86_64"},
{"dot but not arch", "libfoo.so", "libfoo.so", ""},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
name, arch := parseArchSuffix(tt.input)
if name != tt.wantName || arch != tt.wantArch {
t.Run(tt.name, func(t *testing.T) {
gotName, gotArch := parseArchSuffix(tt.input)
if gotName != tt.wantName || gotArch != tt.wantArch {
t.Errorf("parseArchSuffix(%q) = (%q, %q), want (%q, %q)",
tt.input, name, arch, tt.wantName, tt.wantArch)
tt.input, gotName, gotArch, tt.wantName, tt.wantArch)
}
})
}
}
func TestParseList(t *testing.T) {
input := "bash\t5.2.15-3.fc38\tThe GNU Bourne Again shell\n" +
"curl\t8.0.1-1.fc38\tA utility for getting files from remote servers\n"
pkgs := parseList(input)
if len(pkgs) != 2 {
t.Fatalf("expected 2 packages, got %d", len(pkgs))
}
if pkgs[0].Name != "bash" || pkgs[0].Version != "5.2.15-3.fc38" {
t.Errorf("unexpected first package: %+v", pkgs[0])
}
if pkgs[0].Description != "The GNU Bourne Again shell" {
t.Errorf("unexpected description: %q", pkgs[0].Description)
}
if !pkgs[0].Installed {
t.Error("expected Installed=true")
}
}
func TestParseListEdgeCases(t *testing.T) {
t.Run("empty input", func(t *testing.T) {
pkgs := parseList("")
if len(pkgs) != 0 {
t.Errorf("expected 0 packages, got %d", len(pkgs))
}
})
t.Run("whitespace only", func(t *testing.T) {
pkgs := parseList(" \n\n \n")
if len(pkgs) != 0 {
t.Errorf("expected 0 packages, got %d", len(pkgs))
}
})
t.Run("single entry no description", func(t *testing.T) {
pkgs := parseList("vim\t9.0.1\n")
if len(pkgs) != 1 {
t.Fatalf("expected 1 package, got %d", len(pkgs))
}
if pkgs[0].Name != "vim" || pkgs[0].Version != "9.0.1" {
t.Errorf("unexpected package: %+v", pkgs[0])
}
if pkgs[0].Description != "" {
t.Errorf("expected empty description, got %q", pkgs[0].Description)
}
})
t.Run("single field line skipped", func(t *testing.T) {
pkgs := parseList("justname\n")
if len(pkgs) != 0 {
t.Errorf("expected 0 packages (need >=2 tab fields), got %d", len(pkgs))
}
})
t.Run("description with tabs", func(t *testing.T) {
pkgs := parseList("pkg\t1.0\tA description\twith tabs\n")
if len(pkgs) != 1 {
t.Fatalf("expected 1 package, got %d", len(pkgs))
}
// SplitN with 3 means the third part includes everything after the second tab
if pkgs[0].Description != "A description\twith tabs" {
t.Errorf("unexpected description: %q", pkgs[0].Description)
}
})
}
func TestParseInfo(t *testing.T) {
input := `Name : curl
Version : 8.0.1
Release : 1.fc38
Architecture: x86_64
Summary : A utility for getting files from remote servers
`
pkg := parseInfo(input)
if pkg == nil {
t.Fatal("expected non-nil package")
}
if pkg.Name != "curl" {
t.Errorf("expected name 'curl', got %q", pkg.Name)
}
if pkg.Version != "8.0.1-1.fc38" {
t.Errorf("expected version '8.0.1-1.fc38', got %q", pkg.Version)
}
if pkg.Arch != "x86_64" {
t.Errorf("expected arch 'x86_64', got %q", pkg.Arch)
}
if pkg.Description != "A utility for getting files from remote servers" {
t.Errorf("unexpected description: %q", pkg.Description)
}
}
func TestParseInfoEdgeCases(t *testing.T) {
t.Run("empty input", func(t *testing.T) {
pkg := parseInfo("")
if pkg != nil {
t.Error("expected nil for empty input")
}
})
t.Run("name only", func(t *testing.T) {
pkg := parseInfo("Name : bash\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Name != "bash" {
t.Errorf("expected bash, got %q", pkg.Name)
}
})
t.Run("no name returns nil", func(t *testing.T) {
pkg := parseInfo("Version : 1.0\nArch : x86_64\n")
if pkg != nil {
t.Error("expected nil when no Name field")
}
})
t.Run("version without release", func(t *testing.T) {
pkg := parseInfo("Name : test\nVersion : 2.5\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Version != "2.5" {
t.Errorf("expected version '2.5', got %q", pkg.Version)
}
})
t.Run("release without version", func(t *testing.T) {
// Release only appends if version is non-empty
pkg := parseInfo("Name : test\nRelease : 3.el9\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Version != "" {
t.Errorf("expected empty version (release alone shouldn't set it), got %q", pkg.Version)
}
})
t.Run("arch key variant", func(t *testing.T) {
pkg := parseInfo("Name : test\nArch : aarch64\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Arch != "aarch64" {
t.Errorf("expected aarch64, got %q", pkg.Arch)
}
})
t.Run("no colon lines ignored", func(t *testing.T) {
pkg := parseInfo("Name : test\nrandom line\nSummary : A tool\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Description != "A tool" {
t.Errorf("unexpected description: %q", pkg.Description)
}
})
t.Run("value with colons", func(t *testing.T) {
pkg := parseInfo("Name : myapp\nSummary : A tool: does things: well\n")
if pkg == nil {
t.Fatal("expected non-nil")
}
if pkg.Description != "A tool: does things: well" {
t.Errorf("unexpected description: %q", pkg.Description)
}
})
}