Skip to content

Commit

Permalink
Merge pull request #405 from CircleCI-Public/pipeline-parameters
Browse files Browse the repository at this point in the history
Send fabricated pipeline values when validating config
  • Loading branch information
aengelberg committed May 7, 2020
2 parents e661c13 + 2c55c39 commit 4372c84
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 20 deletions.
10 changes: 7 additions & 3 deletions api/api.go
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"

"github.com/CircleCI-Public/circleci-cli/client"
"github.com/CircleCI-Public/circleci-cli/pipeline"
"github.com/CircleCI-Public/circleci-cli/references"
"github.com/Masterminds/semver"
"github.com/go-yaml/yaml"
Expand Down Expand Up @@ -380,7 +381,7 @@ func WhoamiQuery(cl *client.Client) (*WhoamiResponse, error) {
}

// ConfigQuery calls the GQL API to validate and process config
func ConfigQuery(cl *client.Client, configPath string) (*ConfigResponse, error) {
func ConfigQuery(cl *client.Client, configPath string, pipelineValues pipeline.Values) (*ConfigResponse, error) {
var response BuildConfigResponse

config, err := loadYaml(configPath)
Expand All @@ -389,8 +390,8 @@ func ConfigQuery(cl *client.Client, configPath string) (*ConfigResponse, error)
}

query := `
query ValidateConfig ($config: String!) {
buildConfig(configYaml: $config) {
query ValidateConfig ($config: String!, $pipelineValues: [StringKeyVal!]) {
buildConfig(configYaml: $config, pipelineValues: $pipelineValues) {
valid,
errors { message },
sourceYaml,
Expand All @@ -400,6 +401,9 @@ func ConfigQuery(cl *client.Client, configPath string) (*ConfigResponse, error)

request := client.NewRequest(query)
request.Var("config", config)
if pipelineValues != nil {
request.Var("pipelineValues", pipeline.PrepareForGraphQL(pipelineValues))
}
request.SetToken(cl.Token)

err = cl.Run(request, &response)
Expand Down
5 changes: 3 additions & 2 deletions cmd/config.go
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/CircleCI-Public/circleci-cli/client"
"github.com/CircleCI-Public/circleci-cli/filetree"
"github.com/CircleCI-Public/circleci-cli/local"
"github.com/CircleCI-Public/circleci-cli/pipeline"
"github.com/CircleCI-Public/circleci-cli/proxy"
"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/go-yaml/yaml"
Expand Down Expand Up @@ -124,7 +125,7 @@ func validateConfig(opts configOptions) error {
path = opts.args[0]
}

_, err := api.ConfigQuery(opts.cl, path)
_, err := api.ConfigQuery(opts.cl, path, pipeline.FabricatedValues())

if err != nil {
return err
Expand All @@ -140,7 +141,7 @@ func validateConfig(opts configOptions) error {
}

func processConfig(opts configOptions) error {
response, err := api.ConfigQuery(opts.cl, opts.args[0])
response, err := api.ConfigQuery(opts.cl, opts.args[0], nil)

if err != nil {
return err
Expand Down
39 changes: 25 additions & 14 deletions cmd/config_test.go
Expand Up @@ -14,6 +14,26 @@ import (
"gotest.tools/golden"
)

func predictValidateConfigRequest() string {
return `
{
"query": "\n\t\tquery ValidateConfig ($config: String!, $pipelineValues: [StringKeyVal!]) {\n\t\t\tbuildConfig(configYaml: $config, pipelineValues: $pipelineValues) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some config",
"pipelineValues": [
{"key": "git.base_revision", "val": "0123456789abcdef0123456789abcdef0123"},
{"key": "git.branch", "val": "test_git_branch"},
{"key": "git.revision", "val": "0123456789abcdef0123456789abcdef0123"},
{"key": "git.tag", "val": "test_git_tag"},
{"key": "id", "val": "00000000-0000-0000-0000-000000000001"},
{"key": "number", "val": "1"},
{"key": "project.git_url", "val": "https://test.vcs/test/test"},
{"key": "project.type", "val": "vcs_type"}
]
}
}`
}

var _ = Describe("Config", func() {
Describe("with an api and config.yml", func() {
var tempSettings *clitest.TempSettings
Expand Down Expand Up @@ -57,12 +77,7 @@ var _ = Describe("Config", func() {
}
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateConfig ($config: String!) {\n\t\t\tbuildConfig(configYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some config"
}
}`
expectedRequestJson := predictValidateConfigRequest()

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Expand Down Expand Up @@ -90,12 +105,8 @@ var _ = Describe("Config", func() {
}
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateConfig ($config: String!) {\n\t\t\tbuildConfig(configYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some config"
}
}`

expectedRequestJson := predictValidateConfigRequest()

tempSettings.AppendPostHandler(token, clitest.MockRequestResponse{
Status: http.StatusOK,
Expand Down Expand Up @@ -144,7 +155,7 @@ var _ = Describe("Config", func() {
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateConfig ($config: String!) {\n\t\t\tbuildConfig(configYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateConfig ($config: String!, $pipelineValues: [StringKeyVal!]) {\n\t\t\tbuildConfig(configYaml: $config, pipelineValues: $pipelineValues) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some config"
}
Expand Down Expand Up @@ -178,7 +189,7 @@ var _ = Describe("Config", func() {
}`

expectedRequestJson := ` {
"query": "\n\t\tquery ValidateConfig ($config: String!) {\n\t\t\tbuildConfig(configYaml: $config) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"query": "\n\t\tquery ValidateConfig ($config: String!, $pipelineValues: [StringKeyVal!]) {\n\t\t\tbuildConfig(configYaml: $config, pipelineValues: $pipelineValues) {\n\t\t\t\tvalid,\n\t\t\t\terrors { message },\n\t\t\t\tsourceYaml,\n\t\t\t\toutputYaml\n\t\t\t}\n\t\t}",
"variables": {
"config": "some config"
}
Expand Down
2 changes: 1 addition & 1 deletion local/local.go
Expand Up @@ -42,7 +42,7 @@ func Execute(flags *pflag.FlagSet, cfg *settings.Config) error {

processedArgs, configPath := buildAgentArguments(flags)
cl := client.NewClient(cfg.Host, cfg.Endpoint, cfg.Token, cfg.Debug)
configResponse, err := api.ConfigQuery(cl, configPath)
configResponse, err := api.ConfigQuery(cl, configPath, nil)

if err != nil {
return err
Expand Down
45 changes: 45 additions & 0 deletions pipeline/pipeline.go
@@ -0,0 +1,45 @@
package pipeline

import "sort"

// CircleCI provides various `<< pipeline.x >>` values to be used in your config, but sometimes we need to fabricate those values when validating config.
type Values map[string]string


func FabricatedValues() Values {
return map[string]string{
"id": "00000000-0000-0000-0000-000000000001",
"number": "1",
// TODO: Could these be grabbed from git?
"project.git_url": "https://test.vcs/test/test",
"project.type": "vcs_type",
"git.tag": "test_git_tag",
"git.branch": "test_git_branch",
"git.revision": "0123456789abcdef0123456789abcdef0123",
"git.base_revision": "0123456789abcdef0123456789abcdef0123",
}
}

// TODO: type Parameters map[string]string

// KeyVal is a data structure specifically for passing pipeline data to GraphQL which doesn't support free-form maps.
type KeyVal struct {
Key string `json:"key"`
Val string `json:"val"`
}

// PrepareForGraphQL takes a golang homogenous map, and transforms it into a list of keyval pairs, since GraphQL does not support homogenous maps.
func PrepareForGraphQL(kvMap Values) []KeyVal {
// we need to create the slice of KeyVals in a deterministic order for testing purposes
keys := make([]string, 0, len(kvMap))
for k := range kvMap {
keys = append(keys, k)
}
sort.Strings(keys)

kvs := make([]KeyVal, 0, len(kvMap))
for _, k := range keys {
kvs = append(kvs, KeyVal{Key: k, Val: kvMap[k]})
}
return kvs
}

0 comments on commit 4372c84

Please sign in to comment.