diff --git a/README.md b/README.md index 8fe4f09..4748f11 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,19 @@ phases: # The template of the file content. template: | {{ .Task.Name }} + - name: yoo + command: + # read a command template from a file + # The content is parsed with text/template + 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/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..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,7 +51,7 @@ func (task *Task) Set() error { } func (task *Task) SetType() error { - if task.Command.Command.Text != "" { + if task.Command.Command.Text != "" || task.Command.CommandFile != "" { task.Type = constant.Command 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..22928d8 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,60 @@ 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 + } + 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 + } + return nil +} + func (ctrl Controller) Run(ctx context.Context, wd string) error { pr, err := ctrl.getPR(ctx) if err != nil { @@ -382,6 +438,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 +}