{"id":19368455,"url":"https://github.com/anchore/go-make","last_synced_at":"2026-05-16T04:12:26.127Z","repository":{"id":257827880,"uuid":"873110753","full_name":"anchore/go-make","owner":"anchore","description":null,"archived":false,"fork":false,"pushed_at":"2024-11-05T17:54:32.000Z","size":60,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-01-06T22:35:31.414Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anchore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-15T16:06:48.000Z","updated_at":"2024-11-05T17:54:33.000Z","dependencies_parsed_at":"2025-01-06T22:32:13.305Z","dependency_job_id":"b63edaeb-63b1-46d7-9205-cf1fb3e78fdb","html_url":"https://github.com/anchore/go-make","commit_stats":null,"previous_names":["anchore/go-make"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchore%2Fgo-make","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchore%2Fgo-make/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchore%2Fgo-make/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anchore%2Fgo-make/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anchore","download_url":"https://codeload.github.com/anchore/go-make/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240491823,"owners_count":19809977,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T08:06:28.281Z","updated_at":"2026-05-16T04:12:26.113Z","avatar_url":"https://github.com/anchore.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go, make!\n\n`gomake` is a golang-based scripting utility, consisting of a simple DSL to help with common development tasks,\nincluding cross-platform development utilities and a task runner with minimal dependencies.\n\nSome functionality expects certain binaries to be available on the path:\n* `go` -- for running in the first place, but also some commands may invoke `go`\n* `git` -- in order to get revision information and build certain dependencies\n* `docker` -- for running container-based tasks (configurable for CLI compatible commands such as `podman`)\n\nOther binaries used should be configured in a binny config (or `go.mod` `tools` section ** TODO **) and will be downloaded\nas needed during execution.\n\n## Example\n\n```golang\n// file: .make/main.go\npackage main \n\nimport (\n\t. \"github.com/anchore/go-make\"\n\t\"github.com/anchore/go-make/tasks/golint\"\n\t\"github.com/anchore/go-make/tasks/gotest\"\n)\n\nfunc main() {\n\tMakefile(\n\t\tgolint.Tasks(), // centralize common task definitions \n\t\tgotest.Tasks(),\n\t\t// define tasks that resemble configuration / scripts:\n\t\tTask{\n\t\t\tName:         \"build\",\n\t\t\tDescription:  \"make a custom build task\",\n\t\t\tDependencies:  List(\"goreleaser:snapshot-buildfile\"), // can ensure other specific tasks run first, like make dependencies\n\t\t\tRun: func() {\n\t\t\t\t// Run function supports: global template vars, quoting within strings,\n\t\t\t\t// downloading and referencing managed executables\n\t\t\t\tRun(`goreleaser build --config {{TmpDir}}/goreleaser.yaml --clean --snapshot --single-target`)\n\t\t\t},\n\t\t},\n\t\tTask{\n\t\t\tName:         \"custom-tests\",\n\t\t\tDescription:  \"do some custom testing\",\n\t\t\tRunsOn:       List(\"test\"),  // runs whenever \"test\" runs, e.g. make test\n\t\t\tRun: func() {\n\t\t\t\t// failed commands have convenient links here, in this file\n\t\t\t\tRun(`go test ./test`)\n\t\t\t},\n\t\t},\n\t)\n}\n```\n\nSee also: [the build definition in this repository](.make/main.go) and\n[script tests](script/testdata).\n\n## How Tool Versions Work\n\ngo-make uses a two-tier system for tool versions with local overrides:\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     Your Project                                │\n│  ┌─────────────────┐                                            │\n│  │ .binny.yaml     │  ← Local overrides (optional)              │\n│  │ golangci-lint:  │    Takes precedence if defined             │\n│  │   v1.55.0       │                                            │\n│  └────────┬────────┘                                            │\n│           │                                                     │\n│           ▼ overrides                                           │\n│  ┌─────────────────────────────────────────────────────────────┐│\n│  │ go-make module (in Go module cache)                         ││\n│  │  ┌─────────────────┐                                        ││\n│  │  │ .binny.yaml     │  ← Embedded defaults                   ││\n│  │  │ golangci-lint:  │    Used when no local override         ││\n│  │  │   v2.11.4       │                                        ││\n│  │  └─────────────────┘                                        ││\n│  └─────────────────────────────────────────────────────────────┘│\n└─────────────────────────────────────────────────────────────────┘\n```\n\nSpecifically we hav ethe following capabilities:\n\n1. **Embedded defaults**: go-make's `.binny.yaml` is embedded into the module using Go's `//go:embed` directive. When you import go-make, this config is in your Go module cache alongside the source.\n\n2. **Local overrides**: If your project has a `.binny.yaml`, those versions take precedence.\n\n3. **Automatic installation**: When a task uses a tool, go-make checks your local config first, falls back to embedded defaults, then uses [binny](https://github.com/anchore/binny) to install.\n\n\nTo override `go-make`'s version for a specific tool simply create a `.binny.yaml` in your project root:\n\n```yaml\ntools:\n  - name: golangci-lint\n    version:\n      want: v1.55.0  # Use this instead of go-make's default\n    method: github-release\n    with:\n      repo: golangci/golangci-lint\n```\n\n### Q \u0026 A\n\n**Q:** It's too verbose to type `go run -C .make .`\n\n**A:** Most modern `make` implementations seem to support the wildcard,\nso you can just run `make \u003ctask\u003e` by copying [what we have in this repo](Makefile):\n```makefile\n.PHONY: *\n.DEFAULT:\n%:\n\t@go run -C .make . $@\n```\nIf that doesn't work for you, you can generate a Makefile with all the targets\nusing the hidden `makefile` task. Or just use an alias.\n\n**Q:** I see something like: `make: Nothing to be done for 'test'`\n\n**A:** This is how `make` works when you have directory matching the task name.\nAdd an explicit `.PHONY` directive and make target to your `Makefile`, e.g:\n```makefile\n.PHONY: test\ntest:\n\t@go run -C .make . $@\n```\n\n**Q:** I have a `golangci-lint` linter rule: _no dot imports_\n\n**A:** Use a configuration like this:\n```yaml\n    staticcheck:\n      dot-import-whitelist:\n        - github.com/anchore/go-make\n        - github.com/anchore/go-make/lang\n    revive:\n      rules:\n        - name: dot-imports\n          arguments:\n            - allowed-packages:\n                - github.com/anchore/go-make\n                - github.com/anchore/go-make/lang\n```\n\n**Q:** Do I need to put my files in `.make`?\n\n**A:** No, we just do this as a convention. The only thing this affects is what you\nspecify in your `Makefile`, e.g. `@go -C \u003cdir\u003e`\n\n**Q:** Why make this? Surely there are already build tools.\n\n**A:** Yes, there are plenty of build tools.\nWe have a fairly small and specific set of tasks we want to keep consistent across many repositories\nand allow running builds and tests on all platforms, and since we're already using Go,\nthis seemed like a fairly simple solution to leverage the existing module system for distribution.\n\nFor example, we used [Task](https://github.com/go-task/task), which works great but leads to\ndifficulties implementing functionality for Windows, since common *nix tools are not available,\nlike `grep`, and at present doesn't offer an ideal way to share task definitions.\n\nIt's perfectly fine to use additional tools to implement your own functionality,\nsuch as Task or [Bitfield script](https://github.com/bitfield/script) [*](https://bitfieldconsulting.com/posts/scripting).\nAlthough it does provide some utilities, this library is intended as a means of bootstrapping sharable\ntask definitions in a platform-agnostic manner.\n\n## Task DSL Reference\n\n### Task Structure\n\nA `Task` is the fundamental building block for defining work:\n\n```go\nTask{\n    Name:         \"build\",           // unique identifier for the task\n    Description:  \"build the app\",   // shown in help output\n    Dependencies: Deps(\"clean\"),     // tasks that must run first\n    RunsOn:       List(\"default\"),   // labels that trigger this task\n    Tasks:        []Task{...},       // nested subtasks\n    Run: func() {\n        // task implementation\n    },\n}\n```\n\n### Dependencies vs RunsOn\n\nThese two fields serve opposite purposes:\n\n- **Dependencies**: Lists tasks that must complete *before* this task runs. Use when your task requires another task's output.\n  ```go\n  Task{\n      Name:         \"test\",\n      Dependencies: Deps(\"build\"),  // build runs before test\n  }\n  ```\n\n- **RunsOn**: Lists labels (task names) that will cause this task to run. Use when your task should automatically run as part of another task.\n  ```go\n  Task{\n      Name:   \"unit-tests\",\n      RunsOn: List(\"test\"),  // runs whenever \"make test\" is called\n  }\n  ```\n\nThink of it this way: `Dependencies` pulls tasks to run before you, while `RunsOn` hooks your task to run when another task is invoked.\n\n### Hierarchical Tasks\n\nUse `Task.Tasks` to group related tasks. Subtasks are automatically prefixed with the parent name:\n\n```go\nTask{\n    Name: \"release\",\n    Tasks: []Task{\n        {Name: \"snapshot\"},      // becomes \"release:snapshot\"\n        {Name: \"ci-release\"},    // becomes \"release:ci-release\"\n    },\n}\n```\n\n### Builder Methods\n\nTasks support method chaining for convenience:\n\n```go\nmyTask.DependsOn(\"other-task\")   // adds to Dependencies\nmyTask.RunOn(\"label\")            // adds to RunsOn\n```\n\n## Template Variables\n\nCommands passed to `Run()` support Go template syntax with the following built-in variables:\n\n| Variable | Description | Example Value |\n|----------|-------------|---------------|\n| `{{RootDir}}` | Repository root (where .git is located) | `/home/user/myproject` |\n| `{{ToolDir}}` | Directory for managed tools | `/home/user/myproject/.tool` |\n| `{{TmpDir}}` | Temporary directory for build artifacts | System temp or empty for default |\n| `{{OS}}` | Current operating system | `linux`, `darwin`, `windows` |\n| `{{Arch}}` | Current architecture | `amd64`, `arm64` |\n| `{{GitRoot}}` | Same as RootDir (alias) | `/home/user/myproject` |\n\nExample usage:\n\n```go\nRun(`goreleaser build --config {{TmpDir}}/goreleaser.yaml`)\nRun(`{{ToolDir}}/mytool --version`)\n```\n\n### Custom Template Variables\n\nExtend the template context via `template.Globals`:\n\n```go\nimport \"github.com/anchore/go-make/template\"\n\nfunc init() {\n    template.Globals[\"Version\"] = func() string {\n        return \"1.0.0\"\n    }\n}\n\n// Later in a task:\nRun(`echo \"Building version {{Version}}\"`)\n```\n\n## Built-in Tasks\n\ngo-make automatically adds these tasks to every Makefile:\n\n| Task | Description |\n|------|-------------|\n| `help` | Prints all available tasks and descriptions |\n| `clean` | Meta-task label for cleanup (no default action) |\n| `binny:clean` | Deletes the `.tool` directory (runs on `clean`) |\n| `binny:update` | Updates all managed tools (runs on `dependencies:update`) |\n| `binny:install` | Installs all configured tools |\n| `dependencies:update` | Meta-task label for dependency updates |\n| `debuginfo` | Outputs environment variables and GitHub Actions event data |\n| `dos2unix` | Converts CRLF to LF in text files (supports glob argument) |\n| `test` | Meta-task label for tests (no default action) |\n| `makefile` | Generates a traditional Makefile with all defined targets |\n\n**Meta-task labels** like `clean`, `test`, and `dependencies:update` have no default action but provide hooks for your tasks to attach to via `RunsOn`. For example:\n\n```go\nTask{\n    Name:   \"my-clean\",\n    RunsOn: List(\"clean\"),  // runs when \"make clean\" is called\n    Run: func() {\n        file.Delete(\"build/\")\n    },\n}\n```\n\n## Error Handling\n\n### Default Behavior\n\n`Run()` panics on command failure. This stops task execution immediately:\n\n```go\nRun(`go build ./...`)  // panics if build fails\n```\n\n### Handling Failures Gracefully\n\nUse `run.NoFail()` to suppress panics and return an empty string on failure:\n\n```go\nresult := Run(`git describe --tags`, run.NoFail())\nif result == \"\" {\n    // command failed, handle gracefully\n}\n```\n\n### Control Flow Functions\n\nThe `lang` package provides panic-based control flow:\n\n| Function | Behavior |\n|----------|----------|\n| `lang.Throw(err)` | Panics if `err` is non-nil |\n| `lang.Return(val, err)` | Returns `val` if `err` is nil, otherwise panics |\n| `lang.Continue(val, err)` | Returns `val` regardless, logs `err` without panicking |\n| `lang.Catch(fn)` | Executes `fn`, catches any panic and returns it as an error |\n\nExample:\n\n```go\n// panic if file can't be read\ncontents := lang.Return(os.ReadFile(\"config.yaml\"))\n\n// log error but continue\ndata := lang.Continue(fetchOptionalData())\n\n// catch panics from a function\nif err := lang.Catch(riskyOperation); err != nil {\n    log.Info(\"operation failed: %v\", err)\n}\n```\n\n### Error Recovery\n\n`lang.HandleErrors()` is automatically deferred in `Makefile()` to catch panics and print formatted error messages with stack traces.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanchore%2Fgo-make","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanchore%2Fgo-make","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanchore%2Fgo-make/lists"}