1
0
mirror of https://github.com/drone/drone-cli.git synced 2024-11-23 09:21:56 +01:00

added inject and matrix packages

This commit is contained in:
Brad Rydzewski 2015-03-12 23:35:34 -07:00
parent 3c15f21133
commit 211c90ed4a
4 changed files with 160 additions and 48 deletions

54
compiler/inject/inject.go Normal file

@ -0,0 +1,54 @@
package inject
import (
"sort"
"strings"
"github.com/drone/drone-cli/common"
"gopkg.in/yaml.v2"
)
// Inject injects a map of parameters into a raw string and returns
// the resulting string.
//
// Parameters are represented in the string using $$ notation, similar
// to how environment variables are defined in Makefiles.
func Inject(raw string, params map[string]string) string {
if params == nil {
return raw
}
keys := []string{}
for k := range params {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(sort.StringSlice(keys)))
injected := raw
for _, k := range keys {
v := params[k]
injected = strings.Replace(injected, "$$"+k, v, -1)
}
return injected
}
// InjectSafe attempts to safely inject parameters without leaking
// parameters in the Build or Compose section of the yaml file.
//
// The intended use case for this function are public pull requests.
// We want to avoid a malicious pull request that allows someone
// to inject and print private variables.
func InjectSafe(raw string, params map[string]string) string {
before, _ := parse(raw)
after, _ := parse(Inject(raw, params))
before.Notify = after.Notify
before.Publish = after.Publish
before.Deploy = after.Deploy
result, _ := yaml.Marshal(before)
return string(result)
}
// helper funtion to parse a yaml configuration file.
func parse(raw string) (*common.Config, error) {
cfg := common.Config{}
err := yaml.Unmarshal([]byte(raw), &cfg)
return &cfg, err
}

@ -0,0 +1,67 @@
package inject
import (
"testing"
"github.com/franela/goblin"
)
func Test_Inject(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Inject params", func() {
g.It("Should replace vars with $$", func() {
s := "echo $$FOO $BAR"
m := map[string]string{}
m["FOO"] = "BAZ"
g.Assert("echo BAZ $BAR").Equal(Inject(s, m))
})
g.It("Should not replace vars with single $", func() {
s := "echo $FOO $BAR"
m := map[string]string{}
m["FOO"] = "BAZ"
g.Assert(s).Equal(Inject(s, m))
})
g.It("Should not replace vars in nil map", func() {
s := "echo $$FOO $BAR"
g.Assert(s).Equal(Inject(s, nil))
})
})
}
func Test_InjectSafe(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Safely Inject params", func() {
m := map[string]string{}
m["TOKEN"] = "FOO"
m["SECRET"] = "BAR"
c, _ := parse(InjectSafe(yml, m))
g.It("Should replace vars in notify section", func() {
g.Assert(c.Deploy["digital_ocean"].Config["token"]).Equal("FOO")
g.Assert(c.Deploy["digital_ocean"].Config["secret"]).Equal("BAR")
})
g.It("Should not replace vars in script section", func() {
g.Assert(c.Build.Config["commands"].([]interface{})[0]).Equal("echo $$TOKEN")
g.Assert(c.Build.Config["commands"].([]interface{})[1]).Equal("echo $$SECRET")
})
})
}
var yml = `
build:
image: foo
commands:
- echo $$TOKEN
- echo $$SECRET
deploy:
digital_ocean:
token: $$TOKEN
secret: $$SECRET
`

@ -1,4 +1,4 @@
package matrix
package parser
import (
"strings"
@ -18,9 +18,8 @@ type Matrix map[string][]string
// from the build matrix.
type Axis map[string]string
// String returns a string representation of an
// Axis as a comma-separated list of environment
// variables.
// String returns a string representation of an Axis as
// a comma-separated list of environment variables.
func (a Axis) String() string {
var envs []string
for k, v := range a {
@ -31,12 +30,8 @@ func (a Axis) String() string {
// Parse parses the Matrix section of the yaml file and
// returns a list of axis.
//
// Note that this method will cap the result set to
// avoid caclulating permutations on too large of a
// dataset and consuming too many system resources.
func Parse(raw string) ([]Axis, error) {
matrix, err := parse(raw)
matrix, err := parseMatrix(raw)
if err != nil {
return nil, err
}
@ -47,6 +42,14 @@ func Parse(raw string) ([]Axis, error) {
return nil, nil
}
return Calc(matrix), nil
}
// Calc calculates the permutations for th build matrix.
//
// Note that this method will cap the number of permutations
// to 25 to prevent an overly expensive calculation.
func Calc(matrix Matrix) []Axis {
// calculate number of permutations and
// extract the list of tags
// (ie go_version, redis_version, etc)
@ -75,7 +78,7 @@ func Parse(raw string) ([]Axis, error) {
elem := p / decr % len(elems)
axis[tag] = elems[elem]
// enforce a maximum number of rows
// enforce a maximum number of tags
// in the build matrix.
if i > limitTags {
break
@ -92,12 +95,12 @@ func Parse(raw string) ([]Axis, error) {
}
}
return axisList, nil
return axisList
}
// helper function to parse the Matrix data from
// the raw yaml file.
func parse(raw string) (Matrix, error) {
func parseMatrix(raw string) (Matrix, error) {
data := struct {
Matrix map[string][]string
}{}

@ -1,45 +1,33 @@
package matrix
package parser
import (
"testing"
"github.com/franela/goblin"
)
func Test_Parse(t *testing.T) {
axis, err := Parse(matrix)
if err != nil {
t.Error(err)
return
}
func Test_Matrix(t *testing.T) {
if len(axis) != 24 {
t.Errorf("Expected 24 axis, got %d", len(axis))
}
g := goblin.Goblin(t)
g.Describe("Calculate matrix", func() {
unique := map[string]bool{}
for _, a := range axis {
unique[a.String()] = true
}
m := map[string][]string{}
m["go_version"] = []string{"go1", "go1.2"}
m["python_version"] = []string{"3.2", "3.3"}
m["django_version"] = []string{"1.7", "1.7.1", "1.7.2"}
m["redis_version"] = []string{"2.6", "2.8"}
axis := Calc(m)
if len(unique) != 24 {
t.Errorf("Expected 24 unique permutations in matrix, got %d", len(unique))
}
g.It("Should calculate permutations", func() {
g.Assert(len(axis)).Equal(24)
})
g.It("Should not duplicate permutations", func() {
set := map[string]bool{}
for _, perm := range axis {
set[perm.String()] = true
}
g.Assert(len(set)).Equal(24)
})
})
}
var matrix = `
build:
image: $$python_version $$redis_version $$django_version $$go_version
matrix:
python_version:
- 3.2
- 3.3
redis_version:
- 2.6
- 2.8
django_version:
- 1.7
- 1.7.1
- 1.7.2
go_version:
- go1
- go1.2
`