mirror of
https://github.com/anchore/syft.git
synced 2026-05-20 12:15:27 +02:00
* 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>
210 lines
6.8 KiB
Go
210 lines
6.8 KiB
Go
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")
|
|
}
|