Skip to content

Commit

Permalink
[Circle-11767] config expand command (#27)
Browse files Browse the repository at this point in the history
* Adding most basic version of expand.

* Moving expand under the config command.

* Setting the token when calling expand config.

* Adding config file to expand config.

* Breaking out query and load for expand command.

* Using load and query in validate.

* Using the same response type for validate and expand.

* Inlining variables map initialization.

* Pulling out error processing for config commands.

* Fixing linting errors.

* Cleaning up config_test

* Actually testing the graphql request is correct.

* Testing gql errors.

* Testing console output.

* Using persistent flags to pass the config path to validate and expand.

* Adding tests around validate.

* Documenting verify json handler.

* Updating with PR feedback.

* Dep ensure
  • Loading branch information
bradylill authored Jun 21, 2018
1 parent 4f2b4a7 commit e96f9cb
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ TODO
circleci-cli
coverage.txt
dist/
.vscode
2 changes: 1 addition & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

132 changes: 98 additions & 34 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"bytes"
"context"
"fmt"
"io/ioutil"

"github.com/pkg/errors"
Expand All @@ -28,68 +27,133 @@ var validateCommand = &cobra.Command{
RunE: validateConfig,
}

var expandCommand = &cobra.Command{
Use: "expand",
Short: "Expand the config.",
RunE: expandConfig,
}

func init() {
validateCommand.Flags().StringVarP(&configPath, "path", "p", ".circleci/config.yml", "path to build config")
configCmd.PersistentFlags().StringVarP(&configPath, "path", "p", ".circleci/config.yml", "path to build config")
configCmd.AddCommand(validateCommand)
configCmd.AddCommand(expandCommand)
}

func validateConfig(cmd *cobra.Command, args []string) error {
// Define a structure that matches the result of the GQL
// query, so that we can use mapstructure to convert from
// nested maps to a strongly typed struct.
type buildConfigResponse struct {
BuildConfig struct {
Valid bool
SourceYaml string
OutputYaml string

Errors []struct {
Message string
}
}
}

func queryAPI(query string, variables map[string]string, response interface{}) error {
ctx := context.Background()

// Define a structure that matches the result of the GQL
// query, so that we can use mapstructure to convert from
// nested maps to a strongly typed struct.
type validateResult struct {
BuildConfig struct {
Valid bool
SourceYaml string
Errors []struct {
Message string
}
}
request := graphql.NewRequest(query)
request.Header.Set("Authorization", viper.GetString("token"))
for varName, varValue := range variables {
request.Var(varName, varValue)
}

client := graphql.NewClient(viper.GetString("endpoint"))

return client.Run(ctx, request, response)
}

func loadYaml(path string) (string, error) {
config, err := ioutil.ReadFile(path)

if err != nil {
return "", errors.Wrapf(err, "Could not load config file at %s", path)
}

return string(config), nil
}

func (response buildConfigResponse) processErrors() error {
var buffer bytes.Buffer

buffer.WriteString("\n")
for i := range response.BuildConfig.Errors {
buffer.WriteString("-- ")
buffer.WriteString(response.BuildConfig.Errors[i].Message)
buffer.WriteString(",\n")
}

request := graphql.NewRequest(`
return errors.New(buffer.String())
}

func validateConfig(cmd *cobra.Command, args []string) error {
query := `
query ValidateConfig ($config: String!) {
buildConfig(configYaml: $config) {
valid,
errors { message },
sourceYaml
}
}`)

config, err := ioutil.ReadFile(configPath)
}`

config, err := loadYaml(configPath)
if err != nil {
return errors.Wrapf(err, "Could not load config file at %s", configPath)
return err
}

request.Var("config", string(config))
variables := map[string]string{
"config": config,
}

client := graphql.NewClient(viper.GetString("endpoint"))
var response buildConfigResponse
err = queryAPI(query, variables, &response)
if err != nil {
return errors.New("Unable to validate config")
}

var result validateResult
if !response.BuildConfig.Valid {
return response.processErrors()
}

err = client.Run(ctx, request, &result)
Logger.Infoln("Config is valid")
return nil
}

func expandConfig(cmd *cobra.Command, args []string) error {
query := `
query ExpandConfig($config: String!) {
buildConfig(configYaml: $config) {
outputYaml
valid
errors { message }
}
}
`

config, err := loadYaml(configPath)
if err != nil {
return errors.Wrap(err, "GraphQL query failed")
return err
}

if !result.BuildConfig.Valid {

var buffer bytes.Buffer
variables := map[string]string{
"config": config,
}

for i := range result.BuildConfig.Errors {
buffer.WriteString(result.BuildConfig.Errors[i].Message)
buffer.WriteString("\n")
}
var response buildConfigResponse
err = queryAPI(query, variables, &response)
if err != nil {
return errors.New("Unable to expand config")
}

return fmt.Errorf("config file is invalid:\n%s", buffer.String())
if !response.BuildConfig.Valid {
return response.processErrors()
}

fmt.Println("Config is valid")
Logger.Info(response.BuildConfig.OutputYaml)
return nil

}
Loading

0 comments on commit e96f9cb

Please sign in to comment.