[tooling] add benchfind (#62981)

This commit adds benchfind, a cli utility which can be used
to discover Benchmark test targets in a golang tree. The reason
this is useful in a large codebase is that to run `go test -bench ./...`
would require the linking of every test target binary. This can
take 1h+. Previously a `grep` expression was used to find targets:

```sh
grep --exclude-dir api --include "*_test.go" -lr testing.B .  | xargs dirname | xargs go list | sort -u
```

The issue with this is that it can break when the tree contains
other go modules and requires handling of pipefail. Instead a single
utility is provided.

This can now be replaced with:
```sh
benchfind ./...
```

Note that the tool will respect module boundaries, instead of
having to explicitly exclude them, call the CLI multiple times with `-d`
flag.
This commit is contained in:
Luke Okraszewski
2026-01-28 10:54:06 +00:00
committed by GitHub
parent 845caf9a40
commit 3677d382d5
9 changed files with 640 additions and 56 deletions
+19 -6
View File
@@ -887,6 +887,10 @@ DIFF_TEST := $(TOOLINGDIR)/bin/difftest
$(DIFF_TEST): $(wildcard $(TOOLINGDIR)/cmd/difftest/*.go)
cd $(TOOLINGDIR) && go build -o "$@" ./cmd/difftest
BENCHFIND := $(TOOLINGDIR)/bin/benchfind
$(BENCHFIND): $(wildcard $(TOOLINGDIR)/cmd/benchfind/*.go)
cd $(TOOLINGDIR) && go build -o "$@" ./cmd/benchfind
RERUN := $(TOOLINGDIR)/bin/rerun
$(RERUN): $(wildcard $(TOOLINGDIR)/cmd/rerun/*.go)
cd $(TOOLINGDIR) && go build -o "$@" ./cmd/rerun
@@ -1003,17 +1007,26 @@ endif
# Race detection is not enabled because it significantly slows down benchmarks.
# todo: Use gotestsum when it is compatible with benchmark output. Currently will consider all benchmarks failed.
.PHONY: test-go-bench
test-go-bench: PACKAGES = $(shell grep --exclude-dir api --exclude-dir gen --include "*_test.go" -lr testing.B . | xargs dirname | xargs go list | sort -u)
test-go-bench: BENCHMARK_SKIP_PATTERN = "^BenchmarkRoot"
test-go-bench: | $(TEST_LOG_DIR)
go test -run ^$$ -bench . -skip $(BENCHMARK_SKIP_PATTERN) -benchtime 1x $(PACKAGES) \
test-go-bench: $(BENCHFIND) | $(TEST_LOG_DIR)
@PKGS=$$($(BENCHFIND) --tags=$(BUILD_TAGS)) ; \
if [ -z "$$PKGS" ]; then \
echo "No benchmark packages found"; \
exit 1; \
fi ; \
go test -run ^$$ -bench . -skip $(BENCHMARK_SKIP_PATTERN) -benchtime 1x $$PKGS \
| tee $(TEST_LOG_DIR)/bench.txt
test-go-bench-root: PACKAGES = $(shell grep --exclude-dir api --exclude-dir gen --include "*_test.go" -lr BenchmarkRoot . | xargs dirname | xargs go list | sort -u)
.PHONY: test-go-bench-root
test-go-bench-root: BENCHMARK_PATTERN = "^BenchmarkRoot"
test-go-bench-root: BENCHMARK_SKIP_PATTERN = ""
test-go-bench-root: | $(TEST_LOG_DIR)
go test -run ^$$ -bench $(BENCHMARK_PATTERN) -skip $(BENCHMARK_SKIP_PATTERN) -benchtime 1x $(PACKAGES) \
test-go-bench-root: $(BENCHFIND) | $(TEST_LOG_DIR)
@PKGS=$$($(BENCHFIND) --tags=$(BUILD_TAGS)) ; \
if [ -z "$$PKGS" ]; then \
echo "No benchmark packages found"; \
exit 1; \
fi ; \
go test -run ^$$ -bench $(BENCHMARK_PATTERN) -skip $(BENCHMARK_SKIP_PATTERN) -benchtime 1x $$PKGS \
| tee $(TEST_LOG_DIR)/bench.txt
# Make sure untagged vnetdaemon code build/tests.
@@ -0,0 +1,26 @@
# benchfind
A CLI utility to discover modules with Benchmarks without compilation/linking.
Example useage:
```sh
# Find all Benchmark packages in the current dir:
benchfind
# Find all Benchmark packages matching ./lib/...:
benchfind ./lib/...
# Find all Benchmark packages matching given tags:
benchfind --tags=integration,linux
# Find all Benchmark packages and exclude a given package:
benchfind -e github.com/gravitational/teleport/foo/bar
```
The reason this is useful in a large codebase is that to run `go test -bench ./...` would require the linking of every test target binary. This can be very computationally heavy. Instead `benchfind` relies on the golang AST to find the relevant packages.
Example use:
```sh
go test -run ^$$ -bench . $(benchfind)
```
+163
View File
@@ -0,0 +1,163 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"go/ast"
"slices"
"strings"
"github.com/gravitational/trace"
"golang.org/x/tools/go/packages"
)
// Config specifies configuration for finding benchmark packages.
type Config struct {
// Patterns specifies package patterns to search. Defaults to './...' if empty.
Patterns []string
// BuildTags specifies build tags to use when loading packages.
BuildTags string
// Dir specifies the working directory to run package discovery from.
Dir string
// Excludes specifies package path prefixes to exclude.
Excludes []string
}
// Loader abstracts package loading.
type Loader interface {
Load(cfg *packages.Config, patterns ...string) ([]*packages.Package, error)
}
// PackagesLoader is the default Loader using go/packages.
type PackagesLoader struct{}
func (PackagesLoader) Load(cfg *packages.Config, patterns ...string) ([]*packages.Package, error) {
return packages.Load(cfg, patterns...)
}
// Find finds all packages matching the given patterns that contain benchmarks.
func Find(cfg Config) ([]string, error) {
return findWithLoader(cfg, PackagesLoader{})
}
func findWithLoader(cfg Config, loader Loader) ([]string, error) {
if len(cfg.Patterns) == 0 {
cfg.Patterns = []string{"./..."}
}
pkgs, err := loader.Load(&packages.Config{
Dir: cfg.Dir,
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedSyntax, // requires [packages.NeedFiles] to be set.
Tests: true,
BuildFlags: buildFlags(cfg.BuildTags),
}, cfg.Patterns...)
if err != nil {
return nil, trace.Wrap(err)
}
if packages.PrintErrors(pkgs) > 0 {
return nil, trace.BadParameter("failed to load packages")
}
return findBenchmarks(pkgs, cfg.Excludes), nil
}
func findBenchmarks(pkgs []*packages.Package, excludes []string) []string {
seen := make(map[string]struct{})
var result []string
packages.Visit(pkgs, nil, func(p *packages.Package) {
path := strings.TrimSuffix(p.PkgPath, ".test")
path = strings.TrimSuffix(path, "_test")
if matchesAnyPrefix(path, excludes) {
return
}
if pkgsContainBenchmarks(p) {
if _, ok := seen[path]; !ok {
seen[path] = struct{}{}
result = append(result, path)
}
}
})
return result
}
func buildFlags(tags string) []string {
if tags == "" {
return nil
}
return []string{"-tags=" + tags}
}
func pkgsContainBenchmarks(pkg *packages.Package) bool {
return slices.ContainsFunc(pkg.Syntax, filepkgsContainBenchmarks)
}
func filepkgsContainBenchmarks(file *ast.File) bool {
for _, decl := range file.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
if isBenchmark(fn) {
return true
}
}
return false
}
func matchesAnyPrefix(path string, prefixes []string) bool {
for _, p := range prefixes {
if strings.HasPrefix(path, p) {
return true
}
}
return false
}
func isBenchmark(fn *ast.FuncDecl) bool {
if fn.Recv != nil {
return false
}
if fn.Name == nil || !strings.HasPrefix(fn.Name.Name, "Benchmark") {
return false
}
if fn.Type == nil || fn.Type.Params == nil {
return false
}
params := fn.Type.Params.List
if len(params) != 1 {
return false
}
star, ok := params[0].Type.(*ast.StarExpr)
if !ok {
return false
}
sel, ok := star.X.(*ast.SelectorExpr)
if !ok {
return false
}
return sel.Sel.Name == "B"
}
@@ -0,0 +1,238 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"go/ast"
"go/parser"
"go/token"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/tools/go/packages"
)
type fakeLoader struct {
pkgs []*packages.Package
err error
}
func (f fakeLoader) Load(_ *packages.Config, _ ...string) ([]*packages.Package, error) {
return f.pkgs, f.err
}
func mustParseFile(t *testing.T, src string) *ast.File {
t.Helper()
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "test.go", src, 0)
require.NoError(t, err)
return file
}
func TestFindWithLoader(t *testing.T) {
benchFile := mustParseFile(t, `
package p
import "testing"
func BenchmarkFoo(b *testing.B) {}
`)
nonBenchFile := mustParseFile(t, `
package p
func Foo() {}
`)
loader := fakeLoader{
pkgs: []*packages.Package{
{
PkgPath: "github.com/foo/bar",
Syntax: []*ast.File{benchFile},
},
{
PkgPath: "github.com/baz/bar",
Syntax: []*ast.File{nonBenchFile},
},
},
}
cfg := Config{
Patterns: []string{"./..."},
}
result, err := findWithLoader(cfg, loader)
require.NoError(t, err)
require.Equal(t, []string{"github.com/foo/bar"}, result)
}
func TestFindWithLoader_Excludes(t *testing.T) {
benchFile := mustParseFile(t, `
package p
import "testing"
func BenchmarkFoo(b *testing.B) {}
`)
loader := fakeLoader{
pkgs: []*packages.Package{
{
PkgPath: "github.com/foo/bar",
Syntax: []*ast.File{benchFile},
},
},
}
cfg := Config{
Excludes: []string{"github.com/foo"},
}
result, err := findWithLoader(cfg, loader)
require.NoError(t, err)
require.Empty(t, result)
}
func TestFindBenchmarks_DeduplicatesTestPackages(t *testing.T) {
benchFile := mustParseFile(t, `
package p
import "testing"
func BenchmarkFoo(b *testing.B) {}
`)
pkgs := []*packages.Package{
{
PkgPath: "github.com/foo/bar",
Syntax: []*ast.File{benchFile},
},
{
PkgPath: "github.com/foo/bar.test",
Syntax: []*ast.File{benchFile},
},
}
result := findBenchmarks(pkgs, nil)
require.Equal(t, []string{"github.com/foo/bar"}, result)
}
func TestIsBenchmark(t *testing.T) {
tests := []struct {
name string
src string
want bool
}{
{
name: "valid",
src: `
package p
import "testing"
func BenchmarkFoo(b *testing.B) {}
`,
want: true,
},
{
name: "wrong name",
src: `
package p
import "testing"
func TestFoo(b *testing.B) {}
`,
want: false,
},
{
name: "wrong param type",
src: `
package p
func BenchmarkFoo(i int) {}
`,
want: false,
},
{
name: "too many params",
src: `
package p
import "testing"
func BenchmarkFoo(b *testing.B, i int) {}
`,
want: false,
},
{
name: "missing params",
src: `
package p
func Foo() {}
`,
want: false,
},
{
name: "cannot be a method",
src: `
package p
import "testing"
type T struct{}
func (T) BenchmarkFoo(b *testing.B) {}
`,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
file := mustParseFile(t, tt.src)
var fn *ast.FuncDecl
for _, d := range file.Decls {
if f, ok := d.(*ast.FuncDecl); ok {
fn = f
break
}
}
assert.NotNil(t, fn)
assert.Equal(t, tt.want, isBenchmark(fn))
pkg := &packages.Package{
Syntax: []*ast.File{file},
}
assert.Equal(t, tt.want, pkgsContainBenchmarks(pkg))
})
}
}
func TestMatchesAnyPrefix(t *testing.T) {
require.False(t, matchesAnyPrefix("github.com/foo/bar", nil))
require.True(t, matchesAnyPrefix("github.com/foo/bar", []string{"github.com/foo"}))
require.False(t, matchesAnyPrefix("github.com/foo/bar", []string{"github.com/other"}))
}
func TestBuildFlags(t *testing.T) {
require.Nil(t, buildFlags(""))
require.Equal(t, []string{"-tags=foo"}, buildFlags("foo"))
require.Equal(t, []string{"-tags=foo,bar"}, buildFlags("foo,bar"))
}
// TestFind is a smoketest that ensures Find works end-to-end.
func TestFind(t *testing.T) {
dir := filepath.Join(".", "testdata")
expected := []string{
"github.com/gravitational/teleport/build.assets/tooling/cmd/benchfind/testdata",
"github.com/gravitational/teleport/build.assets/tooling/cmd/benchfind/testdata/nested",
}
pkgs, err := Find(Config{
Dir: dir,
})
require.NoError(t, err)
require.Equal(t, expected, pkgs)
}
@@ -0,0 +1,77 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"os"
"github.com/alecthomas/kingpin/v2"
"github.com/gravitational/trace"
)
func run() error {
app := kingpin.New(
"benchfind",
"Find Go packages that define benchmarks without compiling test binaries.",
)
app.HelpFlag.Short('h')
tags := app.Flag("tags", "Comma-separated build tags.").Short('t').String()
cwd := app.Flag("directory", "Working directory to run package discovery from. (Default: current directory)").Short('d').ExistingDir()
excludes := app.Flag("exclude", "List of package prefixes to skip.").Short('e').Strings()
patterns := app.Arg("patterns", `Package patterns. (Default: "./...")`).Default("./...").Strings()
kingpin.MustParse(app.Parse(os.Args[1:]))
dir := *cwd
if dir == "" {
var err error
dir, err = os.Getwd()
if err != nil {
return trace.Wrap(err, "failed to get current working directory")
}
}
cfg := Config{
Patterns: *patterns,
BuildTags: *tags,
Dir: dir,
Excludes: *excludes,
}
pkgs, err := Find(cfg)
if err != nil {
return trace.Wrap(err)
}
for _, p := range pkgs {
fmt.Println(p)
}
return nil
}
func main() {
if err := run(); err != nil {
if trace.IsBadParameter(err) {
fmt.Fprintln(os.Stderr, err.Error())
} else {
fmt.Fprintln(os.Stderr, trace.DebugReport(err))
}
os.Exit(1)
}
}
@@ -0,0 +1,26 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package testdata
import "testing"
// BenchmarkExample is a simple benchmark for testing discovery.
func BenchmarkExample(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = i * i
}
}
@@ -0,0 +1,25 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package nested
import "testing"
func BenchmarkAnother(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = i * i
}
}
@@ -0,0 +1,19 @@
// Teleport
// Copyright (C) 2026 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package nobench
// This file intentionally left blank to represent a package with no benchmarks.
+47 -50
View File
@@ -17,7 +17,9 @@ require (
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d
golang.org/x/mod v0.31.0
golang.org/x/oauth2 v0.34.0
golang.org/x/tools v0.40.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.19.2
howett.net/plist v1.0.1
k8s.io/apiextensions-apiserver v0.34.2
@@ -30,60 +32,11 @@ require (
buf.build/go/protovalidate v1.1.0 // indirect
buf.build/go/spdx v0.2.0 // indirect
cel.dev/expr v0.25.1 // indirect
dario.cat/mergo v1.0.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/bufbuild/protocompile v0.14.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/google/cel-go v0.26.1 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kisielk/gotool v1.0.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.40.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.35.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
pluginrpc.com/pluginrpc v0.5.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
require (
cloud.google.com/go/auth v0.18.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
connectrpc.com/connect v1.19.1 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/age v1.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect
@@ -91,6 +44,10 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect
@@ -126,6 +83,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bufbuild/protocompile v0.14.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
@@ -134,6 +92,7 @@ require (
github.com/crewjam/httperr v0.2.0 // indirect
github.com/crewjam/saml v0.4.14 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/di-wu/parser v0.3.0 // indirect
github.com/di-wu/xsd-datetime v1.0.0 // indirect
github.com/digitorus/pkcs7 v0.0.0-20250730155240-ffadbf3f398c // indirect
@@ -144,10 +103,12 @@ require (
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-chi/chi/v5 v5.2.4 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.24.1 // indirect
github.com/go-openapi/errors v0.22.6 // indirect
@@ -182,16 +143,19 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.1 // indirect
github.com/google/certificate-transparency-go v1.3.2 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-attestation v0.6.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.7 // indirect
github.com/google/go-github/v70 v70.0.0 // indirect
github.com/google/go-querystring v1.2.0 // indirect
github.com/google/go-tpm v0.9.7 // indirect
github.com/google/go-tpm-tools v0.4.7 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/safetext v0.0.0-20240104143208-7a7d9b3d812f // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.9 // indirect
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
@@ -203,6 +167,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/in-toto/attestation v1.1.2 // indirect
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
@@ -217,16 +182,23 @@ require (
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kisielk/gotool v1.0.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/muhlemmer/gu v0.3.1 // indirect
github.com/muhlemmer/httpforwarded v0.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -237,7 +209,9 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/patrickmn/go-cache v2.1.1-0.20191004192108-46f407853014+incompatible // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkoukk/tiktoken-go v0.1.8 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/otp v1.5.0 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
@@ -250,6 +224,7 @@ require (
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sigstore/protobuf-specs v0.5.0 // indirect
github.com/sigstore/rekor v1.5.0 // indirect
github.com/sigstore/rekor-tiles/v2 v2.0.1 // indirect
@@ -258,7 +233,10 @@ require (
github.com/sigstore/timestamp-authority/v2 v2.0.4 // indirect
github.com/sijms/go-ora/v2 v2.9.0 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/theupdateframework/go-tuf/v2 v2.4.1 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
@@ -269,9 +247,11 @@ require (
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/vulcand/predicate v1.2.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/zitadel/logging v0.6.2 // indirect
github.com/zitadel/oidc/v3 v3.45.0 // indirect
github.com/zitadel/schema v1.3.1 // indirect
@@ -290,18 +270,35 @@ require (
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.38.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.14.0 // indirect
google.golang.org/api v0.260.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.35.0 // indirect
k8s.io/apimachinery v0.35.0 // indirect
k8s.io/client-go v0.35.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
mvdan.cc/sh/v3 v3.7.0 // indirect
pluginrpc.com/pluginrpc v0.5.0 // indirect
sigs.k8s.io/controller-runtime v0.22.4 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
replace github.com/alecthomas/kingpin/v2 => github.com/gravitational/kingpin/v2 v2.1.11-0.20230515143221-4ec6b70ecd33