Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: zsh completions and formatting target lists with gotemplates #488

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
support setting target list format
kraashen committed Nov 24, 2023
commit 9149fac8fb1eff51caff160f31f67d5eb80a8d7d
2 changes: 1 addition & 1 deletion completions/mage.zsh
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ _arguments -C \
'-init[create a starting template if no mage files exist]' \
'-h[show help]' \
'-l[list mage targets in this directory]' \
'-list-parsable[when used in conjunction with -l, will list targets in a machine-parseable format]' \
'-format[when used in conjunction with -l, will list targets in a specified golang template format (available vars: .Name, .Synopsis)]' \
'-d[directory to read magefiles from (default "." or "magefiles" if exists)]:magepath:_path_files -/' \
'-debug[turn on debug messages]' \
'-f[force recreation of compiled magefile]' \
1 change: 1 addition & 0 deletions mage/import_test.go
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ func TestMageImportsList(t *testing.T) {
Stdout: stdout,
Stderr: stderr,
List: true,
Keep: true,
}

code := Invoke(inv)
7 changes: 6 additions & 1 deletion mage/main.go
Original file line number Diff line number Diff line change
@@ -107,6 +107,7 @@ type Invocation struct {
Force bool // forces recreation of the compiled binary
Verbose bool // tells the magefile to print out log statements
List bool // tells the magefile to print out a list of targets
Format string // tells the magefile to print out a list of targets in a specific format
Help bool // tells the magefile to print out help for a specific target
Keep bool // tells mage to keep the generated main file after compiling
Timeout time.Duration // tells mage to set a timeout to running the targets
@@ -217,6 +218,7 @@ func Parse(stderr, stdout io.Writer, args []string) (inv Invocation, cmd Command
// commands below

fs.BoolVar(&inv.List, "l", false, "list mage targets in this directory")
fs.StringVar(&inv.Format, "format", "", "format template to use when listing targets")
var showVersion bool
fs.BoolVar(&showVersion, "version", false, "show version info for the mage binary")
var mageInit bool
@@ -252,7 +254,7 @@ Options:
-debug turn on debug messages
-f force recreation of compiled magefile
-goarch sets the GOARCH for the binary created by -compile (default: current arch)
-gocmd <string>
-gocmd <string>
use the given go binary to compile the output (default: "go")
-goos sets the GOOS for the binary created by -compile (default: current OS)
-ldflags sets the ldflags for the binary created by -compile (default: "")
@@ -745,6 +747,9 @@ func RunCompiled(inv Invocation, exePath string, errlog *log.Logger) int {
if inv.List {
c.Env = append(c.Env, "MAGEFILE_LIST=1")
}
if inv.Format != "" {
c.Env = append(c.Env, fmt.Sprintf("MAGEFILE_FORMAT=%s", inv.Format))
}
if inv.Help {
c.Env = append(c.Env, "MAGEFILE_HELP=1")
}
64 changes: 44 additions & 20 deletions mage/main_test.go
Original file line number Diff line number Diff line change
@@ -579,33 +579,57 @@ func TestVerboseFalseEnv(t *testing.T) {
}

func TestList(t *testing.T) {
stdout := &bytes.Buffer{}
inv := Invocation{
Dir: "./testdata/list",
Stdout: stdout,
Stderr: ioutil.Discard,
List: true,
}

code := Invoke(inv)
if code != 0 {
t.Errorf("expected to exit with code 0, but got %v", code)
}
actual := stdout.String()
expected := `
testCases := []struct {
name string
inv Invocation
expected string
}{
{
name: "test default listing",
inv: Invocation{
Dir: "./testdata/list",
Stdout: &bytes.Buffer{},
Stderr: io.Discard,
List: true,
},
expected: `
This is a comment on the package which should get turned into output with the list of targets.

Targets:
somePig* This is the synopsis for SomePig.
testVerbose

* default target
`[1:]

if actual != expected {
t.Logf("expected: %q", expected)
t.Logf(" actual: %q", actual)
t.Fatalf("expected:\n%v\n\ngot:\n%v", expected, actual)
`[1:],
},
{
name: "test listing with formatting",
inv: Invocation{
Dir: "./testdata/list",
Stdout: &bytes.Buffer{},
Stderr: io.Discard,
List: true,
Format: "{{.Name}}|{{.Synopsis}}",
},
expected: `
somePig*|This is the synopsis for SomePig.
testVerbose|
`[1:],
},
}

for _, tc := range testCases {
tc := tc
code := Invoke(tc.inv)
if code != 0 {
t.Errorf("expected to exit with code 0, but got %v", code)
}
actual := tc.inv.Stdout.(*bytes.Buffer).String()
if actual != tc.expected {
t.Logf("expected: %q", tc.expected)
t.Logf(" actual: %q", actual)
t.Fatalf("expected:\n%v\n\ngot:\n%v", tc.expected, actual)
}
}
}

62 changes: 57 additions & 5 deletions mage/template.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ var mageMainfileTplString = `//go:build ignore
package main

import (
"bytes"
"context"
_flag "flag"
_fmt "fmt"
@@ -23,6 +24,7 @@ import (
"syscall"
_tabwriter "text/tabwriter"
"time"
_template "text/template"
{{range .Imports}}{{.UniqueName}} "{{.Path}}"
{{end}}
)
@@ -32,6 +34,7 @@ func main() {
type arguments struct {
Verbose bool // print out log statements
List bool // print out a list of targets
Format string // print out a list of targets in a golang template string specified format
Help bool // print out help for a specific target
Timeout time.Duration // set a timeout to running the targets
Args []string // args contain the non-flag command-line arguments
@@ -62,22 +65,25 @@ func main() {
}
return d
}

args := arguments{}
fs := _flag.FlagSet{}
fs.SetOutput(os.Stdout)

// default flag set with ExitOnError and auto generated PrintDefaults should be sufficient
fs.BoolVar(&args.Verbose, "v", parseBool("MAGEFILE_VERBOSE"), "show verbose output when running targets")
fs.BoolVar(&args.List, "l", parseBool("MAGEFILE_LIST"), "list targets for this binary")
fs.StringVar(&args.Format, "format", os.Getenv("MAGEFILE_FORMAT"), "when used with -l, list targets in a golang template string specified format")
fs.BoolVar(&args.Help, "h", parseBool("MAGEFILE_HELP"), "print out help for a specific target")
fs.DurationVar(&args.Timeout, "t", parseDuration("MAGEFILE_TIMEOUT"), "timeout in duration parsable format (e.g. 5m30s)")
fs.Usage = func() {
_fmt.Fprintf(os.Stdout, ` + "`" + `
%s [options] [target]

Commands:
-l list targets in this binary
-h show this help
-l list targets in this binary
-format when used with -l, list targets in a golang template string specified format
-h show this help

Options:
-h show description of a target
@@ -223,9 +229,47 @@ Options:
}
}

list := func() error {
{{with .Description}}_fmt.Println(` + "`{{.}}\n`" + `)
format := func(tmplStr string) error {
if tmplStr == "" {
return _fmt.Errorf("template string is empty")
}

type Targets struct {
Name string
Synopsis string
}

{{- $default := .DefaultFunc}}
targets := []Targets{
{{- range .Funcs}}
{Name: "{{lowerFirst .TargetName}}{{if and (eq .Name $default.Name) (eq .Receiver $default.Receiver)}}*{{end}}", Synopsis: {{printf "%q" .Synopsis}}},
{{- end}}
{{- range .Imports}}{{$imp := .}}
{{- range .Info.Funcs}}
{Name: "{{lowerFirst .TargetName}}{{if and (eq .Name $default.Name) (eq .Receiver $default.Receiver)}}*{{end}}", Synopsis: {{printf "%q" .Synopsis}}},
{{- end}}
{{- end}}
}

_sort.Slice(targets, func(i, j int) bool {
return targets[i].Name < targets[j].Name
})

tmpl := _template.Must(_template.New("format").Parse(tmplStr))

for _, t := range targets {
var buf bytes.Buffer
if err := tmpl.Execute(&buf, t); err != nil {
return err
}
_fmt.Println(buf.String())
}

return nil
}

list := func() error {
{{with .Description}}_fmt.Println(` + "`{{.}}\n`" + `){{- end}}
{{- $default := .DefaultFunc}}
targets := map[string]string{
{{- range .Funcs}}
@@ -255,6 +299,7 @@ Options:
_fmt.Println("\n* default target")
}
{{- end}}

return err
}

@@ -352,7 +397,14 @@ Options:
_log.SetOutput(_ioutil.Discard)
}
logger := _log.New(os.Stderr, "", 0)
if args.List {

if args.List && args.Format != "" {
if err := format(args.Format); err != nil {
_log.Println(err)
os.Exit(1)
}
return
} else if args.List {
if err := list(); err != nil {
_log.Println(err)
os.Exit(1)