Skip to content

einride/sage

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Folders and files

NameName
Last commit message
Last commit date

Latest commit

2108476 ยท Oct 16, 2024
Feb 12, 2024
Sep 6, 2024
Mar 15, 2024
Oct 7, 2024
Mar 15, 2024
Oct 11, 2022
Sep 17, 2024
Mar 20, 2023
Oct 16, 2024
Aug 2, 2023
Feb 8, 2024
Dec 16, 2021
Mar 15, 2024
Mar 15, 2024
Sep 30, 2024
Jan 28, 2022
Jan 28, 2022
Aug 7, 2023
Jan 28, 2022

Repository files navigation

๐ŸŒฟ Sage

Sage is a Make-like build tool inspired by Mage that provides a curated and maintained set of build tools for Go projects.

Release

Requirements

Getting started

To initilize Sage in a repository, just run:

go run go.einride.tech/sage@latest init

Run make.

Two changes should now have happened. If the project had a previous Makefile it should have been renamed to Makefile.old and a new should have been created. If the project have a dependabot config, a sage config should have been added.

Usage

Sage imports, and targets within the Sagefiles, can be written to Makefiles, you can generate as many Makefiles as you want, see more at Makefiles / Sage namespaces.

Sagefiles

You can have as many Sagefiles as you want in the .sage folder.

Targets

Any public function in the main package will be exported. Functions can have no return value but error. The following arguments are supported: Optional first argument of context.Context, string, int or bool.

func All() {
  sg.Deps(
	  FormatYaml,
	  sg.Fn(ConvcoCheck, "origin/main..HEAD"),
  )
}

func FormatYaml() error {
	return sgyamlfmt.FormatYAML()
}

func ConvcoCheck(ctx context.Context, rev string) error {
	logr.FromContextOrDiscard(ctx).Info("checking...")
	return sgconvco.Command(ctx, "check", rev).Run()
}

Makefiles / Sage namespaces

To generate Makefiles, a main method needs to exist in one of the Sagefiles where we call the sg.GenerateMakefiles method.

func main() {
	sg.GenerateMakefiles(
		sg.Makefile{
			Path:          sg.FromGitRoot("Makefile"),
			DefaultTarget: All,
		},
	)
}

If another makefile is desired, lets say one that only includes Terraform targets, we utilize the sg.Namespace type and just add another Makefile to the GenerateMakefiles method and specify the namespace, path and default target.

func init() {
	sg.GenerateMakefiles(
		sg.Makefile{
			Path:          sg.FromGitRoot("Makefile"),
			DefaultTarget: All,
		},
		sg.Makefile{
			Path:      sg.FromGitRoot("terraform/Makefile"),
			Namespace: Terraform{},
		},
	)
}

type Terraform sg.Namespace

func (Terraform) TerraformInitDev() {
	sg.SerialDeps(
		Terraform.DevConfig,
		Terraform.Init,
	)
}

It is also possible to embed a Namespace in order to add metadata to it and potentially reuse it for different Makefiles, the supported fields for an embedded Namespace are exported String, Int & Boolean.

func main() {
	sg.GenerateMakefiles(
		sg.Makefile{
			Path:          sg.FromGitRoot("Makefile"),
			DefaultTarget: All,
		},
		sg.Makefile{
			Path:          sg.FromGitRoot("names/name1/Makefile"),
			Namespace:     MyNamespace{Name: "name1"},
		},
		sg.Makefile{
			Path:          sg.FromGitRoot("names/name2/Makefile"),
			Namespace:     MyNamespace{Name: "name2"},
        },
	)
}


type MyNamespace struct {
	sg.Namespace
	Name string
}

func (n MyNamespace) PrintName(ctx context.Context) error {
	fmt.Println(n.Name)
}

NOTE: The sg.GenerateMakefiles function is evaluated when the sage binary is built so doing something like this

sg.Makefile{
	Path:          sg.FromGitRoot("names/name1/Makefile"),
	Namespace:     MyNamespace{Name: os.Getenv("Name")},
},

will cause whatever value the environment variable Name has at the time to be hardcoded in the built sage binary.

Dependencies

Dependencies can be defined just by specificing the function, or with sg.Fn if the function takes arguments. Deps runs in parallel while SerialDeps runs serially.

sg.Deps(
	sg.Fn(ConvcoCheck, "origin/main..HEAD"),
	GolangciLint,
)
sg.SerialDeps(
	GoModTidy,
	GitVerifyNoDiff,
)