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

Added support for per target CLI flags/params #238

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 11 additions & 3 deletions mage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func Parse(stderr, stdout io.Writer, args []string) (inv Invocation, cmd Command

fs.Usage = func() {
fmt.Fprint(stdout, `
mage [options] [target]
mage [options] [target] [<flags>]

Mage is a make-like command runner. See https://magefile.org for full docs.

Expand All @@ -207,19 +207,27 @@ Commands:
-version show version info for the mage binary

Options:
-d <string>
-d <string>
run magefiles in the given directory (default ".")
-debug turn on debug messages
-h show description of a target
-f force recreation of compiled magefile
-keep keep intermediate mage files around after running
-gocmd <string>
use the given go binary to compile the output (default: "go")
use the given go binary to compile the output (default: "go")
-goos sets the GOOS for the binary created by -compile (default: current OS)
-goarch sets the GOARCH for the binary created by -compile (default: current arch)
-t <string>
timeout in duration parsable format (e.g. 5m30s)
-v show verbose output when running mage targets

Flags (per target, optional), supported formats:
-- stop processing any further flags
-f -flag --f --flag
boolean flags only
-f=value -flag=value --f=value --flag=value
boolean, integer, string flags
note: file paths passed as values has to be wrapped in quotes
`[1:])
}
err = fs.Parse(args)
Expand Down
46 changes: 41 additions & 5 deletions mage/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import (
"sort"
"strconv"
"strings"
"regexp"
"text/tabwriter"
"time"
{{range .Imports}}{{.UniqueName}} "{{.Path}}"
{{end}}
)

func main() {

// Use local types and functions in order to avoid name conflicts with additional magefiles.
type arguments struct {
Verbose bool // print out log statements
Expand All @@ -38,7 +40,7 @@ func main() {
val := os.Getenv(env)
if val == "" {
return false
}
}
b, err := strconv.ParseBool(val)
if err != nil {
log.Printf("warning: environment variable %s is not a valid bool value: %v", env, val)
Expand All @@ -51,7 +53,7 @@ func main() {
val := os.Getenv(env)
if val == "" {
return 0
}
}
d, err := time.ParseDuration(val)
if err != nil {
log.Printf("warning: environment variable %s is not a valid duration value: %v", env, val)
Expand Down Expand Up @@ -92,7 +94,7 @@ Options:
fs.Usage()
return
}

list := func() error {
{{with .Description}}fmt.Println(` + "`{{.}}\n`" + `)
{{- end}}
Expand Down Expand Up @@ -214,11 +216,45 @@ Options:
}

var unknown []string
var prevTarget string
var filteredTargets = []string{}
var perTargetFlagsMap = make(map[string][]string)
var argLow string
re := regexp.MustCompile(` + "`" + `(^-{1,2}\w+\=\S*)|(^-{1,2}\w+$)` + "`" + `)
for _, arg := range args.Args {
if !targets[strings.ToLower(arg)] {
unknown = append(unknown, arg)
// don't process any more arguments if you detect '--' chars
if arg == "--" {
break
}
// convert alias to full target name
switch strings.ToLower(arg) {
{{range $alias, $func := .Aliases}}
case "{{lower $alias}}":
arg = "{{$func.TargetName}}"
{{- end}}
}
argLow = strings.ToLower(arg)
if !targets[argLow] {
//-----------------------------------------------------------------------------
// Add to unknown targets only if arg is not matching above regex pattern e.g:
// -- : stop processing any more arguments
// -f --f -flag --flag : boolean flags only
// -f=value --f=value -flag=value --flag=value : boolean, integer, string flags
//-----------------------------------------------------------------------------
if re.FindString(arg) == "" {
unknown = append(unknown, arg)
}
perTargetFlagsMap[prevTarget] = append(perTargetFlagsMap[prevTarget], arg)
} else {
// target is always first, then args follows
prevTarget = argLow
filteredTargets = append(filteredTargets, argLow)
}
}

// all ok, save filtered targets names
args.Args = filteredTargets

if len(unknown) == 1 {
logger.Println("Unknown target specified:", unknown[0])
os.Exit(2)
Expand Down
10 changes: 10 additions & 0 deletions mg/runtime.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mg

import (
"context"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -73,3 +74,12 @@ func CacheDir() string {

// Namespace allows for the grouping of similar commands
type Namespace struct{}

// GetFlagsFromContext - Helper function
// for extracting flags from context of a target
func GetFlagsFromContext(ctx context.Context) []string {
if ctx != nil && ctx.Value("flags") != nil {
return ctx.Value("flags").([]string)
}
return []string{}
}
11 changes: 8 additions & 3 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func EnableDebug() {
debug.SetOutput(os.Stderr)
}

// PkgInfo contains inforamtion about a package of files according to mage's
// PkgInfo contains information about a package of files according to mage's
// parsing rules.
type PkgInfo struct {
AstPkg *ast.Package
Expand Down Expand Up @@ -81,29 +81,34 @@ func (f Function) TargetName() string {
// runTarget requires.
func (f Function) ExecCode() (string, error) {
name := f.Name
cliName := name
if f.Receiver != "" {
name = f.Receiver + "{}." + name
cliName = f.Receiver + ":" + cliName
}
if f.Package != "" {
name = f.Package + "." + name
}
cliName = strings.ToLower(cliName)

if f.IsContext && f.IsError {
out := `
wrapFn := func(ctx context.Context) error {
ctx = context.WithValue(ctx, "flags", perTargetFlagsMap["%s"])
return %s(ctx)
}
err := runTarget(wrapFn)`[1:]
return fmt.Sprintf(out, name), nil
return fmt.Sprintf(out, cliName, name), nil
}
if f.IsContext && !f.IsError {
out := `
wrapFn := func(ctx context.Context) error {
ctx = context.WithValue(ctx, "flags", perTargetFlagsMap["%s"])
%s(ctx)
return nil
}
err := runTarget(wrapFn)`[1:]
return fmt.Sprintf(out, name), nil
return fmt.Sprintf(out, cliName, name), nil
}
if !f.IsContext && f.IsError {
out := `
Expand Down