diff --git a/common/command/build_flags.go b/common/command/build_flags.go index 477c83ef8..167e13310 100644 --- a/common/command/build_flags.go +++ b/common/command/build_flags.go @@ -2,6 +2,8 @@ package command import ( "flag" + "fmt" + "strings" ) // BuildOptionFlags sets the proper command line flags needed for @@ -9,4 +11,28 @@ import ( func BuildOptionFlags(fs *flag.FlagSet, f *BuildOptions) { fs.Var((*SliceValue)(&f.Except), "except", "build all builds except these") fs.Var((*SliceValue)(&f.Only), "only", "only build the given builds by name") + fs.Var((*userVarValue)(&f.UserVars), "var", "specify a user variable") +} + +// userVarValue is a flag.Value that parses out user variables in +// the form of 'key=value' and sets it on this map. +type userVarValue map[string]string + +func (v *userVarValue) String() string { + return "" +} + +func (v *userVarValue) Set(raw string) error { + idx := strings.Index(raw, "=") + if idx == -1 { + return fmt.Errorf("No '=' value in arg: %s", raw) + } + + if *v == nil { + *v = make(map[string]string) + } + + key, value := raw[0:idx], raw[idx+1:] + (*v)[key] = value + return nil } diff --git a/common/command/build_flags_test.go b/common/command/build_flags_test.go new file mode 100644 index 000000000..b4f9753fe --- /dev/null +++ b/common/command/build_flags_test.go @@ -0,0 +1,97 @@ +package command + +import ( + "flag" + "reflect" + "testing" +) + +func TestBuildOptionFlags(t *testing.T) { + opts := new(BuildOptions) + fs := flag.NewFlagSet("test", flag.ContinueOnError) + BuildOptionFlags(fs, opts) + + args := []string{ + "-except=foo,bar,baz", + "-only=a,b", + "-var=foo=bar", + "-var", "bar=baz", + "-var=foo=bang", + } + + err := fs.Parse(args) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := []string{"foo", "bar", "baz"} + if !reflect.DeepEqual(opts.Except, expected) { + t.Fatalf("bad: %#v", opts.Except) + } + + expected = []string{"a", "b"} + if !reflect.DeepEqual(opts.Only, expected) { + t.Fatalf("bad: %#v", opts.Only) + } + + if len(opts.UserVars) != 2 { + t.Fatalf("bad: %#v", opts.UserVars) + } + + if opts.UserVars["foo"] != "bang" { + t.Fatalf("bad: %#v", opts.UserVars) + } + + if opts.UserVars["bar"] != "baz" { + t.Fatalf("bad: %#v", opts.UserVars) + } +} + +func TestUserVarValue_implements(t *testing.T) { + var raw interface{} + raw = new(userVarValue) + if _, ok := raw.(flag.Value); !ok { + t.Fatalf("userVarValue should be a Value") + } +} + +func TestUserVarValueSet(t *testing.T) { + sv := new(userVarValue) + err := sv.Set("key=value") + if err != nil { + t.Fatalf("err: %s", err) + } + + vars := map[string]string(*sv) + if vars["key"] != "value" { + t.Fatalf("Bad: %#v", vars) + } + + // Empty value + err = sv.Set("key=") + if err != nil { + t.Fatalf("err: %s", err) + } + + vars = map[string]string(*sv) + if vars["key"] != "" { + t.Fatalf("Bad: %#v", vars) + } + + // Equal in value + err = sv.Set("key=foo=bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + vars = map[string]string(*sv) + if vars["key"] != "foo=bar" { + t.Fatalf("Bad: %#v", vars) + } + + // No equal + err = sv.Set("key") + if err == nil { + t.Fatal("should have error") + } +} diff --git a/common/command/template.go b/common/command/template.go index 90280de9f..a323523b0 100644 --- a/common/command/template.go +++ b/common/command/template.go @@ -10,8 +10,9 @@ import ( // BuildOptions is a set of options related to builds that can be set // from the command line. type BuildOptions struct { - Except []string - Only []string + UserVars map[string]string + Except []string + Only []string } // Validate validates the options