diff --git a/README.md b/README.md index 07faf19..d0f30d2 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Placeholder | Usage ---|--- `{{ .Title }}` | Like `## Plan result` `{{ .Message }}` | A string that can be set from CLI with `--message` option +`{{ .Action }}` | Using in terraform plan, and matched leading message by parsing like `Terraform will perform the following actions:` `{{ .Result }}` | Matched result by parsing like `Plan: 1 to add` or `No changes` `{{ .Body }}` | The entire of Terraform execution result `{{ .Link }}` | The link of the build page on CI diff --git a/terraform/parser.go b/terraform/parser.go index 39ea6e4..54ce369 100644 --- a/terraform/parser.go +++ b/terraform/parser.go @@ -14,6 +14,7 @@ type Parser interface { // ParseResult represents the result of parsed terraform execution type ParseResult struct { Result string + Action string ExitCode int Error error } @@ -30,8 +31,9 @@ type FmtParser struct { // PlanParser is a parser for terraform plan type PlanParser struct { - Pass *regexp.Regexp - Fail *regexp.Regexp + Pass *regexp.Regexp + Action *regexp.Regexp + Fail *regexp.Regexp } // ApplyParser is a parser for terraform apply @@ -55,8 +57,9 @@ func NewFmtParser() *FmtParser { // NewPlanParser is PlanParser initialized with its Regexp func NewPlanParser() *PlanParser { return &PlanParser{ - Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`), - Fail: regexp.MustCompile(`(?m)^(Error: )`), + Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`), + Action: regexp.MustCompile(`(?m)^(An execution plan has been generated)`), + Fail: regexp.MustCompile(`(?m)^(Error: )`), } } @@ -103,9 +106,13 @@ func (p *PlanParser) Parse(body string) ParseResult { } } lines := strings.Split(body, "\n") - var i int - var result, line string + var i, actionStartIdx int + var result, action, line string for i, line = range lines { + if p.Action.MatchString(line) { + // action starts with the line: An execution plan... + actionStartIdx = i + } if p.Pass.MatchString(line) || p.Fail.MatchString(line) { break } @@ -113,11 +120,16 @@ func (p *PlanParser) Parse(body string) ParseResult { switch { case p.Pass.MatchString(line): result = lines[i] + if actionStartIdx != 0 { + // action ends with the line above summary + action = strings.Join(trimLastNewline(lines[actionStartIdx:i]), "\n") + } case p.Fail.MatchString(line): result = strings.Join(trimLastNewline(lines[i:]), "\n") } return ParseResult{ Result: result, + Action: action, ExitCode: exitCode, Error: nil, } diff --git a/terraform/parser_test.go b/terraform/parser_test.go index be341d4..7934c62 100644 --- a/terraform/parser_test.go +++ b/terraform/parser_test.go @@ -251,7 +251,21 @@ func TestPlanParserParse(t *testing.T) { name: "plan ok pattern", body: planSuccessResult, result: ParseResult{ - Result: "Plan: 1 to add, 0 to change, 0 to destroy.", + Result: "Plan: 1 to add, 0 to change, 0 to destroy.", + Action: `An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + google_compute_global_address.my_another_project + id: + address: + ip_version: "IPV4" + name: "my-another-project" + project: "my-project" + self_link: +`, ExitCode: 0, Error: nil, }, diff --git a/terraform/template.go b/terraform/template.go index ca92f51..b7106f0 100644 --- a/terraform/template.go +++ b/terraform/template.go @@ -111,6 +111,7 @@ type FmtTemplate struct { // PlanTemplate is a default template for terraform plan type PlanTemplate struct { Template string + Action string CommonTemplate } @@ -182,7 +183,7 @@ func (t *DefaultTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform fmt into tepmlate +// Execute binds the execution result of terraform fmt into template func (t *FmtTemplate) Execute() (resp string, err error) { tpl, err := template.New("fmt").Parse(t.Template) if err != nil { @@ -202,7 +203,7 @@ func (t *FmtTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform plan into tepmlate +// Execute binds the execution result of terraform plan into template func (t *PlanTemplate) Execute() (resp string, err error) { tpl, err := template.New("plan").Parse(t.Template) if err != nil { @@ -212,6 +213,7 @@ func (t *PlanTemplate) Execute() (resp string, err error) { if err := tpl.Execute(&b, map[string]interface{}{ "Title": t.Title, "Message": t.Message, + "Action": t.Action, "Result": t.Result, "Body": t.Body, "Link": t.Link, @@ -222,7 +224,7 @@ func (t *PlanTemplate) Execute() (resp string, err error) { return resp, err } -// Execute binds the execution result of terraform apply into tepmlate +// Execute binds the execution result of terraform apply into template func (t *ApplyTemplate) Execute() (resp string, err error) { tpl, err := template.New("apply").Parse(t.Template) if err != nil {