Skip to content

Commit

Permalink
feat: support env vars
Browse files Browse the repository at this point in the history
Signed-off-by: Bird <aflybird0@gmail.com>
  • Loading branch information
aFlyBird0 committed Nov 30, 2022
1 parent 1ae449b commit db20e1e
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 33 deletions.
3 changes: 2 additions & 1 deletion internal/pkg/configmanager/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ func renderConfigWithVariables(fileContent string, variables map[string]interfac

str, err := template.New().
FromContent(fileContent).
AddProcessor(template.AddDotForVariablesInConfigProcessor()).
AddDotForVariablesInConfigProcessor().
AddQuoteForVariablesInConfigProcessor().
SetDefaultRender(fileContent, variables).
Render()

Expand Down
41 changes: 37 additions & 4 deletions internal/pkg/configmanager/variables_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package configmanager

import (
"fmt"
"os"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("renderConfigWithVariables", func() {
var vars = map[string]interface{}{
"foo1": "bar1",
"foo2": "bar2",
}
When("render a config with variables", func() {
It("should works fine", func() {
var vars = map[string]interface{}{
"foo1": "bar1",
"foo2": "bar2",
}
result1, err1 := renderConfigWithVariables("[[ foo1 ]]/[[ foo2]]", vars)
result2, err2 := renderConfigWithVariables("a[[ foo1 ]]/[[ foo2]]b", vars)
result3, err3 := renderConfigWithVariables(" [[ foo1 ]] [[ foo2]] ", vars)
Expand All @@ -23,4 +26,34 @@ var _ = Describe("renderConfigWithVariables", func() {
Expect(result3).To(Equal([]byte(" bar1 bar2 ")))
})
})

When("there are env variables", func() {
const (
envKey1, envKey2 = "GITHUB_TOKEN", "GITLAB_TOKEN"
envVal1, envVal2 = "123456", "abcdef"
)
BeforeEach(func() {
DeferCleanup(os.Setenv, envKey1, os.Getenv(envKey1))
DeferCleanup(os.Setenv, envKey2, os.Getenv(envKey2))
})

It("should works fine", func() {
err := os.Setenv(envKey1, envVal1)
Expect(err).NotTo(HaveOccurred())
err = os.Setenv(envKey2, envVal2)
Expect(err).NotTo(HaveOccurred())

content := fmt.Sprintf(`
githubToken: [[env %s]]
gitlabToken: [[ env '%s']]
`, envKey1, envKey2)
expected := fmt.Sprintf(`
githubToken: %s
gitlabToken: %s
`, envVal1, envVal2)
result, err := renderConfigWithVariables(content, nil)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal([]byte(expected)))
})
})
})
30 changes: 25 additions & 5 deletions pkg/util/template/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import "regexp"
// this is because our variables' syntax is [[ varName ]]
// while Go's template is [[ .varName ]]
func addDotForVariablesInConfig(s string) string {
// regex := `\[\[\s*(.*)\s*\]\]`
// r := regexp.MustCompile(regex)
// return r.ReplaceAllString(s, "[[ .$1 ]]")
regex := `\[\[\s*`
// add dot if there is only one word in [[ ]]
regex := `\[\[\s*(\w+)\s*\]\]`
r := regexp.MustCompile(regex)
return r.ReplaceAllString(s, "[[ .")
return r.ReplaceAllString(s, "[[ .$1 ]]")
}

func AddDotForVariablesInConfigProcessor() Processor {
Expand All @@ -19,6 +17,28 @@ func AddDotForVariablesInConfigProcessor() Processor {
}
}

// When [[ ]] has two words and the first word don't contain quota, add quotes to the second word
// e.g. [[ env GITHUB_TOKEN]] -> [[ env "GITHUB_TOKEN" ]]
// [[ env 'GITHUB_TOKEN' ]] -> do nothing
// [[ env "GITHUB_TOKEN" ]] -> do nothing
// [[ "env" "GITHUB_TOKEN" ]] -> do nothing
// [[ GITHUB_TOKEN ]] -> do nothing
func addQuoteForVariablesInConfig(s string) string {
regex := `\[\[\s*([^'"]\w+)\s+([^'"]\w+)\s*\]\]`
r := regexp.MustCompile(regex)
return r.ReplaceAllString(s, "[[ $1 \"$2\" ]]")
}

func AddQuoteForVariablesInConfigProcessor() Processor {
return func(bytes []byte) ([]byte, error) {
return []byte(addQuoteForVariablesInConfig(string(bytes))), nil
}
}

func (r *rendererWithGetter) AddDotForVariablesInConfigProcessor() *rendererWithGetter {
return r.AddProcessor(AddDotForVariablesInConfigProcessor())
}

func (r *rendererWithGetter) AddQuoteForVariablesInConfigProcessor() *rendererWithGetter {
return r.AddProcessor(AddQuoteForVariablesInConfigProcessor())
}
166 changes: 144 additions & 22 deletions pkg/util/template/processor_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,149 @@
package template

import (
"testing"

"github.com/stretchr/testify/assert"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestAddDotForVariablesInConfigNormal(t *testing.T) {
res := addDotForVariablesInConfig("[[varNameA]]")
assert.Equal(t, "[[ .varNameA]]", res, "Adding dot for variable names passed.")
}

func TestAddDotForVariablesInConfigWithSpaces(t *testing.T) {
res := addDotForVariablesInConfig("[[ varNameA]]")
assert.Equal(t, "[[ .varNameA]]", res, "Adding dot for variable names passed.")
}

func TestAddDotForVariablesInConfigWithTrailingSpaces(t *testing.T) {
res := addDotForVariablesInConfig("[[ varNameA ]]")
assert.Equal(t, "[[ .varNameA ]]", res, "Adding dot for variable names passed.")
}

func TestAddDotForVariablesInConfigMultipleVars(t *testing.T) {
res := addDotForVariablesInConfig("[[ varNameA ]]/[[ varNameB ]]/[[ varNameC ]]")
assert.Equal(t, "[[ .varNameA ]]/[[ .varNameB ]]/[[ .varNameC ]]", res, "Adding dot for variable names passed.")
}
var _ = Describe("addDotForVariablesInConfig", func() {
var (
origin, gotten, expected string
)

JustBeforeEach(func() {
gotten = addDotForVariablesInConfig(origin)
})

When("config is normal", func() {
BeforeEach(func() {
origin = "[[varNameA]]"
expected = "[[ .varNameA ]]"
})
It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has spaces", func() {
BeforeEach(func() {
origin = "[[ varNameA ]]"
expected = "[[ .varNameA ]]"
})

It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has trailing spaces", func() {
BeforeEach(func() {
origin = "[[ varNameA ]]"
expected = "[[ .varNameA ]]"
})

It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has multiple variables", func() {
BeforeEach(func() {
origin = "[[ varNameA ]]/[[ varNameB ]]/[[ varNameC ]]"
expected = "[[ .varNameA ]]/[[ .varNameB ]]/[[ .varNameC ]]"
})

It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("there are more than one words", func() {
BeforeEach(func() {
origin = "[[ func varNameA ]]"
expected = origin
})

It("should do nothing", func() {
Expect(gotten).To(Equal(expected))
})
})
})

var _ = Describe("addQuoteForVariablesInConfig", func() {
var (
origin, gotten, expected string
)

JustBeforeEach(func() {
gotten = addQuoteForVariablesInConfig(origin)
})
AfterEach(func() {
Expect(gotten).To(Equal(expected))
})

When("config is normal", func() {
BeforeEach(func() {
origin = `[[env GITHUB_TOKEN]]`
expected = `[[ env "GITHUB_TOKEN" ]]`
})

It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has single quote", func() {
BeforeEach(func() {
origin = `[[ env 'GITHUB_TOKEN']]`
expected = origin
})

It("should do nothing", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has quote", func() {
BeforeEach(func() {
origin = `[[ env "GITHUB_TOKEN"]]`
expected = origin
})

It("should do nothing", func() {
Expect(gotten).To(Equal(expected))
})
})

When("config has multiple variables", func() {
BeforeEach(func() {
origin = `[[ env GITHUB_TOKEN]]/[[ env "GITLAB_TOKEN"]]`
expected = `[[ env "GITHUB_TOKEN" ]]/[[ env "GITLAB_TOKEN"]]`
})

It("should succeed", func() {
Expect(gotten).To(Equal(expected))
})
})

When("the first word has quote", func() {
BeforeEach(func() {
origin = `[[ "env" GITHUB_TOKEN]]`
expected = origin
})

It("should do nothing", func() {
Expect(gotten).To(Equal(expected))
})
})

When("there is only one word", func() {
BeforeEach(func() {
origin = `[[GITHUB_TOKEN]]`
expected = origin
})

It("should do nothing", func() {
Expect(gotten).To(Equal(expected))
})
})
})
6 changes: 5 additions & 1 deletion pkg/util/template/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import (
"os"
"text/template"

"github.com/Masterminds/sprig/v3"

"github.com/devstream-io/devstream/pkg/util/log"
)

func Render(name, templateStr string, variable any, funcMaps ...template.FuncMap) (string, error) {
t := template.New(name).Option("missingkey=error").Delims("[[", "]]")

// use sprig functions such as "env"
t.Funcs(sprig.FuncMap())
for _, funcMap := range funcMaps {
t.Funcs(funcMap)
}

t, err := t.Parse(templateStr)
if err != nil {
log.Warnf("Template parse file failed: %s.", err)
log.Warnf("Template parse file failed, template: %s, err: %s.", templateStr, err)
return "", err
}

Expand Down

0 comments on commit db20e1e

Please sign in to comment.