Skip to content

Commit

Permalink
[CIRCLE-12608] Add validateConfigVersion to newLocalExecute command (#76
Browse files Browse the repository at this point in the history
)

Notify users that version 2.1+ are not available to build locally
when they try to run a build with a config other than 2.0 or 2

This PR uses a flagset to independently add config value

Flag parsing is disabled in the newLocalExecuteCommand
in order to pass 'args' through. This commit works around
that disabled flag paring by adding [config] 'filename'
to the list of flags separately, making it possible
to access from the validateConfigVersion function
  • Loading branch information
hannahhenderson authored Aug 27, 2018
1 parent 255077c commit eb016c8
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 3 deletions.
67 changes: 64 additions & 3 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import (
"syscall"

"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
)

// These options are purely here to retain a mock of the structure of the flags used by `build`.
Expand All @@ -32,6 +35,10 @@ type proxyBuildArgs struct {
envVarArgs []string
}

func addConfigFlag(filename *string, flagset *pflag.FlagSet) {
flagset.StringVarP(filename, "config", "c", defaultConfigPath, "config file")
}

func newLocalExecuteCommand() *cobra.Command {
buildCommand := &cobra.Command{
Use: "execute",
Expand All @@ -40,10 +47,12 @@ func newLocalExecuteCommand() *cobra.Command {
DisableFlagParsing: true,
}

// Used as a convenience work-around when DisableFlagParsing is enabled
// Allows help command to access the combined rollup of flags
opts := proxyBuildArgs{}
flags := buildCommand.Flags()

flags.StringVarP(&opts.configFilename, "config", "c", defaultConfigPath, "config file")
addConfigFlag(&opts.configFilename, flags)
flags.StringVar(&opts.taskInfo.Job, "job", "build", "job to be executed")
flags.IntVar(&opts.taskInfo.NodeTotal, "node-total", 1, "total number of parallel nodes")
flags.IntVar(&opts.taskInfo.NodeIndex, "index", 0, "node index of parallelism")
Expand Down Expand Up @@ -117,7 +126,7 @@ func loadCurrentBuildAgentSha() string {
settingsJSON, err := ioutil.ReadFile(buildAgentSettingsPath())

if err != nil {
Logger.Error("Faild to load build agent settings JSON", err)
Logger.Error("Failed to load build agent settings JSON", err)
return ""
}

Expand All @@ -126,7 +135,7 @@ func loadCurrentBuildAgentSha() string {
err = json.Unmarshal(settingsJSON, &settings)

if err != nil {
Logger.Error("Faild to parse build agent settings JSON", err)
Logger.Error("Failed to parse build agent settings JSON", err)
return ""
}

Expand Down Expand Up @@ -154,7 +163,59 @@ func picardImage() (string, error) {
return fmt.Sprintf("%s@%s", picardRepo, sha), nil
}

func configVersion(configBytes []byte) (string, bool) {
var raw map[string]interface{}
if err := yaml.Unmarshal(configBytes, &raw); err != nil {
return "", false
}

var configWithVersion struct {
Version string `yaml:"version"`
}
if err := mapstructure.WeakDecode(raw, &configWithVersion); err != nil {
return "", false
}
return configWithVersion.Version, true
}

func validateConfigVersion(args []string) error {
invalidConfigError := `
You attempted to run a local build with version '%s' of configuration.
Local builds do not support that version at this time.
You can use 'circleci config process' to pre-process your config into a version that local builds can run (see 'circleci help config process' for more information)`
configFlags := pflag.NewFlagSet("configFlags", pflag.ContinueOnError)
configFlags.ParseErrorsWhitelist.UnknownFlags = true
var filename string
addConfigFlag(&filename, configFlags)

err := configFlags.Parse(args)
if err != nil {
return errors.New("Unable to parse flags")
}

// nolint: gosec
configBytes, err := ioutil.ReadFile(filename)
if err != nil {
return errors.Wrapf(err, "Unable to read config file")
}

version, isVersion := configVersion(configBytes)
if !isVersion || version == "" {
return errors.New("Unable to identify config version")
}

if version != "2.0" && version != "2" {
return fmt.Errorf(invalidConfigError, version)
}

return nil
}

func runExecute(cmd *cobra.Command, args []string) error {
err := validateConfigVersion(args)
if err != nil {
return err
}

pwd, err := os.Getwd()

Expand Down
30 changes: 30 additions & 0 deletions cmd/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,34 @@ var _ = Describe("build", func() {
})

})

Describe("config version is tested", func() {
It("passes for valid versions", func() {
err := validateConfigVersion([]string{"--config", "testdata/config-versions/version-2.yml"})
Expect(err).ToNot(HaveOccurred())

err = validateConfigVersion([]string{"--config", "testdata/config-versions/version-2-0.yml"})
Expect(err).ToNot(HaveOccurred())
})

It("passes when other flags are used, too", func() {
err := validateConfigVersion([]string{"--config", "testdata/config-versions/version-2.yml", "--job", "foobar"})
Expect(err).ToNot(HaveOccurred())
})

It("fails when version number is not '2' or '2.0'", func() {
err := validateConfigVersion([]string{"--config", "testdata/config-versions/version-2-1.yml"})
Expect(err).To(HaveOccurred())
})

It("fails when version number is not specified", func() {
err := validateConfigVersion([]string{"--config", "testdata/config-versions/version-empty.yml"})
Expect(err).To(HaveOccurred())
})

It("fails when version is not defined", func() {
err := validateConfigVersion([]string{"--config", "testdata/config-versions/version-none.yml"})
Expect(err).To(HaveOccurred())
})
})
})
1 change: 1 addition & 0 deletions cmd/testdata/config-versions/version-2-0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version: 2.0
1 change: 1 addition & 0 deletions cmd/testdata/config-versions/version-2-1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version: 2.1
1 change: 1 addition & 0 deletions cmd/testdata/config-versions/version-2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version: 2
1 change: 1 addition & 0 deletions cmd/testdata/config-versions/version-empty.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version:
Empty file.

0 comments on commit eb016c8

Please sign in to comment.