diff --git a/cmd/config.go b/cmd/config.go index 7216e6b25..b293ff787 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -26,6 +26,7 @@ type configOptions struct { // Path to the config.yml file to operate on. // Used to for compatibility with `circleci config validate --path` var configPath string +var ignoreDeprecatedImages bool // should we ignore deprecated images warning var configAnnotations = map[string]string{ "": "The path to your config (use \"-\" for STDIN)", @@ -71,6 +72,7 @@ func newConfigCommand(config *settings.Config) *cobra.Command { } validateCommand.Annotations[""] = configAnnotations[""] validateCommand.PersistentFlags().StringVarP(&configPath, "config", "c", ".circleci/config.yml", "path to config file") + validateCommand.PersistentFlags().BoolVar(&ignoreDeprecatedImages, "ignore-deprecated-images", false, "ignores the deprecated images error") if err := validateCommand.PersistentFlags().MarkHidden("config"); err != nil { panic(err) } @@ -132,11 +134,21 @@ func validateConfig(opts configOptions, flags *pflag.FlagSet) error { orgSlug, _ := flags.GetString("org-slug") - _, err := api.ConfigQuery(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) + response, err := api.ConfigQuery(opts.cl, path, orgSlug, nil, pipeline.LocalPipelineValues()) if err != nil { return err } + // check if a deprecated Linux VM image is being used + // link here to blog post when available + // returns an error if a deprecated image is used + if !ignoreDeprecatedImages { + err := deprecatedImageCheck(response) + if err != nil { + return err + } + } + if path == "-" { fmt.Printf("Config input is valid.\n") } else { diff --git a/cmd/deprecated-images.go b/cmd/deprecated-images.go new file mode 100644 index 000000000..607edfc88 --- /dev/null +++ b/cmd/deprecated-images.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "github.com/CircleCI-Public/circleci-cli/api" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +// CircleCI Linux VM images that will be permanently removed on May 31st. +var deprecatedImages = []string{ + "circleci/classic:201710-01", + "circleci/classic:201703-01", + "circleci/classic:201707-01", + "circleci/classic:201708-01", + "circleci/classic:201709-01", + "circleci/classic:201710-02", + "circleci/classic:201711-01", + "circleci/classic", + "circleci/classic:latest", + "circleci/classic:edge", + "circleci/classic:201808-01", + "ubuntu-1604:201903-01", + "ubuntu-1604:202004-01", + "ubuntu-1604:202007-01", + "ubuntu-1604:202010-01", + "ubuntu-1604:202101-01", + "ubuntu-1604:202104-01", +} + +// Simplified Config -> job Structure for an image + +type job struct { + Machine interface{} `yaml:"machine"` +} + +// Simplified Config Structure for an image +type processedConfig struct { + Jobs map[string]job `yaml:"jobs"` +} + +// Processes the config down to v2.0, then checks image used against the block list +func deprecatedImageCheck(response *api.ConfigResponse) error { + + aConfig := processedConfig{} + err := yaml.Unmarshal([]byte(response.OutputYaml), &aConfig) + if err != nil { + return err + } + + // check each job + for key := range aConfig.Jobs { + + switch aConfig.Jobs[key].Machine.(type) { + case bool, nil: + // using machine true + continue + } + + image := aConfig.Jobs[key].Machine.(map[string]interface{})["image"] + + // using the `docker`/`xcode` executors + if image == nil { + continue + } + + for _, v := range deprecatedImages { + if image.(string) == v { + return errors.New("The config is using a deprecated Linux VM image (" + v + "). Please see https://circleci.com/blog/ubuntu-14-16-image-deprecation/. This error can be ignored by using the '--ignore-deprecated-images' flag.") + } + } + } + + return nil +}