diff --git a/.make/go.mod b/.make/go.mod index 1af7544c8..317043a52 100644 --- a/.make/go.mod +++ b/.make/go.mod @@ -2,13 +2,11 @@ module github.com/anchore/syft/.make go 1.25.8 -require ( - github.com/anchore/go-make v0.7.0 - github.com/goccy/go-yaml v1.19.2 -) +require github.com/anchore/go-make v0.7.0 require ( github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect golang.org/x/mod v0.37.0 // indirect golang.org/x/sys v0.46.0 // indirect ) diff --git a/.make/main.go b/.make/main.go index 274462506..90fcffea7 100644 --- a/.make/main.go +++ b/.make/main.go @@ -1,28 +1,19 @@ 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/gotask" "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 @@ -89,110 +80,58 @@ func main() { 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). + // --- everything else is implemented in Taskfile.yaml. gotask.Tasks() + // discovers every (non-internal) Taskfile task — including the namespaced + // `generate:cpe-index:*` tasks from the included task.d file — and surfaces + // them as first-class go-make tasks (with their canonical names and `desc:`) + // that forward to `task `. Descriptions live in Taskfile.yaml as the + // single source of truth; no per-task wrapping needed here. + gotask.Tasks(), - // static analysis extras - wrap("check-json-schema-drift").RunOn("static-analysis"), - wrap("check-capability-drift"), - wrap("check-binary-fixture-size").RunOn("static-analysis"), + // gotask.Tasks() discovers canonical task names only, not Taskfile aliases, + // so re-expose `refresh-fixtures`'s `fixtures` alias for manual use. + Task{ + Name: "fixtures", + Description: "Clear and fetch all test fixture cache (alias of refresh-fixtures)", + Dependencies: Deps("refresh-fixtures"), + }, - // 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"), + // gotask.Tasks() can't attach RunsOn labels, so wire the syft-specific + // Taskfile tasks into go-make's native phases here. These thin hooks have + // no body and no description (hidden from `make help`); they only pull the + // discovered tasks in when the labeled phase runs. + Task{ + Name: "static-analysis:syft", + RunsOn: lang.List("static-analysis"), + Dependencies: Deps("check-json-schema-drift", "check-binary-fixture-size"), + }, + Task{ + Name: "test:syft", + RunsOn: lang.List("test"), + Dependencies: Deps("validate-cyclonedx-schema", "test-utils", "check-docker-cache"), + }, + // 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). + Task{ + Name: "unit:syft", + RunsOn: lang.List("unit"), + Dependencies: Deps("refresh-fixtures"), + }, + Task{ + Name: "clean:syft", + RunsOn: lang.List("clean"), + Dependencies: Deps( + "clean-snapshot", + "clean-docker-cache", + "clean-oras-cache", + "clean-cache", + "clean-test-observations", + ), + }, ) } -// wrap creates a go-make Task that delegates execution to `task `. 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: // /snapshot/-build__/syft, where arch maps amd64->amd64_v1 // and arm64->arm64_v8.0 to match goreleaser's per-target output directory naming. diff --git a/Taskfile.yaml b/Taskfile.yaml index d3d59426b..d0ae4f20d 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -2,9 +2,9 @@ version: "3" # NOTE: most generic tasks (static-analysis, format, lint, unit, snapshot, release, # changelog, ci-release, etc.) are now provided natively by anchore/go-make and -# defined in .make/main.go. This file holds the syft-specific tasks that go-make -# wraps via `wrap("")` calls — keep descriptions (`desc:`) populated so they -# show up in `make help`. +# defined in .make/main.go. This file holds the syft-specific tasks, which go-make +# auto-discovers via gotask.Tasks() and surfaces as `make ` — keep +# descriptions (`desc:`) populated so they show up in `make help`. includes: generate:cpe-index: ./task.d/generate/cpe-index.yaml