From ad1751ac653eae66d4f6f4f007bee41f257825c9 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 11 Oct 2020 22:33:53 +0900 Subject: [PATCH 1/3] feat: add fields command.command_file and env.value_file BREAKING CHNAGE: change the data structure of command.env * AS IS: map[string]string * TO BE: []map[string]interface{} --- pkg/config/command.go | 37 ++++++++++------------------ pkg/config/config.go | 2 +- pkg/config/task.go | 4 +++ pkg/config/template.go | 10 ++++++++ pkg/controller/controller.go | 47 ++++++++++++++++++++++++++++++++++++ pkg/template/template.go | 27 ++++++++++++++++++--- 6 files changed, 98 insertions(+), 29 deletions(-) diff --git a/pkg/config/command.go b/pkg/config/command.go index 2e3aa30..82e6132 100644 --- a/pkg/config/command.go +++ b/pkg/config/command.go @@ -1,12 +1,15 @@ package config -import "github.com/suzuki-shunsuke/buildflow/pkg/template" +import ( + "github.com/suzuki-shunsuke/buildflow/pkg/template" +) type Command struct { - Shell string - ShellOpts []string `yaml:"shell_options"` - Command Template - Env Envs + Shell string + ShellOpts []string `yaml:"shell_options"` + Command Template + CommandFile string `yaml:"command_file"` + Env Envs } func (cmd Command) SetDefault() Command { @@ -25,30 +28,16 @@ type Envs struct { } type EnvVar struct { - Key template.Template - Value template.Template + Key template.Template + Value template.Template + ValueFile string `yaml:"value_file"` } func (envs *Envs) UnmarshalYAML(unmarshal func(interface{}) error) error { - m := map[string]string{} + m := []EnvVar{} if err := unmarshal(&m); err != nil { return err } - arr := make([]EnvVar, 0, len(m)) - for k, v := range m { - key, err := template.Compile(k) - if err != nil { - return err - } - val, err := template.Compile(v) - if err != nil { - return err - } - arr = append(arr, EnvVar{ - Key: key, - Value: val, - }) - } - envs.Vars = arr + envs.Vars = m return nil } diff --git a/pkg/config/config.go b/pkg/config/config.go index 6a9c3da..681602a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -105,7 +105,7 @@ for task in Tasks { } for j, task := range phase.Tasks { - if task.Command.Command.Text != "" { + if task.Command.Command.Text != "" || task.Command.CommandFile != "" { task.Command = task.Command.SetDefault() } task.When.SetDefaultBool(true) diff --git a/pkg/config/task.go b/pkg/config/task.go index 6ea13c9..8dce201 100644 --- a/pkg/config/task.go +++ b/pkg/config/task.go @@ -54,6 +54,10 @@ func (task *Task) SetType() error { task.Type = constant.Command return nil } + if task.Command.CommandFile != "" { + task.Type = constant.Command + return nil + } if task.ReadFile.Path.Text != "" { task.Type = constant.ReadFile return nil diff --git a/pkg/config/template.go b/pkg/config/template.go index 083e3d9..422e803 100644 --- a/pkg/config/template.go +++ b/pkg/config/template.go @@ -28,3 +28,13 @@ func (tmpl Template) New(params interface{}) (Template, error) { Template: tmpl.Template, }, err } + +func (tmpl *Template) SetText(text string) error { + t, err := template.Compile(text) + if err != nil { + return err + } + tmpl.Text = text + tmpl.Template = t + return nil +} diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index caa3a8b..abdd982 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log" + "path/filepath" "sync" "github.com/google/go-github/v32/github" @@ -14,6 +15,7 @@ import ( "github.com/suzuki-shunsuke/buildflow/pkg/domain" "github.com/suzuki-shunsuke/buildflow/pkg/execute" gh "github.com/suzuki-shunsuke/buildflow/pkg/github" + "github.com/suzuki-shunsuke/buildflow/pkg/template" "github.com/suzuki-shunsuke/go-dataeq/dataeq" ) @@ -369,6 +371,47 @@ func (ctrl Controller) runPhase(ctx context.Context, params Params, idx int, wd var ErrBuildFail = errors.New("build failed") +func (ctrl Controller) ReadExternalFiles(ctx context.Context, wd string) error { //nolint:gocognit + for i, phase := range ctrl.Config.Phases { + for j, task := range phase.Tasks { + if task.Command.CommandFile != "" { + p := task.Command.CommandFile + if !filepath.IsAbs(p) { + p = filepath.Join(wd, p) + } + result, err := ctrl.FileReader.Read(p) + if err != nil { + return err + } + if err := task.Command.Command.SetText(result.Text); err != nil { + return err + } + } + for k, v := range task.Command.Env.Vars { + if v.ValueFile != "" { + p := v.ValueFile + if !filepath.IsAbs(p) { + p = filepath.Join(wd, p) + } + result, err := ctrl.FileReader.Read(p) + if err != nil { + return err + } + tpl, err := template.Compile(result.Text) + if err != nil { + return err + } + v.Value = tpl + } + task.Command.Env.Vars[k] = v + } + phase.Tasks[j] = task + } + ctrl.Config.Phases[i] = phase + } + return nil +} + func (ctrl Controller) Run(ctx context.Context, wd string) error { pr, err := ctrl.getPR(ctx) if err != nil { @@ -382,6 +425,10 @@ func (ctrl Controller) Run(ctx context.Context, wd string) error { }).Debug("pull request") } + if err := ctrl.ReadExternalFiles(ctx, wd); err != nil { + return err + } + params, err := ctrl.getParams(ctx, pr) if err != nil { return err diff --git a/pkg/template/template.go b/pkg/template/template.go index 4881317..ae85f05 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -9,7 +9,8 @@ import ( ) type Template struct { - tpl *template.Template + Template *template.Template + Text string } func Compile(tpl string) (Template, error) { @@ -18,17 +19,35 @@ func Compile(tpl string) (Template, error) { return Template{}, err } return Template{ - tpl: tmpl, + Template: tmpl, + Text: tpl, }, nil } +func (tpl Template) GetRaw() string { + return tpl.Text +} + func (tpl Template) Render(params interface{}) (string, error) { - if tpl.tpl == nil { + if tpl.Template == nil { return "", nil } buf := &bytes.Buffer{} - if err := tpl.tpl.Execute(buf, params); err != nil { + if err := tpl.Template.Execute(buf, params); err != nil { return "", err } return buf.String(), nil } + +func (tpl *Template) UnmarshalYAML(unmarshal func(interface{}) error) error { + var src string + if err := unmarshal(&src); err != nil { + return err + } + t, err := Compile(src) + if err != nil { + return err + } + *tpl = t + return nil +} From 14f2c0ac74a25d2920a185c8a81a5792ab5abfd1 Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 11 Oct 2020 22:40:53 +0900 Subject: [PATCH 2/3] docs: update README --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fe4f09..b5f3aec 100644 --- a/README.md +++ b/README.md @@ -403,7 +403,12 @@ phases: # environment variables # In the environment variable name and value text/template can be used env: - token: "{{ .Task.Name }}" + - key: token + value: "{{ .Task.Name }}" + - key: foo + # read the environment variable from a file + # the content is parsed with text/template + value_file: foo.txt # The condition whether the task is run. # The default is true. # The value should be true or false or a tengo script. @@ -452,6 +457,11 @@ phases: # The template of the file content. template: | {{ .Task.Name }} + - name: zoo + command: + # read a command template from a file + # The content is parsed with text/template + command_file: zoo.txt condition: # When the skip is true, the phase is skipped. # The value should be true or false or a tengo script. From 18aa218cca30bf0aea81f2c618dde77f6a5b0ece Mon Sep 17 00:00:00 2001 From: Shunsuke Suzuki Date: Sun, 11 Oct 2020 22:52:28 +0900 Subject: [PATCH 3/3] feat: add a field write_file.template_file --- README.md | 12 ++++++++++-- pkg/config/task.go | 11 ++++------- pkg/controller/controller.go | 13 +++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b5f3aec..4748f11 100644 --- a/README.md +++ b/README.md @@ -457,11 +457,19 @@ phases: # The template of the file content. template: | {{ .Task.Name }} - - name: zoo + - name: yoo command: # read a command template from a file # The content is parsed with text/template - command_file: zoo.txt + command_file: yoo.txt + - name: write_file external file + write_file: + # The file path to be written + # If the path is the relative path, this is treated as the relative path from the directory where the configuration file exists. + path: foo.txt + # The template file. + # The content is parsed with text/template + template_file: zoo.txt condition: # When the skip is true, the phase is skipped. # The value should be true or false or a tengo script. diff --git a/pkg/config/task.go b/pkg/config/task.go index 8dce201..fb8bcf3 100644 --- a/pkg/config/task.go +++ b/pkg/config/task.go @@ -27,8 +27,9 @@ type Task struct { } type WriteFile struct { - Path Template - Template Template + Path Template + Template Template + TemplateFile string `yaml:"template_file"` } func (task *Task) Set() error { @@ -50,11 +51,7 @@ func (task *Task) Set() error { } func (task *Task) SetType() error { - if task.Command.Command.Text != "" { - task.Type = constant.Command - return nil - } - if task.Command.CommandFile != "" { + if task.Command.Command.Text != "" || task.Command.CommandFile != "" { task.Type = constant.Command return nil } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index abdd982..22928d8 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -405,6 +405,19 @@ func (ctrl Controller) ReadExternalFiles(ctx context.Context, wd string) error { } task.Command.Env.Vars[k] = v } + if task.WriteFile.TemplateFile != "" { + p := task.WriteFile.TemplateFile + if !filepath.IsAbs(p) { + p = filepath.Join(wd, p) + } + result, err := ctrl.FileReader.Read(p) + if err != nil { + return err + } + if err := task.WriteFile.Template.SetText(result.Text); err != nil { + return err + } + } phase.Tasks[j] = task } ctrl.Config.Phases[i] = phase