{"id":19420195,"url":"https://github.com/einride/sage","last_synced_at":"2026-04-23T17:00:51.144Z","repository":{"id":37750718,"uuid":"416402410","full_name":"einride/sage","owner":"einride","description":"🌿 A Make-like build tool for Go projects","archived":false,"fork":false,"pushed_at":"2026-04-20T15:46:36.000Z","size":900,"stargazers_count":47,"open_issues_count":17,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2026-04-20T17:40:29.736Z","etag":null,"topics":["build-tools","ci","go","golang","makefile"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/go.einride.tech/sage","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/einride.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-10-12T15:54:19.000Z","updated_at":"2026-04-16T18:23:28.000Z","dependencies_parsed_at":"2026-04-02T11:00:34.378Z","dependency_job_id":null,"html_url":"https://github.com/einride/sage","commit_stats":{"total_commits":517,"total_committers":49,"mean_commits":"10.551020408163266","dds":0.6827852998065764,"last_synced_commit":"1e5b9af61081eddb0291f862af630e941b222fd6"},"previous_names":["einride/mage-tools"],"tags_count":583,"template":false,"template_full_name":null,"purl":"pkg:github/einride/sage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/einride%2Fsage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/einride%2Fsage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/einride%2Fsage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/einride%2Fsage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/einride","download_url":"https://codeload.github.com/einride/sage/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/einride%2Fsage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32189659,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T15:28:30.493Z","status":"ssl_error","status_checked_at":"2026-04-23T15:28:29.972Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["build-tools","ci","go","golang","makefile"],"created_at":"2024-11-10T13:21:45.058Z","updated_at":"2026-04-23T17:00:51.125Z","avatar_url":"https://github.com/einride.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# :herb: Sage\n\nSage is a Make-like build tool inspired by [Mage](https://magefile.org/) that\nprovides a curated and maintained set of build [tools](./tools) for Go projects.\n\nFor a detailed and AI-generated documentation on the architecture behind this\nproject, which you can chat with, see the\n[DeepWiki documentation](https://deepwiki.com/einride/sage).\n\n[![Release](https://github.com/einride/sage/actions/workflows/release.yml/badge.svg)](https://github.com/einride/sage/actions/workflows/release.yml)\n[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/einride/sage)\n\n## Requirements\n\n- [Go](https://golang.org/doc/install) \u003e= 1.22\n- [GNU Make](https://www.gnu.org/software/make/)\n\n## Getting started\n\nTo initilize Sage in a repository, just run:\n\n```bash\ngo run go.einride.tech/sage@latest init\n```\n\nRun `make`.\n\nTwo changes should now have happened. If the project had a previous `Makefile`\nit should have been renamed to `Makefile.old` and a new should have been\ncreated. If the project have a dependabot config, a sage config should have been\nadded.\n\n## Usage\n\nSage imports, and targets within the Sagefiles, can be written to Makefiles, you\ncan generate as many Makefiles as you want, see more at\n[Makefiles / Sage namespaces](https://github.com/einride/sage#makefiles--sage-namespaces).\n\n### Sagefiles\n\nYou can have as many Sagefiles as you want in the `.sage` folder.\n\n#### Targets\n\nAny public function in the main package will be exported. Functions can have no\nreturn value but error. The following arguments are supported: Optional first\nargument of context.Context, string, int or bool.\n\n```golang\nfunc All() {\n  sg.Deps(\n\t  FormatYaml,\n\t  sg.Fn(ConvcoCheck, \"origin/main..HEAD\"),\n  )\n}\n\nfunc FormatYaml() error {\n\treturn sgyamlfmt.FormatYAML()\n}\n\nfunc ConvcoCheck(ctx context.Context, rev string) error {\n\tlogr.FromContextOrDiscard(ctx).Info(\"checking...\")\n\treturn sgconvco.Command(ctx, \"check\", rev).Run()\n}\n```\n\n#### Makefiles / Sage namespaces\n\nTo generate Makefiles, a `main` method needs to exist in one of the Sagefiles\nwhere we call the `sg.GenerateMakefiles` method.\n\n```golang\nfunc main() {\n\tsg.GenerateMakefiles(\n\t\tsg.Makefile{\n\t\t\tPath:          sg.FromGitRoot(\"Makefile\"),\n\t\t\tDefaultTarget: All,\n\t\t},\n\t)\n}\n```\n\nIf another makefile is desired, lets say one that only includes Terraform\ntargets, we utilize the `sg.Namespace` type and just add another `Makefile` to\nthe `GenerateMakefiles` method and specify the namespace, path and default\ntarget.\n\n```golang\n\nfunc init() {\n\tsg.GenerateMakefiles(\n\t\tsg.Makefile{\n\t\t\tPath:          sg.FromGitRoot(\"Makefile\"),\n\t\t\tDefaultTarget: All,\n\t\t},\n\t\tsg.Makefile{\n\t\t\tPath:      sg.FromGitRoot(\"terraform/Makefile\"),\n\t\t\tNamespace: Terraform{},\n\t\t},\n\t)\n}\n\ntype Terraform sg.Namespace\n\nfunc (Terraform) TerraformInitDev() {\n\tsg.SerialDeps(\n\t\tTerraform.DevConfig,\n\t\tTerraform.Init,\n\t)\n}\n```\n\nIt is also possible to embed a Namespace in order to add metadata to it and\npotentially reuse it for different Makefiles, the supported fields for an\nembedded Namespace are exported String, Int \u0026 Boolean.\n\n```golang\n\nfunc main() {\n\tsg.GenerateMakefiles(\n\t\tsg.Makefile{\n\t\t\tPath:          sg.FromGitRoot(\"Makefile\"),\n\t\t\tDefaultTarget: All,\n\t\t},\n\t\tsg.Makefile{\n\t\t\tPath:          sg.FromGitRoot(\"names/name1/Makefile\"),\n\t\t\tNamespace:     MyNamespace{Name: \"name1\"},\n\t\t},\n\t\tsg.Makefile{\n\t\t\tPath:          sg.FromGitRoot(\"names/name2/Makefile\"),\n\t\t\tNamespace:     MyNamespace{Name: \"name2\"},\n        },\n\t)\n}\n\n\ntype MyNamespace struct {\n\tsg.Namespace\n\tName string\n}\n\nfunc (n MyNamespace) PrintName(ctx context.Context) error {\n\tfmt.Println(n.Name)\n}\n```\n\nNOTE: The `sg.GenerateMakefiles` function is evaluated when the sage binary is\nbuilt so doing something like this\n\n```golang\nsg.Makefile{\n\tPath:          sg.FromGitRoot(\"names/name1/Makefile\"),\n\tNamespace:     MyNamespace{Name: os.Getenv(\"Name\")},\n},\n```\n\nwill cause whatever value the environment variable `Name` has at the time to be\nhardcoded in the built sage binary.\n\n#### Custom Make target names\n\nBy default, Sage converts Go function names from PascalCase to kebab-case for\nMake targets (e.g. `BuildImage` becomes `build-image`). This conversion treats\nevery letter-to-number boundary as a word break, so `BuildImageV2` becomes\n`build-image-v-2`.\n\nTo override the generated target name, add a `//sage:target` annotation in the\nfunction's doc comment:\n\n```golang\n//sage:target build-image-v2\nfunc BuildImageV2(ctx context.Context) error {\n\t// ...\n}\n```\n\nThis generates:\n\n```makefile\n.PHONY: build-image-v2\nbuild-image-v2: $(sagefile)\n\t@$(sagefile) BuildImageV2\n```\n\nTarget names must consist of lowercase letters, digits, hyphens, underscores,\nand dots, and must not start with a dot or hyphen.\n\n#### Dependencies\n\nDependencies can be defined just by specificing the function, or with `sg.Fn` if\nthe function takes arguments. `Deps` runs in parallel while `SerialDeps` runs\nserially.\n\n```golang\nsg.Deps(\n\tsg.Fn(ConvcoCheck, \"origin/main..HEAD\"),\n\tGolangciLint,\n)\nsg.SerialDeps(\n\tGoModTidy,\n\tGitVerifyNoDiff,\n)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feinride%2Fsage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feinride%2Fsage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feinride%2Fsage/lists"}