Files
Lucas Manuel Rodriguez 874311f38a Add CI check to prevent Go's "testing" package on production binaries (#45977)
Resolves #45220.

Manual dispatch of golangci-lint automation to catch any issues in macOS
and Windows: https://github.com/fleetdm/fleet/actions/runs/26230838836.

## Testing

- [X] QA'd all new/changed functionality manually

Manually tested by adding fake imports of "testing" in production
packages.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Enforced a new pre-lint build check that fails when production
binaries depend on test-only packages, preventing accidental testing
imports in releases.
* Added a CLI-based dependency audit and integrated it into the lint
workflow, plus updated build help text to surface the new check.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45977?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-21 12:42:43 -03:00

118 lines
3.5 KiB
Go

// Command check-no-testing-in-prod fails the build if any Fleet-owned (i.e.
// github.com/fleetdm/fleet/v4/...) package reachable from cmd/fleet,
// cmd/fleetctl, or orbit/cmd/orbit directly imports Go's "testing"
// package.
//
// Background: see https://github.com/fleetdm/fleet/issues/45220. Test
// helpers used to be sprinkled across production packages as
// testing_utils.go files that imported "testing"; that pulled the test
// scaffolding (and its -test.* flags, etc.) into the shipping binaries.
// The cleanup moved each such helper into a sibling *test subpackage or
// _test.go file. This guard makes sure nothing slips back.
//
// Usage:
//
// go run ./tools/check-no-testing-in-prod
//
// Wired into make lint-go (see Makefile).
//
// Note: third-party dependencies (currently a few pkcs7 forks and
// apache/thrift) are intentionally NOT checked here -- they're out of our
// control without an upstream swap. The intent of this check is to make
// sure Fleet code never reintroduces the issue.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"slices"
"sort"
"strings"
)
// Production binaries to audit. Any first-party package reachable from
// these must not import "testing" directly.
//
// Important: these are bare binary paths, NOT "./cmd/fleet/..." — the
// "..." form expands to include _test.go-only sibling packages that aren't
// actually linked into the binary.
var roots = []string{
"./cmd/fleet",
"./cmd/fleetctl",
"./orbit/cmd/orbit",
}
const fleetModulePrefix = "github.com/fleetdm/fleet/v4/"
type pkgInfo struct {
ImportPath string `json:"ImportPath"`
Imports []string `json:"Imports"`
Standard bool `json:"Standard"`
}
func main() {
// Use the same build tags as the production build (see Makefile).
//
// -e lets `go list` report packages even when they have load errors. We
// need this so the audit works on fresh checkouts where `make
// generate-go` hasn't been run yet (server/bindata/generated.go is not
// committed; without -e, the `-tags full` build sees no Go files in
// that package and fails). The import graph we walk is still complete
// because go list emits the package record either way.
args := []string{
"list",
"-tags", "full,fts5,netgo",
"-e",
"-deps",
"-json",
}
args = append(args, roots...)
cmd := exec.Command("go", args...)
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
fmt.Fprintf(os.Stderr, "go list failed: %v\n", err)
os.Exit(2)
}
dec := json.NewDecoder(strings.NewReader(string(out)))
var offenders []string
seen := make(map[string]struct{})
for {
var p pkgInfo
if err := dec.Decode(&p); err != nil {
break
}
if p.Standard {
continue
}
if !strings.HasPrefix(p.ImportPath, fleetModulePrefix) {
continue
}
if slices.Contains(p.Imports, "testing") {
if _, ok := seen[p.ImportPath]; !ok {
offenders = append(offenders, p.ImportPath)
seen[p.ImportPath] = struct{}{}
}
}
}
if len(offenders) == 0 {
return
}
sort.Strings(offenders)
fmt.Fprintln(os.Stderr, "check-no-testing-in-prod: the following Fleet packages are reachable from")
fmt.Fprintln(os.Stderr, "the production binaries (cmd/fleet, cmd/fleetctl, orbit/cmd/orbit) AND import")
fmt.Fprintln(os.Stderr, "the \"testing\" package directly. Move the test-only code into a sibling")
fmt.Fprintln(os.Stderr, "*test subpackage or a _test.go file. See https://github.com/fleetdm/fleet/issues/45220.")
fmt.Fprintln(os.Stderr)
for _, o := range offenders {
fmt.Fprintf(os.Stderr, " - %s\n", o)
}
os.Exit(1)
}