mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
Port to go-make (#4923)
* port to go-make Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * refresh fixtures on running unit tests Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * address refresh cache issues with old now-gitignored files Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
module github.com/anchore/syft/.make
|
||||
|
||||
go 1.25.8
|
||||
|
||||
require (
|
||||
github.com/anchore/go-make v0.4.0
|
||||
github.com/goccy/go-yaml v1.19.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
github.com/anchore/go-make v0.4.0 h1:poFE4PXcHwvix2E8AhdM7YHZ15bMhZb+W02i3+qrP8M=
|
||||
github.com/anchore/go-make v0.4.0/go.mod h1:Nc/tkwQHW1d1Vi8+0rtS/vSrH6pxieaUQXLdrctn+8g=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
|
||||
. "github.com/anchore/go-make"
|
||||
"github.com/anchore/go-make/file"
|
||||
"github.com/anchore/go-make/git"
|
||||
"github.com/anchore/go-make/lang"
|
||||
"github.com/anchore/go-make/run"
|
||||
"github.com/anchore/go-make/tasks/golint"
|
||||
"github.com/anchore/go-make/tasks/goreleaser"
|
||||
"github.com/anchore/go-make/tasks/gotest"
|
||||
)
|
||||
|
||||
// taskfileDescriptions maps Taskfile.yaml task names to their `desc:` field.
|
||||
// Loaded at package init so wrap() can use Taskfile.yaml as the single source
|
||||
// of truth for wrapped-task descriptions.
|
||||
var taskfileDescriptions = mustReadTaskfileDescriptions()
|
||||
|
||||
func main() {
|
||||
Makefile(
|
||||
// shared anchore tasks
|
||||
golint.Tasks(),
|
||||
goreleaser.Tasks(),
|
||||
|
||||
// unit tests: exclude packages under any test/ directory (matches the syft
|
||||
// Taskfile's prior `grep -v` against test paths). Coverage threshold of 62%
|
||||
// preserves the prior coverage gate that used to live in scripts/coverage.py.
|
||||
gotest.Tasks(
|
||||
gotest.Name("unit"),
|
||||
gotest.ExcludeGlob("**/test/**"),
|
||||
gotest.CoverageThreshold(62),
|
||||
),
|
||||
|
||||
// integration tests: native go-make Task. The race-detector smoke against a
|
||||
// real image stays bundled here (RunsOn integration) so `make integration`
|
||||
// behaves like the Taskfile version did.
|
||||
gotest.Tasks(
|
||||
gotest.Name("integration"),
|
||||
gotest.IncludeGlob("./cmd/syft/internal/test/integration/..."),
|
||||
gotest.Verbose(),
|
||||
gotest.NoCoverage(),
|
||||
),
|
||||
Task{
|
||||
Name: "integration:race-smoke",
|
||||
Description: "exercise the CLI with the race detector",
|
||||
RunsOn: lang.List("integration"),
|
||||
Run: func() {
|
||||
Run("go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2")
|
||||
},
|
||||
},
|
||||
|
||||
// cli tests: native go-make Task. Requires SYFT_BINARY_LOCATION pointing at
|
||||
// an *absolute* path to the snapshot binary. Intentionally does NOT depend
|
||||
// on snapshot: in CI we download a pre-built snapshot artifact and re-running
|
||||
// goreleaser here would both burn ~10m and clobber the downloaded binary.
|
||||
// Locally, the failure message tells you to run `make snapshot` first.
|
||||
Task{
|
||||
Name: "cli",
|
||||
Description: "Run CLI tests",
|
||||
RunsOn: lang.List("test"),
|
||||
Run: func() {
|
||||
bin := snapshotBinPath()
|
||||
if !file.Exists(bin) {
|
||||
panic(fmt.Sprintf("snapshot binary not found at %s; run `make snapshot` first", bin))
|
||||
}
|
||||
Log("testing binary: %s", bin)
|
||||
Run(
|
||||
"go test -count=1 -timeout=15m -v ./test/cli",
|
||||
run.Env("SYFT_BINARY_LOCATION", bin),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
|
||||
Task{
|
||||
Name: "default",
|
||||
Description: "Run all validation tasks",
|
||||
Dependencies: Deps("static-analysis", "test", "install-test"),
|
||||
},
|
||||
|
||||
// --- everything below is implemented in Taskfile.yaml and surfaced here
|
||||
// via wrap(). Descriptions come from Taskfile.yaml (single source of truth).
|
||||
|
||||
// static analysis extras
|
||||
wrap("check-json-schema-drift").RunOn("static-analysis"),
|
||||
wrap("check-capability-drift"),
|
||||
wrap("check-binary-fixture-size").RunOn("static-analysis"),
|
||||
|
||||
// test extras
|
||||
wrap("validate-cyclonedx-schema").RunOn("test"),
|
||||
wrap("test-utils").RunOn("test"),
|
||||
wrap("check-docker-cache").RunOn("test"),
|
||||
wrap("snapshot-smoke-test"),
|
||||
|
||||
// update commands
|
||||
wrap("update-format-golden-files"),
|
||||
|
||||
// fixture cache plumbing (heavy ORAS logic, lives in Taskfile).
|
||||
// refresh-fixtures hooks into "unit" so `make unit` triggers the
|
||||
// stale-cache detection + download just like `task unit` did on main
|
||||
// (its `deps: [tmpdir, fixtures]` is what kept the fixture cache fresh).
|
||||
wrap("fingerprints"),
|
||||
wrap("refresh-fixtures").RunOn("unit"),
|
||||
wrap("fixtures"),
|
||||
wrap("build-fixtures"),
|
||||
wrap("download-test-fixture-cache"),
|
||||
wrap("upload-test-fixture-cache"),
|
||||
wrap("show-test-image-cache"),
|
||||
|
||||
// install-script tests (delegates to test/install/Makefile)
|
||||
wrap("install-test"),
|
||||
wrap("install-test-cache-save"),
|
||||
wrap("install-test-cache-load"),
|
||||
wrap("install-test-ci-mac"),
|
||||
|
||||
// compare tests
|
||||
wrap("generate-compare-file"),
|
||||
wrap("compare-mac"),
|
||||
wrap("compare-linux"),
|
||||
wrap("compare-test-deb-package-install"),
|
||||
wrap("compare-test-rpm-package-install"),
|
||||
|
||||
// code/data generation (umbrella + per-target; each lives in Taskfile)
|
||||
wrap("generate"),
|
||||
wrap("generate-json-schema"),
|
||||
wrap("generate-license-list"),
|
||||
wrap("generate-cpe-dictionary-index"),
|
||||
wrap("generate-capabilities"),
|
||||
|
||||
// cleanup (each hooks into go-make's built-in `clean` label)
|
||||
wrap("clean-snapshot").RunOn("clean"),
|
||||
wrap("clean-docker-cache").RunOn("clean"),
|
||||
wrap("clean-oras-cache").RunOn("clean"),
|
||||
wrap("clean-cache").RunOn("clean"),
|
||||
wrap("clean-test-observations").RunOn("clean"),
|
||||
)
|
||||
}
|
||||
|
||||
// wrap creates a go-make Task that delegates execution to `task <name>`. The
|
||||
// task's description is pulled from Taskfile.yaml's `desc:` field — descriptions
|
||||
// for wrapped tasks must always live in Taskfile.yaml, never here.
|
||||
func wrap(name string) Task {
|
||||
desc, ok := taskfileDescriptions[name]
|
||||
if !ok || desc == "" {
|
||||
// loud-fail at startup so missing descs can't sneak through review.
|
||||
panic(fmt.Sprintf("Taskfile.yaml task %q is missing a `desc:` field; please add one", name))
|
||||
}
|
||||
return Task{
|
||||
Name: name,
|
||||
Description: desc,
|
||||
Run: func() { Run("task " + name) },
|
||||
}
|
||||
}
|
||||
|
||||
// mustReadTaskfileDescriptions parses Taskfile.yaml at the repo root and returns
|
||||
// a map of task name -> desc. Runs at package init time so wrap() can use it.
|
||||
func mustReadTaskfileDescriptions() map[string]string {
|
||||
root := git.Root()
|
||||
if root == "" {
|
||||
return nil
|
||||
}
|
||||
path := filepath.Join(root, "Taskfile.yaml")
|
||||
data, err := os.ReadFile(path) //nolint:gosec // G304: path resolved from git.Root()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var tf struct {
|
||||
Tasks map[string]struct {
|
||||
Desc string `yaml:"desc"`
|
||||
Aliases []string `yaml:"aliases"`
|
||||
} `yaml:"tasks"`
|
||||
}
|
||||
lang.Throw(yaml.Unmarshal(data, &tf))
|
||||
out := make(map[string]string, len(tf.Tasks))
|
||||
for name, t := range tf.Tasks {
|
||||
out[name] = t.Desc
|
||||
// aliases inherit the canonical task's description so wrap() can find them.
|
||||
for _, alias := range t.Aliases {
|
||||
out[alias] = t.Desc
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// snapshotBinPath replicates the SNAPSHOT_BIN computation from the prior Taskfile:
|
||||
// <repoRoot>/snapshot/<os>-build_<os>_<arch>/syft, where arch maps amd64->amd64_v1
|
||||
// and arm64->arm64_v8.0 to match goreleaser's per-target output directory naming.
|
||||
// Returns an absolute path: the cli tests' getSyftBinaryLocation contract requires
|
||||
// SYFT_BINARY_LOCATION to be absolute because subtests run with cmd.Dir = t.TempDir().
|
||||
func snapshotBinPath() string {
|
||||
osName := runtime.GOOS
|
||||
var arch string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
arch = "amd64_v1"
|
||||
case "arm64":
|
||||
arch = "arm64_v8.0"
|
||||
default:
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
return filepath.Join(RootDir(), "snapshot", osName+"-build_"+osName+"_"+arch, "syft")
|
||||
}
|
||||
Reference in New Issue
Block a user